fix: eliminate all non-json C backend errors (79→0)

Second round of C backend fixes, building on d8871ac which reduced
errors from 286 to 111. This eliminates all 79 non-json errors:

- Fix function references as values (wrap in LuxClosure*)
- Fix fold/map/filter with type-aware calling conventions
- Add String.indexOf/lastIndexOf emission and C runtime functions
- Add File.readDir with dirent.h implementation
- Fix string concat in closure bodies
- Exclude ADT constructors from closure free variable capture
- Fix match result type inference (prioritize pattern binding types)
- Fix Option inner type inference (usage-based for List.head)
- Fix void* to struct cast (dereference through pointer)
- Handle constructors in emit_expr_with_env

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-18 05:56:21 -05:00
parent d8871acf7e
commit 2909bf14b6

View File

@@ -355,6 +355,11 @@ impl CBackend {
fn emit_expr_with_env(&mut self, expr: &Expr, captured: &HashSet<&str>) -> Result<String, CGenError> { fn emit_expr_with_env(&mut self, expr: &Expr, captured: &HashSet<&str>) -> Result<String, CGenError> {
match expr { match expr {
Expr::Var(ident) => { Expr::Var(ident) => {
// Check if this is a unit constructor (no-argument variant)
if let Some(type_name) = self.variant_to_type.get(&ident.name).cloned() {
let variant_name = &ident.name;
return Ok(format!("({}){{{}_TAG_{}}}", type_name, type_name, variant_name.to_uppercase()));
}
let escaped = self.escape_c_keyword(&ident.name); let escaped = self.escape_c_keyword(&ident.name);
if captured.contains(ident.name.as_str()) { if captured.contains(ident.name.as_str()) {
Ok(format!("env->{}", escaped)) Ok(format!("env->{}", escaped))
@@ -382,6 +387,15 @@ impl CBackend {
} }
} }
// Check for string concatenation - use lux_string_concat instead of +
if matches!(op, BinaryOp::Add) {
let left_is_string = self.infer_expr_type(left).as_deref() == Some("LuxString");
let right_is_string = self.infer_expr_type(right).as_deref() == Some("LuxString");
if left_is_string || right_is_string {
return Ok(format!("lux_string_concat({}, {})", l, r));
}
}
let op_str = match op { let op_str = match op {
BinaryOp::Add => "+", BinaryOp::Add => "+",
BinaryOp::Sub => "-", BinaryOp::Sub => "-",
@@ -418,13 +432,37 @@ impl CBackend {
Ok(format!("({} ? {} : {})", c, t, e)) Ok(format!("({} ? {} : {})", c, t, e))
} }
Expr::Call { func, args, .. } => { Expr::Call { func, args, .. } => {
match func.as_ref() {
Expr::Var(ident) if self.variant_to_type.contains_key(&ident.name) => {
// ADT constructor call inside closure
let type_name = self.variant_to_type.get(&ident.name).unwrap().clone();
let variant_name = ident.name.clone();
let variant_lower = variant_name.to_lowercase();
let variant_upper = variant_name.to_uppercase();
// Emit args with env substitution for captured variables
let arg_strs: Result<Vec<_>, _> = args.iter()
.map(|a| self.emit_expr_with_env(a, captured))
.collect();
let arg_values = arg_strs?;
if arg_values.is_empty() {
Ok(format!("({}){{{}_TAG_{}}}", type_name, type_name, variant_upper))
} else {
let field_inits: Vec<String> = arg_values.iter()
.enumerate()
.map(|(i, v)| format!(".field{} = {}", i, v))
.collect();
Ok(format!("({}){{{}_TAG_{}, .data.{} = {{{}}}}}",
type_name, type_name, variant_upper,
variant_lower, field_inits.join(", ")))
}
}
Expr::Var(ident) if self.functions.contains(&ident.name) => {
let arg_strs: Result<Vec<_>, _> = args.iter() let arg_strs: Result<Vec<_>, _> = args.iter()
.map(|a| self.emit_expr_with_env(a, captured)) .map(|a| self.emit_expr_with_env(a, captured))
.collect(); .collect();
let args_str = arg_strs?.join(", "); let args_str = arg_strs?.join(", ");
match func.as_ref() {
Expr::Var(ident) if self.functions.contains(&ident.name) => {
let c_func_name = self.mangle_name(&ident.name); let c_func_name = self.mangle_name(&ident.name);
// Pass evidence if this function uses effects // Pass evidence if this function uses effects
let is_effectful = self.effectful_functions.contains(&ident.name); let is_effectful = self.effectful_functions.contains(&ident.name);
@@ -440,6 +478,10 @@ impl CBackend {
} }
} }
_ => { _ => {
let arg_strs: Result<Vec<_>, _> = args.iter()
.map(|a| self.emit_expr_with_env(a, captured))
.collect();
let args_str = arg_strs?.join(", ");
let closure_expr = self.emit_expr_with_env(func, captured)?; let closure_expr = self.emit_expr_with_env(func, captured)?;
let param_types: Vec<&str> = args.iter().map(|_| "LuxInt").collect(); let param_types: Vec<&str> = args.iter().map(|_| "LuxInt").collect();
let params_str = if param_types.is_empty() { let params_str = if param_types.is_empty() {
@@ -747,6 +789,7 @@ impl CBackend {
self.writeln(" void (*delete_file)(void* env, LuxString path);"); self.writeln(" void (*delete_file)(void* env, LuxString path);");
self.writeln(" LuxBool (*isDir)(void* env, LuxString path);"); self.writeln(" LuxBool (*isDir)(void* env, LuxString path);");
self.writeln(" void (*mkdir)(void* env, LuxString path);"); self.writeln(" void (*mkdir)(void* env, LuxString path);");
self.writeln(" LuxList* (*readDir)(void* env, LuxString path);");
self.writeln(" void* env;"); self.writeln(" void* env;");
self.writeln("} LuxFileHandler;"); self.writeln("} LuxFileHandler;");
self.writeln(""); self.writeln("");
@@ -933,6 +976,26 @@ impl CBackend {
self.writeln(" mkdir(path, 0755);"); self.writeln(" mkdir(path, 0755);");
self.writeln("}"); self.writeln("}");
self.writeln(""); self.writeln("");
self.writeln("#include <dirent.h>");
self.writeln("// Forward declarations needed by lux_file_readDir");
self.writeln("static LuxList* lux_list_new(int64_t capacity);");
self.writeln("static void lux_list_push(LuxList* list, void* element);");
self.writeln("static LuxList* lux_file_readDir(LuxString path) {");
self.writeln(" LuxList* result = lux_list_new(16);");
self.writeln(" DIR* dir = opendir(path);");
self.writeln(" if (!dir) return result;");
self.writeln(" struct dirent* entry;");
self.writeln(" while ((entry = readdir(dir)) != NULL) {");
self.writeln(" if (entry->d_name[0] == '.' && (entry->d_name[1] == '\\0' || (entry->d_name[1] == '.' && entry->d_name[2] == '\\0'))) continue;");
self.writeln(" size_t len = strlen(entry->d_name);");
self.writeln(" LuxString name = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" memcpy(name, entry->d_name, len + 1);");
self.writeln(" lux_list_push(result, (void*)name);");
self.writeln(" }");
self.writeln(" closedir(dir);");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString default_file_read(void* env, LuxString path) {"); self.writeln("static LuxString default_file_read(void* env, LuxString path) {");
self.writeln(" (void)env;"); self.writeln(" (void)env;");
self.writeln(" return lux_file_read(path);"); self.writeln(" return lux_file_read(path);");
@@ -968,6 +1031,11 @@ impl CBackend {
self.writeln(" lux_file_mkdir(path);"); self.writeln(" lux_file_mkdir(path);");
self.writeln("}"); self.writeln("}");
self.writeln(""); self.writeln("");
self.writeln("static LuxList* default_file_readDir(void* env, LuxString path) {");
self.writeln(" (void)env;");
self.writeln(" return lux_file_readDir(path);");
self.writeln("}");
self.writeln("");
self.writeln("static LuxFileHandler default_file_handler = {"); self.writeln("static LuxFileHandler default_file_handler = {");
self.writeln(" .read = default_file_read,"); self.writeln(" .read = default_file_read,");
self.writeln(" .write = default_file_write,"); self.writeln(" .write = default_file_write,");
@@ -976,6 +1044,7 @@ impl CBackend {
self.writeln(" .delete_file = default_file_delete,"); self.writeln(" .delete_file = default_file_delete,");
self.writeln(" .isDir = default_file_isDir,"); self.writeln(" .isDir = default_file_isDir,");
self.writeln(" .mkdir = default_file_mkdir,"); self.writeln(" .mkdir = default_file_mkdir,");
self.writeln(" .readDir = default_file_readDir,");
self.writeln(" .env = NULL"); self.writeln(" .env = NULL");
self.writeln("};"); self.writeln("};");
self.writeln(""); self.writeln("");
@@ -1625,6 +1694,20 @@ impl CBackend {
self.writeln("static Option lux_option_none(void) { return (Option){Option_TAG_NONE}; }"); self.writeln("static Option lux_option_none(void) { return (Option){Option_TAG_NONE}; }");
self.writeln("static Option lux_option_some(void* value) { return (Option){Option_TAG_SOME, .data.some = {value}}; }"); self.writeln("static Option lux_option_some(void* value) { return (Option){Option_TAG_SOME, .data.some = {value}}; }");
self.writeln(""); self.writeln("");
self.writeln("// String indexOf / lastIndexOf — return Option<Int>");
self.writeln("static Option lux_string_indexOf(LuxString s, LuxString needle) {");
self.writeln(" char* pos = strstr(s, needle);");
self.writeln(" if (pos) return lux_option_some((void*)(intptr_t)(pos - s));");
self.writeln(" return lux_option_none();");
self.writeln("}");
self.writeln("static Option lux_string_lastIndexOf(LuxString s, LuxString needle) {");
self.writeln(" char* last = NULL;");
self.writeln(" char* pos = strstr(s, needle);");
self.writeln(" while (pos) { last = pos; pos = strstr(pos + 1, needle); }");
self.writeln(" if (last) return lux_option_some((void*)(intptr_t)(last - s));");
self.writeln(" return lux_option_none();");
self.writeln("}");
self.writeln("");
self.writeln("// === Boxing/Unboxing ==="); self.writeln("// === Boxing/Unboxing ===");
self.writeln("// All boxed values are RC-managed."); self.writeln("// All boxed values are RC-managed.");
self.writeln(""); self.writeln("");
@@ -2309,6 +2392,14 @@ impl CBackend {
// This is a constructor - emit struct literal // This is a constructor - emit struct literal
let variant_name = &ident.name; let variant_name = &ident.name;
Ok(format!("({}){{{}_TAG_{}}}", type_name, type_name, variant_name.to_uppercase())) Ok(format!("({}){{{}_TAG_{}}}", type_name, type_name, variant_name.to_uppercase()))
} else if self.functions.contains(&ident.name) {
// Function used as a value — wrap in a closure struct
let mangled = self.mangle_name(&ident.name);
let temp = format!("_fn_ref_{}", self.fresh_name());
self.writeln(&format!("LuxClosure* {} = lux_rc_alloc(sizeof(LuxClosure), LUX_TAG_CLOSURE);", temp));
self.writeln(&format!("{}->env = NULL;", temp));
self.writeln(&format!("{}->fn_ptr = (void*){};", temp, mangled));
Ok(temp)
} else { } else {
// Escape C reserved keywords // Escape C reserved keywords
Ok(self.escape_c_keyword(&ident.name)) Ok(self.escape_c_keyword(&ident.name))
@@ -3035,6 +3126,17 @@ impl CBackend {
} }
return Ok("NULL".to_string()); return Ok("NULL".to_string());
} }
"readDir" | "listDir" => {
let path = self.emit_expr(&args[0])?;
let temp = format!("_readdir_{}", self.fresh_name());
if self.has_evidence {
self.writeln(&format!("LuxList* {} = ev->file->readDir(ev->file->env, {});", temp, path));
} else {
self.writeln(&format!("LuxList* {} = lux_file_readDir({});", temp, path));
}
self.register_rc_var(&temp, "LuxList*");
return Ok(temp);
}
_ => {} _ => {}
} }
} }
@@ -3250,6 +3352,20 @@ impl CBackend {
self.register_rc_var(&temp, "LuxString"); self.register_rc_var(&temp, "LuxString");
return Ok(temp); return Ok(temp);
} }
"indexOf" => {
let s = self.emit_expr(&args[0])?;
let needle = self.emit_expr(&args[1])?;
let temp = format!("_indexOf_{}", self.fresh_name());
self.writeln(&format!("Option {} = lux_string_indexOf({}, {});", temp, s, needle));
return Ok(temp);
}
"lastIndexOf" => {
let s = self.emit_expr(&args[0])?;
let needle = self.emit_expr(&args[1])?;
let temp = format!("_lastIndexOf_{}", self.fresh_name());
self.writeln(&format!("Option {} = lux_string_lastIndexOf({}, {});", temp, s, needle));
return Ok(temp);
}
_ => {} _ => {}
} }
} }
@@ -3325,8 +3441,14 @@ impl CBackend {
Some("LuxInt") => format!("lux_box_int({})", val), Some("LuxInt") => format!("lux_box_int({})", val),
Some("LuxBool") => format!("lux_box_bool({})", val), Some("LuxBool") => format!("lux_box_bool({})", val),
Some("LuxString") => format!("lux_box_string({})", val), Some("LuxString") => format!("lux_box_string({})", val),
Some("LuxFloat") => format!("lux_box_float({})", val),
Some(t) if t.ends_with('*') => val.to_string(), // Already a pointer Some(t) if t.ends_with('*') => val.to_string(), // Already a pointer
_ => format!("lux_box_int({})", val), // Default to int boxing Some(t) if t != "void" => {
// Non-primitive struct type — allocate on heap and copy
let tag = self.adt_type_tags.get(t).copied().unwrap_or(100);
format!("(void*)memcpy(lux_rc_alloc(sizeof({}), {}), &({}), sizeof({}))", t, tag, val, t)
}
_ => format!("(void*)(intptr_t)({})", val), // Unknown type — cast to void*
} }
} }
@@ -3469,10 +3591,17 @@ impl CBackend {
} }
// Higher-order operations - inline loops // Higher-order operations - inline loops
// Type-aware: detects element types from the callback to use correct boxing/unboxing
"map" => { "map" => {
if args.len() != 2 { if args.len() != 2 {
return Err(CGenError { message: "List.map takes 2 arguments".to_string(), span: None }); return Err(CGenError { message: "List.map takes 2 arguments".to_string(), span: None });
} }
// Infer types BEFORE emitting (which consumes args via emit_expr)
let elem_type = self.infer_callback_param_type(&args[1], 0);
let ret_type = self.infer_callback_return_type(&args[1]);
let elem_is_prim = Self::is_primitive_c_type(&elem_type);
let ret_is_prim = Self::is_primitive_c_type(&ret_type);
let list = self.emit_expr(&args[0])?; let list = self.emit_expr(&args[0])?;
let closure = self.emit_expr(&args[1])?; let closure = self.emit_expr(&args[1])?;
let id = self.fresh_name(); let id = self.fresh_name();
@@ -3482,6 +3611,26 @@ impl CBackend {
let fn_var = format!("_fn_{}", id); let fn_var = format!("_fn_{}", id);
let mapped_var = format!("_mapped_{}", id); let mapped_var = format!("_mapped_{}", id);
// Build the call expression and result store based on types
let arg_pass = if elem_is_prim {
format!("{}({})", Self::unbox_fn_for_type(&elem_type), elem_var)
} else {
elem_var.clone()
};
let fn_cast = if elem_is_prim {
format!("({}(*)(void*, {}))", ret_type, elem_type)
} else {
format!("(void*(*)(void*, void*))")
};
let result_decl_type = if ret_is_prim { ret_type.clone() } else { "void*".to_string() };
let result_store = |mapped: &str| -> String {
if ret_is_prim {
format!("{}({})", Self::box_fn_for_type(&ret_type), mapped)
} else {
mapped.to_string()
}
};
// FBIP: Check if we can reuse the list in-place // FBIP: Check if we can reuse the list in-place
self.writeln(&format!("LuxList* {};", result_var)); self.writeln(&format!("LuxList* {};", result_var));
self.writeln(&format!("if (LUX_RC_HEADER({})->rc == 1) {{", list)); self.writeln(&format!("if (LUX_RC_HEADER({})->rc == 1) {{", list));
@@ -3489,15 +3638,16 @@ impl CBackend {
if self.debug_rc { if self.debug_rc {
self.writeln("lux_fbip_reuse_count++;"); self.writeln("lux_fbip_reuse_count++;");
} }
self.writeln(&format!("// FBIP: Reuse list in-place")); self.writeln("// FBIP: Reuse list in-place");
self.writeln(&format!("{} = {};", result_var, list)); self.writeln(&format!("{} = {};", result_var, list));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var)); self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1; self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var)); self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure)); self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("LuxInt {} = ((LuxInt(*)(void*, LuxInt)){}->fn_ptr)({}->env, lux_unbox_int({}));", mapped_var, fn_var, fn_var, elem_var)); self.writeln(&format!("{} {} = ({}{}->fn_ptr)({}->env, {});",
result_decl_type, mapped_var, fn_cast, fn_var, fn_var, arg_pass));
self.writeln(&format!("lux_decref({}); // Decref old element", elem_var)); self.writeln(&format!("lux_decref({}); // Decref old element", elem_var));
self.writeln(&format!("{}->elements[{}] = lux_box_int({});", result_var, i_var, mapped_var)); self.writeln(&format!("{}->elements[{}] = {};", result_var, i_var, result_store(&mapped_var)));
self.indent -= 1; self.indent -= 1;
self.writeln("}"); self.writeln("}");
self.indent -= 1; self.indent -= 1;
@@ -3506,21 +3656,22 @@ impl CBackend {
if self.debug_rc { if self.debug_rc {
self.writeln("lux_fbip_copy_count++;"); self.writeln("lux_fbip_copy_count++;");
} }
self.writeln(&format!("// Allocate new list")); self.writeln("// Allocate new list");
self.writeln(&format!("{} = lux_list_new({}->length);", result_var, list)); self.writeln(&format!("{} = lux_list_new({}->length);", result_var, list));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var)); self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1; self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var)); self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure)); self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("LuxInt {} = ((LuxInt(*)(void*, LuxInt)){}->fn_ptr)({}->env, lux_unbox_int({}));", mapped_var, fn_var, fn_var, elem_var)); self.writeln(&format!("{} {} = ({}{}->fn_ptr)({}->env, {});",
self.writeln(&format!("{}->elements[{}] = lux_box_int({});", result_var, i_var, mapped_var)); result_decl_type, mapped_var, fn_cast, fn_var, fn_var, arg_pass));
self.writeln(&format!("{}->elements[{}] = {};", result_var, i_var, result_store(&mapped_var)));
self.indent -= 1; self.indent -= 1;
self.writeln("}"); self.writeln("}");
self.writeln(&format!("{}->length = {}->length;", result_var, list)); self.writeln(&format!("{}->length = {}->length;", result_var, list));
self.indent -= 1; self.indent -= 1;
self.writeln("}"); self.writeln("}");
// Decref the closure if it was a temporary (inline lambda) // Decref the closure if it was a temporary (inline lambda or fn ref)
if closure.starts_with("_closure_") { if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
self.writeln(&format!("lux_decref_closure({});", closure)); self.writeln(&format!("lux_decref_closure({});", closure));
} }
@@ -3530,6 +3681,9 @@ impl CBackend {
if args.len() != 2 { if args.len() != 2 {
return Err(CGenError { message: "List.filter takes 2 arguments".to_string(), span: None }); return Err(CGenError { message: "List.filter takes 2 arguments".to_string(), span: None });
} }
let elem_type = self.infer_callback_param_type(&args[1], 0);
let elem_is_prim = Self::is_primitive_c_type(&elem_type);
let list = self.emit_expr(&args[0])?; let list = self.emit_expr(&args[0])?;
let closure = self.emit_expr(&args[1])?; let closure = self.emit_expr(&args[1])?;
let id = self.fresh_name(); let id = self.fresh_name();
@@ -3540,6 +3694,17 @@ impl CBackend {
let fn_var = format!("_fn_{}", id); let fn_var = format!("_fn_{}", id);
let keep_var = format!("_keep_{}", id); let keep_var = format!("_keep_{}", id);
let arg_pass = if elem_is_prim {
format!("{}({})", Self::unbox_fn_for_type(&elem_type), elem_var)
} else {
elem_var.clone()
};
let fn_cast = if elem_is_prim {
format!("(LuxBool(*)(void*, {}))", elem_type)
} else {
"(LuxBool(*)(void*, void*))".to_string()
};
// FBIP: Check if we can filter in-place // FBIP: Check if we can filter in-place
self.writeln(&format!("LuxList* {};", result_var)); self.writeln(&format!("LuxList* {};", result_var));
self.writeln(&format!("int64_t {} = 0;", count_var)); self.writeln(&format!("int64_t {} = 0;", count_var));
@@ -3548,13 +3713,13 @@ impl CBackend {
if self.debug_rc { if self.debug_rc {
self.writeln("lux_fbip_reuse_count++;"); self.writeln("lux_fbip_reuse_count++;");
} }
self.writeln(&format!("// FBIP: Filter in-place")); self.writeln("// FBIP: Filter in-place");
self.writeln(&format!("{} = {};", result_var, list)); self.writeln(&format!("{} = {};", result_var, list));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var)); self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1; self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var)); self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure)); self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("LuxBool {} = ((LuxBool(*)(void*, LuxInt)){}->fn_ptr)({}->env, lux_unbox_int({}));", keep_var, fn_var, fn_var, elem_var)); self.writeln(&format!("LuxBool {} = ({}{}->fn_ptr)({}->env, {});", keep_var, fn_cast, fn_var, fn_var, arg_pass));
self.writeln(&format!("if ({}) {{", keep_var)); self.writeln(&format!("if ({}) {{", keep_var));
self.indent += 1; self.indent += 1;
self.writeln(&format!("{}->elements[{}++] = {};", result_var, count_var, elem_var)); self.writeln(&format!("{}->elements[{}++] = {};", result_var, count_var, elem_var));
@@ -3573,13 +3738,13 @@ impl CBackend {
if self.debug_rc { if self.debug_rc {
self.writeln("lux_fbip_copy_count++;"); self.writeln("lux_fbip_copy_count++;");
} }
self.writeln(&format!("// Allocate new list")); self.writeln("// Allocate new list");
self.writeln(&format!("{} = lux_list_new({}->length);", result_var, list)); self.writeln(&format!("{} = lux_list_new({}->length);", result_var, list));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var)); self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1; self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var)); self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure)); self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("LuxBool {} = ((LuxBool(*)(void*, LuxInt)){}->fn_ptr)({}->env, lux_unbox_int({}));", keep_var, fn_var, fn_var, elem_var)); self.writeln(&format!("LuxBool {} = ({}{}->fn_ptr)({}->env, {});", keep_var, fn_cast, fn_var, fn_var, arg_pass));
self.writeln(&format!("if ({}) {{", keep_var)); self.writeln(&format!("if ({}) {{", keep_var));
self.indent += 1; self.indent += 1;
// Incref the element since it's now shared between lists // Incref the element since it's now shared between lists
@@ -3592,17 +3757,22 @@ impl CBackend {
self.writeln(&format!("{}->length = {};", result_var, count_var)); self.writeln(&format!("{}->length = {};", result_var, count_var));
self.indent -= 1; self.indent -= 1;
self.writeln("}"); self.writeln("}");
// Decref the closure if it was a temporary (inline lambda) // Decref the closure if it was a temporary (inline lambda or fn ref)
if closure.starts_with("_closure_") { if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
self.writeln(&format!("lux_decref_closure({});", closure)); self.writeln(&format!("lux_decref_closure({});", closure));
} }
Ok(result_var) Ok(result_var)
} }
"fold" => { "fold" | "foldLeft" => {
if args.len() != 3 { if args.len() != 3 {
return Err(CGenError { message: "List.fold takes 3 arguments".to_string(), span: None }); return Err(CGenError { message: "List.fold takes 3 arguments".to_string(), span: None });
} }
// Infer accumulator type from init, element type from callback
let init_type = self.infer_expr_type(&args[1]);
let elem_type = self.infer_callback_param_type(&args[2], 1);
let elem_is_prim = Self::is_primitive_c_type(&elem_type);
let list = self.emit_expr(&args[0])?; let list = self.emit_expr(&args[0])?;
let init = self.emit_expr(&args[1])?; let init = self.emit_expr(&args[1])?;
let closure = self.emit_expr(&args[2])?; let closure = self.emit_expr(&args[2])?;
@@ -3612,16 +3782,34 @@ impl CBackend {
let elem_var = format!("_elem_{}", id); let elem_var = format!("_elem_{}", id);
let fn_var = format!("_fn_{}", id); let fn_var = format!("_fn_{}", id);
self.writeln(&format!("LuxInt {} = {};", result_var, init)); let acc_c_type = match init_type.as_deref() {
Some(t) if t == "LuxInt" || t == "LuxBool" || t == "LuxString" ||
t == "LuxFloat" || t.ends_with('*') => t.to_string(),
Some(t) if t != "void" => t.to_string(), // Struct types like FMState, BState
_ => "void*".to_string(),
};
let arg_pass = if elem_is_prim {
format!("{}({})", Self::unbox_fn_for_type(&elem_type), elem_var)
} else {
elem_var.clone()
};
// Build fn pointer cast: acc_type (*)(void*, acc_type, elem_type_or_void*)
let elem_c_in_cast = if elem_is_prim { elem_type.clone() } else { "void*".to_string() };
let fn_cast = format!("({} (*)(void*, {}, {}))", acc_c_type, acc_c_type, elem_c_in_cast);
self.writeln(&format!("{} {} = ({})({});", acc_c_type, result_var, acc_c_type, init));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var)); self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1; self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var)); self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure)); self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("{} = ((LuxInt(*)(void*, LuxInt, LuxInt)){}->fn_ptr)({}->env, {}, lux_unbox_int({}));", result_var, fn_var, fn_var, result_var, elem_var)); self.writeln(&format!("{} = ({}{}->fn_ptr)({}->env, {}, {});",
result_var, fn_cast, fn_var, fn_var, result_var, arg_pass));
self.indent -= 1; self.indent -= 1;
self.writeln("}"); self.writeln("}");
// Decref the closure if it was a temporary (inline lambda) // Decref the closure if it was a temporary (inline lambda or fn ref)
if closure.starts_with("_closure_") { if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
self.writeln(&format!("lux_decref_closure({});", closure)); self.writeln(&format!("lux_decref_closure({});", closure));
} }
@@ -3631,6 +3819,9 @@ impl CBackend {
if args.len() != 2 { if args.len() != 2 {
return Err(CGenError { message: "List.find takes 2 arguments".to_string(), span: None }); return Err(CGenError { message: "List.find takes 2 arguments".to_string(), span: None });
} }
let elem_type = self.infer_callback_param_type(&args[1], 0);
let elem_is_prim = Self::is_primitive_c_type(&elem_type);
let list = self.emit_expr(&args[0])?; let list = self.emit_expr(&args[0])?;
let closure = self.emit_expr(&args[1])?; let closure = self.emit_expr(&args[1])?;
let id = self.fresh_name(); let id = self.fresh_name();
@@ -3640,12 +3831,23 @@ impl CBackend {
let fn_var = format!("_fn_{}", id); let fn_var = format!("_fn_{}", id);
let matches_var = format!("_matches_{}", id); let matches_var = format!("_matches_{}", id);
let arg_pass = if elem_is_prim {
format!("{}({})", Self::unbox_fn_for_type(&elem_type), elem_var)
} else {
elem_var.clone()
};
let fn_cast = if elem_is_prim {
format!("(LuxBool(*)(void*, {}))", elem_type)
} else {
"(LuxBool(*)(void*, void*))".to_string()
};
self.writeln(&format!("Option {} = lux_option_none();", result_var)); self.writeln(&format!("Option {} = lux_option_none();", result_var));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var)); self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1; self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var)); self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure)); self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("LuxBool {} = ((LuxBool(*)(void*, LuxInt)){}->fn_ptr)({}->env, lux_unbox_int({}));", matches_var, fn_var, fn_var, elem_var)); self.writeln(&format!("LuxBool {} = ({}{}->fn_ptr)({}->env, {});", matches_var, fn_cast, fn_var, fn_var, arg_pass));
self.writeln(&format!("if ({}) {{", matches_var)); self.writeln(&format!("if ({}) {{", matches_var));
self.indent += 1; self.indent += 1;
self.writeln(&format!("{} = lux_option_some({});", result_var, elem_var)); self.writeln(&format!("{} = lux_option_some({});", result_var, elem_var));
@@ -3654,8 +3856,8 @@ impl CBackend {
self.writeln("}"); self.writeln("}");
self.indent -= 1; self.indent -= 1;
self.writeln("}"); self.writeln("}");
// Decref the closure if it was a temporary (inline lambda) // Decref the closure if it was a temporary (inline lambda or fn ref)
if closure.starts_with("_closure_") { if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
self.writeln(&format!("lux_decref_closure({});", closure)); self.writeln(&format!("lux_decref_closure({});", closure));
} }
@@ -3665,6 +3867,9 @@ impl CBackend {
if args.len() != 2 { if args.len() != 2 {
return Err(CGenError { message: "List.any takes 2 arguments".to_string(), span: None }); return Err(CGenError { message: "List.any takes 2 arguments".to_string(), span: None });
} }
let elem_type = self.infer_callback_param_type(&args[1], 0);
let elem_is_prim = Self::is_primitive_c_type(&elem_type);
let list = self.emit_expr(&args[0])?; let list = self.emit_expr(&args[0])?;
let closure = self.emit_expr(&args[1])?; let closure = self.emit_expr(&args[1])?;
let id = self.fresh_name(); let id = self.fresh_name();
@@ -3673,12 +3878,23 @@ impl CBackend {
let elem_var = format!("_elem_{}", id); let elem_var = format!("_elem_{}", id);
let fn_var = format!("_fn_{}", id); let fn_var = format!("_fn_{}", id);
let arg_pass = if elem_is_prim {
format!("{}({})", Self::unbox_fn_for_type(&elem_type), elem_var)
} else {
elem_var.clone()
};
let fn_cast = if elem_is_prim {
format!("(LuxBool(*)(void*, {}))", elem_type)
} else {
"(LuxBool(*)(void*, void*))".to_string()
};
self.writeln(&format!("LuxBool {} = 0;", result_var)); self.writeln(&format!("LuxBool {} = 0;", result_var));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var)); self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1; self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var)); self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure)); self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("if (((LuxBool(*)(void*, LuxInt)){}->fn_ptr)({}->env, lux_unbox_int({}))) {{", fn_var, fn_var, elem_var)); self.writeln(&format!("if (({}{}->fn_ptr)({}->env, {})) {{", fn_cast, fn_var, fn_var, arg_pass));
self.indent += 1; self.indent += 1;
self.writeln(&format!("{} = 1;", result_var)); self.writeln(&format!("{} = 1;", result_var));
self.writeln("break;"); self.writeln("break;");
@@ -3686,8 +3902,8 @@ impl CBackend {
self.writeln("}"); self.writeln("}");
self.indent -= 1; self.indent -= 1;
self.writeln("}"); self.writeln("}");
// Decref the closure if it was a temporary (inline lambda) // Decref the closure if it was a temporary (inline lambda or fn ref)
if closure.starts_with("_closure_") { if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
self.writeln(&format!("lux_decref_closure({});", closure)); self.writeln(&format!("lux_decref_closure({});", closure));
} }
@@ -3697,6 +3913,9 @@ impl CBackend {
if args.len() != 2 { if args.len() != 2 {
return Err(CGenError { message: "List.all takes 2 arguments".to_string(), span: None }); return Err(CGenError { message: "List.all takes 2 arguments".to_string(), span: None });
} }
let elem_type = self.infer_callback_param_type(&args[1], 0);
let elem_is_prim = Self::is_primitive_c_type(&elem_type);
let list = self.emit_expr(&args[0])?; let list = self.emit_expr(&args[0])?;
let closure = self.emit_expr(&args[1])?; let closure = self.emit_expr(&args[1])?;
let id = self.fresh_name(); let id = self.fresh_name();
@@ -3705,12 +3924,23 @@ impl CBackend {
let elem_var = format!("_elem_{}", id); let elem_var = format!("_elem_{}", id);
let fn_var = format!("_fn_{}", id); let fn_var = format!("_fn_{}", id);
let arg_pass = if elem_is_prim {
format!("{}({})", Self::unbox_fn_for_type(&elem_type), elem_var)
} else {
elem_var.clone()
};
let fn_cast = if elem_is_prim {
format!("(LuxBool(*)(void*, {}))", elem_type)
} else {
"(LuxBool(*)(void*, void*))".to_string()
};
self.writeln(&format!("LuxBool {} = 1;", result_var)); self.writeln(&format!("LuxBool {} = 1;", result_var));
self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var)); self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
self.indent += 1; self.indent += 1;
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var)); self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure)); self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure));
self.writeln(&format!("if (!((LuxBool(*)(void*, LuxInt)){}->fn_ptr)({}->env, lux_unbox_int({}))) {{", fn_var, fn_var, elem_var)); self.writeln(&format!("if (!({}{}->fn_ptr)({}->env, {})) {{", fn_cast, fn_var, fn_var, arg_pass));
self.indent += 1; self.indent += 1;
self.writeln(&format!("{} = 0;", result_var)); self.writeln(&format!("{} = 0;", result_var));
self.writeln("break;"); self.writeln("break;");
@@ -3718,8 +3948,8 @@ impl CBackend {
self.writeln("}"); self.writeln("}");
self.indent -= 1; self.indent -= 1;
self.writeln("}"); self.writeln("}");
// Decref the closure if it was a temporary (inline lambda) // Decref the closure if it was a temporary (inline lambda or fn ref)
if closure.starts_with("_closure_") { if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
self.writeln(&format!("lux_decref_closure({});", closure)); self.writeln(&format!("lux_decref_closure({});", closure));
} }
@@ -3794,19 +4024,21 @@ impl CBackend {
let scrutinee_inner_type = self.infer_option_inner_type(expr); let scrutinee_inner_type = self.infer_option_inner_type(expr);
if let Some(inner_type) = scrutinee_inner_type { if let Some(inner_type) = scrutinee_inner_type {
inner_type inner_type
} else if let Some(body_type) = self.infer_expr_type(&arm.body) {
// Try to infer from body type
if matches!(body_type.as_str(), "LuxString" | "LuxInt" | "LuxFloat" | "LuxBool" | "LuxList*") {
body_type
} else { } else {
// Try to find the var's usage in the body and infer from that // Try usage-based inference first — this looks at how the variable
self.infer_var_type_from_usage(&arm.body, &var_name) // is actually used in the body, which is more reliable than body return type
.unwrap_or_else(|| "LuxString".to_string()) if let Some(usage_type) = self.infer_var_type_from_usage(&arm.body, &var_name) {
usage_type
} else if let Expr::Var(v) = &arm.body {
// If body is just this variable (identity match), use the match result type
if v.name == *var_name && result_type != "LuxInt" && result_type != "void" {
result_type.clone()
} else {
"LuxString".to_string()
} }
} else { } else {
// Try to find the var's usage in the body "LuxString".to_string()
self.infer_var_type_from_usage(&arm.body, &var_name) }
.unwrap_or_else(|| "LuxString".to_string())
} }
} else { } else {
c_type.clone() c_type.clone()
@@ -3815,7 +4047,15 @@ impl CBackend {
// Emit the binding with proper casting // Emit the binding with proper casting
if c_type == "void*" { if c_type == "void*" {
// Cast from void* to actual type // Cast from void* to actual type
if Self::is_primitive_c_type(&actual_type) {
// For primitive types stored as void*, cast via intptr_t
self.writeln(&format!("{} {} = ({})(intptr_t)({});", actual_type, var_name, actual_type, c_expr));
} else if !actual_type.ends_with('*') && actual_type != "void" {
// Struct types: cast to pointer and dereference
self.writeln(&format!("{} {} = *({}*)({});", actual_type, var_name, actual_type, c_expr));
} else {
self.writeln(&format!("{} {} = ({})({});", actual_type, var_name, actual_type, c_expr)); self.writeln(&format!("{} {} = ({})({});", actual_type, var_name, actual_type, c_expr));
}
self.var_types.insert(var_name.clone(), actual_type); self.var_types.insert(var_name.clone(), actual_type);
} else if actual_type.ends_with('*') && actual_type != "void*" { } else if actual_type.ends_with('*') && actual_type != "void*" {
// Dereference pointer types (but not void*) // Dereference pointer types (but not void*)
@@ -3854,29 +4094,40 @@ impl CBackend {
/// Infer the result type of a match expression by checking all arm bodies /// Infer the result type of a match expression by checking all arm bodies
/// Returns the first concrete type found, or "LuxInt" as default /// Returns the first concrete type found, or "LuxInt" as default
fn infer_match_result_type(&self, arms: &[MatchArm]) -> String { fn infer_match_result_type(&self, arms: &[MatchArm]) -> String {
// Priority: check all arms for concrete types since bound variables // For pattern-bound variables, prioritize the pattern's field type info
// from patterns may not have known types in var_types yet // over var_types which may have stale entries from earlier functions
let mut found_void = false; let mut found_void = false;
let mut found_void_ptr = false;
let mut best_type: Option<String> = None;
for arm in arms { for arm in arms {
if let Some(t) = self.infer_expr_type(&arm.body) { // First: for pattern-bound variables, use the pattern's type info
if t == "void" {
found_void = true;
} else {
// Found a non-void type, use it
return t;
}
}
// If the body is a simple Var referencing a pattern-bound variable,
// try to infer the type from the pattern context
if let Expr::Var(body_ident) = &arm.body { if let Expr::Var(body_ident) = &arm.body {
if let Some(t) = self.infer_pattern_binding_type(&arm.pattern, &body_ident.name) { if let Some(t) = self.infer_pattern_binding_type(&arm.pattern, &body_ident.name) {
if t != "void*" { match t.as_str() {
"void" => { found_void = true; continue; }
"void*" => { found_void_ptr = true; continue; }
_ => return t,
}
}
}
// Second: try general expression type inference
if let Some(t) = self.infer_expr_type(&arm.body) {
match t.as_str() {
"void" => { found_void = true; }
_ => {
if best_type.is_none() {
best_type = Some(t);
}
}
}
}
}
if let Some(t) = best_type {
return t; return t;
} }
if found_void_ptr {
return "void*".to_string();
} }
}
}
// If all arms were void, return void
if found_void { if found_void {
return "void".to_string(); return "void".to_string();
} }
@@ -3992,7 +4243,8 @@ impl CBackend {
if let Expr::Var(module) = object.as_ref() { if let Expr::Var(module) = object.as_ref() {
match module.name.as_str() { match module.name.as_str() {
"String" => match field.name.as_str() { "String" => match field.name.as_str() {
"length" | "indexOf" | "lastIndexOf" => return Some("LuxInt".to_string()), "length" => return Some("LuxInt".to_string()),
"indexOf" | "lastIndexOf" => return Some("Option".to_string()),
"substring" | "trim" | "toLower" | "toUpper" | "replace" "substring" | "trim" | "toLower" | "toUpper" | "replace"
| "join" | "fromChar" | "repeat" => return Some("LuxString".to_string()), | "join" | "fromChar" | "repeat" => return Some("LuxString".to_string()),
"split" | "lines" | "chars" => return Some("LuxList*".to_string()), "split" | "lines" | "chars" => return Some("LuxList*".to_string()),
@@ -4004,7 +4256,18 @@ impl CBackend {
"map" | "filter" | "concat" | "reverse" | "take" | "drop" "map" | "filter" | "concat" | "reverse" | "take" | "drop"
| "range" | "sort" | "sortBy" | "flatten" | "intersperse" => return Some("LuxList*".to_string()), | "range" | "sort" | "sortBy" | "flatten" | "intersperse" => return Some("LuxList*".to_string()),
"head" | "tail" | "get" | "find" | "last" => return Some("Option".to_string()), "head" | "tail" | "get" | "find" | "last" => return Some("Option".to_string()),
"length" | "fold" | "foldLeft" => return Some("LuxInt".to_string()), "length" => return Some("LuxInt".to_string()),
"fold" | "foldLeft" => {
// Fold return type is the type of the init value (2nd arg)
if let Expr::Call { args: call_args, .. } = expr {
if call_args.len() >= 2 {
if let Some(init_type) = self.infer_expr_type(&call_args[1]) {
return Some(init_type);
}
}
}
return None;
}
"isEmpty" | "any" | "all" | "contains" => return Some("LuxBool".to_string()), "isEmpty" | "any" | "all" | "contains" => return Some("LuxBool".to_string()),
_ => {} _ => {}
}, },
@@ -4055,7 +4318,7 @@ impl CBackend {
.or_else(|| self.infer_expr_type(else_branch)) .or_else(|| self.infer_expr_type(else_branch))
} }
Expr::List { .. } => Some("LuxList*".to_string()), Expr::List { .. } => Some("LuxList*".to_string()),
Expr::EffectOp { effect, operation, .. } => { Expr::EffectOp { effect, operation, args, .. } => {
// List operations have known return types // List operations have known return types
if effect.name == "List" { if effect.name == "List" {
match operation.name.as_str() { match operation.name.as_str() {
@@ -4064,7 +4327,15 @@ impl CBackend {
// Operations returning Option // Operations returning Option
"head" | "tail" | "get" | "find" => Some("Option".to_string()), "head" | "tail" | "get" | "find" => Some("Option".to_string()),
// Operations returning Int // Operations returning Int
"length" | "fold" => Some("LuxInt".to_string()), "length" => Some("LuxInt".to_string()),
// Fold returns the type of the init value
"fold" | "foldLeft" => {
if args.len() >= 2 {
self.infer_expr_type(&args[1])
} else {
None
}
}
// Operations returning Bool // Operations returning Bool
"isEmpty" | "any" | "all" => Some("LuxBool".to_string()), "isEmpty" | "any" | "all" => Some("LuxBool".to_string()),
_ => None, _ => None,
@@ -4113,7 +4384,8 @@ impl CBackend {
match operation.name.as_str() { match operation.name.as_str() {
"trim" | "substring" | "toLower" | "toUpper" | "replace" "trim" | "substring" | "toLower" | "toUpper" | "replace"
| "join" | "fromChar" | "repeat" => Some("LuxString".to_string()), | "join" | "fromChar" | "repeat" => Some("LuxString".to_string()),
"length" | "indexOf" | "lastIndexOf" => Some("LuxInt".to_string()), "length" => Some("LuxInt".to_string()),
"indexOf" | "lastIndexOf" => Some("Option".to_string()),
"lines" | "split" | "chars" => Some("LuxList*".to_string()), "lines" | "split" | "chars" => Some("LuxList*".to_string()),
"contains" | "startsWith" | "endsWith" => Some("LuxBool".to_string()), "contains" | "startsWith" | "endsWith" => Some("LuxBool".to_string()),
"parseInt" | "parseFloat" => Some("Option".to_string()), "parseInt" | "parseFloat" => Some("Option".to_string()),
@@ -4214,8 +4486,8 @@ impl CBackend {
match operation.name.as_str() { match operation.name.as_str() {
"head" | "get" | "find" => { "head" | "get" | "find" => {
// These return Option<T> where T is the element type // These return Option<T> where T is the element type
// Default to LuxString for strings, but could be improved // Return None to let usage-based inference determine the actual type
Some("LuxString".to_string()) None
} }
"tail" => { "tail" => {
// tail returns Option<List<T>> - so inner type is LuxList* // tail returns Option<List<T>> - so inner type is LuxList*
@@ -4236,12 +4508,12 @@ impl CBackend {
None None
} }
} }
Expr::Call { func, .. } => { Expr::Call { func, args, .. } => {
if let Expr::Field { object, field, .. } = func.as_ref() { if let Expr::Field { object, field, .. } = func.as_ref() {
if let Expr::Var(module) = object.as_ref() { if let Expr::Var(module) = object.as_ref() {
match module.name.as_str() { match module.name.as_str() {
"List" => match field.name.as_str() { "List" => match field.name.as_str() {
"head" | "get" | "find" => Some("LuxString".to_string()), "head" | "get" | "find" => None,
"tail" => Some("LuxList*".to_string()), "tail" => Some("LuxList*".to_string()),
_ => None, _ => None,
}, },
@@ -4256,6 +4528,32 @@ impl CBackend {
} else { } else {
None None
} }
} else if let Expr::Var(ident) = func.as_ref() {
// User function that returns Option — check if this is String.indexOf/lastIndexOf
// emitted as a direct call, or try to look at the function's implementation
if let Some(ret) = self.function_return_types.get(&ident.name) {
if ret == "Option" {
// Check the function's param types to infer what kind of Option it returns
// For functions like findClosing that take string and int args and return Option<Int>,
// try to infer from the first non-string argument type
if let Some(param_types) = self.function_param_types.get(&ident.name) {
// Functions operating on string positions typically return Option<Int>
let has_int_params = param_types.iter().any(|t| t == "LuxInt");
if has_int_params {
return Some("LuxInt".to_string());
}
}
// Also check: if any of the call args are Int literals, likely Option<Int>
for arg in args {
if let Some(t) = self.infer_expr_type(arg) {
if t == "LuxInt" {
return Some("LuxInt".to_string());
}
}
}
}
}
None
} else { } else {
None None
} }
@@ -4281,6 +4579,19 @@ impl CBackend {
} }
} }
} }
// Check module method calls (String.substring, etc.)
if let Expr::Field { object, field, .. } = func.as_ref() {
if let Expr::Var(module) = object.as_ref() {
match (module.name.as_str(), field.name.as_str()) {
("String", "substring") if i >= 1 => return Some("LuxInt".to_string()),
("String", "indexOf" | "lastIndexOf") => return Some("LuxString".to_string()),
("String", "replace") => return Some("LuxString".to_string()),
("List", "get") if i == 1 => return Some("LuxInt".to_string()),
("List", "take" | "drop") if i == 1 => return Some("LuxInt".to_string()),
_ => {}
}
}
}
} }
} }
// Recursively check arguments // Recursively check arguments
@@ -4288,26 +4599,66 @@ impl CBackend {
return Some(t); return Some(t);
} }
} }
// Check the function itself recursively
if let Some(t) = self.infer_var_type_from_usage(func, var_name) {
return Some(t);
}
None None
} }
Expr::BinaryOp { left, right, .. } => { Expr::BinaryOp { op, left, right, .. } => {
// If var is used in arithmetic, it's likely LuxInt
let is_var_in_left = matches!(left.as_ref(), Expr::Var(id) if id.name == var_name);
let is_var_in_right = matches!(right.as_ref(), Expr::Var(id) if id.name == var_name);
if (is_var_in_left || is_var_in_right) &&
matches!(op, BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Mod |
BinaryOp::Lt | BinaryOp::Le | BinaryOp::Gt | BinaryOp::Ge) {
// Check the other operand — if it's an Int literal or known Int, this var is Int
let other = if is_var_in_left { right } else { left };
let other_type = self.infer_expr_type(other);
if matches!(other_type.as_deref(), Some("LuxInt") | Some("LuxFloat")) {
return other_type;
}
}
self.infer_var_type_from_usage(left, var_name) self.infer_var_type_from_usage(left, var_name)
.or_else(|| self.infer_var_type_from_usage(right, var_name)) .or_else(|| self.infer_var_type_from_usage(right, var_name))
} }
Expr::Block { statements, result, .. } => { Expr::Block { statements, result, .. } => {
for stmt in statements { for stmt in statements {
if let crate::ast::Statement::Expr(e) = stmt { match stmt {
crate::ast::Statement::Expr(e) => {
if let Some(t) = self.infer_var_type_from_usage(e, var_name) { if let Some(t) = self.infer_var_type_from_usage(e, var_name) {
return Some(t); return Some(t);
} }
} }
crate::ast::Statement::Let { value, .. } => {
if let Some(t) = self.infer_var_type_from_usage(value, var_name) {
return Some(t);
}
}
}
} }
self.infer_var_type_from_usage(result, var_name) self.infer_var_type_from_usage(result, var_name)
} }
Expr::If { then_branch, else_branch, .. } => { Expr::If { condition, then_branch, else_branch, .. } => {
self.infer_var_type_from_usage(then_branch, var_name) self.infer_var_type_from_usage(condition, var_name)
.or_else(|| self.infer_var_type_from_usage(then_branch, var_name))
.or_else(|| self.infer_var_type_from_usage(else_branch, var_name)) .or_else(|| self.infer_var_type_from_usage(else_branch, var_name))
} }
Expr::Match { scrutinee, arms, .. } => {
if let Some(t) = self.infer_var_type_from_usage(scrutinee, var_name) {
return Some(t);
}
for arm in arms {
if let Some(t) = self.infer_var_type_from_usage(&arm.body, var_name) {
return Some(t);
}
}
None
}
Expr::Let { value, body, .. } => {
self.infer_var_type_from_usage(value, var_name)
.or_else(|| self.infer_var_type_from_usage(body, var_name))
}
_ => None, _ => None,
} }
} }
@@ -4508,6 +4859,70 @@ impl CBackend {
} }
} }
/// Infer the C type of a callback's parameter at the given index
fn infer_callback_param_type(&self, callback_expr: &Expr, param_index: usize) -> String {
match callback_expr {
Expr::Lambda { params, .. } => {
if let Some(param) = params.get(param_index) {
self.type_expr_to_c(&param.typ).unwrap_or_else(|_| "void*".to_string())
} else {
"void*".to_string()
}
}
Expr::Var(ident) => {
if let Some(types) = self.function_param_types.get(&ident.name) {
types.get(param_index).cloned().unwrap_or_else(|| "void*".to_string())
} else {
"void*".to_string()
}
}
_ => "void*".to_string(),
}
}
/// Infer the C return type of a callback expression
fn infer_callback_return_type(&self, callback_expr: &Expr) -> String {
match callback_expr {
Expr::Lambda { return_type, .. } => {
return_type.as_ref()
.and_then(|t| self.type_expr_to_c(t).ok())
.unwrap_or_else(|| "void*".to_string())
}
Expr::Var(ident) => {
self.function_return_types.get(&ident.name)
.cloned()
.unwrap_or_else(|| "void*".to_string())
}
_ => "void*".to_string(),
}
}
/// Check if a C type is a primitive (non-pointer) type requiring boxing/unboxing
fn is_primitive_c_type(typ: &str) -> bool {
matches!(typ, "LuxInt" | "LuxBool" | "LuxFloat" | "char")
}
/// Get the unbox function for a primitive C type
fn unbox_fn_for_type(typ: &str) -> &str {
match typ {
"LuxInt" => "lux_unbox_int",
"LuxBool" => "lux_unbox_bool",
"LuxFloat" => "lux_unbox_float",
_ => "lux_unbox_int",
}
}
/// Get the box function for a primitive C type
fn box_fn_for_type(typ: &str) -> &str {
match typ {
"LuxInt" => "lux_box_int",
"LuxBool" => "lux_box_bool",
"LuxFloat" => "lux_box_float",
"LuxString" => "lux_box_string",
_ => "",
}
}
fn fresh_name(&mut self) -> usize { fn fresh_name(&mut self) -> usize {
self.name_counter += 1; self.name_counter += 1;
self.name_counter self.name_counter
@@ -4872,8 +5287,11 @@ impl CBackend {
fn collect_free_vars(&self, expr: &Expr, bound: &HashSet<String>, free: &mut Vec<String>) { fn collect_free_vars(&self, expr: &Expr, bound: &HashSet<String>, free: &mut Vec<String>) {
match expr { match expr {
Expr::Var(ident) => { Expr::Var(ident) => {
// If not bound locally and not a known function, it's a free variable // If not bound locally, not a known function, and not a constructor, it's a free variable
if !bound.contains(&ident.name) && !self.functions.contains(&ident.name) { if !bound.contains(&ident.name)
&& !self.functions.contains(&ident.name)
&& !self.variant_to_type.contains_key(&ident.name)
{
free.push(ident.name.clone()); free.push(ident.name.clone());
} }
} }