feat: add pattern variable binding to C backend
Implements full pattern matching with variable binding for the C backend:
- Extract variable bindings from patterns (Var, Constructor, Tuple, Record)
- Infer C types for bound variables using variant field type tracking
- Handle recursive ADTs with pointer fields and heap allocation
- Dereference pointer bindings automatically for value semantics
Key implementation details:
- variant_to_type: Maps variant names to parent type for tag generation
- variant_field_types: Maps (type, variant) to field types for inference
- Recursive type fields use Type* pointers with malloc/memcpy
- Pattern bindings dereference pointers to maintain value semantics
Examples that now work:
- match opt { Some(x) => x, None => 0 }
- match tree { Leaf(n) => n, Node(l, r) => sum(l) + sum(r) }
Updates documentation to reflect C backend progress.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,9 +3,30 @@
|
||||
//! 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.
|
||||
//!
|
||||
//! ## 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
|
||||
//!
|
||||
//! ## Limitations
|
||||
//!
|
||||
//! - **Lists**: Not yet implemented (use interpreter)
|
||||
//! - **Memory**: No automatic deallocation (memory leaks for closures/ADTs)
|
||||
//! - **Effects**: Only Console.print supported
|
||||
|
||||
use crate::ast::*;
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use std::fmt::Write;
|
||||
|
||||
/// C code generation errors
|
||||
@@ -53,6 +74,10 @@ pub struct CBackend {
|
||||
local_vars: HashSet<String>,
|
||||
/// Functions that return closures
|
||||
closure_returning_functions: HashSet<String>,
|
||||
/// Mapping from variant names to their parent type name
|
||||
variant_to_type: HashMap<String, String>,
|
||||
/// Mapping from (type_name, variant_name) to field types
|
||||
variant_field_types: HashMap<(String, String), Vec<String>>,
|
||||
}
|
||||
|
||||
impl CBackend {
|
||||
@@ -67,6 +92,8 @@ impl CBackend {
|
||||
closures: Vec::new(),
|
||||
local_vars: HashSet::new(),
|
||||
closure_returning_functions: HashSet::new(),
|
||||
variant_to_type: HashMap::new(),
|
||||
variant_field_types: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,6 +407,36 @@ impl CBackend {
|
||||
self.writeln("");
|
||||
}
|
||||
TypeDef::Enum(variants) => {
|
||||
// Record variant -> type mapping for pattern matching
|
||||
for variant in variants {
|
||||
self.variant_to_type.insert(variant.name.name.clone(), name.clone());
|
||||
|
||||
// Also record field types for pattern binding type inference
|
||||
// Use self-check for recursive types (which become pointers)
|
||||
let field_types: Vec<String> = match &variant.fields {
|
||||
VariantFields::Tuple(fields) => {
|
||||
fields.iter().map(|f| {
|
||||
self.type_to_c_with_self_check(f, name).unwrap_or_else(|_| "LuxInt".to_string())
|
||||
}).collect()
|
||||
}
|
||||
VariantFields::Record(fields) => {
|
||||
fields.iter().map(|f| {
|
||||
self.type_to_c_with_self_check(&f.typ, name).unwrap_or_else(|_| "LuxInt".to_string())
|
||||
}).collect()
|
||||
}
|
||||
VariantFields::Unit => vec![],
|
||||
};
|
||||
self.variant_field_types.insert(
|
||||
(name.clone(), variant.name.name.clone()),
|
||||
field_types
|
||||
);
|
||||
}
|
||||
|
||||
// Forward declare the main struct for recursive types
|
||||
self.writeln(&format!("struct {};", name));
|
||||
self.writeln(&format!("typedef struct {} {};", name, name));
|
||||
self.writeln("");
|
||||
|
||||
// Emit tag enum
|
||||
self.writeln(&format!("typedef enum {}_Tag {{", name));
|
||||
self.indent += 1;
|
||||
@@ -400,13 +457,13 @@ impl CBackend {
|
||||
match &variant.fields {
|
||||
VariantFields::Tuple(fields) => {
|
||||
for (i, field) in fields.iter().enumerate() {
|
||||
let c_type = self.type_to_c(field)?;
|
||||
let c_type = self.type_to_c_with_self_check(field, name)?;
|
||||
self.writeln(&format!("{} field{};", c_type, i));
|
||||
}
|
||||
}
|
||||
VariantFields::Record(fields) => {
|
||||
for field in fields {
|
||||
let c_type = self.type_to_c(&field.typ)?;
|
||||
let c_type = self.type_to_c_with_self_check(&field.typ, name)?;
|
||||
self.writeln(&format!("{} {};", c_type, field.name.name));
|
||||
}
|
||||
}
|
||||
@@ -418,8 +475,8 @@ impl CBackend {
|
||||
}
|
||||
}
|
||||
|
||||
// Emit main union struct
|
||||
self.writeln(&format!("typedef struct {} {{", name));
|
||||
// Emit main union struct (typedef already created above)
|
||||
self.writeln(&format!("struct {} {{", name));
|
||||
self.indent += 1;
|
||||
self.writeln(&format!("{}_Tag tag;", name));
|
||||
self.writeln("union {");
|
||||
@@ -433,7 +490,7 @@ impl CBackend {
|
||||
self.indent -= 1;
|
||||
self.writeln("} data;");
|
||||
self.indent -= 1;
|
||||
self.writeln(&format!("}} {};", name));
|
||||
self.writeln("};");
|
||||
self.writeln("");
|
||||
}
|
||||
TypeDef::Alias(_) => {
|
||||
@@ -499,7 +556,16 @@ impl CBackend {
|
||||
match expr {
|
||||
Expr::Literal(lit) => self.emit_literal(lit),
|
||||
|
||||
Expr::Var(ident) => Ok(ident.name.clone()),
|
||||
Expr::Var(ident) => {
|
||||
// Check if this is a unit constructor (no-argument variant)
|
||||
if let Some(type_name) = self.variant_to_type.get(&ident.name) {
|
||||
// This is a constructor - emit struct literal
|
||||
let variant_name = &ident.name;
|
||||
Ok(format!("({}){{{}_TAG_{}}}", type_name, type_name, variant_name.to_uppercase()))
|
||||
} else {
|
||||
Ok(ident.name.clone())
|
||||
}
|
||||
}
|
||||
|
||||
Expr::BinaryOp { op, left, right, .. } => {
|
||||
let l = self.emit_expr(left)?;
|
||||
@@ -568,6 +634,49 @@ impl CBackend {
|
||||
let c_func_name = self.mangle_name(&ident.name);
|
||||
Ok(format!("{}({})", c_func_name, args_str))
|
||||
}
|
||||
Expr::Var(ident) if self.variant_to_type.contains_key(&ident.name) => {
|
||||
// ADT constructor call - create struct with tag and data
|
||||
let type_name = self.variant_to_type.get(&ident.name).unwrap().clone();
|
||||
let variant_name = ident.name.clone();
|
||||
let variant_lower = variant_name.to_lowercase();
|
||||
let variant_upper = variant_name.to_uppercase();
|
||||
|
||||
// Look up field types to handle pointer fields
|
||||
let field_types = self.variant_field_types
|
||||
.get(&(type_name.clone(), variant_name.clone()))
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
// Generate struct initialization
|
||||
let arg_values: Result<Vec<_>, _> = args.iter().map(|a| self.emit_expr(a)).collect();
|
||||
let arg_values = arg_values?;
|
||||
|
||||
// Build field initializers, handling pointer fields
|
||||
let field_inits: Vec<String> = arg_values.iter()
|
||||
.enumerate()
|
||||
.map(|(i, v)| {
|
||||
let field_type = field_types.get(i).map(|s| s.as_str()).unwrap_or("LuxInt");
|
||||
if field_type.ends_with('*') {
|
||||
// Pointer field - allocate and copy
|
||||
let base_type = &field_type[..field_type.len()-1];
|
||||
format!(".field{} = ({}*)memcpy(malloc(sizeof({})), &({}), sizeof({}))",
|
||||
i, base_type, base_type, v, base_type)
|
||||
} else {
|
||||
format!(".field{} = {}", i, v)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if field_inits.is_empty() {
|
||||
// Unit variant
|
||||
Ok(format!("({}){{{}_TAG_{}}}", type_name, type_name, variant_upper))
|
||||
} else {
|
||||
// Variant with data
|
||||
Ok(format!("({}){{{}_TAG_{}, .data.{} = {{{}}}}}",
|
||||
type_name, type_name, variant_upper,
|
||||
variant_lower, field_inits.join(", ")))
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Indirect call - treat as closure
|
||||
let closure_expr = self.emit_expr(func)?;
|
||||
@@ -653,12 +762,18 @@ impl CBackend {
|
||||
for stmt in statements {
|
||||
match stmt {
|
||||
Statement::Let { name, value, .. } => {
|
||||
// First, infer type from value expression (before emitting)
|
||||
let inferred_type = self.infer_expr_type(value);
|
||||
|
||||
let val = self.emit_expr(value)?;
|
||||
// Infer type from value: closures return LuxClosure*
|
||||
let typ = if val.starts_with("_closure_") || self.is_closure_returning_call(value) {
|
||||
"LuxClosure*"
|
||||
|
||||
// Determine final type
|
||||
let typ = if let Some(t) = inferred_type {
|
||||
t
|
||||
} else if val.starts_with("_closure_") || self.is_closure_returning_call(value) {
|
||||
"LuxClosure*".to_string()
|
||||
} else {
|
||||
"LuxInt"
|
||||
"LuxInt".to_string()
|
||||
};
|
||||
self.writeln(&format!("{} {} = {};", typ, name.name, val));
|
||||
}
|
||||
@@ -722,12 +837,22 @@ impl CBackend {
|
||||
|
||||
fn emit_match(&mut self, expr: &Expr, arms: &[MatchArm]) -> Result<String, CGenError> {
|
||||
let scrutinee = self.emit_expr(expr)?;
|
||||
let scrutinee_var = format!("scrutinee_{}", self.fresh_name());
|
||||
let result_var = format!("match_result_{}", self.fresh_name());
|
||||
|
||||
self.writeln(&format!("LuxInt {};", result_var));
|
||||
// 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());
|
||||
|
||||
// Use the inferred type for scrutinee, fall back to LuxInt for simple patterns
|
||||
let scrutinee_type = type_name.as_deref().unwrap_or("LuxInt");
|
||||
self.writeln(&format!("{} {} = {};", scrutinee_type, scrutinee_var, scrutinee));
|
||||
self.writeln(&format!("{} {};", result_type, result_var));
|
||||
|
||||
for (i, arm) in arms.iter().enumerate() {
|
||||
let condition = self.pattern_to_condition(&arm.pattern, &scrutinee)?;
|
||||
let condition = self.pattern_to_condition(&arm.pattern, &scrutinee_var, type_name.as_deref())?;
|
||||
|
||||
if i == 0 {
|
||||
self.writeln(&format!("if ({}) {{", condition));
|
||||
@@ -736,6 +861,20 @@ impl CBackend {
|
||||
}
|
||||
|
||||
self.indent += 1;
|
||||
|
||||
// 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));
|
||||
} else {
|
||||
self.writeln(&format!("{} {} = {};", c_type, var_name, c_expr));
|
||||
}
|
||||
}
|
||||
|
||||
let body = self.emit_expr(&arm.body)?;
|
||||
self.writeln(&format!("{} = {};", result_var, body));
|
||||
self.indent -= 1;
|
||||
@@ -745,18 +884,159 @@ impl CBackend {
|
||||
Ok(result_var)
|
||||
}
|
||||
|
||||
fn pattern_to_condition(&self, pattern: &Pattern, scrutinee: &str) -> Result<String, CGenError> {
|
||||
/// Try to infer the type name from match arms by looking at constructor patterns
|
||||
fn infer_type_name_from_arms(&self, arms: &[MatchArm]) -> Option<String> {
|
||||
for arm in arms {
|
||||
if let Pattern::Constructor { name, .. } = &arm.pattern {
|
||||
// Look up the variant in our mapping
|
||||
if let Some(type_name) = self.variant_to_type.get(&name.name) {
|
||||
return Some(type_name.clone());
|
||||
}
|
||||
// Fallback for built-in types that might not be in the map
|
||||
match name.name.as_str() {
|
||||
"Some" | "None" => return Some("Option".to_string()),
|
||||
"Ok" | "Err" => return Some("Result".to_string()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Try to infer the C type of an expression
|
||||
fn infer_expr_type(&self, expr: &Expr) -> Option<String> {
|
||||
match expr {
|
||||
Expr::Literal(lit) => {
|
||||
match &lit.kind {
|
||||
LiteralKind::Int(_) => Some("LuxInt".to_string()),
|
||||
LiteralKind::Float(_) => Some("LuxFloat".to_string()),
|
||||
LiteralKind::Bool(_) => Some("LuxBool".to_string()),
|
||||
LiteralKind::String(_) => Some("LuxString".to_string()),
|
||||
LiteralKind::Char(_) => Some("char".to_string()),
|
||||
LiteralKind::Unit => Some("void".to_string()),
|
||||
}
|
||||
}
|
||||
Expr::Var(ident) => {
|
||||
// Check if it's a unit constructor
|
||||
if let Some(type_name) = self.variant_to_type.get(&ident.name) {
|
||||
Some(type_name.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Expr::Call { func, .. } => {
|
||||
// Check if calling a constructor
|
||||
if let Expr::Var(ident) = func.as_ref() {
|
||||
if let Some(type_name) = self.variant_to_type.get(&ident.name) {
|
||||
return Some(type_name.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
Expr::Block { result, .. } => {
|
||||
// Type of block is the type of the result expression
|
||||
self.infer_expr_type(result)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn pattern_to_condition(&self, pattern: &Pattern, scrutinee: &str, type_name: Option<&str>) -> Result<String, CGenError> {
|
||||
match pattern {
|
||||
Pattern::Wildcard(_) => Ok("1".to_string()),
|
||||
Pattern::Var(ident) => Ok(format!("(1) /* bind {} = {} */", ident.name, scrutinee)),
|
||||
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))
|
||||
}
|
||||
Pattern::Constructor { name, .. } => {
|
||||
Ok(format!("{}.tag == TAG_{}", scrutinee, name.name.to_uppercase()))
|
||||
Pattern::Constructor { name, fields, .. } => {
|
||||
// Get the type name for proper tag generation
|
||||
let tn = type_name.unwrap_or("Option"); // Default fallback
|
||||
let tag_check = format!("{}.tag == {}_TAG_{}", scrutinee, tn, name.name.to_uppercase());
|
||||
|
||||
// If there are nested patterns, we need to check those too
|
||||
if fields.is_empty() {
|
||||
Ok(tag_check)
|
||||
} else {
|
||||
let mut conditions = vec![tag_check];
|
||||
let variant_lower = name.name.to_lowercase();
|
||||
|
||||
for (i, field_pattern) in fields.iter().enumerate() {
|
||||
// Access the field data
|
||||
let field_access = format!("{}.data.{}.field{}", scrutinee, variant_lower, i);
|
||||
let field_condition = self.pattern_to_condition(field_pattern, &field_access, None)?;
|
||||
if field_condition != "1" {
|
||||
conditions.push(field_condition);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(conditions.join(" && "))
|
||||
}
|
||||
}
|
||||
Pattern::Tuple { elements, .. } => {
|
||||
// For tuples, check each element pattern
|
||||
let mut conditions = Vec::new();
|
||||
for (i, elem_pattern) in elements.iter().enumerate() {
|
||||
let elem_access = format!("{}.field{}", scrutinee, i);
|
||||
let elem_condition = self.pattern_to_condition(elem_pattern, &elem_access, None)?;
|
||||
if elem_condition != "1" {
|
||||
conditions.push(elem_condition);
|
||||
}
|
||||
}
|
||||
if conditions.is_empty() {
|
||||
Ok("1".to_string())
|
||||
} else {
|
||||
Ok(conditions.join(" && "))
|
||||
}
|
||||
}
|
||||
Pattern::Record { .. } => Ok("1".to_string()), // TODO: record pattern matching
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)> {
|
||||
match pattern {
|
||||
Pattern::Wildcard(_) => vec![],
|
||||
Pattern::Var(ident) => {
|
||||
let typ = expected_type.unwrap_or("LuxInt").to_string();
|
||||
vec![(ident.name.clone(), scrutinee.to_string(), typ)]
|
||||
}
|
||||
Pattern::Literal(_) => vec![],
|
||||
Pattern::Constructor { name, fields, .. } => {
|
||||
let mut bindings = Vec::new();
|
||||
let variant_lower = name.name.to_lowercase();
|
||||
|
||||
// Look up field types for this variant
|
||||
let type_name = self.variant_to_type.get(&name.name);
|
||||
let field_types = type_name.and_then(|tn| {
|
||||
self.variant_field_types.get(&(tn.clone(), name.name.clone()))
|
||||
});
|
||||
|
||||
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());
|
||||
bindings.extend(self.extract_pattern_bindings(field_pattern, &field_access, field_type));
|
||||
}
|
||||
|
||||
bindings
|
||||
}
|
||||
Pattern::Tuple { elements, .. } => {
|
||||
let mut bindings = Vec::new();
|
||||
for (i, elem_pattern) in elements.iter().enumerate() {
|
||||
let elem_access = format!("{}.field{}", scrutinee, i);
|
||||
bindings.extend(self.extract_pattern_bindings(elem_pattern, &elem_access, None));
|
||||
}
|
||||
bindings
|
||||
}
|
||||
Pattern::Record { fields, .. } => {
|
||||
let mut bindings = Vec::new();
|
||||
for (field_name, field_pattern) in fields {
|
||||
let field_access = format!("{}.{}", scrutinee, field_name.name);
|
||||
bindings.extend(self.extract_pattern_bindings(field_pattern, &field_access, None));
|
||||
}
|
||||
bindings
|
||||
}
|
||||
_ => Ok("1".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -824,6 +1104,17 @@ impl CBackend {
|
||||
self.type_expr_to_c(type_expr)
|
||||
}
|
||||
|
||||
/// Convert type to C, using pointers for self-referential types
|
||||
fn type_to_c_with_self_check(&self, type_expr: &TypeExpr, parent_type: &str) -> Result<String, CGenError> {
|
||||
if let TypeExpr::Named(ident) = type_expr {
|
||||
if ident.name == parent_type {
|
||||
// Self-referential type - use pointer
|
||||
return Ok(format!("{}*", parent_type));
|
||||
}
|
||||
}
|
||||
self.type_expr_to_c(type_expr)
|
||||
}
|
||||
|
||||
fn type_expr_to_c(&self, type_expr: &TypeExpr) -> Result<String, CGenError> {
|
||||
match type_expr {
|
||||
TypeExpr::Named(ident) => {
|
||||
|
||||
Reference in New Issue
Block a user