diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 97d22e5b..e9f998cd 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -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; }; diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index 8c816a8e..837017b1 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -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; }; diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 2f0855b6..906ea588 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -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; };