diff --git a/lang/el-compiler/src/codegen.el b/lang/el-compiler/src/codegen.el index fe1056f..1dd89ae 100644 --- a/lang/el-compiler/src/codegen.el +++ b/lang/el-compiler/src/codegen.el @@ -862,10 +862,23 @@ fn cg_expr(expr: Map) -> String { // arithmetic BinOp (or vice-versa). Without this check the // fallthrough to str_eq produces str_eq(int_value, int_value) // which reads the integer as a char* and segfaults. + // EITHER side provably Int is enough. Requiring BOTH meant a call + // whose return type codegen cannot infer poisoned the operator: + // getint(5) == a -> str_eq(getint(5), a) + // even with `a` declared Int. str_eq then reads an integer as a + // char* and segfaults. Only an integer LITERAL on one side forced + // the numeric form, so the bug was invisible in the common case. + // + // Loosening to OR is strictly safer: when one side is a known Int, + // str_eq is always wrong (it dereferences that int), while numeric + // comparison is at worst a wrong answer on an already ill-typed + // program. When neither side is Int nothing changes, so string + // comparison is untouched. if is_int_expr(left) { - if is_int_expr(right) { - return "(" + left_c + " == " + right_c + ")" - } + return "(" + left_c + " == " + right_c + ")" + } + if is_int_expr(right) { + return "(" + left_c + " == " + right_c + ")" } // Float literal or negative float literal: use plain == (bit-equal // el_val_t comparison). This handles `r0 == 3.0`, `neg == -3.0`, etc. @@ -921,10 +934,12 @@ fn cg_expr(expr: Map) -> String { } // Same mixed Ident/BinOp fix as EqEq: use is_int_expr to detect // integer-typed operands before falling through to !str_eq. + // Either side Int is enough — see the EqEq note above. if is_int_expr(left) { - if is_int_expr(right) { - return "(" + left_c + " != " + right_c + ")" - } + return "(" + left_c + " != " + right_c + ")" + } + if is_int_expr(right) { + return "(" + left_c + " != " + right_c + ")" } // Float-typed operands use plain != (bit-equal comparison). if is_float_expr(left) {