diff --git a/src/codegen/c_backend.rs b/src/codegen/c_backend.rs index 61910d4..cb374b9 100644 --- a/src/codegen/c_backend.rs +++ b/src/codegen/c_backend.rs @@ -355,6 +355,11 @@ impl CBackend { fn emit_expr_with_env(&mut self, expr: &Expr, captured: &HashSet<&str>) -> Result { 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, _> = 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, _> = 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 = 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, _> = 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, _> = 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 "); + 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"); + 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 = 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 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> - 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, + // 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 + 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 + 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, free: &mut Vec) { 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()); } }