diff --git a/src/codegen/c_backend.rs b/src/codegen/c_backend.rs index dc361dc..95defe2 100644 --- a/src/codegen/c_backend.rs +++ b/src/codegen/c_backend.rs @@ -115,6 +115,8 @@ pub struct CBackend { debug_rc: bool, /// Mapping from function names to their C return types function_return_types: HashMap, + /// Mapping from function names to their parameter types + function_param_types: HashMap>, /// Type tags for ADT types (starting at 100) adt_type_tags: HashMap, /// Next available ADT type tag @@ -142,8 +144,9 @@ impl CBackend { effectful_functions: HashSet::new(), has_evidence: false, rc_scopes: Vec::new(), - debug_rc: true, // Enable memory tracking for now + debug_rc: false, // Set to true to enable RC leak tracking function_return_types: HashMap::new(), + function_param_types: HashMap::new(), adt_type_tags: HashMap::new(), next_adt_tag: 100, // ADT tags start at 100 adt_with_pointers: HashSet::new(), @@ -166,6 +169,11 @@ impl CBackend { if let Ok(ret_type) = self.type_expr_to_c(&f.return_type) { self.function_return_types.insert(f.name.name.clone(), ret_type); } + // Store function parameter types for usage inference + let param_types: Vec = f.params.iter() + .filter_map(|p| self.type_expr_to_c(&p.typ).ok()) + .collect(); + self.function_param_types.insert(f.name.name.clone(), param_types); // Check if this function returns a closure if matches!(&f.return_type, TypeExpr::Function { .. }) { self.closure_returning_functions.insert(f.name.name.clone()); @@ -434,15 +442,23 @@ impl CBackend { self.writeln(" LUX_TAG_ADT = 100 // ADT types start at 100"); self.writeln("} LuxTypeTag;"); self.writeln(""); + self.writeln("// Magic number to identify RC-managed memory (allows safe decref of static strings)"); + self.writeln("#define LUX_RC_MAGIC 0x4C555852 // 'LUXR' in ASCII"); + self.writeln(""); self.writeln("// RC header placed before every heap-allocated object"); self.writeln("typedef struct {"); + self.writeln(" int32_t magic; // Magic number to identify RC-managed memory"); self.writeln(" int32_t rc; // Reference count"); self.writeln(" int32_t tag; // Type tag for polymorphic drop"); + self.writeln(" int32_t _pad; // Padding for alignment"); self.writeln("} LuxRcHeader;"); self.writeln(""); self.writeln("// Get the RC header from an object pointer"); self.writeln("#define LUX_RC_HEADER(ptr) (((LuxRcHeader*)(ptr)) - 1)"); self.writeln(""); + self.writeln("// Check if a pointer is RC-managed (has valid magic number)"); + self.writeln("#define LUX_IS_RC_MANAGED(ptr) (ptr && LUX_RC_HEADER(ptr)->magic == LUX_RC_MAGIC)"); + self.writeln(""); self.writeln("// Forward declarations of drop functions"); self.writeln("static void lux_drop(void* ptr, int32_t tag);"); self.writeln("static void lux_drop_adt(void* ptr, int32_t tag);"); @@ -464,6 +480,7 @@ impl CBackend { self.writeln("static void* lux_rc_alloc(size_t size, int32_t tag) {"); self.writeln(" LuxRcHeader* hdr = (LuxRcHeader*)malloc(sizeof(LuxRcHeader) + size);"); self.writeln(" if (!hdr) return NULL;"); + self.writeln(" hdr->magic = LUX_RC_MAGIC;"); self.writeln(" hdr->rc = 1;"); self.writeln(" hdr->tag = tag;"); if self.debug_rc { @@ -472,14 +489,14 @@ impl CBackend { self.writeln(" return hdr + 1; // Return pointer after header"); self.writeln("}"); self.writeln(""); - self.writeln("// Increment reference count"); + self.writeln("// Increment reference count (only for RC-managed memory)"); self.writeln("static inline void lux_incref(void* ptr) {"); - self.writeln(" if (ptr) LUX_RC_HEADER(ptr)->rc++;"); + self.writeln(" if (LUX_IS_RC_MANAGED(ptr)) LUX_RC_HEADER(ptr)->rc++;"); self.writeln("}"); self.writeln(""); self.writeln("// Decrement reference count, call drop if zero (generic)"); self.writeln("static inline void lux_decref(void* ptr) {"); - self.writeln(" if (ptr) {"); + self.writeln(" if (LUX_IS_RC_MANAGED(ptr)) {"); self.writeln(" LuxRcHeader* hdr = LUX_RC_HEADER(ptr);"); self.writeln(" if (--hdr->rc == 0) {"); self.writeln(" lux_drop(ptr, hdr->tag);"); @@ -553,6 +570,14 @@ impl CBackend { self.writeln(" return strstr(haystack, needle) != NULL;"); self.writeln("}"); self.writeln(""); + self.writeln("static LuxString lux_string_dup(LuxString s) {"); + self.writeln(" if (!s) return \"\";"); + self.writeln(" size_t len = strlen(s);"); + self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);"); + self.writeln(" memcpy(result, s, len + 1);"); + self.writeln(" return result;"); + self.writeln("}"); + self.writeln(""); self.writeln("// === Built-in Effect Implementations ==="); self.writeln(""); self.writeln("static void lux_console_print(LuxString msg) {"); @@ -574,6 +599,13 @@ impl CBackend { self.writeln(" return result;"); self.writeln("}"); self.writeln(""); + // Forward declare Option type (needed by LuxProcessHandler) + self.writeln("// Built-in Option type (forward declaration for use in handlers)"); + self.writeln("typedef enum { Option_TAG_NONE, Option_TAG_SOME } Option_Tag;"); + self.writeln("typedef struct { void* field0; } Option_Some_Data;"); + self.writeln("typedef struct { Option_Tag tag; union { Option_Some_Data some; } data; } Option;"); + self.writeln(""); + self.writeln("// === Evidence Passing Types ==="); self.writeln("// These enable O(1) effect handler lookup instead of O(n) stack search."); self.writeln("// See docs/EVIDENCE_PASSING.md for details."); @@ -634,6 +666,15 @@ impl CBackend { self.writeln(" void* env;"); self.writeln("} LuxHttpHandler;"); self.writeln(""); + self.writeln("// Handler struct for Process effect"); + self.writeln("typedef struct {"); + self.writeln(" LuxString (*exec)(void* env, LuxString cmd);"); + self.writeln(" Option (*getenv_fn)(void* env, LuxString name);"); + self.writeln(" LuxList* (*args)(void* env);"); + self.writeln(" LuxString (*cwd)(void* env);"); + self.writeln(" void* env;"); + self.writeln("} LuxProcessHandler;"); + self.writeln(""); self.writeln("// Evidence struct - passed to effectful functions"); self.writeln("// Contains pointers to current handlers for each effect type"); self.writeln("typedef struct {"); @@ -644,6 +685,7 @@ impl CBackend { self.writeln(" LuxTimeHandler* time;"); self.writeln(" LuxFileHandler* file;"); self.writeln(" LuxHttpHandler* http;"); + self.writeln(" LuxProcessHandler* process;"); self.writeln("} LuxEvidence;"); self.writeln(""); self.writeln("// Default Console handler using built-in implementations"); @@ -991,6 +1033,220 @@ impl CBackend { self.writeln(" .env = NULL"); self.writeln("};"); self.writeln(""); + self.writeln("// === Process Effect Built-in Implementations ==="); + self.writeln(""); + self.writeln("// Forward declarations for list functions used by Process/String"); + self.writeln("static LuxList* lux_list_new(int64_t capacity);"); + self.writeln("static void lux_list_push(LuxList* list, void* element);"); + self.writeln(""); + self.writeln("static LuxString lux_process_exec(LuxString cmd) {"); + self.writeln(" FILE* fp = popen(cmd, \"r\");"); + self.writeln(" if (!fp) return \"\";"); + self.writeln(" char buffer[4096];"); + self.writeln(" size_t total_len = 0;"); + self.writeln(" size_t capacity = 4096;"); + self.writeln(" char* temp = (char*)malloc(capacity);"); + self.writeln(" if (!temp) { pclose(fp); return \"\"; }"); + self.writeln(" temp[0] = '\\0';"); + self.writeln(" while (fgets(buffer, sizeof(buffer), fp) != NULL) {"); + self.writeln(" size_t chunk_len = strlen(buffer);"); + self.writeln(" if (total_len + chunk_len + 1 > capacity) {"); + self.writeln(" capacity *= 2;"); + self.writeln(" char* new_temp = (char*)realloc(temp, capacity);"); + self.writeln(" if (!new_temp) { free(temp); pclose(fp); return \"\"; }"); + self.writeln(" temp = new_temp;"); + self.writeln(" }"); + self.writeln(" strcpy(temp + total_len, buffer);"); + self.writeln(" total_len += chunk_len;"); + self.writeln(" }"); + self.writeln(" pclose(fp);"); + self.writeln(" // Copy to RC-managed string"); + self.writeln(" LuxString result = (LuxString)lux_rc_alloc(total_len + 1, LUX_TAG_STRING);"); + self.writeln(" if (!result) { free(temp); return \"\"; }"); + self.writeln(" memcpy(result, temp, total_len + 1);"); + self.writeln(" free(temp);"); + self.writeln(" return result;"); + self.writeln("}"); + self.writeln(""); + self.writeln("static Option lux_process_env(LuxString name) {"); + self.writeln(" char* value = getenv(name);"); + self.writeln(" if (value) {"); + self.writeln(" Option result;"); + self.writeln(" result.tag = Option_TAG_SOME;"); + self.writeln(" result.data.some.field0 = (void*)lux_string_dup(value);"); + self.writeln(" return result;"); + self.writeln(" } else {"); + self.writeln(" Option result;"); + self.writeln(" result.tag = Option_TAG_NONE;"); + self.writeln(" return result;"); + self.writeln(" }"); + self.writeln("}"); + self.writeln(""); + self.writeln("// Global argc/argv for Process.args()"); + self.writeln("static int lux_argc = 0;"); + self.writeln("static char** lux_argv = NULL;"); + self.writeln(""); + self.writeln("static LuxList* lux_process_args(void) {"); + self.writeln(" LuxList* list = lux_list_new(lux_argc > 0 ? lux_argc : 1);"); + self.writeln(" for (int i = 0; i < lux_argc; i++) {"); + self.writeln(" lux_list_push(list, (void*)lux_string_dup(lux_argv[i]));"); + self.writeln(" }"); + self.writeln(" return list;"); + self.writeln("}"); + self.writeln(""); + self.writeln("static LuxString lux_process_cwd(void) {"); + self.writeln(" char* cwd = getcwd(NULL, 0);"); + self.writeln(" if (!cwd) return \"\";"); + self.writeln(" return cwd;"); + self.writeln("}"); + self.writeln(""); + self.writeln("static LuxString default_process_exec(void* env, LuxString cmd) {"); + self.writeln(" (void)env;"); + self.writeln(" return lux_process_exec(cmd);"); + self.writeln("}"); + self.writeln(""); + self.writeln("static Option default_process_env(void* env, LuxString name) {"); + self.writeln(" (void)env;"); + self.writeln(" return lux_process_env(name);"); + self.writeln("}"); + self.writeln(""); + self.writeln("static LuxList* default_process_args(void* env) {"); + self.writeln(" (void)env;"); + self.writeln(" return lux_process_args();"); + self.writeln("}"); + self.writeln(""); + self.writeln("static LuxString default_process_cwd(void* env) {"); + self.writeln(" (void)env;"); + self.writeln(" return lux_process_cwd();"); + self.writeln("}"); + self.writeln(""); + self.writeln("static LuxProcessHandler default_process_handler = {"); + self.writeln(" .exec = default_process_exec,"); + self.writeln(" .getenv_fn = default_process_env,"); + self.writeln(" .args = default_process_args,"); + self.writeln(" .cwd = default_process_cwd,"); + self.writeln(" .env = NULL"); + self.writeln("};"); + self.writeln(""); + self.writeln("// === String Built-in Functions ==="); + self.writeln(""); + self.writeln("static LuxString lux_string_trim(LuxString s) {"); + self.writeln(" if (!s) return \"\";"); + self.writeln(" const char* start = s;"); + self.writeln(" const char* end = s + strlen(s) - 1;"); + self.writeln(" while (*start && (*start == ' ' || *start == '\\t' || *start == '\\n' || *start == '\\r')) start++;"); + self.writeln(" while (end > start && (*end == ' ' || *end == '\\t' || *end == '\\n' || *end == '\\r')) end--;"); + self.writeln(" size_t len = end - start + 1;"); + self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);"); + self.writeln(" if (!result) return \"\";"); + self.writeln(" strncpy(result, start, len);"); + self.writeln(" result[len] = '\\0';"); + self.writeln(" return result;"); + self.writeln("}"); + self.writeln(""); + self.writeln("static LuxInt lux_string_length(LuxString s) {"); + self.writeln(" if (!s) return 0;"); + self.writeln(" return (LuxInt)strlen(s);"); + self.writeln("}"); + self.writeln(""); + self.writeln("static LuxList* lux_string_lines(LuxString s) {"); + self.writeln(" LuxList* list = lux_list_new(8);"); + self.writeln(" if (!s || !*s) return list;"); + self.writeln(" const char* start = s;"); + self.writeln(" const char* p = s;"); + self.writeln(" while (*p) {"); + self.writeln(" if (*p == '\\n') {"); + self.writeln(" size_t len = p - start;"); + self.writeln(" LuxString line = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);"); + self.writeln(" if (line) {"); + self.writeln(" strncpy(line, start, len);"); + self.writeln(" line[len] = '\\0';"); + self.writeln(" lux_list_push(list, (void*)line);"); + self.writeln(" }"); + self.writeln(" start = p + 1;"); + self.writeln(" }"); + self.writeln(" p++;"); + self.writeln(" }"); + self.writeln(" if (start != p) {"); + self.writeln(" size_t len = p - start;"); + self.writeln(" LuxString line = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);"); + self.writeln(" if (line) {"); + self.writeln(" strncpy(line, start, len);"); + self.writeln(" line[len] = '\\0';"); + self.writeln(" lux_list_push(list, (void*)line);"); + self.writeln(" }"); + self.writeln(" }"); + self.writeln(" return list;"); + self.writeln("}"); + self.writeln(""); + self.writeln("static LuxList* lux_string_split(LuxString s, LuxString sep) {"); + self.writeln(" LuxList* list = lux_list_new(8);"); + self.writeln(" if (!s || !*s) return list;"); + self.writeln(" if (!sep || !*sep) {"); + self.writeln(" lux_list_push(list, (void*)lux_string_dup(s));"); + self.writeln(" return list;"); + self.writeln(" }"); + self.writeln(" size_t sep_len = strlen(sep);"); + self.writeln(" const char* start = s;"); + self.writeln(" const char* found;"); + self.writeln(" while ((found = strstr(start, sep)) != NULL) {"); + self.writeln(" size_t len = found - start;"); + self.writeln(" LuxString part = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);"); + self.writeln(" if (part) {"); + self.writeln(" strncpy(part, start, len);"); + self.writeln(" part[len] = '\\0';"); + self.writeln(" lux_list_push(list, (void*)part);"); + self.writeln(" }"); + self.writeln(" start = found + sep_len;"); + self.writeln(" }"); + self.writeln(" if (*start) {"); + self.writeln(" lux_list_push(list, (void*)lux_string_dup(start));"); + self.writeln(" }"); + self.writeln(" return list;"); + self.writeln("}"); + self.writeln(""); + // Note: lux_string_contains is already defined in the String Operations section + self.writeln(""); + self.writeln("static Option lux_string_parseInt(LuxString s) {"); + self.writeln(" Option result;"); + self.writeln(" if (!s || !*s) {"); + self.writeln(" result.tag = Option_TAG_NONE;"); + self.writeln(" return result;"); + self.writeln(" }"); + self.writeln(" char* endptr;"); + self.writeln(" errno = 0;"); + self.writeln(" long long val = strtoll(s, &endptr, 10);"); + self.writeln(" if (errno != 0 || endptr == s || *endptr != '\\0') {"); + self.writeln(" result.tag = Option_TAG_NONE;"); + self.writeln(" return result;"); + self.writeln(" }"); + self.writeln(" result.tag = Option_TAG_SOME;"); + self.writeln(" LuxInt* boxed = (LuxInt*)lux_rc_alloc(sizeof(LuxInt), LUX_TAG_BOXED_INT);"); + self.writeln(" *boxed = (LuxInt)val;"); + self.writeln(" result.data.some.field0 = (void*)boxed;"); + self.writeln(" return result;"); + self.writeln("}"); + self.writeln(""); + self.writeln("static Option lux_string_parseFloat(LuxString s) {"); + self.writeln(" Option result;"); + self.writeln(" if (!s || !*s) {"); + self.writeln(" result.tag = Option_TAG_NONE;"); + self.writeln(" return result;"); + self.writeln(" }"); + self.writeln(" char* endptr;"); + self.writeln(" errno = 0;"); + self.writeln(" double val = strtod(s, &endptr);"); + self.writeln(" if (errno != 0 || endptr == s || *endptr != '\\0') {"); + self.writeln(" result.tag = Option_TAG_NONE;"); + self.writeln(" return result;"); + self.writeln(" }"); + self.writeln(" result.tag = Option_TAG_SOME;"); + self.writeln(" LuxFloat* boxed = (LuxFloat*)malloc(sizeof(LuxFloat));"); + self.writeln(" *boxed = val;"); + self.writeln(" result.data.some.field0 = (void*)boxed;"); + self.writeln(" return result;"); + self.writeln("}"); + self.writeln(""); self.writeln("// Default evidence with built-in handlers"); self.writeln("static LuxEvidence default_evidence = {"); self.writeln(" .console = &default_console_handler,"); @@ -999,7 +1255,8 @@ impl CBackend { self.writeln(" .random = &default_random_handler,"); self.writeln(" .time = &default_time_handler,"); self.writeln(" .file = &default_file_handler,"); - self.writeln(" .http = &default_http_handler"); + self.writeln(" .http = &default_http_handler,"); + self.writeln(" .process = &default_process_handler"); self.writeln("};"); self.writeln(""); self.writeln("// === List Types ==="); @@ -1011,10 +1268,7 @@ impl CBackend { self.writeln(" int64_t capacity;"); self.writeln("};"); self.writeln(""); - self.writeln("// Built-in Option type for List.head, List.tail, List.get, List.find"); - self.writeln("typedef enum { Option_TAG_NONE, Option_TAG_SOME } Option_Tag;"); - self.writeln("typedef struct { void* field0; } Option_Some_Data;"); - self.writeln("typedef struct { Option_Tag tag; union { Option_Some_Data some; } data; } Option;"); + // Note: Option type is already defined earlier (before handler structs) self.writeln(""); // Emit specialized decref implementations (now that types are defined) @@ -1032,6 +1286,14 @@ impl CBackend { self.writeln(" return list;"); self.writeln("}"); self.writeln(""); + self.writeln("static void lux_list_push(LuxList* list, void* element) {"); + self.writeln(" if (list->length >= list->capacity) {"); + self.writeln(" list->capacity *= 2;"); + self.writeln(" list->elements = (void**)realloc(list->elements, sizeof(void*) * list->capacity);"); + self.writeln(" }"); + self.writeln(" list->elements[list->length++] = element;"); + self.writeln("}"); + self.writeln(""); self.writeln("static int64_t lux_list_length(LuxList* list) { return list->length; }"); self.writeln("static LuxBool lux_list_isEmpty(LuxList* list) { return list->length == 0; }"); self.writeln(""); @@ -1284,9 +1546,11 @@ impl CBackend { self.writeln("}"); self.writeln(""); - self.writeln("// Specialized decref for strings - no sub-references"); + self.writeln("// Specialized decref for strings - safe for both RC and static strings"); self.writeln("static inline void lux_decref_string(LuxString str) {"); - self.writeln(" if (str) {"); + self.writeln(" // Check magic number to distinguish RC-managed from static strings"); + self.writeln(" // Static string literals don't have our header, so we skip them"); + self.writeln(" if (LUX_IS_RC_MANAGED(str)) {"); self.writeln(" LuxRcHeader* hdr = LUX_RC_HEADER(str);"); self.writeln(" if (--hdr->rc == 0) {"); if self.debug_rc { @@ -1575,13 +1839,24 @@ impl CBackend { // String literals Expr::Literal(lit) => matches!(lit.kind, LiteralKind::String(_)), - // toString function call + // Variables - check if we know their type + Expr::Var(ident) => { + let escaped = self.escape_c_keyword(&ident.name); + self.var_types.get(&escaped).map(|t| t == "LuxString").unwrap_or(false) + } + + // toString function call or other string-returning functions Expr::Call { func, .. } => { if let Expr::Var(ident) = func.as_ref() { - ident.name == "toString" - } else { - false + if ident.name == "toString" { + return true; + } + // Check function return type + if let Some(ret_type) = self.function_return_types.get(&ident.name) { + return ret_type == "LuxString"; + } } + false } // String concatenation with + @@ -1590,6 +1865,27 @@ impl CBackend { && (self.is_string_expr(left) || self.is_string_expr(right)) } + // Effect operations that return strings + Expr::EffectOp { effect, operation, .. } => { + match (effect.name.as_str(), operation.name.as_str()) { + ("Process", "exec") | ("Process", "cwd") => true, + ("Console", "readLine") => true, + ("File", "read") => true, + ("Http", "get") | ("Http", "post") | ("Http", "put") | ("Http", "delete") => true, + ("String", "trim") => true, + _ => false, + } + } + + // Field access on String module + Expr::Field { object, field, .. } => { + if let Expr::Var(module) = object.as_ref() { + module.name == "String" && matches!(field.name.as_str(), "trim") + } else { + false + } + } + _ => false, } } @@ -1680,12 +1976,21 @@ impl CBackend { None }; + // Also check if result is an RC temp (like _str_concat_N) that we should skip + let skip_var = skip_var.or_else(|| { + if self.is_rc_temp(&result) { + Some(result.clone()) + } else { + None + } + }); + // Check if result is an RC type that we need to keep alive let is_rc_result = self.is_rc_type(&ret_type); let has_rc_locals = !self.rc_scopes.last().map_or(true, |s| s.is_empty()); if let Some(ref var_name) = skip_var { - // Result is a local variable - skip decref'ing it and just return + // Result is a local variable or RC temp - skip decref'ing it and just return self.pop_rc_scope_except(Some(var_name)); self.writeln(&format!("return {};", result)); } else if is_rc_result && has_rc_locals { @@ -1742,6 +2047,23 @@ impl CBackend { } Expr::BinaryOp { op, left, right, .. } => { + // Check if args are function calls returning String - need to track for cleanup + let left_is_string_call = if let Expr::Call { func, .. } = left.as_ref() { + if let Expr::Var(ident) = func.as_ref() { + self.function_return_types.get(&ident.name) + .map(|t| t == "LuxString") + .unwrap_or(false) + } else { false } + } else { false }; + + let right_is_string_call = if let Expr::Call { func, .. } = right.as_ref() { + if let Expr::Var(ident) = func.as_ref() { + self.function_return_types.get(&ident.name) + .map(|t| t == "LuxString") + .unwrap_or(false) + } else { false } + } else { false }; + let l = self.emit_expr(left)?; let r = self.emit_expr(right)?; @@ -1750,19 +2072,41 @@ impl CBackend { let left_is_string = self.is_string_expr(left); let right_is_string = self.is_string_expr(right); if left_is_string || right_is_string { + // If args are function calls returning String, store in temp for cleanup + let (actual_l, l_temp) = if left_is_string_call && !self.is_rc_temp(&l) { + let temp = format!("_concat_arg_{}", self.fresh_name()); + self.writeln(&format!("LuxString {} = {};", temp, l)); + (temp.clone(), Some(temp)) + } else { + (l.clone(), None) + }; + + let (actual_r, r_temp) = if right_is_string_call && !self.is_rc_temp(&r) { + let temp = format!("_concat_arg_{}", self.fresh_name()); + self.writeln(&format!("LuxString {} = {};", temp, r)); + (temp.clone(), Some(temp)) + } else { + (r.clone(), None) + }; + // String concat returns RC-managed string - track it let temp = format!("_str_concat_{}", self.fresh_name()); - self.writeln(&format!("LuxString {} = lux_string_concat({}, {});", temp, l, r)); + self.writeln(&format!("LuxString {} = lux_string_concat({}, {});", temp, actual_l, actual_r)); self.register_rc_var(&temp, "LuxString"); - // Also need to decref any temporary strings used as inputs - // (toString results, nested concats) + + // Decref any temporary strings used as inputs + // Now safe for both RC-managed and static strings due to magic check if self.is_rc_temp(&l) { self.writeln(&format!("lux_decref_string({});", l)); self.unregister_rc_var(&l); + } else if let Some(ref temp_l) = l_temp { + self.writeln(&format!("lux_decref_string({});", temp_l)); } if self.is_rc_temp(&r) { self.writeln(&format!("lux_decref_string({});", r)); self.unregister_rc_var(&r); + } else if let Some(ref temp_r) = r_temp { + self.writeln(&format!("lux_decref_string({});", temp_r)); } return Ok(temp); } @@ -1801,46 +2145,80 @@ impl CBackend { } Expr::If { condition, then_branch, else_branch, .. } => { - // Check if branches create RC values - if so, use if-statement to avoid - // allocating both when only one is needed - let then_creates_rc = self.expr_creates_rc_value(then_branch); - let else_creates_rc = self.expr_creates_rc_value(else_branch); + // Check if this is a void/Unit-returning if (for side effects only) + let then_type = self.infer_expr_type(then_branch); + let else_type = self.infer_expr_type(else_branch); + let is_void = then_type.as_ref().map(|t| t == "void").unwrap_or(false) + || else_type.as_ref().map(|t| t == "void").unwrap_or(false) + || matches!(else_branch.as_ref(), Expr::Literal(lit) if matches!(lit.kind, LiteralKind::Unit)); - if then_creates_rc || else_creates_rc { - // Use if-statement pattern to avoid allocating unused branch - let result_type = self.infer_expr_type(then_branch) - .or_else(|| self.infer_expr_type(else_branch)) - .unwrap_or_else(|| "LuxInt".to_string()); - let result_var = format!("_if_result_{}", self.fresh_name()); - - // Declare result variable - self.writeln(&format!("{} {};", result_type, result_var)); - - // Emit condition + if is_void { + // Void/Unit-returning if - emit as simple if statement let cond = self.emit_expr(condition)?; self.writeln(&format!("if ({}) {{", cond)); self.indent += 1; - - // Emit then branch let then_val = self.emit_expr(then_branch)?; - self.writeln(&format!("{} = {};", result_var, then_val)); + // Only emit if it's not just a Unit literal + if then_val != "0" && !then_val.is_empty() { + self.writeln(&format!("{};", then_val)); + } self.indent -= 1; - self.writeln("} else {"); - self.indent += 1; - // Emit else branch - let else_val = self.emit_expr(else_branch)?; - self.writeln(&format!("{} = {};", result_var, else_val)); - self.indent -= 1; + // Check if else branch is a Unit literal (meaning no else was provided) + let is_missing_else = matches!(else_branch.as_ref(), Expr::Literal(lit) if matches!(lit.kind, LiteralKind::Unit)); + if !is_missing_else { + self.writeln("} else {"); + self.indent += 1; + let else_val = self.emit_expr(else_branch)?; + if else_val != "0" && !else_val.is_empty() { + self.writeln(&format!("{};", else_val)); + } + self.indent -= 1; + } self.writeln("}"); - - Ok(result_var) + Ok("0".to_string()) // Return dummy value for void expression } else { - // Simple ternary for non-RC values - let cond = self.emit_expr(condition)?; - let then_val = self.emit_expr(then_branch)?; - let else_val = self.emit_expr(else_branch)?; - Ok(format!("({} ? {} : {})", cond, then_val, else_val)) + // Check if branches create RC values - if so, use if-statement to avoid + // allocating both when only one is needed + let then_creates_rc = self.expr_creates_rc_value(then_branch); + let else_creates_rc = self.expr_creates_rc_value(else_branch); + + if then_creates_rc || else_creates_rc { + // Use if-statement pattern to avoid allocating unused branch + let result_type = self.infer_expr_type(then_branch) + .or_else(|| self.infer_expr_type(else_branch)) + .unwrap_or_else(|| "LuxInt".to_string()); + let result_var = format!("_if_result_{}", self.fresh_name()); + + // Declare result variable + self.writeln(&format!("{} {};", result_type, result_var)); + + // Emit condition + let cond = self.emit_expr(condition)?; + self.writeln(&format!("if ({}) {{", cond)); + self.indent += 1; + + // Emit then branch + let then_val = self.emit_expr(then_branch)?; + self.writeln(&format!("{} = {};", result_var, then_val)); + self.indent -= 1; + self.writeln("} else {"); + self.indent += 1; + + // Emit else branch + let else_val = self.emit_expr(else_branch)?; + self.writeln(&format!("{} = {};", result_var, else_val)); + self.indent -= 1; + self.writeln("}"); + + Ok(result_var) + } else { + // Simple ternary for non-RC values + let cond = self.emit_expr(condition)?; + let then_val = self.emit_expr(then_branch)?; + let else_val = self.emit_expr(else_branch)?; + Ok(format!("({} ? {} : {})", cond, then_val, else_val)) + } } } @@ -1890,22 +2268,24 @@ impl CBackend { let c_func_name = self.mangle_name(&ident.name); // Pass evidence if this function uses effects and we have evidence let is_effectful = self.effectful_functions.contains(&ident.name); - if is_effectful && self.has_evidence { + let call_expr = if is_effectful && self.has_evidence { if args_str.is_empty() { - Ok(format!("{}(ev)", c_func_name)) + format!("{}(ev)", c_func_name) } else { - Ok(format!("{}(ev, {})", c_func_name, args_str)) + format!("{}(ev, {})", c_func_name, args_str) } } else if is_effectful { // Calling effectful function but don't have evidence - use default if args_str.is_empty() { - Ok(format!("{}(&default_evidence)", c_func_name)) + format!("{}(&default_evidence)", c_func_name) } else { - Ok(format!("{}(&default_evidence, {})", c_func_name, args_str)) + format!("{}(&default_evidence, {})", c_func_name, args_str) } } else { - Ok(format!("{}({})", c_func_name, args_str)) - } + format!("{}({})", c_func_name, args_str) + }; + + Ok(call_expr) } Expr::Var(ident) if self.variant_to_type.contains_key(&ident.name) => { // ADT constructor call - create struct with tag and data @@ -2081,6 +2461,10 @@ impl CBackend { // Ownership transfer: unregister source, register dest self.unregister_rc_var(&source_name); self.register_rc_var(&escaped_name, &typ); + } else if self.is_rc_temp(&val) { + // Value is already an RC temp - transfer ownership to bound variable + self.unregister_rc_var(&val); + self.register_rc_var(&escaped_name, &typ); } else if self.expr_creates_rc_value(value) { // Register RC variable if it creates a new RC value self.register_rc_var(&escaped_name, &typ); @@ -2113,7 +2497,14 @@ impl CBackend { // Emit the result expression let result_val = self.emit_expr(result)?; - // Pop scope and emit decrefs for block-local variables + // Check if result references RC-tracked variables from this scope + // If so, we need to save the result before cleanup to avoid use-after-free + let result_uses_rc_vars = self.expr_uses_rc_vars_from_scope(result); + + // Infer result type to check if it's void + let result_type = self.infer_expr_type(result); + let is_void = result_type.as_ref().map_or(false, |t| t == "void" || t == "LuxUnit"); + // Check if the result is a variable from this scope - if so, skip decref'ing it let skip_var = if let Expr::Var(ident) = result.as_ref() { if self.is_var_in_current_rc_scope(&ident.name) { @@ -2124,9 +2515,20 @@ impl CBackend { } else { None }; - self.pop_rc_scope_except(skip_var); - Ok(result_val) + // If result uses RC vars that would be freed, save it first (but not for void results) + let final_result = if result_uses_rc_vars && skip_var.is_none() && !self.is_rc_temp(&result_val) && !is_void { + let typ = result_type.unwrap_or_else(|| "LuxInt".to_string()); + let temp = format!("_block_result_{}", self.fresh_name()); + self.writeln(&format!("{} {} = {};", typ, temp, result_val)); + self.pop_rc_scope_except(None); + temp + } else { + self.pop_rc_scope_except(skip_var); + result_val + }; + + Ok(final_result) } Expr::EffectOp { effect, operation, args, .. } => { @@ -2140,19 +2542,54 @@ impl CBackend { // Built-in Console effect if effect.name == "Console" { if operation.name == "print" { - let arg = self.emit_expr(&args[0])?; - if self.has_evidence { - self.writeln(&format!("ev->console->print(ev->console->env, {});", arg)); + // Check if arg is a function call that returns an RC type (like String) + // If so, we need to store the result and decref it after printing + let needs_rc_cleanup = if let Expr::Call { func, .. } = &args[0] { + if let Expr::Var(ident) = func.as_ref() { + // Check if function returns LuxString + self.function_return_types.get(&ident.name) + .map(|t| t == "LuxString" || self.is_rc_type(t)) + .unwrap_or(false) + } else { + false + } } else { - self.writeln(&format!("lux_console_print({});", arg)); + false + }; + + let arg = self.emit_expr(&args[0])?; + + // If it's a function call returning RC type, store in temp for cleanup + let actual_arg = if needs_rc_cleanup && !self.is_rc_temp(&arg) { + let temp = format!("_print_arg_{}", self.fresh_name()); + self.writeln(&format!("LuxString {} = {};", temp, arg)); + temp + } else { + arg.clone() + }; + + if self.has_evidence { + self.writeln(&format!("ev->console->print(ev->console->env, {});", actual_arg)); + } else { + self.writeln(&format!("lux_console_print({});", actual_arg)); + } + // Immediately decref string concat temps or function return temps after use + if self.is_rc_temp(&arg) { + self.writeln(&format!("lux_decref_string({});", arg)); + self.unregister_rc_var(&arg); + } else if needs_rc_cleanup { + self.writeln(&format!("lux_decref_string({});", actual_arg)); } return Ok("NULL".to_string()); } else if operation.name == "readLine" { + let temp = format!("_readLine_{}", self.fresh_name()); if self.has_evidence { - return Ok("ev->console->readLine(ev->console->env)".to_string()); + self.writeln(&format!("LuxString {} = ev->console->readLine(ev->console->env);", temp)); } else { - return Ok("lux_console_readLine()".to_string()); + self.writeln(&format!("LuxString {} = lux_console_readLine();", temp)); } + self.register_rc_var(&temp, "LuxString"); + return Ok(temp); } } @@ -2214,11 +2651,14 @@ impl CBackend { match operation.name.as_str() { "read" => { let path = self.emit_expr(&args[0])?; + let temp = format!("_file_read_{}", self.fresh_name()); if self.has_evidence { - return Ok(format!("ev->file->read(ev->file->env, {})", path)); + self.writeln(&format!("LuxString {} = ev->file->read(ev->file->env, {});", temp, path)); } else { - return Ok(format!("lux_file_read({})", path)); + self.writeln(&format!("LuxString {} = lux_file_read({});", temp, path)); } + self.register_rc_var(&temp, "LuxString"); + return Ok(temp); } "write" => { let path = self.emit_expr(&args[0])?; @@ -2283,37 +2723,145 @@ impl CBackend { match operation.name.as_str() { "get" => { let url = self.emit_expr(&args[0])?; + let temp = format!("_http_get_{}", self.fresh_name()); if self.has_evidence { - return Ok(format!("ev->http->get(ev->http->env, {})", url)); + self.writeln(&format!("LuxString {} = ev->http->get(ev->http->env, {});", temp, url)); } else { - return Ok(format!("lux_http_get({})", url)); + self.writeln(&format!("LuxString {} = lux_http_get({});", temp, url)); } + self.register_rc_var(&temp, "LuxString"); + return Ok(temp); } "post" => { let url = self.emit_expr(&args[0])?; let body = self.emit_expr(&args[1])?; + let temp = format!("_http_post_{}", self.fresh_name()); if self.has_evidence { - return Ok(format!("ev->http->post(ev->http->env, {}, {})", url, body)); + self.writeln(&format!("LuxString {} = ev->http->post(ev->http->env, {}, {});", temp, url, body)); } else { - return Ok(format!("lux_http_post({}, {})", url, body)); + self.writeln(&format!("LuxString {} = lux_http_post({}, {});", temp, url, body)); } + self.register_rc_var(&temp, "LuxString"); + return Ok(temp); } "put" => { let url = self.emit_expr(&args[0])?; let body = self.emit_expr(&args[1])?; + let temp = format!("_http_put_{}", self.fresh_name()); if self.has_evidence { - return Ok(format!("ev->http->put(ev->http->env, {}, {})", url, body)); + self.writeln(&format!("LuxString {} = ev->http->put(ev->http->env, {}, {});", temp, url, body)); } else { - return Ok(format!("lux_http_put({}, {})", url, body)); + self.writeln(&format!("LuxString {} = lux_http_put({}, {});", temp, url, body)); } + self.register_rc_var(&temp, "LuxString"); + return Ok(temp); } "delete" => { let url = self.emit_expr(&args[0])?; + let temp = format!("_http_delete_{}", self.fresh_name()); if self.has_evidence { - return Ok(format!("ev->http->delete_req(ev->http->env, {})", url)); + self.writeln(&format!("LuxString {} = ev->http->delete_req(ev->http->env, {});", temp, url)); } else { - return Ok(format!("lux_http_delete({})", url)); + self.writeln(&format!("LuxString {} = lux_http_delete({});", temp, url)); } + self.register_rc_var(&temp, "LuxString"); + return Ok(temp); + } + _ => {} + } + } + + // Built-in Process effect + if effect.name == "Process" { + match operation.name.as_str() { + "exec" => { + let cmd = self.emit_expr(&args[0])?; + let temp = format!("_exec_{}", self.fresh_name()); + if self.has_evidence { + self.writeln(&format!("LuxString {} = ev->process->exec(ev->process->env, {});", temp, cmd)); + } else { + self.writeln(&format!("LuxString {} = lux_process_exec({});", temp, cmd)); + } + self.register_rc_var(&temp, "LuxString"); + return Ok(temp); + } + "env" => { + let name = self.emit_expr(&args[0])?; + // Process.env returns static string or NULL, not RC-managed + if self.has_evidence { + return Ok(format!("ev->process->getenv_fn(ev->process->env, {})", name)); + } else { + return Ok(format!("lux_process_env({})", name)); + } + } + "args" => { + let temp = format!("_args_{}", self.fresh_name()); + if self.has_evidence { + self.writeln(&format!("LuxList* {} = ev->process->args(ev->process->env);", temp)); + } else { + self.writeln(&format!("LuxList* {} = lux_process_args();", temp)); + } + self.register_rc_var(&temp, "LuxList*"); + return Ok(temp); + } + "cwd" => { + let temp = format!("_cwd_{}", self.fresh_name()); + if self.has_evidence { + self.writeln(&format!("LuxString {} = ev->process->cwd(ev->process->env);", temp)); + } else { + self.writeln(&format!("LuxString {} = lux_process_cwd();", temp)); + } + self.register_rc_var(&temp, "LuxString"); + return Ok(temp); + } + _ => {} + } + } + + // Built-in String module (not an effect, but handled similarly) + if effect.name == "String" { + match operation.name.as_str() { + "trim" => { + let s = self.emit_expr(&args[0])?; + // Create temp variable and track for cleanup (returns RC-managed string) + let temp = format!("_trim_{}", self.fresh_name()); + self.writeln(&format!("LuxString {} = lux_string_trim({});", temp, s)); + self.register_rc_var(&temp, "LuxString"); + return Ok(temp); + } + "length" => { + let s = self.emit_expr(&args[0])?; + return Ok(format!("lux_string_length({})", s)); + } + "lines" => { + let s = self.emit_expr(&args[0])?; + // Create temp variable and track for cleanup (returns RC-managed list) + let temp = format!("_lines_{}", self.fresh_name()); + self.writeln(&format!("LuxList* {} = lux_string_lines({});", temp, s)); + self.register_rc_var(&temp, "LuxList*"); + return Ok(temp); + } + "split" => { + let s = self.emit_expr(&args[0])?; + let sep = self.emit_expr(&args[1])?; + // Create temp variable and track for cleanup (returns RC-managed list) + let temp = format!("_split_{}", self.fresh_name()); + self.writeln(&format!("LuxList* {} = lux_string_split({}, {});", temp, s, sep)); + self.register_rc_var(&temp, "LuxList*"); + return Ok(temp); + } + "contains" => { + let s = self.emit_expr(&args[0])?; + let sub = self.emit_expr(&args[1])?; + return Ok(format!("lux_string_contains({}, {})", s, sub)); + } + "parseInt" => { + let s = self.emit_expr(&args[0])?; + return Ok(format!("lux_string_parseInt({})", s)); + } + "parseFloat" => { + let s = self.emit_expr(&args[0])?; + return Ok(format!("lux_string_parseFloat({})", s)); } _ => {} } @@ -2785,13 +3333,27 @@ impl CBackend { // Infer the type name from the first constructor pattern we find let type_name = self.infer_type_name_from_arms(arms); - // Infer the result type from the first arm body - let result_type = self.infer_expr_type(&arms[0].body).unwrap_or_else(|| "LuxInt".to_string()); + // Infer the result type from match arm bodies + // Try all arms since variables bound in patterns may not have known types yet + let result_type = self.infer_match_result_type(arms); + let is_void = result_type == "void"; - // Use the inferred type for scrutinee, fall back to LuxInt for simple patterns - let scrutinee_type = type_name.as_deref().unwrap_or("LuxInt"); + // Infer scrutinee type: first from constructor patterns, then from scrutinee expr, then from literal patterns + let scrutinee_type = if let Some(ref tn) = type_name { + tn.clone() + } else if let Some(expr_type) = self.infer_expr_type(expr) { + expr_type + } else if self.has_string_literal_patterns(arms) { + "LuxString".to_string() + } else { + "LuxInt".to_string() + }; self.writeln(&format!("{} {} = {};", scrutinee_type, scrutinee_var, scrutinee)); - self.writeln(&format!("{} {};", result_type, result_var)); + + // Don't declare a result variable for void-returning matches + if !is_void { + self.writeln(&format!("{} {};", result_type, result_var)); + } for (i, arm) in arms.iter().enumerate() { let condition = self.pattern_to_condition(&arm.pattern, &scrutinee_var, type_name.as_deref())?; @@ -2807,23 +3369,100 @@ impl CBackend { // Extract and emit variable bindings from the pattern let bindings = self.extract_pattern_bindings(&arm.pattern, &scrutinee_var, type_name.as_deref()); for (var_name, c_expr, c_type) in &bindings { - // If the type is a pointer, dereference when binding - // This way the variable holds a value, not a pointer - if c_type.ends_with('*') { - let base_type = &c_type[..c_type.len()-1]; - self.writeln(&format!("{} {} = *({});", base_type, var_name, c_expr)); + // For void* from generic types like Option, try to infer the actual type + let actual_type = if c_type == "void*" { + // First try to infer from the scrutinee expression - this gives us the Option type + 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()) + } } else { - self.writeln(&format!("{} {} = {};", c_type, var_name, c_expr)); + c_type.clone() + }; + + // 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)); + self.var_types.insert(var_name.clone(), actual_type); + } else if actual_type.ends_with('*') && actual_type != "void*" { + // Dereference pointer types (but not void*) + let base_type = &actual_type[..actual_type.len()-1]; + self.writeln(&format!("{} {} = *({});", base_type, var_name, c_expr)); + self.var_types.insert(var_name.clone(), base_type.to_string()); + } else { + self.writeln(&format!("{} {} = {};", actual_type, var_name, c_expr)); + self.var_types.insert(var_name.clone(), actual_type); } } let body = self.emit_expr(&arm.body)?; - self.writeln(&format!("{} = {};", result_var, body)); + if is_void { + // For void expressions, just emit the body without assignment + self.writeln(&format!("{};", body)); + } else { + self.writeln(&format!("{} = {};", result_var, body)); + } self.indent -= 1; } self.writeln("}"); - Ok(result_var) + + // For void matches, return a dummy NULL + if is_void { + Ok("NULL".to_string()) + } else { + Ok(result_var) + } + } + + /// 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 + let mut found_void = false; + 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 all arms were void, return void + if found_void { + return "void".to_string(); + } + // Default fallback + "LuxInt".to_string() + } + + /// Check if any arm has a string literal pattern + fn has_string_literal_patterns(&self, arms: &[MatchArm]) -> bool { + for arm in arms { + if let Pattern::Literal(lit) = &arm.pattern { + if matches!(&lit.kind, LiteralKind::String(_)) { + return true; + } + } + } + false } /// Try to infer the type name from match arms by looking at constructor patterns @@ -2951,10 +3590,30 @@ impl CBackend { "readInt" => Some("LuxInt".to_string()), _ => None, } + } else if effect.name == "Process" { + match operation.name.as_str() { + "exec" | "cwd" => Some("LuxString".to_string()), + "env" => Some("Option".to_string()), + "args" => Some("LuxList*".to_string()), + _ => None, + } + } else if effect.name == "String" { + match operation.name.as_str() { + "trim" => Some("LuxString".to_string()), + "length" => Some("LuxInt".to_string()), + "lines" | "split" => Some("LuxList*".to_string()), + "contains" => Some("LuxBool".to_string()), + "parseInt" | "parseFloat" => Some("Option".to_string()), + _ => None, + } } else { None } } + Expr::Match { arms, .. } => { + // Type of match is the type of its arms + Some(self.infer_match_result_type(arms)) + } _ => None, } } @@ -2965,7 +3624,12 @@ impl CBackend { Pattern::Var(_) => Ok("1".to_string()), // Var always matches, binding handled separately Pattern::Literal(lit) => { let lit_val = self.emit_literal_value(&lit.kind)?; - Ok(format!("{} == {}", scrutinee, lit_val)) + // For strings, use strcmp instead of pointer comparison + if matches!(&lit.kind, LiteralKind::String(_)) { + Ok(format!("strcmp({}, {}) == 0", scrutinee, lit_val)) + } else { + Ok(format!("{} == {}", scrutinee, lit_val)) + } } Pattern::Constructor { name, fields, .. } => { // Get the type name for proper tag generation @@ -3011,6 +3675,106 @@ impl CBackend { } } + /// Try to infer the inner type of an Option expression + /// For List.tail, List.head, etc., returns the element/list type + fn infer_option_inner_type(&self, expr: &Expr) -> Option { + match expr { + Expr::EffectOp { effect, operation, .. } => { + if effect.name == "List" { + 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()) + } + "tail" => { + // tail returns Option> - so inner type is LuxList* + Some("LuxList*".to_string()) + } + _ => None, + } + } else if effect.name == "Process" && operation.name == "env" { + Some("LuxString".to_string()) + } else if effect.name == "String" && (operation.name == "parseInt" || operation.name == "parseFloat") { + if operation.name == "parseInt" { + Some("LuxInt".to_string()) + } else { + Some("LuxFloat".to_string()) + } + } else { + None + } + } + Expr::Call { func, .. } => { + if let Expr::Field { object, field, .. } = func.as_ref() { + if let Expr::Var(module) = object.as_ref() { + if module.name == "List" { + match field.name.as_str() { + "head" | "get" | "find" => Some("LuxString".to_string()), + "tail" => Some("LuxList*".to_string()), + _ => None, + } + } else { + None + } + } else { + None + } + } else { + None + } + } + _ => None, + } + } + + /// Infer a variable's type by looking at how it's used in an expression + fn infer_var_type_from_usage(&self, expr: &Expr, var_name: &str) -> Option { + match expr { + Expr::Call { func, args, .. } => { + // Check if the variable is used as an argument + for (i, arg) in args.iter().enumerate() { + if let Expr::Var(ident) = arg { + if ident.name == var_name { + // Try to get the expected parameter type from function signature + if let Expr::Var(func_ident) = func.as_ref() { + if let Some(param_types) = self.function_param_types.get(&func_ident.name) { + if let Some(param_type) = param_types.get(i) { + return Some(param_type.clone()); + } + } + } + } + } + // Recursively check arguments + if let Some(t) = self.infer_var_type_from_usage(arg, var_name) { + return Some(t); + } + } + None + } + Expr::BinaryOp { left, right, .. } => { + 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); + } + } + } + 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) + .or_else(|| self.infer_var_type_from_usage(else_branch, var_name)) + } + _ => None, + } + } + /// Extract variable bindings from a pattern. /// Returns a list of (var_name, c_expression, c_type) triples. fn extract_pattern_bindings(&self, pattern: &Pattern, scrutinee: &str, expected_type: Option<&str>) -> Vec<(String, String, String)> { @@ -3034,7 +3798,17 @@ impl CBackend { for (i, field_pattern) in fields.iter().enumerate() { let field_access = format!("{}.data.{}.field{}", scrutinee, variant_lower, i); - let field_type = field_types.and_then(|ft| ft.get(i)).map(|s| s.as_str()); + let mut field_type = field_types.and_then(|ft| ft.get(i)).map(|s| s.as_str()); + + // Special handling for built-in Option type + // Option fields are void* in C but we need to preserve type info + if name.name == "Some" && field_type.is_none() { + // For Option, the inner value should be treated as void* + // but we can infer more specific types from context + // Default to void* for safety (requires cast at use site) + field_type = Some("void*"); + } + bindings.extend(self.extract_pattern_bindings(field_pattern, &field_access, field_type)); } @@ -3068,7 +3842,7 @@ impl CBackend { LiteralKind::Int(n) => Ok(format!("{}", n)), LiteralKind::Float(f) => Ok(format!("{}", f)), LiteralKind::Bool(b) => Ok(if *b { "true" } else { "false" }.to_string()), - LiteralKind::String(s) => Ok(format!("\"{}\"", s.replace("\"", "\\\""))), + LiteralKind::String(s) => Ok(format!("\"{}\"", escape_c_string(s))), LiteralKind::Char(c) => Ok(format!("'{}'", c)), LiteralKind::Unit => Ok("NULL".to_string()), } @@ -3096,6 +3870,11 @@ impl CBackend { self.writeln("int main(int argc, char** argv) {"); self.indent += 1; + // Store argc/argv for Process.args() + self.writeln("lux_argc = argc;"); + self.writeln("lux_argv = argv;"); + self.writeln(""); + // Execute top-level let bindings with run expressions for decl in &program.declarations { if let Declaration::Let(let_decl) = decl { @@ -3356,6 +4135,32 @@ impl CBackend { } } + /// Check if an expression references RC-tracked variables from the current scope + fn expr_uses_rc_vars_from_scope(&self, expr: &Expr) -> bool { + match expr { + Expr::Var(ident) => self.is_var_in_current_rc_scope(&ident.name), + Expr::Call { func, args, .. } => { + self.expr_uses_rc_vars_from_scope(func) + || args.iter().any(|a| self.expr_uses_rc_vars_from_scope(a)) + } + Expr::BinaryOp { left, right, .. } => { + self.expr_uses_rc_vars_from_scope(left) + || self.expr_uses_rc_vars_from_scope(right) + } + Expr::UnaryOp { operand, .. } => self.expr_uses_rc_vars_from_scope(operand), + Expr::Field { object, .. } => self.expr_uses_rc_vars_from_scope(object), + Expr::EffectOp { args, .. } => { + args.iter().any(|a| self.expr_uses_rc_vars_from_scope(a)) + } + Expr::If { condition, then_branch, else_branch, .. } => { + self.expr_uses_rc_vars_from_scope(condition) + || self.expr_uses_rc_vars_from_scope(then_branch) + || self.expr_uses_rc_vars_from_scope(else_branch) + } + _ => false, + } + } + /// Check if an expression creates a new RC-managed value that needs tracking fn expr_creates_rc_value(&self, expr: &Expr) -> bool { match expr { @@ -3365,7 +4170,7 @@ impl CBackend { // Lambdas create RC-managed closures Expr::Lambda { .. } => true, - // Calls to List.* that return lists or user functions returning RC types + // Calls to List.* / String.* that return RC values, or user functions Expr::Call { func, .. } => { if let Expr::Field { object, field, .. } = func.as_ref() { if let Expr::Var(module) = object.as_ref() { @@ -3376,24 +4181,46 @@ impl CBackend { "take" | "drop" | "range" ); } + if module.name == "String" { + // These String operations return new RC strings + return matches!(field.name.as_str(), + "trim" | "lines" | "split" + ); + } } } // Check if calling a user-defined function that returns an RC type if let Expr::Var(fn_name) = func.as_ref() { if let Some(ret_type) = self.function_return_types.get(&fn_name.name) { - return ret_type.ends_with('*') && ret_type != "LuxString"; + // Include LuxString since our string functions now return RC strings + return ret_type.ends_with('*') || ret_type == "LuxString"; } } false } - // Effect operations that return lists + // Effect operations that return RC values Expr::EffectOp { effect, operation, .. } => { if effect.name == "List" { matches!(operation.name.as_str(), "map" | "filter" | "concat" | "reverse" | "take" | "drop" | "range" ) + } else if effect.name == "Process" { + // Process.exec returns RC-managed string + matches!(operation.name.as_str(), "exec") + } else if effect.name == "Console" { + // Console.readLine returns RC-managed string + matches!(operation.name.as_str(), "readLine") + } else if effect.name == "File" { + // File.read returns RC-managed string + matches!(operation.name.as_str(), "read") + } else if effect.name == "Http" { + // Http operations return RC-managed strings + matches!(operation.name.as_str(), "get" | "post" | "put" | "delete") + } else if effect.name == "String" { + // String.trim, lines, split return RC values + matches!(operation.name.as_str(), "trim" | "lines" | "split") } else { false } @@ -3562,6 +4389,27 @@ impl Default for CBackend { } } +/// Escape a string for use as a C string literal +fn escape_c_string(s: &str) -> String { + let mut result = String::with_capacity(s.len() * 2); + for c in s.chars() { + match c { + '\\' => result.push_str("\\\\"), + '"' => result.push_str("\\\""), + '\n' => result.push_str("\\n"), + '\r' => result.push_str("\\r"), + '\t' => result.push_str("\\t"), + '\0' => result.push_str("\\0"), + c if c.is_ascii_control() => { + // Escape other control characters as hex + result.push_str(&format!("\\x{:02x}", c as u8)); + } + c => result.push(c), + } + } + result +} + #[cfg(test)] mod tests { use super::*;