Remove is_tail field from Call node

The `is_tail` field on the `BoundKind::Call` node was an intermediate
representation for tail-call optimization and is no longer needed as a
distinct field. The TCO pass now embeds this information directly into
the `RuntimeMetadata` of the `ExecNode`, which is the VM's internal AST.
This simplifies the `BoundKind::Call` structure and removes redundant
information.
This commit is contained in:
Michael Schimmel
2026-02-21 22:01:03 +01:00
parent 78dff07930
commit ff1024ee49
10 changed files with 236 additions and 561 deletions
+10 -9
View File
@@ -14,7 +14,7 @@ use crate::ast::compiler::lambda_collector::LambdaCollector;
use crate::ast::compiler::macros::{MacroEvaluator, MacroExpander, MacroRegistry};
use crate::ast::compiler::optimizer::Optimizer;
use crate::ast::compiler::specializer::{FunctionRegistry, MonoCache, Specializer};
use crate::ast::compiler::tco::TCO;
use crate::ast::compiler::tco::{TCO, ExecNode};
use crate::ast::rtl;
use crate::ast::rtl::intrinsics;
@@ -67,9 +67,10 @@ impl MacroEvaluator for RuntimeMacroEvaluator {
let checker = TypeChecker::new(self.global_types.clone());
let typed_ast = checker.check(bound_ast, &[])?;
let exec_ast = TCO::optimize(typed_ast);
let mut vm = VM::new(self.global_values.clone());
vm.run(&typed_ast)
vm.run(&exec_ast)
}
}
@@ -176,7 +177,7 @@ impl Environment {
}
/// Backend: Optimization (TCO, etc.)
pub fn link(&self, node: TypedNode) -> TypedNode {
pub fn link(&self, node: TypedNode) -> ExecNode {
// 1. Specialize (Always performed for correctness)
let specialized = self.specialize_node(node);
@@ -184,7 +185,7 @@ impl Environment {
let optimizer = Optimizer::new(self.optimization_level);
let optimized = optimizer.optimize(specialized);
// 3. TCO (Always performed)
// 3. TCO (Always performed, converts to ExecNode)
TCO::optimize(optimized)
}
@@ -229,7 +230,7 @@ impl Environment {
let optimizer = Optimizer::new(opt_level);
let optimized_ast = optimizer.optimize(specialized_ast);
// 4. TCO
// 4. TCO (converts to ExecNode)
let tco_ast = TCO::optimize(optimized_ast);
// 5. Compile to Value (VM)
@@ -240,7 +241,7 @@ impl Environment {
};
// 6. Determine correct return type from the newly inferred function signature
let ret_type = if let StaticType::Function(sig) = &tco_ast.ty {
let ret_type = if let StaticType::Function(sig) = &tco_ast.ty.ty {
sig.ret.clone()
} else {
StaticType::Any
@@ -261,7 +262,7 @@ impl Environment {
}
/// Runtime: Execute the linked AST in the VM
pub fn run(&self, node: &TypedNode) -> Result<Value, String> {
pub fn run(&self, node: &ExecNode) -> Result<Value, String> {
let mut vm = VM::new(self.global_values.clone());
let mut result = vm.run(node)?;
@@ -269,7 +270,7 @@ impl Environment {
if let Value::Object(obj) = &result
&& let Some(closure) = obj.as_any().downcast_ref::<crate::ast::vm::Closure>()
{
result = vm.run(&closure.function_node)?;
result = vm.run(&closure.exec_node)?;
}
// IMPORTANT: Resolve any pending tail call requests from the top-level execution
@@ -314,7 +315,7 @@ impl Environment {
if let Ok(Value::Object(obj)) = &result
&& let Some(closure) = obj.as_any().downcast_ref::<crate::ast::vm::Closure>()
{
result = vm.run_with_observer(&mut observer, &closure.function_node);
result = vm.run_with_observer(&mut observer, &closure.exec_node);
}
// Resolve top-level tail calls