From b87a6d7adaa7d242955658f1385e43d9b1de98bb Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Fri, 13 Mar 2026 18:00:58 +0100 Subject: [PATCH] Refactor closure instantiation and inlining This commit makes several changes related to how closures are handled: - **Dumper:** Removes redundant introspection of closure ASTs, as this is no longer relevant. - **Optimizer:** Updates `try_inline` to accept `ExecNode` for parameters and adds a check to ensure the `function_node` is a `Lambda` before attempting inlining. - **Environment:** Simplifies closure creation by passing an `ExecNode` for parameters and ensuring the correct `stack_size` is used. - **VM:** Adjusts `Closure` to store an `ExecNode` for parameters and updates `VM::unpack` to accept an `ExecNode`. --- src/ast/compiler/dumper.rs | 24 ----------------------- src/ast/compiler/optimizer/engine.rs | 29 ++++++++++++++++------------ src/ast/environment.rs | 7 +++---- src/ast/vm.rs | 13 ++++++------- 4 files changed, 26 insertions(+), 47 deletions(-) diff --git a/src/ast/compiler/dumper.rs b/src/ast/compiler/dumper.rs index 5bb0286..22641d9 100644 --- a/src/ast/compiler/dumper.rs +++ b/src/ast/compiler/dumper.rs @@ -1,6 +1,4 @@ use crate::ast::compiler::bound_nodes::{BoundKind, Node}; -use crate::ast::types::Value; -use crate::ast::vm::Closure; use std::fmt::Debug; /// Human-readable AST dumper for the bound AST. @@ -38,28 +36,6 @@ impl Dumper { BoundKind::Nop => self.log("Nop", node), BoundKind::Constant(v) => { self.log(&format!("Constant: {}", v), node); - // Introspect Closure AST if possible - if let Value::Object(obj) = v - && let Some(closure) = obj.as_any().downcast_ref::() - { - self.indent += 1; - self.write_indent(); - self.output.push_str("--- Specialized Body ---\n"); - // We need to cast the inner TypedNode to the generic T required by visit. - // Since Dumper is generic over T, but Closure stores TypedNode (where T = StaticType), - // we can only fully dump if T is StaticType. - // However, we can hack it by creating a new Dumper for the inner AST string. - - // We can't call self.visit because types mismatch if T != StaticType. - // So we just recursively dump to string and append. - let inner_dump = Dumper::dump(&closure.function_node); - for line in inner_dump.lines() { - self.write_indent(); - self.output.push_str(line); - self.output.push('\n'); - } - self.indent -= 1; - } } BoundKind::Get { addr, name } => { self.log(&format!("Get: {} ({:?})", name.name, addr), node) diff --git a/src/ast/compiler/optimizer/engine.rs b/src/ast/compiler/optimizer/engine.rs index cd4cd8f..c5e9bbe 100644 --- a/src/ast/compiler/optimizer/engine.rs +++ b/src/ast/compiler/optimizer/engine.rs @@ -334,19 +334,24 @@ impl Optimizer { } path.inlining_depth += 1; - let collapsed = self.try_inline( - &closure.parameter_node, - &arg_nodes, - &closure.function_node, - sub, - path, - Some(closure_sub), - ); - path.inlining_depth -= 1; - path.exit_lambda(&closure.function_node.identity); + if let BoundKind::Lambda { params, .. } = &closure.function_node.kind { + let collapsed = self.try_inline( + params, + &arg_nodes, + &closure.function_node, + sub, + path, + Some(closure_sub), + ); + path.inlining_depth -= 1; + path.exit_lambda(&closure.function_node.identity); - if let Some(res) = collapsed { - return res; + if let Some(res) = collapsed { + return res; + } + } else { + path.inlining_depth -= 1; + path.exit_lambda(&closure.function_node.identity); } } diff --git a/src/ast/environment.rs b/src/ast/environment.rs index aef2aa8..e13e77a 100644 --- a/src/ast/environment.rs +++ b/src/ast/environment.rs @@ -666,14 +666,13 @@ impl Environment { } = &node.kind && upvalues.is_empty() { - let stack_size = node.ty.stack_size; let closure = Rc::new(crate::ast::vm::Closure::new( - params.ty.original.clone(), + params.clone(), body.ty.original.clone(), body.clone(), - vec![], + Vec::new(), *positional_count, - stack_size, + node.ty.stack_size, )); let closure_obj: Rc = closure; diff --git a/src/ast/vm.rs b/src/ast/vm.rs index fbd3f5d..cf3b616 100644 --- a/src/ast/vm.rs +++ b/src/ast/vm.rs @@ -1,6 +1,5 @@ use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind}; use crate::ast::compiler::lowering::ExecNode; -use crate::ast::compiler::bound_nodes::Node; use crate::ast::types::{Object, Value}; use std::any::Any; use std::cell::RefCell; @@ -8,8 +7,8 @@ use std::rc::Rc; #[derive(Debug, Clone)] pub struct Closure { - /// The analyzed parameter pattern. - pub parameter_node: Rc, + /// The executable parameter pattern. + pub parameter_node: Rc, /// The analyzed body (before TCO). pub function_node: Rc, /// The executable node (after TCO). @@ -23,7 +22,7 @@ pub struct Closure { impl Closure { #[inline] pub fn new( - params: Rc, + params: Rc, body: Rc, exec: Rc, upvalues: Vec>>, @@ -512,7 +511,7 @@ impl VM { } let stack_size = node.ty.stack_size; let closure = Closure::new( - params.ty.original.clone(), + params.clone(), body.ty.original.clone(), body.clone(), captured, @@ -1026,9 +1025,9 @@ impl VM { } } - fn unpack( + fn unpack( &mut self, - pattern: &Node, + pattern: &ExecNode, values: &[Value], offset: &mut usize, ) -> Result<(), String> {