Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b5a0a729e6 | |||
| b26dd47aef | |||
| b55e6bfd53 | |||
| dbb06f6ee4 |
@@ -862,10 +862,23 @@ fn cg_expr(expr: Map<String, Any>) -> 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, Any>) -> 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) {
|
||||
@@ -1495,6 +1510,11 @@ fn cg_stmt(stmt: Map<String, Any>, indent: String, declared: [String]) -> [Strin
|
||||
if str_eq(ltype, "Int") {
|
||||
add_int_name(name)
|
||||
}
|
||||
// Same as params: Bool is an int in the value model. Without this a
|
||||
// `let ok: Bool = ...` compared to another Bool lowered to str_eq.
|
||||
if str_eq(ltype, "Bool") {
|
||||
add_int_name(name)
|
||||
}
|
||||
if str_eq(ltype, "Float") {
|
||||
add_float_name(name)
|
||||
}
|
||||
@@ -3112,6 +3132,15 @@ fn build_int_names_for_params(params: [Map<String, Any>]) -> Bool {
|
||||
if str_eq(ptype, "Int") {
|
||||
add_int_name(pname)
|
||||
}
|
||||
// Bool is an integer in the value model (type_to_c maps Bool -> "int";
|
||||
// el_runtime.h: "Bool -> el_val_t (0 = false, nonzero = true)"), but
|
||||
// Bool names were registered nowhere. So `cond == want` between two
|
||||
// Bool params fell through to str_eq and dereferenced 0 or 1 as a
|
||||
// char* — an immediate segfault. Track them as int-like, which is what
|
||||
// they are.
|
||||
if str_eq(ptype, "Bool") {
|
||||
add_int_name(pname)
|
||||
}
|
||||
if str_eq(ptype, "Float") {
|
||||
add_float_name(pname)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
fn getstr(x: String) -> String { return x }
|
||||
fn getint(x: Int) -> Int { return x }
|
||||
fn ok(label: String) -> Void { println("ok " + label) }
|
||||
fn bad(label: String) -> Void { println("FAIL " + label) }
|
||||
|
||||
let s1: String = "hello"
|
||||
let s2: String = "hello"
|
||||
let s3: String = "world"
|
||||
let i1: Int = 5
|
||||
let i2: Int = 5
|
||||
let i3: Int = 9
|
||||
|
||||
if "abc" == "abc" { ok("str literal eq") } else { bad("str literal eq") }
|
||||
if "abc" == "xyz" { bad("str literal ne") } else { ok("str literal ne") }
|
||||
if s1 == s2 { ok("str var eq") } else { bad("str var eq") }
|
||||
if s1 == s3 { bad("str var ne") } else { ok("str var ne") }
|
||||
if getstr("hi") == "hi" { ok("str call vs literal") } else { bad("str call vs literal") }
|
||||
if s1 == getstr("hello") { ok("str var vs call") } else { bad("str var vs call") }
|
||||
if s1 == getstr("nope") { bad("str var vs call ne") } else { ok("str var vs call ne") }
|
||||
if i1 == i2 { ok("int var eq") } else { bad("int var eq") }
|
||||
if i1 == i3 { bad("int var ne") } else { ok("int var ne") }
|
||||
if getint(5) == i1 { ok("int call vs var") } else { bad("int call vs var") }
|
||||
if getint(9) == i1 { bad("int call vs var ne") } else { ok("int call vs var ne") }
|
||||
if s1 != s3 { ok("str NOTEQ") } else { bad("str NOTEQ") }
|
||||
if s1 != s2 { bad("str NOTEQ same") } else { ok("str NOTEQ same") }
|
||||
if i1 != i3 { ok("int NOTEQ") } else { bad("int NOTEQ") }
|
||||
if getint(9) != i1 { ok("int call NOTEQ") } else { bad("int call NOTEQ") }
|
||||
println("done")
|
||||
Reference in New Issue
Block a user