lang: reject mismatched operand types in scalar arithmetic (issue 0055)

lowerBinaryOp derived the result type from the LHS alone and emitted
add/sub/mul/div/mod without checking the RHS, so `s64 + string` lowered
as `add : s64` and reinterpreted the string's bytes — printing garbage
instead of erroring.

Add isArithOperand (int / float / vector / pointer, plus custom int
widths) and, for `+ - * / %`, diagnose `cannot apply '<op>' to operands
of type '<lhs>' and '<rhs>'` and return a placeholder sentinel instead of
the corrupting op. `.unresolved` operands pass through so a type we
couldn't infer is never falsely rejected; the existing optional-unwrap
and int×float promotion are accounted for before the check.

Ordering (`< <= > >=`) and bitwise/shift (`& | ^ << >>`) ops share the
same LHS-derived-type hole and are left as a noted follow-up in the issue.

Regression: examples/214-arith-operand-type-check.sx (s64 + string, and
non-numeric LHS string * s64).
This commit is contained in:
agra
2026-05-30 09:56:32 +03:00
parent 8e74e4acb2
commit 6016b08712
5 changed files with 197 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
// Scalar arithmetic (`+ - * / %`) requires numeric operands. Mixing a
// non-numeric type (here `string`) is rejected at compile time. The
// result type is otherwise taken from the left operand, so without this
// check `n + s` would lower as `add : s64` and reinterpret the string's
// bytes as an integer — silently producing garbage.
#import "modules/std.sx";
main :: () -> s32 {
n : s64 = 40;
s : string = "nope";
x := n + s; // s64 + string — rejected
y := s * n; // string * s64 — rejected (non-numeric LHS too)
0;
}