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`.
This commit is contained in:
Michael Schimmel
2026-03-13 18:00:58 +01:00
parent ef5c2367a7
commit b87a6d7ada
4 changed files with 26 additions and 47 deletions
-24
View File
@@ -1,6 +1,4 @@
use crate::ast::compiler::bound_nodes::{BoundKind, Node}; use crate::ast::compiler::bound_nodes::{BoundKind, Node};
use crate::ast::types::Value;
use crate::ast::vm::Closure;
use std::fmt::Debug; use std::fmt::Debug;
/// Human-readable AST dumper for the bound AST. /// Human-readable AST dumper for the bound AST.
@@ -38,28 +36,6 @@ impl Dumper {
BoundKind::Nop => self.log("Nop", node), BoundKind::Nop => self.log("Nop", node),
BoundKind::Constant(v) => { BoundKind::Constant(v) => {
self.log(&format!("Constant: {}", v), node); 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::<Closure>()
{
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 } => { BoundKind::Get { addr, name } => {
self.log(&format!("Get: {} ({:?})", name.name, addr), node) self.log(&format!("Get: {} ({:?})", name.name, addr), node)
+17 -12
View File
@@ -334,19 +334,24 @@ impl Optimizer {
} }
path.inlining_depth += 1; path.inlining_depth += 1;
let collapsed = self.try_inline( if let BoundKind::Lambda { params, .. } = &closure.function_node.kind {
&closure.parameter_node, let collapsed = self.try_inline(
&arg_nodes, params,
&closure.function_node, &arg_nodes,
sub, &closure.function_node,
path, sub,
Some(closure_sub), path,
); Some(closure_sub),
path.inlining_depth -= 1; );
path.exit_lambda(&closure.function_node.identity); path.inlining_depth -= 1;
path.exit_lambda(&closure.function_node.identity);
if let Some(res) = collapsed { if let Some(res) = collapsed {
return res; return res;
}
} else {
path.inlining_depth -= 1;
path.exit_lambda(&closure.function_node.identity);
} }
} }
+3 -4
View File
@@ -666,14 +666,13 @@ impl Environment {
} = &node.kind } = &node.kind
&& upvalues.is_empty() && upvalues.is_empty()
{ {
let stack_size = node.ty.stack_size;
let closure = Rc::new(crate::ast::vm::Closure::new( let closure = Rc::new(crate::ast::vm::Closure::new(
params.ty.original.clone(), params.clone(),
body.ty.original.clone(), body.ty.original.clone(),
body.clone(), body.clone(),
vec![], Vec::new(),
*positional_count, *positional_count,
stack_size, node.ty.stack_size,
)); ));
let closure_obj: Rc<dyn crate::ast::types::Object> = closure; let closure_obj: Rc<dyn crate::ast::types::Object> = closure;
+6 -7
View File
@@ -1,6 +1,5 @@
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind}; use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind};
use crate::ast::compiler::lowering::ExecNode; use crate::ast::compiler::lowering::ExecNode;
use crate::ast::compiler::bound_nodes::Node;
use crate::ast::types::{Object, Value}; use crate::ast::types::{Object, Value};
use std::any::Any; use std::any::Any;
use std::cell::RefCell; use std::cell::RefCell;
@@ -8,8 +7,8 @@ use std::rc::Rc;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Closure { pub struct Closure {
/// The analyzed parameter pattern. /// The executable parameter pattern.
pub parameter_node: Rc<AnalyzedNode>, pub parameter_node: Rc<ExecNode>,
/// The analyzed body (before TCO). /// The analyzed body (before TCO).
pub function_node: Rc<AnalyzedNode>, pub function_node: Rc<AnalyzedNode>,
/// The executable node (after TCO). /// The executable node (after TCO).
@@ -23,7 +22,7 @@ pub struct Closure {
impl Closure { impl Closure {
#[inline] #[inline]
pub fn new( pub fn new(
params: Rc<AnalyzedNode>, params: Rc<ExecNode>,
body: Rc<AnalyzedNode>, body: Rc<AnalyzedNode>,
exec: Rc<ExecNode>, exec: Rc<ExecNode>,
upvalues: Vec<Rc<RefCell<Value>>>, upvalues: Vec<Rc<RefCell<Value>>>,
@@ -512,7 +511,7 @@ impl VM {
} }
let stack_size = node.ty.stack_size; let stack_size = node.ty.stack_size;
let closure = Closure::new( let closure = Closure::new(
params.ty.original.clone(), params.clone(),
body.ty.original.clone(), body.ty.original.clone(),
body.clone(), body.clone(),
captured, captured,
@@ -1026,9 +1025,9 @@ impl VM {
} }
} }
fn unpack<T>( fn unpack(
&mut self, &mut self,
pattern: &Node<T>, pattern: &ExecNode,
values: &[Value], values: &[Value],
offset: &mut usize, offset: &mut usize,
) -> Result<(), String> { ) -> Result<(), String> {