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:
2026-02-19 02:31:49 -05:00
parent 3a2376cd49
commit 89741b4a32
2 changed files with 22 additions and 8 deletions

View File

@@ -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;