diff --git a/src/codegen/c_backend.rs b/src/codegen/c_backend.rs index 6e6bb8a..61910d4 100644 --- a/src/codegen/c_backend.rs +++ b/src/codegen/c_backend.rs @@ -196,6 +196,18 @@ impl CBackend { self.closures.clear(); self.emit_prelude(); + // Register built-in variant → type mappings + self.variant_to_type.insert("Some".to_string(), "Option".to_string()); + self.variant_to_type.insert("None".to_string(), "Option".to_string()); + self.variant_to_type.insert("Ok".to_string(), "Result".to_string()); + self.variant_to_type.insert("Err".to_string(), "Result".to_string()); + + // Register built-in variant field types + self.variant_field_types.insert(("Option".to_string(), "Some".to_string()), vec!["void*".to_string()]); + self.variant_field_types.insert(("Option".to_string(), "None".to_string()), vec![]); + self.variant_field_types.insert(("Result".to_string(), "Ok".to_string()), vec!["void*".to_string()]); + self.variant_field_types.insert(("Result".to_string(), "Err".to_string()), vec!["void*".to_string()]); + // First pass: collect all function names, types, and effects for decl in &program.declarations { match decl { @@ -359,8 +371,8 @@ impl CBackend { // Check for string comparison - use strcmp instead of pointer comparison if matches!(op, BinaryOp::Eq | BinaryOp::Ne) { - let left_is_string = self.is_string_expr(left); - let right_is_string = self.is_string_expr(right); + 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 { if matches!(op, BinaryOp::Eq) { return Ok(format!("(strcmp({}, {}) == 0)", l, r)); @@ -679,6 +691,13 @@ impl CBackend { 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(""); + // Built-in Result type + self.writeln("// Built-in Result type"); + self.writeln("typedef enum { Result_TAG_OK, Result_TAG_ERR } Result_Tag;"); + self.writeln("typedef struct { void* field0; } Result_Ok_Data;"); + self.writeln("typedef struct { void* field0; } Result_Err_Data;"); + self.writeln("typedef struct { Result_Tag tag; union { Result_Ok_Data ok; Result_Err_Data err; } data; } Result;"); + self.writeln(""); self.writeln("// === Evidence Passing Types ==="); self.writeln("// These enable O(1) effect handler lookup instead of O(n) stack search."); @@ -2018,6 +2037,7 @@ impl CBackend { } /// Check if an expression is a string expression (for concatenation detection) + #[allow(dead_code)] fn is_string_expr(&self, expr: &Expr) -> bool { match expr { // String literals @@ -2169,6 +2189,14 @@ impl CBackend { // Push function scope for RC tracking self.push_rc_scope(); + // Register function parameter types in var_types so closures can look them up + for param in &func.params { + let escaped = self.escape_c_keyword(¶m.name.name); + if let Ok(c_type) = self.type_expr_to_c(¶m.typ) { + self.var_types.insert(escaped, c_type); + } + } + // Emit function body let result = self.emit_expr(&func.body)?; @@ -2310,8 +2338,8 @@ impl CBackend { // Check for string concatenation if matches!(op, BinaryOp::Add) { - let left_is_string = self.is_string_expr(left); - let right_is_string = self.is_string_expr(right); + 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 { // 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) { @@ -2355,8 +2383,8 @@ impl CBackend { // Check for string comparison - use strcmp instead of pointer comparison if matches!(op, BinaryOp::Eq | BinaryOp::Ne) { - let left_is_string = self.is_string_expr(left); - let right_is_string = self.is_string_expr(right); + 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 { if matches!(op, BinaryOp::Eq) { return Ok(format!("(strcmp({}, {}) == 0)", l, r)); @@ -2437,7 +2465,10 @@ impl CBackend { 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 { + let then_emits_stmts = self.expr_emits_statements(then_branch); + let else_emits_stmts = self.expr_emits_statements(else_branch); + + if then_creates_rc || else_creates_rc || then_emits_stmts || else_emits_stmts { // 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)) @@ -2452,16 +2483,25 @@ impl CBackend { self.writeln(&format!("if ({}) {{", cond)); self.indent += 1; + // Push RC scope for then branch so temps are cleaned up within the branch + self.push_rc_scope(); // Emit then branch let then_val = self.emit_expr(then_branch)?; self.writeln(&format!("{} = {};", result_var, then_val)); + // Pop RC scope - cleanup temps before leaving the branch + // Skip the then_val if it's being assigned to result (ownership transfer) + self.pop_rc_scope_except(Some(&then_val)); self.indent -= 1; self.writeln("} else {"); self.indent += 1; + // Push RC scope for else branch + self.push_rc_scope(); // Emit else branch let else_val = self.emit_expr(else_branch)?; self.writeln(&format!("{} = {};", result_var, else_val)); + // Pop RC scope - cleanup temps before leaving the branch + self.pop_rc_scope_except(Some(&else_val)); self.indent -= 1; self.writeln("}"); @@ -2644,9 +2684,16 @@ impl CBackend { .map(|t| self.type_expr_to_c(t).unwrap_or_else(|_| "LuxInt".to_string())) .unwrap_or_else(|| "LuxInt".to_string()); - // Determine captured variable types (default to LuxInt for now, escape C keywords) + // Determine captured variable types from var_types (fallback to LuxInt) let env_fields: Vec<(String, String)> = free_vars.iter() - .map(|v| (self.escape_c_keyword(v), "LuxInt".to_string())) + .map(|v| { + let escaped = self.escape_c_keyword(v); + let typ = self.var_types.get(&escaped) + .or_else(|| self.var_types.get(v.as_str())) + .cloned() + .unwrap_or_else(|| "LuxInt".to_string()); + (escaped, typ) + }) .collect(); // Store closure info for later emission @@ -3735,6 +3782,9 @@ impl CBackend { self.indent += 1; + // Push RC scope for this match arm so temp variables are cleaned up within the arm + self.push_rc_scope(); + // 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 { @@ -3782,8 +3832,11 @@ impl CBackend { if is_void { // For void expressions, just emit the body without assignment self.writeln(&format!("{};", body)); + self.pop_rc_scope(); } else { self.writeln(&format!("{} = {};", result_var, body)); + // Pop RC scope, skip the body value if it's being assigned (ownership transfer) + self.pop_rc_scope_except(Some(&body)); } self.indent -= 1; } @@ -3813,6 +3866,15 @@ impl CBackend { return t; } } + // If the body is a simple Var referencing a pattern-bound variable, + // try to infer the type from the pattern context + if let Expr::Var(body_ident) = &arm.body { + if let Some(t) = self.infer_pattern_binding_type(&arm.pattern, &body_ident.name) { + if t != "void*" { + return t; + } + } + } } // If all arms were void, return void if found_void { @@ -3822,6 +3884,40 @@ impl CBackend { "LuxInt".to_string() } + /// Infer the type of a pattern-bound variable by looking at what the pattern + /// destructs. E.g. Ok(x) in a Result match -> x is void* (generic). + fn infer_pattern_binding_type(&self, pattern: &Pattern, var_name: &str) -> Option { + match pattern { + Pattern::Constructor { name, fields, .. } => { + // Check if any field binds the variable + for (i, field) in fields.iter().enumerate() { + if let Pattern::Var(ident) = field { + if ident.name == var_name { + // Look up the field type from variant_field_types + if let Some(type_name) = self.variant_to_type.get(&name.name) { + if let Some(field_types) = self.variant_field_types.get(&(type_name.clone(), name.name.clone())) { + if let Some(ft) = field_types.get(i) { + return Some(ft.clone()); + } + } + } + } + } + } + None + } + Pattern::Var(ident) => { + if ident.name == var_name { + // This is a wildcard-like binding, type comes from scrutinee + None + } else { + None + } + } + _ => None, + } + } + /// Check if any arm has a string literal pattern fn has_string_literal_patterns(&self, arms: &[MatchArm]) -> bool { for arm in arms { @@ -3891,14 +3987,60 @@ impl CBackend { return Some(ret_type.clone()); } } + // Check module method calls like String.substring, List.map, etc. + if let Expr::Field { object, field, .. } = func.as_ref() { + 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()), + "substring" | "trim" | "toLower" | "toUpper" | "replace" + | "join" | "fromChar" | "repeat" => return Some("LuxString".to_string()), + "split" | "lines" | "chars" => return Some("LuxList*".to_string()), + "startsWith" | "endsWith" | "contains" => return Some("LuxBool".to_string()), + "parseInt" | "parseFloat" => return Some("Option".to_string()), + _ => {} + }, + "List" => match field.name.as_str() { + "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()), + "isEmpty" | "any" | "all" | "contains" => return Some("LuxBool".to_string()), + _ => {} + }, + "Int" => match field.name.as_str() { + "toString" => return Some("LuxString".to_string()), + "parse" => return Some("Option".to_string()), + "abs" | "min" | "max" => return Some("LuxInt".to_string()), + _ => {} + }, + "Float" => match field.name.as_str() { + "toString" => return Some("LuxString".to_string()), + "parse" => return Some("Option".to_string()), + _ => return Some("LuxFloat".to_string()), + }, + _ => {} + } + } + } None } Expr::BinaryOp { op, left, right, .. } => { // String concatenation with + returns LuxString if matches!(op, BinaryOp::Add) { - if self.is_string_expr(left) || self.is_string_expr(right) { + let left_type = self.infer_expr_type(left); + let right_type = self.infer_expr_type(right); + if left_type.as_deref() == Some("LuxString") || right_type.as_deref() == Some("LuxString") { return Some("LuxString".to_string()); } + return left_type.or(right_type); + } + // Comparison ops return bool + if matches!(op, BinaryOp::Eq | BinaryOp::Ne | BinaryOp::Lt | BinaryOp::Le | BinaryOp::Gt | BinaryOp::Ge) { + return Some("LuxBool".to_string()); + } + if matches!(op, BinaryOp::And | BinaryOp::Or) { + return Some("LuxBool".to_string()); } // For other binary ops, infer from operands self.infer_expr_type(left).or_else(|| self.infer_expr_type(right)) @@ -3945,6 +4087,7 @@ impl CBackend { "read" => Some("LuxString".to_string()), "write" | "append" | "delete" | "mkdir" => Some("void".to_string()), "exists" | "isDir" => Some("LuxBool".to_string()), + "readDir" | "listDir" => Some("LuxList*".to_string()), _ => None, } } else if effect.name == "Http" { @@ -3968,10 +4111,11 @@ impl CBackend { } } 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()), + "trim" | "substring" | "toLower" | "toUpper" | "replace" + | "join" | "fromChar" | "repeat" => Some("LuxString".to_string()), + "length" | "indexOf" | "lastIndexOf" => Some("LuxInt".to_string()), + "lines" | "split" | "chars" => Some("LuxList*".to_string()), + "contains" | "startsWith" | "endsWith" => Some("LuxBool".to_string()), "parseInt" | "parseFloat" => Some("Option".to_string()), _ => None, } @@ -3983,6 +4127,23 @@ impl CBackend { // Type of match is the type of its arms Some(self.infer_match_result_type(arms)) } + Expr::Field { object, field, .. } => { + // Check if it's a record field access — look up object type + field + if let Some(obj_type) = self.infer_expr_type(object) { + // Look through variant_field_types for record fields + // For now, check var_types for the field access result + let field_key = format!("{}.{}", obj_type, field.name); + if let Some(t) = self.var_types.get(&field_key) { + return Some(t.clone()); + } + } + None + } + Expr::Let { body, .. } => { + // Type of a let expression is the type of its body + self.infer_expr_type(body) + } + Expr::Lambda { .. } => Some("LuxClosure*".to_string()), _ => None, } } @@ -4064,11 +4225,12 @@ impl CBackend { } } 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 if effect.name == "String" { + match operation.name.as_str() { + "parseInt" => Some("LuxInt".to_string()), + "parseFloat" => Some("LuxFloat".to_string()), + "indexOf" | "lastIndexOf" => Some("LuxInt".to_string()), + _ => None, } } else { None @@ -4077,14 +4239,19 @@ impl CBackend { 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() { + match module.name.as_str() { + "List" => match field.name.as_str() { "head" | "get" | "find" => Some("LuxString".to_string()), "tail" => Some("LuxList*".to_string()), _ => None, - } - } else { - None + }, + "String" => match field.name.as_str() { + "indexOf" | "lastIndexOf" => Some("LuxInt".to_string()), + "parseInt" => Some("LuxInt".to_string()), + "parseFloat" => Some("LuxFloat".to_string()), + _ => None, + }, + _ => None, } } else { None @@ -4093,6 +4260,7 @@ impl CBackend { None } } + Expr::Var(_) => None, _ => None, } } @@ -4536,6 +4704,49 @@ impl CBackend { } } + /// Check if an expression will emit C statements (variable declarations, etc.) + /// which means it cannot be safely used in a ternary expression. + fn expr_emits_statements(&self, expr: &Expr) -> bool { + match expr { + Expr::Block { statements, result, .. } => { + !statements.is_empty() || self.expr_emits_statements(result) + } + Expr::Let { .. } => true, + Expr::Match { .. } => true, + Expr::If { then_branch, else_branch, .. } => { + self.expr_emits_statements(then_branch) || self.expr_emits_statements(else_branch) + } + Expr::Call { func, args, .. } => { + // Constructor calls with field access (e.g. BState(...)) may be fine, + // but calls to functions returning strings will emit concat temps + if let Expr::Var(ident) = func.as_ref() { + if let Some(ret_type) = self.function_return_types.get(&ident.name) { + if ret_type == "LuxString" { + return true; + } + } + } + // Check if any argument emits statements + args.iter().any(|a| self.expr_emits_statements(a)) + } + Expr::BinaryOp { op, left, right, .. } => { + // String concatenation emits temp variables + if matches!(op, BinaryOp::Add) { + let lt = self.infer_expr_type(left); + let rt = self.infer_expr_type(right); + if lt.as_deref() == Some("LuxString") || rt.as_deref() == Some("LuxString") { + return true; + } + } + self.expr_emits_statements(left) || self.expr_emits_statements(right) + } + Expr::Lambda { .. } => true, + Expr::List { .. } => true, + Expr::EffectOp { .. } => true, + _ => 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 {