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:
@@ -355,6 +355,11 @@ impl CBackend {
|
||||
fn emit_expr_with_env(&mut self, expr: &Expr, captured: &HashSet<&str>) -> Result<String, CGenError> {
|
||||
match expr {
|
||||
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);
|
||||
if captured.contains(ident.name.as_str()) {
|
||||
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 {
|
||||
BinaryOp::Add => "+",
|
||||
BinaryOp::Sub => "-",
|
||||
@@ -418,13 +432,37 @@ impl CBackend {
|
||||
Ok(format!("({} ? {} : {})", c, t, e))
|
||||
}
|
||||
Expr::Call { func, args, .. } => {
|
||||
let arg_strs: Result<Vec<_>, _> = args.iter()
|
||||
.map(|a| self.emit_expr_with_env(a, captured))
|
||||
.collect();
|
||||
let args_str = arg_strs?.join(", ");
|
||||
|
||||
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()
|
||||
.map(|a| self.emit_expr_with_env(a, captured))
|
||||
.collect();
|
||||
let args_str = arg_strs?.join(", ");
|
||||
let c_func_name = self.mangle_name(&ident.name);
|
||||
// Pass evidence if this function uses effects
|
||||
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 param_types: Vec<&str> = args.iter().map(|_| "LuxInt").collect();
|
||||
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(" LuxBool (*isDir)(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("} LuxFileHandler;");
|
||||
self.writeln("");
|
||||
@@ -933,6 +976,26 @@ impl CBackend {
|
||||
self.writeln(" mkdir(path, 0755);");
|
||||
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(" (void)env;");
|
||||
self.writeln(" return lux_file_read(path);");
|
||||
@@ -968,6 +1031,11 @@ impl CBackend {
|
||||
self.writeln(" lux_file_mkdir(path);");
|
||||
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(" .read = default_file_read,");
|
||||
self.writeln(" .write = default_file_write,");
|
||||
@@ -976,6 +1044,7 @@ impl CBackend {
|
||||
self.writeln(" .delete_file = default_file_delete,");
|
||||
self.writeln(" .isDir = default_file_isDir,");
|
||||
self.writeln(" .mkdir = default_file_mkdir,");
|
||||
self.writeln(" .readDir = default_file_readDir,");
|
||||
self.writeln(" .env = NULL");
|
||||
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_some(void* value) { return (Option){Option_TAG_SOME, .data.some = {value}}; }");
|
||||
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("// All boxed values are RC-managed.");
|
||||
self.writeln("");
|
||||
@@ -2309,6 +2392,14 @@ impl CBackend {
|
||||
// This is a constructor - emit struct literal
|
||||
let variant_name = &ident.name;
|
||||
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 {
|
||||
// Escape C reserved keywords
|
||||
Ok(self.escape_c_keyword(&ident.name))
|
||||
@@ -3035,6 +3126,17 @@ impl CBackend {
|
||||
}
|
||||
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");
|
||||
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("LuxBool") => format!("lux_box_bool({})", 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
|
||||
_ => 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
|
||||
// Type-aware: detects element types from the callback to use correct boxing/unboxing
|
||||
"map" => {
|
||||
if args.len() != 2 {
|
||||
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 closure = self.emit_expr(&args[1])?;
|
||||
let id = self.fresh_name();
|
||||
@@ -3482,6 +3611,26 @@ impl CBackend {
|
||||
let fn_var = format!("_fn_{}", 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
|
||||
self.writeln(&format!("LuxList* {};", result_var));
|
||||
self.writeln(&format!("if (LUX_RC_HEADER({})->rc == 1) {{", list));
|
||||
@@ -3489,15 +3638,16 @@ impl CBackend {
|
||||
if self.debug_rc {
|
||||
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!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
|
||||
self.indent += 1;
|
||||
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
|
||||
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!("{}->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.writeln("}");
|
||||
self.indent -= 1;
|
||||
@@ -3506,21 +3656,22 @@ impl CBackend {
|
||||
if self.debug_rc {
|
||||
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!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
|
||||
self.indent += 1;
|
||||
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
|
||||
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!("{}->elements[{}] = lux_box_int({});", result_var, i_var, mapped_var));
|
||||
self.writeln(&format!("{} {} = ({}{}->fn_ptr)({}->env, {});",
|
||||
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.writeln("}");
|
||||
self.writeln(&format!("{}->length = {}->length;", result_var, list));
|
||||
self.indent -= 1;
|
||||
self.writeln("}");
|
||||
// Decref the closure if it was a temporary (inline lambda)
|
||||
if closure.starts_with("_closure_") {
|
||||
// Decref the closure if it was a temporary (inline lambda or fn ref)
|
||||
if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
|
||||
self.writeln(&format!("lux_decref_closure({});", closure));
|
||||
}
|
||||
|
||||
@@ -3530,6 +3681,9 @@ impl CBackend {
|
||||
if args.len() != 2 {
|
||||
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 closure = self.emit_expr(&args[1])?;
|
||||
let id = self.fresh_name();
|
||||
@@ -3540,6 +3694,17 @@ impl CBackend {
|
||||
let fn_var = format!("_fn_{}", 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
|
||||
self.writeln(&format!("LuxList* {};", result_var));
|
||||
self.writeln(&format!("int64_t {} = 0;", count_var));
|
||||
@@ -3548,13 +3713,13 @@ impl CBackend {
|
||||
if self.debug_rc {
|
||||
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!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
|
||||
self.indent += 1;
|
||||
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
|
||||
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.indent += 1;
|
||||
self.writeln(&format!("{}->elements[{}++] = {};", result_var, count_var, elem_var));
|
||||
@@ -3573,13 +3738,13 @@ impl CBackend {
|
||||
if self.debug_rc {
|
||||
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!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
|
||||
self.indent += 1;
|
||||
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
|
||||
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.indent += 1;
|
||||
// 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.indent -= 1;
|
||||
self.writeln("}");
|
||||
// Decref the closure if it was a temporary (inline lambda)
|
||||
if closure.starts_with("_closure_") {
|
||||
// Decref the closure if it was a temporary (inline lambda or fn ref)
|
||||
if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
|
||||
self.writeln(&format!("lux_decref_closure({});", closure));
|
||||
}
|
||||
|
||||
Ok(result_var)
|
||||
}
|
||||
"fold" => {
|
||||
"fold" | "foldLeft" => {
|
||||
if args.len() != 3 {
|
||||
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 init = self.emit_expr(&args[1])?;
|
||||
let closure = self.emit_expr(&args[2])?;
|
||||
@@ -3612,16 +3782,34 @@ impl CBackend {
|
||||
let elem_var = format!("_elem_{}", 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.indent += 1;
|
||||
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
|
||||
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.writeln("}");
|
||||
// Decref the closure if it was a temporary (inline lambda)
|
||||
if closure.starts_with("_closure_") {
|
||||
// Decref the closure if it was a temporary (inline lambda or fn ref)
|
||||
if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
|
||||
self.writeln(&format!("lux_decref_closure({});", closure));
|
||||
}
|
||||
|
||||
@@ -3631,6 +3819,9 @@ impl CBackend {
|
||||
if args.len() != 2 {
|
||||
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 closure = self.emit_expr(&args[1])?;
|
||||
let id = self.fresh_name();
|
||||
@@ -3640,12 +3831,23 @@ impl CBackend {
|
||||
let fn_var = format!("_fn_{}", 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!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
|
||||
self.indent += 1;
|
||||
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
|
||||
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.indent += 1;
|
||||
self.writeln(&format!("{} = lux_option_some({});", result_var, elem_var));
|
||||
@@ -3654,8 +3856,8 @@ impl CBackend {
|
||||
self.writeln("}");
|
||||
self.indent -= 1;
|
||||
self.writeln("}");
|
||||
// Decref the closure if it was a temporary (inline lambda)
|
||||
if closure.starts_with("_closure_") {
|
||||
// Decref the closure if it was a temporary (inline lambda or fn ref)
|
||||
if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
|
||||
self.writeln(&format!("lux_decref_closure({});", closure));
|
||||
}
|
||||
|
||||
@@ -3665,6 +3867,9 @@ impl CBackend {
|
||||
if args.len() != 2 {
|
||||
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 closure = self.emit_expr(&args[1])?;
|
||||
let id = self.fresh_name();
|
||||
@@ -3673,12 +3878,23 @@ impl CBackend {
|
||||
let elem_var = format!("_elem_{}", 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!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
|
||||
self.indent += 1;
|
||||
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
|
||||
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.writeln(&format!("{} = 1;", result_var));
|
||||
self.writeln("break;");
|
||||
@@ -3686,8 +3902,8 @@ impl CBackend {
|
||||
self.writeln("}");
|
||||
self.indent -= 1;
|
||||
self.writeln("}");
|
||||
// Decref the closure if it was a temporary (inline lambda)
|
||||
if closure.starts_with("_closure_") {
|
||||
// Decref the closure if it was a temporary (inline lambda or fn ref)
|
||||
if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
|
||||
self.writeln(&format!("lux_decref_closure({});", closure));
|
||||
}
|
||||
|
||||
@@ -3697,6 +3913,9 @@ impl CBackend {
|
||||
if args.len() != 2 {
|
||||
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 closure = self.emit_expr(&args[1])?;
|
||||
let id = self.fresh_name();
|
||||
@@ -3705,12 +3924,23 @@ impl CBackend {
|
||||
let elem_var = format!("_elem_{}", 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!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var));
|
||||
self.indent += 1;
|
||||
self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var));
|
||||
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.writeln(&format!("{} = 0;", result_var));
|
||||
self.writeln("break;");
|
||||
@@ -3718,8 +3948,8 @@ impl CBackend {
|
||||
self.writeln("}");
|
||||
self.indent -= 1;
|
||||
self.writeln("}");
|
||||
// Decref the closure if it was a temporary (inline lambda)
|
||||
if closure.starts_with("_closure_") {
|
||||
// Decref the closure if it was a temporary (inline lambda or fn ref)
|
||||
if closure.starts_with("_closure_") || closure.starts_with("_fn_ref_") {
|
||||
self.writeln(&format!("lux_decref_closure({});", closure));
|
||||
}
|
||||
|
||||
@@ -3794,19 +4024,21 @@ impl CBackend {
|
||||
let scrutinee_inner_type = self.infer_option_inner_type(expr);
|
||||
if let Some(inner_type) = scrutinee_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 {
|
||||
// Try to find the var's usage in the body and infer from that
|
||||
self.infer_var_type_from_usage(&arm.body, &var_name)
|
||||
.unwrap_or_else(|| "LuxString".to_string())
|
||||
}
|
||||
} else {
|
||||
// Try to find the var's usage in the body
|
||||
self.infer_var_type_from_usage(&arm.body, &var_name)
|
||||
.unwrap_or_else(|| "LuxString".to_string())
|
||||
// Try usage-based inference first — this looks at how the variable
|
||||
// is actually used in the body, which is more reliable than body return type
|
||||
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 {
|
||||
"LuxString".to_string()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c_type.clone()
|
||||
@@ -3815,7 +4047,15 @@ impl CBackend {
|
||||
// Emit the binding with proper casting
|
||||
if c_type == "void*" {
|
||||
// Cast from void* to actual type
|
||||
self.writeln(&format!("{} {} = ({})({});", actual_type, var_name, actual_type, c_expr));
|
||||
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.var_types.insert(var_name.clone(), actual_type);
|
||||
} else if actual_type.ends_with('*') && actual_type != "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
|
||||
/// Returns the first concrete type found, or "LuxInt" as default
|
||||
fn infer_match_result_type(&self, arms: &[MatchArm]) -> String {
|
||||
// Priority: check all arms for concrete types since bound variables
|
||||
// from patterns may not have known types in var_types yet
|
||||
// For pattern-bound variables, prioritize the pattern's field type info
|
||||
// over var_types which may have stale entries from earlier functions
|
||||
let mut found_void = false;
|
||||
let mut found_void_ptr = false;
|
||||
let mut best_type: Option<String> = None;
|
||||
for arm in arms {
|
||||
if let Some(t) = self.infer_expr_type(&arm.body) {
|
||||
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
|
||||
// First: for pattern-bound variables, use the pattern's type info
|
||||
if let Expr::Var(body_ident) = &arm.body {
|
||||
if let Some(t) = self.infer_pattern_binding_type(&arm.pattern, &body_ident.name) {
|
||||
if t != "void*" {
|
||||
return t;
|
||||
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 all arms were void, return void
|
||||
if let Some(t) = best_type {
|
||||
return t;
|
||||
}
|
||||
if found_void_ptr {
|
||||
return "void*".to_string();
|
||||
}
|
||||
if found_void {
|
||||
return "void".to_string();
|
||||
}
|
||||
@@ -3992,7 +4243,8 @@ impl CBackend {
|
||||
if let Expr::Var(module) = object.as_ref() {
|
||||
match module.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"
|
||||
| "join" | "fromChar" | "repeat" => return Some("LuxString".to_string()),
|
||||
"split" | "lines" | "chars" => return Some("LuxList*".to_string()),
|
||||
@@ -4004,7 +4256,18 @@ impl CBackend {
|
||||
"map" | "filter" | "concat" | "reverse" | "take" | "drop"
|
||||
| "range" | "sort" | "sortBy" | "flatten" | "intersperse" => return Some("LuxList*".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()),
|
||||
_ => {}
|
||||
},
|
||||
@@ -4055,7 +4318,7 @@ impl CBackend {
|
||||
.or_else(|| self.infer_expr_type(else_branch))
|
||||
}
|
||||
Expr::List { .. } => Some("LuxList*".to_string()),
|
||||
Expr::EffectOp { effect, operation, .. } => {
|
||||
Expr::EffectOp { effect, operation, args, .. } => {
|
||||
// List operations have known return types
|
||||
if effect.name == "List" {
|
||||
match operation.name.as_str() {
|
||||
@@ -4064,7 +4327,15 @@ impl CBackend {
|
||||
// Operations returning Option
|
||||
"head" | "tail" | "get" | "find" => Some("Option".to_string()),
|
||||
// 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
|
||||
"isEmpty" | "any" | "all" => Some("LuxBool".to_string()),
|
||||
_ => None,
|
||||
@@ -4113,7 +4384,8 @@ impl CBackend {
|
||||
match operation.name.as_str() {
|
||||
"trim" | "substring" | "toLower" | "toUpper" | "replace"
|
||||
| "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()),
|
||||
"contains" | "startsWith" | "endsWith" => Some("LuxBool".to_string()),
|
||||
"parseInt" | "parseFloat" => Some("Option".to_string()),
|
||||
@@ -4214,8 +4486,8 @@ impl CBackend {
|
||||
match operation.name.as_str() {
|
||||
"head" | "get" | "find" => {
|
||||
// These return Option<T> where T is the element type
|
||||
// Default to LuxString for strings, but could be improved
|
||||
Some("LuxString".to_string())
|
||||
// Return None to let usage-based inference determine the actual type
|
||||
None
|
||||
}
|
||||
"tail" => {
|
||||
// tail returns Option<List<T>> - so inner type is LuxList*
|
||||
@@ -4236,12 +4508,12 @@ impl CBackend {
|
||||
None
|
||||
}
|
||||
}
|
||||
Expr::Call { func, .. } => {
|
||||
Expr::Call { func, args, .. } => {
|
||||
if let Expr::Field { object, field, .. } = func.as_ref() {
|
||||
if let Expr::Var(module) = object.as_ref() {
|
||||
match module.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()),
|
||||
_ => None,
|
||||
},
|
||||
@@ -4256,6 +4528,32 @@ impl CBackend {
|
||||
} else {
|
||||
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 {
|
||||
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
|
||||
@@ -4288,26 +4599,66 @@ impl CBackend {
|
||||
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
|
||||
}
|
||||
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)
|
||||
.or_else(|| self.infer_var_type_from_usage(right, var_name))
|
||||
}
|
||||
Expr::Block { statements, result, .. } => {
|
||||
for stmt in statements {
|
||||
if let crate::ast::Statement::Expr(e) = stmt {
|
||||
if let Some(t) = self.infer_var_type_from_usage(e, var_name) {
|
||||
return Some(t);
|
||||
match stmt {
|
||||
crate::ast::Statement::Expr(e) => {
|
||||
if let Some(t) = self.infer_var_type_from_usage(e, var_name) {
|
||||
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)
|
||||
}
|
||||
Expr::If { then_branch, else_branch, .. } => {
|
||||
self.infer_var_type_from_usage(then_branch, var_name)
|
||||
Expr::If { condition, then_branch, else_branch, .. } => {
|
||||
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))
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -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(¶m.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 {
|
||||
self.name_counter += 1;
|
||||
self.name_counter
|
||||
@@ -4872,8 +5287,11 @@ impl CBackend {
|
||||
fn collect_free_vars(&self, expr: &Expr, bound: &HashSet<String>, free: &mut Vec<String>) {
|
||||
match expr {
|
||||
Expr::Var(ident) => {
|
||||
// If not bound locally and not a known function, it's a free variable
|
||||
if !bound.contains(&ident.name) && !self.functions.contains(&ident.name) {
|
||||
// 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)
|
||||
&& !self.variant_to_type.contains_key(&ident.name)
|
||||
{
|
||||
free.push(ident.name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user