fix: move top-level let initialization into main() in C backend
Top-level let bindings with function calls (e.g., `let result = factorial(10)`) were emitted as static initializers, which is invalid C since function calls aren't compile-time constants. Now globals are declared with zero-init and initialized inside main() before any run expressions execute. Also fixes validate.sh to use exit codes instead of grep for cargo check/build. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -279,7 +279,7 @@ impl CBackend {
|
||||
Declaration::Let(let_decl) => {
|
||||
// Skip run expressions - they're handled in the main wrapper
|
||||
if !matches!(&let_decl.value, Expr::Run { .. }) {
|
||||
self.emit_global_let(&let_decl.name, &let_decl.value)?;
|
||||
self.emit_global_let(&let_decl.name)?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -5561,9 +5561,9 @@ impl CBackend {
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_global_let(&mut self, name: &Ident, value: &Expr) -> Result<(), CGenError> {
|
||||
let val = self.emit_expr(value)?;
|
||||
self.writeln(&format!("static LuxInt {} = {};", name.name, val));
|
||||
fn emit_global_let(&mut self, name: &Ident) -> Result<(), CGenError> {
|
||||
// Declare global variable without initializer (initialized in main)
|
||||
self.writeln(&format!("static LuxInt {} = 0;", name.name));
|
||||
self.writeln("");
|
||||
Ok(())
|
||||
}
|
||||
@@ -5574,12 +5574,16 @@ impl CBackend {
|
||||
matches!(d, Declaration::Function(f) if f.name.name == "main")
|
||||
});
|
||||
|
||||
// Check for top-level run expressions
|
||||
// Check for top-level run expressions or let bindings
|
||||
let has_run = program.declarations.iter().any(|d| {
|
||||
matches!(d, Declaration::Let(let_decl) if matches!(&let_decl.value, Expr::Run { .. }))
|
||||
});
|
||||
|
||||
if has_main || has_run {
|
||||
let has_global_lets = program.declarations.iter().any(|d| {
|
||||
matches!(d, Declaration::Let(let_decl) if !matches!(&let_decl.value, Expr::Run { .. }))
|
||||
});
|
||||
|
||||
if has_main || has_run || has_global_lets {
|
||||
self.writeln("int main(int argc, char** argv) {");
|
||||
self.indent += 1;
|
||||
|
||||
@@ -5588,6 +5592,16 @@ impl CBackend {
|
||||
self.writeln("lux_argv = argv;");
|
||||
self.writeln("");
|
||||
|
||||
// Initialize top-level let bindings (non-run) inside main
|
||||
for decl in &program.declarations {
|
||||
if let Declaration::Let(let_decl) = decl {
|
||||
if !matches!(&let_decl.value, Expr::Run { .. }) {
|
||||
let val = self.emit_expr(&let_decl.value)?;
|
||||
self.writeln(&format!("{} = {};", let_decl.name.name, val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute top-level let bindings with run expressions
|
||||
// Track if main was already called via a run expression
|
||||
let mut main_called_via_run = false;
|
||||
|
||||
Reference in New Issue
Block a user