diff --git a/nix-js/src/codegen.rs b/nix-js/src/codegen.rs index 28c296a..7bd8f9b 100644 --- a/nix-js/src/codegen.rs +++ b/nix-js/src/codegen.rs @@ -186,37 +186,45 @@ impl Compile for Call { } } +/// Determines if a Thunk should be kept (not unwrapped) for non-recursive let bindings. +/// Returns true for complex expressions that should remain lazy to preserve Nix semantics. +fn should_keep_thunk(ir: &Ir) -> bool { + match ir { + // Simple literals can be evaluated eagerly + Ir::Int(_) | Ir::Float(_) | Ir::Bool(_) | Ir::Null(_) | Ir::Str(_) => false, + // Builtin references are safe to evaluate eagerly + Ir::Builtin(_) | Ir::Builtins(_) => false, + Ir::ExprRef(_) => false, + _ => true, + } +} + +fn unwrap_thunk(ir: &Ir, ctx: &impl CodegenContext) -> String { + if let Ir::Thunk(inner) = ir { + let inner_ir = ctx.get_ir(*inner); + if should_keep_thunk(inner_ir) { + ir.compile(ctx) + } else { + inner_ir.compile(ctx) + } + } else { + ir.compile(ctx) + } +} + impl Compile for Let { fn compile(&self, ctx: &Ctx) -> String { let info = &self.binding_sccs; let mut js_statements = Vec::new(); for (scc_exprs, is_recursive) in info.sccs.iter() { - if *is_recursive { - for &expr in scc_exprs { - js_statements.push(format!("let expr{}", expr.0)); - } - for &expr in scc_exprs { - let value = ctx.get_ir(expr).compile(ctx); - js_statements.push(format!("expr{}={}", expr.0, value)); - } - } else { - for &expr in scc_exprs { - let ir = ctx.get_ir(expr); - let value = if let Ir::Thunk(inner) = ir { - let inner_ir = ctx.get_ir(*inner); - // Don't unwrap Thunk if inner is a Let expression - // to avoid generating IIFE that executes immediately - if matches!(inner_ir, Ir::Let(_)) { - ir.compile(ctx) - } else { - inner_ir.compile(ctx) - } - } else { - ir.compile(ctx) - }; - js_statements.push(format!("const expr{}={}", expr.0, value)); - } + for &expr in scc_exprs { + let value = if *is_recursive { + ctx.get_ir(expr).compile(ctx) + } else { + unwrap_thunk(ctx.get_ir(expr), ctx) + }; + js_statements.push(format!("const expr{}={}", expr.0, value)); } }