feat: add list support to C backend and improve compile workflow

C Backend Lists:
- Add LuxList type (dynamic array with void* boxing)
- Implement all 16 list operations: length, isEmpty, concat, reverse,
  range, take, drop, head, tail, get, map, filter, fold, find, any, all
- Higher-order operations generate inline loops with closure calls
- Fix unique variable names to prevent redefinition errors

Compile Command:
- `lux compile file.lux` now produces a binary (like rustc, go build)
- Add `--emit-c` flag to output C code instead
- Binary name derived from source filename (foo.lux -> ./foo)
- Clean up temp files after compilation

Documentation:
- Create docs/C_BACKEND.md with full strategy documentation
- Document compilation pipeline, runtime types, limitations
- Compare with Koka, Rust, Zig, Go, Nim, OCaml approaches
- Outline future roadmap (evidence passing, Perceus RC)
- Fix misleading doc comment (remove false Perceus claim)
- Update OVERVIEW.md and ROADMAP.md to reflect list completion

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-14 11:02:26 -05:00
parent d284ee58a8
commit 909dbf7a97
5 changed files with 954 additions and 87 deletions

View File

@@ -1,29 +1,46 @@
//! C code generation backend for Lux
//!
//! Compiles Lux programs to C code that can be compiled with GCC/Clang.
//! Inspired by Koka's approach: effects compile to evidence passing,
//! no garbage collector needed with Perceus-style reference counting.
//!
//! ## Compilation Strategy
//!
//! Lux source → Parse → Type check → Generate C → Invoke cc/gcc/clang → Binary
//!
//! This approach is similar to Koka, Nim, and Chicken Scheme. It leverages
//! decades of C compiler optimizations (GCC/Clang) without reimplementing them.
//!
//! ## Runtime Type Representations
//!
//! | Lux Type | C Type |
//! |----------|--------|
//! | Int | `int64_t` (LuxInt) |
//! | Float | `double` (LuxFloat) |
//! | Bool | `bool` (LuxBool) |
//! | String | `char*` (LuxString) |
//! | Closure | `struct {void* env, void* fn_ptr}` (LuxClosure) |
//! | ADT | Tagged union (enum tag + union of variant structs) |
//! | List | `struct {void** elements, int64_t length, capacity}` (LuxList) |
//!
//! ## Supported Features
//!
//! - **Functions**: Direct function calls with proper name mangling
//! - **Closures**: Lambda expressions with captured environments
//! - Environment structs hold captured variables
//! - Closures are `{void* env, void* fn_ptr}` structs
//! - **ADTs**: Algebraic data types (enums with data)
//! - Tag enums for variant discrimination
//! - Union structs for variant data
//! - Recursive types use pointers with heap allocation
//! - **Pattern Matching**: Full pattern variable binding
//! - Constructor patterns extract variant data
//! - Nested patterns supported
//! - Type inference for bound variables
//! - **Functions**: Direct function calls with name mangling (`foo` → `foo_lux`)
//! - **Closures**: Heap-allocated environment struct + function pointer
//! - **ADTs**: Tagged unions with exhaustive pattern matching
//! - **Pattern Matching**: Compiles to if/else chains checking tags
//! - **Lists**: Dynamic arrays with void* boxing for generic elements
//!
//! ## Limitations
//! ## Current Limitations
//!
//! - **Lists**: Not yet implemented (use interpreter)
//! - **Memory**: No automatic deallocation (memory leaks for closures/ADTs)
//! - **Effects**: Only Console.print supported
//! - **Memory**: No deallocation - everything leaks (GC/RC not yet implemented)
//! - **Effects**: Only `Console.print` supported (hardcoded to printf)
//! - **If/else side effects**: Uses ternary `?:`, so both branches execute
//! during codegen if they contain effects like Console.print
//!
//! ## Future Work (see docs/C_BACKEND.md)
//!
//! - Evidence passing for zero-cost effects (like Koka)
//! - Perceus-style reference counting for memory management
//! - More effects (File, Http, etc.)
use crate::ast::*;
use std::collections::{HashSet, HashMap};
@@ -368,6 +385,90 @@ impl CBackend {
self.writeln(" printf(\"%s\\n\", msg);");
self.writeln("}");
self.writeln("");
self.writeln("// === List Types ===");
self.writeln("");
self.writeln("typedef struct {");
self.writeln(" void** elements;");
self.writeln(" int64_t length;");
self.writeln(" int64_t capacity;");
self.writeln("} LuxList;");
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;");
self.writeln("");
self.writeln("// === List Operations ===");
self.writeln("");
self.writeln("static LuxList* lux_list_new(int64_t capacity) {");
self.writeln(" LuxList* list = malloc(sizeof(LuxList));");
self.writeln(" list->capacity = capacity > 0 ? capacity : 4;");
self.writeln(" list->elements = malloc(sizeof(void*) * list->capacity);");
self.writeln(" list->length = 0;");
self.writeln(" return list;");
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("");
self.writeln("static LuxList* lux_list_concat(LuxList* a, LuxList* b) {");
self.writeln(" LuxList* result = lux_list_new(a->length + b->length);");
self.writeln(" for (int64_t i = 0; i < a->length; i++) result->elements[i] = a->elements[i];");
self.writeln(" for (int64_t i = 0; i < b->length; i++) result->elements[a->length + i] = b->elements[i];");
self.writeln(" result->length = a->length + b->length;");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxList* lux_list_reverse(LuxList* list) {");
self.writeln(" LuxList* result = lux_list_new(list->length);");
self.writeln(" for (int64_t i = 0; i < list->length; i++) result->elements[i] = list->elements[list->length - 1 - i];");
self.writeln(" result->length = list->length;");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxList* lux_list_take(LuxList* list, int64_t n) {");
self.writeln(" if (n <= 0) return lux_list_new(0);");
self.writeln(" if (n > list->length) n = list->length;");
self.writeln(" LuxList* result = lux_list_new(n);");
self.writeln(" for (int64_t i = 0; i < n; i++) result->elements[i] = list->elements[i];");
self.writeln(" result->length = n;");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxList* lux_list_drop(LuxList* list, int64_t n) {");
self.writeln(" if (n >= list->length) return lux_list_new(0);");
self.writeln(" if (n <= 0) return list;");
self.writeln(" int64_t new_len = list->length - n;");
self.writeln(" LuxList* result = lux_list_new(new_len);");
self.writeln(" for (int64_t i = 0; i < new_len; i++) result->elements[i] = list->elements[n + i];");
self.writeln(" result->length = new_len;");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxList* lux_list_range(int64_t start, int64_t end) {");
self.writeln(" if (end <= start) return lux_list_new(0);");
self.writeln(" int64_t len = end - start;");
self.writeln(" LuxList* result = lux_list_new(len);");
self.writeln(" for (int64_t i = 0; i < len; i++) {");
self.writeln(" LuxInt* p = malloc(sizeof(LuxInt)); *p = start + i;");
self.writeln(" result->elements[i] = p;");
self.writeln(" }");
self.writeln(" result->length = len;");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
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("// === Boxing/Unboxing ===");
self.writeln("");
self.writeln("static void* lux_box_int(LuxInt n) { LuxInt* p = malloc(sizeof(LuxInt)); *p = n; return p; }");
self.writeln("static LuxInt lux_unbox_int(void* p) { return *(LuxInt*)p; }");
self.writeln("static void* lux_box_bool(LuxBool b) { LuxBool* p = malloc(sizeof(LuxBool)); *p = b; return p; }");
self.writeln("static LuxBool lux_unbox_bool(void* p) { return *(LuxBool*)p; }");
self.writeln("static void* lux_box_string(LuxString s) { return s; }");
self.writeln("static LuxString lux_unbox_string(void* p) { return (LuxString)p; }");
self.writeln("");
self.writeln("// === Forward Declarations ===");
self.writeln("");
}
@@ -614,9 +715,10 @@ impl CBackend {
let val = self.emit_expr(value)?;
let var_name = format!("{}_{}", name.name, self.fresh_name());
// For simple cases, we can use a compound literal or statement expression
// For now, emit as a block
self.writeln(&format!("LuxInt {} = {};", var_name, val));
// Infer the type from the value expression
let var_type = self.infer_expr_type(value).unwrap_or_else(|| "LuxInt".to_string());
self.writeln(&format!("{} {} = {};", var_type, var_name, val));
// Substitute the name in the body
// For now, assume the variable is directly usable
@@ -625,6 +727,15 @@ impl CBackend {
}
Expr::Call { func, args, .. } => {
// Check for List module calls first (List.map, List.filter, etc.)
if let Expr::Field { object, field, .. } = func.as_ref() {
if let Expr::Var(module_name) = object.as_ref() {
if module_name.name == "List" {
return self.emit_list_operation(&field.name, args);
}
}
}
let arg_strs: Result<Vec<_>, _> = args.iter().map(|a| self.emit_expr(a)).collect();
let args_str = arg_strs?.join(", ");
@@ -795,6 +906,11 @@ impl CBackend {
return Ok("NULL".to_string());
}
// List module operations (treated as effect by parser but handled specially)
if effect.name == "List" {
return self.emit_list_operation(&operation.name, args);
}
// For other effects, emit evidence-passing call
let arg_strs: Result<Vec<_>, _> = args.iter().map(|a| self.emit_expr(a)).collect();
Ok(format!("ev_{}__{}({})",
@@ -820,6 +936,10 @@ impl CBackend {
self.emit_match(scrutinee, arms)
}
Expr::List { elements, .. } => {
self.emit_list_literal(elements)
}
_ => Err(CGenError {
message: format!("Unsupported expression type in C backend"),
span: None,
@@ -827,6 +947,322 @@ impl CBackend {
}
}
fn emit_list_literal(&mut self, elements: &[Expr]) -> Result<String, CGenError> {
let list_var = format!("_list_{}", self.fresh_name());
let len = elements.len();
self.writeln(&format!("LuxList* {} = lux_list_new({});", list_var, len));
for (i, elem) in elements.iter().enumerate() {
let elem_val = self.emit_expr(elem)?;
let boxed = self.box_value(&elem_val, self.infer_expr_type(elem).as_deref());
self.writeln(&format!("{}->elements[{}] = {};", list_var, i, boxed));
}
self.writeln(&format!("{}->length = {};", list_var, len));
Ok(list_var)
}
fn box_value(&self, val: &str, type_hint: Option<&str>) -> String {
match type_hint {
Some("LuxInt") => format!("lux_box_int({})", val),
Some("LuxBool") => format!("lux_box_bool({})", val),
Some("LuxString") => format!("lux_box_string({})", val),
Some(t) if t.ends_with('*') => val.to_string(), // Already a pointer
_ => format!("lux_box_int({})", val), // Default to int boxing
}
}
fn unbox_value(&self, val: &str, type_hint: Option<&str>) -> String {
match type_hint {
Some("LuxInt") => format!("lux_unbox_int({})", val),
Some("LuxBool") => format!("lux_unbox_bool({})", val),
Some("LuxString") => format!("lux_unbox_string({})", val),
_ => format!("lux_unbox_int({})", val), // Default to int unboxing
}
}
/// Emit code for List module operations (List.map, List.filter, etc.)
fn emit_list_operation(&mut self, op: &str, args: &[Expr]) -> Result<String, CGenError> {
match op {
// Simple operations - direct C function calls
"length" => {
if args.len() != 1 {
return Err(CGenError { message: "List.length takes 1 argument".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
Ok(format!("lux_list_length({})", list))
}
"isEmpty" => {
if args.len() != 1 {
return Err(CGenError { message: "List.isEmpty takes 1 argument".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
Ok(format!("lux_list_isEmpty({})", list))
}
"concat" => {
if args.len() != 2 {
return Err(CGenError { message: "List.concat takes 2 arguments".to_string(), span: None });
}
let list1 = self.emit_expr(&args[0])?;
let list2 = self.emit_expr(&args[1])?;
Ok(format!("lux_list_concat({}, {})", list1, list2))
}
"reverse" => {
if args.len() != 1 {
return Err(CGenError { message: "List.reverse takes 1 argument".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
Ok(format!("lux_list_reverse({})", list))
}
"range" => {
if args.len() != 2 {
return Err(CGenError { message: "List.range takes 2 arguments".to_string(), span: None });
}
let start = self.emit_expr(&args[0])?;
let end = self.emit_expr(&args[1])?;
Ok(format!("lux_list_range({}, {})", start, end))
}
"take" => {
if args.len() != 2 {
return Err(CGenError { message: "List.take takes 2 arguments".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let n = self.emit_expr(&args[1])?;
Ok(format!("lux_list_take({}, {})", list, n))
}
"drop" => {
if args.len() != 2 {
return Err(CGenError { message: "List.drop takes 2 arguments".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let n = self.emit_expr(&args[1])?;
Ok(format!("lux_list_drop({}, {})", list, n))
}
// Access operations - return Option
"head" => {
if args.len() != 1 {
return Err(CGenError { message: "List.head takes 1 argument".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let result_var = format!("_head_{}", self.fresh_name());
self.writeln(&format!("Option {} = ({}->length > 0) ? lux_option_some({}->elements[0]) : lux_option_none();", result_var, list, list));
Ok(result_var)
}
"tail" => {
if args.len() != 1 {
return Err(CGenError { message: "List.tail takes 1 argument".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let result_var = format!("_tail_{}", self.fresh_name());
self.writeln(&format!("Option {};", result_var));
self.writeln(&format!("if ({0}->length > 0) {{", list));
self.indent += 1;
self.writeln(&format!("LuxList* _tail_list = lux_list_new({0}->length - 1);", list));
self.writeln(&format!("for (int64_t i = 1; i < {0}->length; i++) {{", list));
self.indent += 1;
self.writeln(&format!("_tail_list->elements[i-1] = {0}->elements[i];", list));
self.indent -= 1;
self.writeln("}");
self.writeln(&format!("_tail_list->length = {0}->length - 1;", list));
self.writeln(&format!("{} = lux_option_some(_tail_list);", result_var));
self.indent -= 1;
self.writeln("} else {");
self.indent += 1;
self.writeln(&format!("{} = lux_option_none();", result_var));
self.indent -= 1;
self.writeln("}");
Ok(result_var)
}
"get" => {
if args.len() != 2 {
return Err(CGenError { message: "List.get takes 2 arguments".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let idx = self.emit_expr(&args[1])?;
let result_var = format!("_get_{}", self.fresh_name());
self.writeln(&format!("Option {} = ({} >= 0 && {} < {}->length) ? lux_option_some({}->elements[{}]) : lux_option_none();", result_var, idx, idx, list, list, idx));
Ok(result_var)
}
// Higher-order operations - inline loops
"map" => {
if args.len() != 2 {
return Err(CGenError { message: "List.map takes 2 arguments".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let closure = self.emit_expr(&args[1])?;
let id = self.fresh_name();
let result_var = format!("_map_{}", id);
let i_var = format!("_i_{}", id);
let elem_var = format!("_elem_{}", id);
let fn_var = format!("_fn_{}", id);
let mapped_var = format!("_mapped_{}", id);
self.writeln(&format!("LuxList* {} = 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.indent -= 1;
self.writeln("}");
self.writeln(&format!("{}->length = {}->length;", result_var, list));
Ok(result_var)
}
"filter" => {
if args.len() != 2 {
return Err(CGenError { message: "List.filter takes 2 arguments".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let closure = self.emit_expr(&args[1])?;
let id = self.fresh_name();
let result_var = format!("_filter_{}", id);
let count_var = format!("_count_{}", id);
let i_var = format!("_i_{}", id);
let elem_var = format!("_elem_{}", id);
let fn_var = format!("_fn_{}", id);
let keep_var = format!("_keep_{}", id);
self.writeln(&format!("LuxList* {} = lux_list_new({}->length);", result_var, list));
self.writeln(&format!("int64_t {} = 0;", count_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({}));", keep_var, fn_var, fn_var, elem_var));
self.writeln(&format!("if ({}) {{", keep_var));
self.indent += 1;
self.writeln(&format!("{}->elements[{}++] = {};", result_var, count_var, elem_var));
self.indent -= 1;
self.writeln("}");
self.indent -= 1;
self.writeln("}");
self.writeln(&format!("{}->length = {};", result_var, count_var));
Ok(result_var)
}
"fold" => {
if args.len() != 3 {
return Err(CGenError { message: "List.fold takes 3 arguments".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let init = self.emit_expr(&args[1])?;
let closure = self.emit_expr(&args[2])?;
let id = self.fresh_name();
let result_var = format!("_fold_{}", id);
let i_var = format!("_i_{}", id);
let elem_var = format!("_elem_{}", id);
let fn_var = format!("_fn_{}", id);
self.writeln(&format!("LuxInt {} = {};", result_var, 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.indent -= 1;
self.writeln("}");
Ok(result_var)
}
"find" => {
if args.len() != 2 {
return Err(CGenError { message: "List.find takes 2 arguments".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let closure = self.emit_expr(&args[1])?;
let id = self.fresh_name();
let result_var = format!("_find_{}", id);
let i_var = format!("_i_{}", id);
let elem_var = format!("_elem_{}", id);
let fn_var = format!("_fn_{}", id);
let matches_var = format!("_matches_{}", id);
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!("if ({}) {{", matches_var));
self.indent += 1;
self.writeln(&format!("{} = lux_option_some({});", result_var, elem_var));
self.writeln("break;");
self.indent -= 1;
self.writeln("}");
self.indent -= 1;
self.writeln("}");
Ok(result_var)
}
"any" => {
if args.len() != 2 {
return Err(CGenError { message: "List.any takes 2 arguments".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let closure = self.emit_expr(&args[1])?;
let id = self.fresh_name();
let result_var = format!("_any_{}", id);
let i_var = format!("_i_{}", id);
let elem_var = format!("_elem_{}", id);
let fn_var = format!("_fn_{}", id);
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.indent += 1;
self.writeln(&format!("{} = 1;", result_var));
self.writeln("break;");
self.indent -= 1;
self.writeln("}");
self.indent -= 1;
self.writeln("}");
Ok(result_var)
}
"all" => {
if args.len() != 2 {
return Err(CGenError { message: "List.all takes 2 arguments".to_string(), span: None });
}
let list = self.emit_expr(&args[0])?;
let closure = self.emit_expr(&args[1])?;
let id = self.fresh_name();
let result_var = format!("_all_{}", id);
let i_var = format!("_i_{}", id);
let elem_var = format!("_elem_{}", id);
let fn_var = format!("_fn_{}", id);
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.indent += 1;
self.writeln(&format!("{} = 0;", result_var));
self.writeln("break;");
self.indent -= 1;
self.writeln("}");
self.indent -= 1;
self.writeln("}");
Ok(result_var)
}
_ => Err(CGenError {
message: format!("Unsupported List operation: {}", op),
span: None,
}),
}
}
fn emit_expr_with_substitution(&mut self, expr: &Expr, from: &str, to: &str) -> Result<String, CGenError> {
// Simple substitution - in a real implementation, this would be more sophisticated
match expr {
@@ -937,6 +1373,25 @@ impl CBackend {
// Type of block is the type of the result expression
self.infer_expr_type(result)
}
Expr::List { .. } => Some("LuxList*".to_string()),
Expr::EffectOp { effect, operation, .. } => {
// List operations have known return types
if effect.name == "List" {
match operation.name.as_str() {
// Operations returning lists
"map" | "filter" | "concat" | "reverse" | "take" | "drop" | "range" => Some("LuxList*".to_string()),
// Operations returning Option
"head" | "tail" | "get" | "find" => Some("Option".to_string()),
// Operations returning Int
"length" | "fold" => Some("LuxInt".to_string()),
// Operations returning Bool
"isEmpty" | "any" | "all" => Some("LuxBool".to_string()),
_ => None,
}
} else {
None
}
}
_ => None,
}
}
@@ -1128,11 +1583,11 @@ impl CBackend {
}
}
TypeExpr::App(base, _) => {
// For now, use void* for generic types
// Handle generic types
if let TypeExpr::Named(name) = base.as_ref() {
match name.name.as_str() {
"List" => Ok("void*".to_string()),
"Option" => Ok("void*".to_string()),
"List" => Ok("LuxList*".to_string()),
"Option" => Ok("Option".to_string()),
_ => Ok("void*".to_string()),
}
} else {