feat: complete evidence threading in C backend
C backend now fully threads evidence through effectful function calls:
- Track effectful functions via effectful_functions HashSet
- Add has_evidence flag to track context during code generation
- Add LuxEvidence* ev parameter to effectful function signatures
- Transform effect operations to use ev->console->print() when evidence available
- Update function calls to pass evidence (ev or &default_evidence)
- Update main entry point to pass &default_evidence
Generated code now uses zero-cost evidence passing:
void greet_lux(LuxEvidence* ev) {
ev->console->print(ev->console->env, "Hello!");
}
This completes the evidence passing implementation for both interpreter
(O(1) HashMap lookup) and C backend (direct function pointer calls).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -95,6 +95,10 @@ pub struct CBackend {
|
||||
variant_to_type: HashMap<String, String>,
|
||||
/// Mapping from (type_name, variant_name) to field types
|
||||
variant_field_types: HashMap<(String, String), Vec<String>>,
|
||||
/// Functions that use effects (have evidence parameter)
|
||||
effectful_functions: HashSet<String>,
|
||||
/// Whether we're currently inside an effectful function (has evidence available)
|
||||
has_evidence: bool,
|
||||
}
|
||||
|
||||
impl CBackend {
|
||||
@@ -111,6 +115,8 @@ impl CBackend {
|
||||
closure_returning_functions: HashSet::new(),
|
||||
variant_to_type: HashMap::new(),
|
||||
variant_field_types: HashMap::new(),
|
||||
effectful_functions: HashSet::new(),
|
||||
has_evidence: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +126,7 @@ impl CBackend {
|
||||
self.closures.clear();
|
||||
self.emit_prelude();
|
||||
|
||||
// First pass: collect all function names and types
|
||||
// First pass: collect all function names, types, and effects
|
||||
for decl in &program.declarations {
|
||||
match decl {
|
||||
Declaration::Function(f) => {
|
||||
@@ -129,6 +135,10 @@ impl CBackend {
|
||||
if matches!(&f.return_type, TypeExpr::Function { .. }) {
|
||||
self.closure_returning_functions.insert(f.name.name.clone());
|
||||
}
|
||||
// Check if this function uses effects (evidence passing)
|
||||
if !f.effects.is_empty() {
|
||||
self.effectful_functions.insert(f.name.name.clone());
|
||||
}
|
||||
}
|
||||
Declaration::Type(t) => {
|
||||
self.collect_type(t)?;
|
||||
@@ -305,7 +315,18 @@ impl CBackend {
|
||||
match func.as_ref() {
|
||||
Expr::Var(ident) if self.functions.contains(&ident.name) => {
|
||||
let c_func_name = self.mangle_name(&ident.name);
|
||||
Ok(format!("{}({})", c_func_name, args_str))
|
||||
// Pass evidence if this function uses effects
|
||||
let is_effectful = self.effectful_functions.contains(&ident.name);
|
||||
if is_effectful {
|
||||
// Inside closures, use default evidence
|
||||
if args_str.is_empty() {
|
||||
Ok(format!("{}(&default_evidence)", c_func_name))
|
||||
} else {
|
||||
Ok(format!("{}(&default_evidence, {})", c_func_name, args_str))
|
||||
}
|
||||
} else {
|
||||
Ok(format!("{}({})", c_func_name, args_str))
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let closure_expr = self.emit_expr_with_env(func, captured)?;
|
||||
@@ -678,7 +699,19 @@ impl CBackend {
|
||||
let ret_type = self.type_expr_to_c(&f.return_type)?;
|
||||
let params = self.emit_params(&f.params)?;
|
||||
let mangled = self.mangle_name(&f.name.name);
|
||||
self.writeln(&format!("{} {}({});", ret_type, mangled, params));
|
||||
|
||||
// Add evidence parameter for effectful functions
|
||||
let full_params = if !f.effects.is_empty() {
|
||||
if params == "void" {
|
||||
"LuxEvidence* ev".to_string()
|
||||
} else {
|
||||
format!("LuxEvidence* ev, {}", params)
|
||||
}
|
||||
} else {
|
||||
params
|
||||
};
|
||||
|
||||
self.writeln(&format!("{} {}({});", ret_type, mangled, full_params));
|
||||
}
|
||||
}
|
||||
self.writeln("");
|
||||
@@ -690,12 +723,35 @@ impl CBackend {
|
||||
let params = self.emit_params(&func.params)?;
|
||||
let mangled = self.mangle_name(&func.name.name);
|
||||
|
||||
self.writeln(&format!("{} {}({}) {{", ret_type, mangled, params));
|
||||
// Check if this function uses effects (needs evidence parameter)
|
||||
let is_effectful = !func.effects.is_empty();
|
||||
|
||||
// Build parameter list with evidence if needed
|
||||
let full_params = if is_effectful {
|
||||
if params == "void" {
|
||||
"LuxEvidence* ev".to_string()
|
||||
} else {
|
||||
format!("LuxEvidence* ev, {}", params)
|
||||
}
|
||||
} else {
|
||||
params
|
||||
};
|
||||
|
||||
self.writeln(&format!("{} {}({}) {{", ret_type, mangled, full_params));
|
||||
self.indent += 1;
|
||||
|
||||
// Set evidence availability for expression generation
|
||||
let prev_has_evidence = self.has_evidence;
|
||||
if is_effectful {
|
||||
self.has_evidence = true;
|
||||
}
|
||||
|
||||
// Emit function body
|
||||
let result = self.emit_expr(&func.body)?;
|
||||
|
||||
// Restore previous evidence state
|
||||
self.has_evidence = prev_has_evidence;
|
||||
|
||||
if ret_type != "void" {
|
||||
self.writeln(&format!("return {};", result));
|
||||
}
|
||||
@@ -809,7 +865,24 @@ impl CBackend {
|
||||
Expr::Var(ident) if self.functions.contains(&ident.name) => {
|
||||
// Direct call to a known function
|
||||
let c_func_name = self.mangle_name(&ident.name);
|
||||
Ok(format!("{}({})", c_func_name, args_str))
|
||||
// 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 {
|
||||
if args_str.is_empty() {
|
||||
Ok(format!("{}(ev)", c_func_name))
|
||||
} else {
|
||||
Ok(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))
|
||||
} else {
|
||||
Ok(format!("{}(&default_evidence, {})", c_func_name, args_str))
|
||||
}
|
||||
} else {
|
||||
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
|
||||
@@ -965,24 +1038,47 @@ impl CBackend {
|
||||
Expr::EffectOp { effect, operation, args, .. } => {
|
||||
self.effects_used.insert(effect.name.clone());
|
||||
|
||||
// Built-in effects
|
||||
if effect.name == "Console" && operation.name == "print" {
|
||||
let arg = self.emit_expr(&args[0])?;
|
||||
self.writeln(&format!("lux_console_print({});", arg));
|
||||
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);
|
||||
}
|
||||
|
||||
// Built-in Console effect
|
||||
if effect.name == "Console" {
|
||||
if operation.name == "print" {
|
||||
let arg = self.emit_expr(&args[0])?;
|
||||
if self.has_evidence {
|
||||
// Use evidence passing
|
||||
self.writeln(&format!("ev->console->print(ev->console->env, {});", arg));
|
||||
} else {
|
||||
// Fallback to direct call
|
||||
self.writeln(&format!("lux_console_print({});", arg));
|
||||
}
|
||||
return Ok("NULL".to_string());
|
||||
} else if operation.name == "readLine" {
|
||||
if self.has_evidence {
|
||||
return Ok("ev->console->readLine(ev->console->env)".to_string());
|
||||
} else {
|
||||
return Ok("lux_console_readLine()".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For other effects, emit evidence-passing call
|
||||
let arg_strs: Result<Vec<_>, _> = args.iter().map(|a| self.emit_expr(a)).collect();
|
||||
Ok(format!("ev_{}__{}({})",
|
||||
effect.name.to_lowercase(),
|
||||
operation.name,
|
||||
arg_strs?.join(", ")))
|
||||
if self.has_evidence {
|
||||
Ok(format!("ev->{}->{}(ev->{}->env{}{})",
|
||||
effect.name.to_lowercase(),
|
||||
operation.name,
|
||||
effect.name.to_lowercase(),
|
||||
if arg_strs.as_ref().map_or(true, |v| v.is_empty()) { "" } else { ", " },
|
||||
arg_strs?.join(", ")))
|
||||
} else {
|
||||
Ok(format!("lux_{}__{}({})",
|
||||
effect.name.to_lowercase(),
|
||||
operation.name,
|
||||
arg_strs?.join(", ")))
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Record { fields, .. } => {
|
||||
@@ -1606,7 +1702,12 @@ impl CBackend {
|
||||
if let Expr::Call { func, .. } = expr.as_ref() {
|
||||
if let Expr::Var(fn_name) = func.as_ref() {
|
||||
let mangled = self.mangle_name(&fn_name.name);
|
||||
self.writeln(&format!("{}();", mangled));
|
||||
// Pass default evidence if function uses effects
|
||||
if self.effectful_functions.contains(&fn_name.name) {
|
||||
self.writeln(&format!("{}(&default_evidence);", mangled));
|
||||
} else {
|
||||
self.writeln(&format!("{}();", mangled));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user