land int signatures, and repair a silent miscompilation they exposed
This commit is contained in:
@@ -1517,6 +1517,16 @@ fn cg_stmt(stmt: Map<String, Any>, indent: String, declared: [String]) -> [Strin
|
||||
if str_eq(ltype, "Int") {
|
||||
add_int_name(name)
|
||||
}
|
||||
// UNANNOTATED let: take the type from what the initialiser RETURNS.
|
||||
// Without this, `let a = str_len(s)` loses the Int and a later `a + b`
|
||||
// lowers to el_str_concat on two integers -- silently, with no error,
|
||||
// producing a program that prints nothing where it should print 7.
|
||||
// Verified present in the pre-change compiler too, so this is a repair
|
||||
// rather than a regression. The return types were already needed for
|
||||
// dispatch; they were just never consulted here.
|
||||
if str_eq(ltype, "") {
|
||||
if is_int_expr(val) { 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") {
|
||||
@@ -1943,48 +1953,10 @@ fn is_duration_name(name: String) -> Bool {
|
||||
// string-concat on `+` when one side is a Call. New builtins must be added
|
||||
// here when they return Int and may participate in arithmetic.
|
||||
fn is_int_call(call_expr: Map<String, Any>) -> Bool {
|
||||
let func = call_expr["func"]
|
||||
let fk: String = func["expr"]
|
||||
if !str_eq(fk, "Ident") { return false }
|
||||
let name: String = func["name"]
|
||||
if str_eq(name, "str_len") { return true }
|
||||
if str_eq(name, "str_index_of") { return true }
|
||||
if str_eq(name, "str_to_int") { return true }
|
||||
if str_eq(name, "str_char_code") { return true }
|
||||
if str_eq(name, "str_count") { return true }
|
||||
if str_eq(name, "str_count_chars") { return true }
|
||||
if str_eq(name, "str_count_bytes") { return true }
|
||||
if str_eq(name, "str_count_lines") { return true }
|
||||
if str_eq(name, "str_count_words") { return true }
|
||||
if str_eq(name, "str_count_letters") { return true }
|
||||
if str_eq(name, "str_count_digits") { return true }
|
||||
if str_eq(name, "str_last_index_of") { return true }
|
||||
if str_eq(name, "str_find_chars") { return true }
|
||||
if str_eq(name, "native_list_len") { return true }
|
||||
if str_eq(name, "el_list_len") { return true }
|
||||
if str_eq(name, "len") { return true }
|
||||
if str_eq(name, "json_get_int") { return true }
|
||||
if str_eq(name, "json_array_len") { return true }
|
||||
if str_eq(name, "engram_node_count") { return true }
|
||||
if str_eq(name, "engram_edge_count") { return true }
|
||||
if str_eq(name, "time_now") { return true }
|
||||
if str_eq(name, "time_now_utc") { return true }
|
||||
if str_eq(name, "time_diff") { return true }
|
||||
if str_eq(name, "time_add") { return true }
|
||||
if str_eq(name, "time_from_parts") { return true }
|
||||
if str_eq(name, "el_abs") { return true }
|
||||
if str_eq(name, "el_max") { return true }
|
||||
if str_eq(name, "el_min") { return true }
|
||||
if str_eq(name, "float_to_int") { return true }
|
||||
if str_eq(name, "unix_timestamp") { return true }
|
||||
if str_eq(name, "instant_to_unix_seconds") { return true }
|
||||
if str_eq(name, "instant_to_unix_millis") { return true }
|
||||
if str_eq(name, "duration_to_seconds") { return true }
|
||||
if str_eq(name, "duration_to_millis") { return true }
|
||||
if str_eq(name, "duration_to_nanos") { return true }
|
||||
return false
|
||||
call_returns(call_expr, "Int")
|
||||
}
|
||||
|
||||
|
||||
// Known runtime builtins that return Float. Parallel to is_int_call — lets a
|
||||
// Call participate in float arithmetic (and get inferred into __float_names on
|
||||
// an unannotated `let`). New Float-returning builtins must be added here.
|
||||
|
||||
@@ -924,3 +924,22 @@ test "compiler-no-longer-emits-prohibition-errors" {
|
||||
let out: String = compile_capture(src)
|
||||
assert !str_contains(out, "boundary violation"), "the emitter does not adjudicate"
|
||||
}
|
||||
|
||||
// ── Int return types drive + dispatch ────────────────────────────────────────
|
||||
//
|
||||
// El has one type, so `a + b` must be dispatched from what the operands ARE.
|
||||
// The 35 Int-returning builtins moved to signatures.rel; the dispatch stayed,
|
||||
// because choosing between arithmetic and concatenation is emission.
|
||||
|
||||
test "int-returning-builtin-drives-arithmetic-dispatch" {
|
||||
let src: String = "fn main() { let a = str_len(\"hello\") let b = str_len(\"hi\") let c = a + b println(int_to_str(c)) }"
|
||||
let out: String = compile_capture(src)
|
||||
assert str_contains(out, "(a + b)"), "Int + Int is arithmetic"
|
||||
assert !str_contains(out, "el_str_concat(a, b)"), "and NOT concatenation"
|
||||
}
|
||||
|
||||
test "string-plus-string-still-concatenates" {
|
||||
let src: String = "fn main() { let s = \"a\" + \"b\" println(s) }"
|
||||
let out: String = compile_capture(src)
|
||||
assert str_contains(out, "el_str_concat"), "String + String still concatenates"
|
||||
}
|
||||
|
||||
@@ -31,3 +31,43 @@ el_duration_sub returns Duration
|
||||
el_duration_scale returns Duration
|
||||
el_duration_div returns Duration
|
||||
ttl_cache_age returns Duration
|
||||
|
||||
# Int-returning builtins. Previously 35 hardcoded names in is_int_call().
|
||||
# These decide whether `a + b` is arithmetic or concatenation, so the
|
||||
# compiler reads them at emission time -- dispatch, not adjudication.
|
||||
|
||||
str_len returns Int
|
||||
str_index_of returns Int
|
||||
str_to_int returns Int
|
||||
str_char_code returns Int
|
||||
str_count returns Int
|
||||
str_count_chars returns Int
|
||||
str_count_bytes returns Int
|
||||
str_count_lines returns Int
|
||||
str_count_words returns Int
|
||||
str_count_letters returns Int
|
||||
str_count_digits returns Int
|
||||
str_last_index_of returns Int
|
||||
str_find_chars returns Int
|
||||
native_list_len returns Int
|
||||
el_list_len returns Int
|
||||
len returns Int
|
||||
json_get_int returns Int
|
||||
json_array_len returns Int
|
||||
engram_node_count returns Int
|
||||
engram_edge_count returns Int
|
||||
time_now returns Int
|
||||
time_now_utc returns Int
|
||||
time_diff returns Int
|
||||
time_add returns Int
|
||||
time_from_parts returns Int
|
||||
el_abs returns Int
|
||||
el_max returns Int
|
||||
el_min returns Int
|
||||
float_to_int returns Int
|
||||
unix_timestamp returns Int
|
||||
instant_to_unix_seconds returns Int
|
||||
instant_to_unix_millis returns Int
|
||||
duration_to_seconds returns Int
|
||||
duration_to_millis returns Int
|
||||
duration_to_nanos returns Int
|
||||
|
||||
Reference in New Issue
Block a user