Refactor lambda binding and parameter handling

Introduce `BoundKind::Parameter` to represent function parameters.
Modify `Binder` to correctly handle parameters within lambda
definitions, ensuring they are only defined within function scopes.
Update `LambdaCollector` to register the body of lambdas as templates
for global definitions.
Adjust `Dumper` to accurately represent lambda parameters.
Update `Specializer` and `TCO` to handle the new `BoundKind::Parameter`.
Refactor `Call` and `TailCall` to use a single `args` node, often a
tuple.
Adjust type signatures in RTL to use `StaticType::Tuple` for function
parameters.
This commit is contained in:
Michael Schimmel
2026-02-20 12:14:22 +01:00
parent 8d6d3be5c2
commit e2279f214b
17 changed files with 497 additions and 247 deletions
+45 -22
View File
@@ -49,13 +49,13 @@ impl Specializer {
fn visit_node(&self, node: TypedNode) -> TypedNode {
let (new_kind, new_ty) = match node.kind {
BoundKind::Call { callee, args } => {
let (new_callee, new_args, ret_ty) = self.specialize_call_logic(*callee, args, node.ty.clone());
(BoundKind::Call { callee: Box::new(new_callee), args: new_args }, ret_ty)
let (new_callee, new_args, ret_ty) = self.specialize_call_logic(*callee, *args, node.ty.clone());
(BoundKind::Call { callee: Box::new(new_callee), args: Box::new(new_args) }, ret_ty)
},
BoundKind::TailCall { callee, args } => {
let (new_callee, new_args, ret_ty) = self.specialize_call_logic(*callee, args, node.ty.clone());
(BoundKind::TailCall { callee: Box::new(new_callee), args: new_args }, ret_ty)
let (new_callee, new_args, ret_ty) = self.specialize_call_logic(*callee, *args, node.ty.clone());
(BoundKind::TailCall { callee: Box::new(new_callee), args: Box::new(new_args) }, ret_ty)
},
// Recursive traversal for other nodes
@@ -69,9 +69,10 @@ impl Specializer {
let exprs = exprs.into_iter().map(|e| self.visit_node(e)).collect();
(BoundKind::Block { exprs }, node.ty)
},
BoundKind::Lambda { param_count, upvalues, body } => {
BoundKind::Lambda { params, upvalues, body } => {
let params = Box::new(self.visit_node(*params));
let body = Rc::new(self.visit_node((*body).clone()));
(BoundKind::Lambda { param_count, upvalues, body }, node.ty)
(BoundKind::Lambda { params, upvalues, body }, node.ty)
},
BoundKind::DefLocal { name, slot, value, captured_by } => {
let value = Box::new(self.visit_node(*value));
@@ -109,10 +110,10 @@ impl Specializer {
}
}
fn specialize_call_logic(&self, callee: TypedNode, args: Vec<TypedNode>, original_ty: StaticType) -> (TypedNode, Vec<TypedNode>, StaticType) {
fn specialize_call_logic(&self, callee: TypedNode, args: TypedNode, original_ty: StaticType) -> (TypedNode, TypedNode, StaticType) {
// 1. Specialize children first
let new_callee = self.visit_node(callee);
let new_args: Vec<TypedNode> = args.into_iter().map(|a| self.visit_node(a)).collect();
let new_args = self.visit_node(args);
// 2. Check if this call is a candidate (Callee is Get(Address))
let address = if let BoundKind::Get { addr, .. } = &new_callee.kind {
@@ -123,7 +124,12 @@ impl Specializer {
};
// 3. Check if all argument types are statically known
let arg_types: Vec<StaticType> = new_args.iter().map(|a| a.ty.clone()).collect();
let arg_types: Vec<StaticType> = if let StaticType::Tuple(elements) = &new_args.ty {
elements.clone()
} else {
vec![new_args.ty.clone()]
};
if arg_types.iter().any(|t| matches!(t, StaticType::Any)) {
// Cannot specialize with unknown types
return (new_callee, new_args, original_ty);
@@ -139,7 +145,7 @@ impl Specializer {
identity: new_callee.identity.clone(),
kind: BoundKind::Constant(val.clone()),
ty: StaticType::Function(Box::new(Signature {
params: arg_types,
params: StaticType::Tuple(arg_types),
ret: ret_ty.clone(),
})),
};
@@ -158,7 +164,7 @@ impl Specializer {
identity: new_callee.identity.clone(),
kind: BoundKind::Constant(val.clone()),
ty: StaticType::Function(Box::new(Signature {
params: arg_types,
params: StaticType::Tuple(arg_types),
ret: ret_ty.clone(),
})),
};
@@ -192,7 +198,7 @@ impl Specializer {
identity: new_callee.identity.clone(),
kind: BoundKind::Constant(res_val),
ty: StaticType::Function(Box::new(Signature {
params: arg_types,
params: StaticType::Tuple(arg_types),
ret: res_ty.clone(),
})),
};
@@ -262,7 +268,19 @@ mod tests {
let func_node = BoundNode {
identity: make_identity(),
kind: BoundKind::Lambda {
param_count: 1,
params: Box::new(BoundNode {
identity: make_identity(),
kind: BoundKind::Tuple {
elements: vec![
BoundNode {
identity: make_identity(),
kind: BoundKind::Parameter { name: name.clone(), slot: 0 },
ty: ()
}
]
},
ty: ()
}),
upvalues: vec![],
body: Rc::new(BoundNode { identity: make_identity(), kind: BoundKind::Nop, ty: () })
},
@@ -278,12 +296,13 @@ mod tests {
let spec = Specializer::new(Some(Rc::new(registry)), Some(compiler), None, None);
// Call(Get(Local(0)), [Arg(Int)])
// Call(Get(Local(0)), Tuple([Arg(Int)]))
let callee = make_typed_node(BoundKind::Get { addr, name: name.clone() }, StaticType::Any);
let arg = make_typed_node(BoundKind::Constant(Value::Int(1)), StaticType::Int);
let args_tuple = make_typed_node(BoundKind::Tuple { elements: vec![arg] }, StaticType::Tuple(vec![StaticType::Int]));
let call_node = make_typed_node(
BoundKind::Call { callee: Box::new(callee), args: vec![arg] },
BoundKind::Call { callee: Box::new(callee), args: Box::new(args_tuple) },
StaticType::Any
);
@@ -310,12 +329,13 @@ mod tests {
let name0 = Symbol::from("f");
let name1 = Symbol::from("x");
// Call(Get(Local(0)), [Get(Local(1))]) where arg is Any
let callee = make_typed_node(BoundKind::Get { addr: Address::Local(0), name: name0 }, StaticType::Function(Box::new(Signature { params: vec![StaticType::Any], ret: StaticType::Void })));
// Call(Get(Local(0)), Tuple([Get(Local(1))])) where arg is Any
let callee = make_typed_node(BoundKind::Get { addr: Address::Local(0), name: name0 }, StaticType::Function(Box::new(Signature { params: StaticType::Tuple(vec![StaticType::Any]), ret: StaticType::Void })));
let arg = make_typed_node(BoundKind::Get { addr: Address::Local(1), name: name1 }, StaticType::Any);
let args_tuple = make_typed_node(BoundKind::Tuple { elements: vec![arg] }, StaticType::Tuple(vec![StaticType::Any]));
let call_node = make_typed_node(
BoundKind::Call { callee: Box::new(callee), args: vec![arg] },
BoundKind::Call { callee: Box::new(callee), args: Box::new(args_tuple) },
StaticType::Void
);
@@ -349,12 +369,13 @@ mod tests {
spec.cache.borrow_mut().insert(key, (specialized_val.clone(), ret_ty.clone()));
// Create the call node: Call(Get(0), [Arg(Int)])
// Create the call node: Call(Get(0), Tuple([Arg(Int)]))
let callee = make_typed_node(BoundKind::Get { addr, name }, StaticType::Any);
let arg = make_typed_node(BoundKind::Constant(Value::Int(1)), StaticType::Int);
let args_tuple = make_typed_node(BoundKind::Tuple { elements: vec![arg] }, StaticType::Tuple(vec![StaticType::Int]));
let call_node = make_typed_node(
BoundKind::Call { callee: Box::new(callee), args: vec![arg] },
BoundKind::Call { callee: Box::new(callee), args: Box::new(args_tuple) },
StaticType::Any
);
@@ -393,9 +414,10 @@ mod tests {
let callee = make_typed_node(BoundKind::Get { addr, name }, StaticType::Any);
let arg = make_typed_node(BoundKind::Constant(Value::Int(1)), StaticType::Int);
let args_tuple = make_typed_node(BoundKind::Tuple { elements: vec![arg] }, StaticType::Tuple(vec![StaticType::Int]));
let call_node = make_typed_node(
BoundKind::Call { callee: Box::new(callee), args: vec![arg] },
BoundKind::Call { callee: Box::new(callee), args: Box::new(args_tuple) },
StaticType::Any
);
@@ -431,10 +453,11 @@ mod tests {
let callee = make_typed_node(BoundKind::Get { addr, name }, StaticType::Any);
let arg = make_typed_node(BoundKind::Constant(Value::Int(1)), StaticType::Int);
let args_tuple = make_typed_node(BoundKind::Tuple { elements: vec![arg] }, StaticType::Tuple(vec![StaticType::Int]));
// Use TailCall here
let call_node = make_typed_node(
BoundKind::TailCall { callee: Box::new(callee), args: vec![arg] },
BoundKind::TailCall { callee: Box::new(callee), args: Box::new(args_tuple) },
StaticType::Any
);