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:
+23
-14
@@ -110,6 +110,19 @@ impl Binder {
|
||||
}))
|
||||
},
|
||||
|
||||
UntypedKind::Parameter(sym) => {
|
||||
// Parameters are ONLY allowed if we are inside a function (Binder stack > 1)
|
||||
if self.functions.len() <= 1 {
|
||||
return Err(format!("Parameter '{}' is not allowed in root scope.", sym.name));
|
||||
}
|
||||
let current_fn = self.functions.last_mut().unwrap();
|
||||
let slot = current_fn.scope.define(sym)?;
|
||||
Ok(self.make_node(node.identity.clone(), BoundKind::Parameter {
|
||||
name: sym.clone(),
|
||||
slot
|
||||
}))
|
||||
},
|
||||
|
||||
UntypedKind::If { cond, then_br, else_br } => {
|
||||
let cond = self.bind(cond)?;
|
||||
let then_br = self.bind(then_br)?;
|
||||
@@ -176,20 +189,19 @@ impl Binder {
|
||||
},
|
||||
|
||||
UntypedKind::Lambda { params, body } => {
|
||||
let identity = node.identity.clone();
|
||||
self.functions.push(FunctionCompiler::new());
|
||||
|
||||
{
|
||||
let current_fn = self.functions.last_mut().unwrap();
|
||||
for param in params {
|
||||
current_fn.scope.define(param)?;
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Bind the parameter pattern/tuple
|
||||
let params_bound = self.bind(params)?;
|
||||
|
||||
// 2. Bind the body
|
||||
let body_bound = self.bind(body)?;
|
||||
|
||||
let compiled_fn = self.functions.pop().unwrap();
|
||||
|
||||
Ok(self.make_node(node.identity.clone(), BoundKind::Lambda {
|
||||
param_count: params.len() as u32,
|
||||
Ok(self.make_node(identity, BoundKind::Lambda {
|
||||
params: Box::new(params_bound),
|
||||
upvalues: compiled_fn.upvalues,
|
||||
body: Rc::new(body_bound),
|
||||
}))
|
||||
@@ -197,14 +209,11 @@ impl Binder {
|
||||
|
||||
UntypedKind::Call { callee, args } => {
|
||||
let callee = self.bind(callee)?;
|
||||
let mut bound_args = Vec::new();
|
||||
for arg in args {
|
||||
bound_args.push(self.bind(arg)?);
|
||||
}
|
||||
let args = self.bind(args)?;
|
||||
|
||||
Ok(self.make_node(node.identity.clone(), BoundKind::Call {
|
||||
callee: Box::new(callee),
|
||||
args: bound_args
|
||||
args: Box::new(args)
|
||||
}))
|
||||
},
|
||||
|
||||
|
||||
@@ -21,11 +21,23 @@ impl<T> Clone for Box<dyn BoundExtension<T>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Type alias for a node that has been bound. Defaults to untyped (T = ()).
|
||||
pub type BoundNode<T = ()> = Node<BoundKind<T>, T>;
|
||||
|
||||
/// Type alias for a node that has been fully type-checked.
|
||||
pub type TypedNode = BoundNode<StaticType>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum BoundKind<T = ()> {
|
||||
Nop,
|
||||
Constant(Value),
|
||||
|
||||
/// A positional parameter in a Lambda's parameter list.
|
||||
Parameter {
|
||||
name: Symbol,
|
||||
slot: u32,
|
||||
},
|
||||
|
||||
// Variable Access (Resolved)
|
||||
Get {
|
||||
addr: Address,
|
||||
@@ -35,53 +47,53 @@ pub enum BoundKind<T = ()> {
|
||||
// Variable Update (Assignment)
|
||||
Set {
|
||||
addr: Address,
|
||||
value: Box<Node<BoundKind<T>, T>>,
|
||||
value: Box<BoundNode<T>>,
|
||||
},
|
||||
|
||||
// Variable Declaration (Local)
|
||||
DefLocal {
|
||||
name: Symbol,
|
||||
slot: u32,
|
||||
value: Box<Node<BoundKind<T>, T>>,
|
||||
value: Box<BoundNode<T>>,
|
||||
captured_by: Vec<Identity>,
|
||||
},
|
||||
|
||||
If {
|
||||
cond: Box<Node<BoundKind<T>, T>>,
|
||||
then_br: Box<Node<BoundKind<T>, T>>,
|
||||
else_br: Option<Box<Node<BoundKind<T>, T>>>,
|
||||
cond: Box<BoundNode<T>>,
|
||||
then_br: Box<BoundNode<T>>,
|
||||
else_br: Option<Box<BoundNode<T>>>,
|
||||
},
|
||||
|
||||
// Global Definition (Locals are implicit by stack position)
|
||||
DefGlobal {
|
||||
name: Symbol,
|
||||
global_index: u32,
|
||||
value: Box<Node<BoundKind<T>, T>>,
|
||||
value: Box<BoundNode<T>>,
|
||||
},
|
||||
|
||||
Lambda {
|
||||
param_count: u32,
|
||||
params: Box<BoundNode<T>>,
|
||||
// The list of variables captured from enclosing scopes
|
||||
upvalues: Vec<Address>,
|
||||
body: Rc<Node<BoundKind<T>, T>>,
|
||||
body: Rc<BoundNode<T>>,
|
||||
},
|
||||
|
||||
Call {
|
||||
callee: Box<Node<BoundKind<T>, T>>,
|
||||
args: Vec<Node<BoundKind<T>, T>>,
|
||||
callee: Box<BoundNode<T>>,
|
||||
args: Box<BoundNode<T>>,
|
||||
},
|
||||
|
||||
TailCall {
|
||||
callee: Box<Node<BoundKind<T>, T>>,
|
||||
args: Vec<Node<BoundKind<T>, T>>,
|
||||
callee: Box<BoundNode<T>>,
|
||||
args: Box<BoundNode<T>>,
|
||||
},
|
||||
|
||||
Block {
|
||||
exprs: Vec<Node<BoundKind<T>, T>>,
|
||||
exprs: Vec<BoundNode<T>>,
|
||||
},
|
||||
|
||||
Tuple {
|
||||
elements: Vec<Node<BoundKind<T>, T>>,
|
||||
elements: Vec<BoundNode<T>>,
|
||||
},
|
||||
|
||||
Map {
|
||||
@@ -93,7 +105,7 @@ pub enum BoundKind<T = ()> {
|
||||
/// The original call from the untyped AST.
|
||||
original_call: Rc<Node<crate::ast::nodes::UntypedKind>>,
|
||||
/// The result of binding the expanded AST.
|
||||
bound_expanded: Box<Node<BoundKind<T>, T>>,
|
||||
bound_expanded: Box<BoundNode<T>>,
|
||||
},
|
||||
|
||||
/// DSL-specific extension slot
|
||||
@@ -101,25 +113,26 @@ pub enum BoundKind<T = ()> {
|
||||
}
|
||||
|
||||
/// A single entry in a Map literal (Key-Value pair)
|
||||
pub type MapEntry<T> = (Node<BoundKind<T>, T>, Node<BoundKind<T>, T>);
|
||||
|
||||
/// Type alias for a node that has been bound (addresses resolved) but not yet type-checked.
|
||||
pub type BoundNode = Node<BoundKind<()>, ()>;
|
||||
|
||||
/// Type alias for a node that has been fully type-checked and is ready for execution.
|
||||
pub type TypedNode = Node<BoundKind<StaticType>, StaticType>;
|
||||
pub type MapEntry<T> = (BoundNode<T>, BoundNode<T>);
|
||||
|
||||
impl<T> BoundKind<T> {
|
||||
pub fn display_name(&self) -> String {
|
||||
match self {
|
||||
BoundKind::Nop => "NOP".to_string(),
|
||||
BoundKind::Constant(v) => format!("CONST({})", v),
|
||||
BoundKind::Parameter { name, slot } => format!("PARAM({}, Slot:{})", name.name, slot),
|
||||
BoundKind::Get { addr, name } => format!("GET({}, {:?})", name.name, addr),
|
||||
BoundKind::Set { addr, .. } => format!("SET({:?})", addr),
|
||||
BoundKind::DefLocal { name, slot, .. } => format!("DEF_LOCAL({}, Slot:{})", name.name, slot),
|
||||
BoundKind::If { .. } => "IF".to_string(),
|
||||
BoundKind::DefGlobal { name, global_index, .. } => format!("DEF_GLOBAL({}, Idx:{})", name.name, global_index),
|
||||
BoundKind::Lambda { upvalues, .. } => format!("LAMBDA(Captures:{})", upvalues.len()),
|
||||
BoundKind::Lambda { params, upvalues, .. } => {
|
||||
let p_str = match ¶ms.kind {
|
||||
BoundKind::Tuple { elements } => format!("p:{}", elements.len()),
|
||||
_ => "p:1".to_string(),
|
||||
};
|
||||
format!("LAMBDA({}, Captures:{})", p_str, upvalues.len())
|
||||
},
|
||||
BoundKind::Call { .. } => "CALL".to_string(),
|
||||
BoundKind::TailCall { .. } => "T-CALL".to_string(),
|
||||
BoundKind::Block { .. } => "BLOCK".to_string(),
|
||||
|
||||
@@ -117,9 +117,14 @@ impl Dumper {
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::Lambda { param_count, upvalues, body } => {
|
||||
self.log(&format!("Lambda (Params: {}, Upvalues: {})", param_count, upvalues.len()), node);
|
||||
BoundKind::Lambda { params, upvalues, body } => {
|
||||
self.log(&format!("Lambda (Upvalues: {})", upvalues.len()), node);
|
||||
self.indent += 1;
|
||||
|
||||
self.write_indent();
|
||||
self.output.push_str("Parameters:\n");
|
||||
self.visit(params);
|
||||
|
||||
if !upvalues.is_empty() {
|
||||
self.write_indent();
|
||||
self.output.push_str(&format!("Upvalues: {:?}\n", upvalues));
|
||||
@@ -128,23 +133,37 @@ impl Dumper {
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::Parameter { name, slot } => {
|
||||
self.log(&format!("Parameter (Name: '{}', Slot: {})", name.name, slot), node);
|
||||
}
|
||||
|
||||
BoundKind::Call { callee, args } => {
|
||||
self.log("Call", node);
|
||||
self.indent += 1;
|
||||
|
||||
self.write_indent();
|
||||
self.output.push_str("Callee:\n");
|
||||
self.visit(callee);
|
||||
for arg in args {
|
||||
self.visit(arg);
|
||||
}
|
||||
|
||||
self.write_indent();
|
||||
self.output.push_str("Arguments:\n");
|
||||
self.visit(args);
|
||||
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::TailCall { callee, args } => {
|
||||
self.log("TailCall (TCO)", node);
|
||||
self.indent += 1;
|
||||
|
||||
self.write_indent();
|
||||
self.output.push_str("Callee:\n");
|
||||
self.visit(callee);
|
||||
for arg in args {
|
||||
self.visit(arg);
|
||||
}
|
||||
|
||||
self.write_indent();
|
||||
self.output.push_str("Arguments:\n");
|
||||
self.visit(args);
|
||||
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,10 @@ impl<'a> LambdaCollector<'a> {
|
||||
}
|
||||
|
||||
BoundKind::DefGlobal { global_index, value, .. } => {
|
||||
// If we define a global that is a lambda, register it as a template.
|
||||
if let BoundKind::Lambda { .. } = &value.kind {
|
||||
self.registry.insert(*global_index, (**value).clone());
|
||||
// If we define a global that is a lambda, register its BODY as the template.
|
||||
// This allows the VM to skip the Lambda/Parameter nodes during execution.
|
||||
if let BoundKind::Lambda { body, .. } = &value.kind {
|
||||
self.registry.insert(*global_index, (**body).clone());
|
||||
}
|
||||
self.visit(value);
|
||||
}
|
||||
@@ -33,9 +34,9 @@ impl<'a> LambdaCollector<'a> {
|
||||
BoundKind::Set { addr, value } => {
|
||||
// Also track assignments to globals if they hold lambdas.
|
||||
if let Address::Global(global_index) = addr
|
||||
&& let BoundKind::Lambda { .. } = &value.kind
|
||||
&& let BoundKind::Lambda { body, .. } = &value.kind
|
||||
{
|
||||
self.registry.insert(*global_index, (**value).clone());
|
||||
self.registry.insert(*global_index, (**body).clone());
|
||||
}
|
||||
self.visit(value);
|
||||
}
|
||||
@@ -48,7 +49,8 @@ impl<'a> LambdaCollector<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
BoundKind::Lambda { body, .. } => {
|
||||
BoundKind::Lambda { params, body, .. } => {
|
||||
self.visit(params);
|
||||
self.visit(body);
|
||||
}
|
||||
|
||||
@@ -58,9 +60,7 @@ impl<'a> LambdaCollector<'a> {
|
||||
|
||||
BoundKind::Call { callee, args } | BoundKind::TailCall { callee, args } => {
|
||||
self.visit(callee);
|
||||
for arg in args {
|
||||
self.visit(arg);
|
||||
}
|
||||
self.visit(args);
|
||||
}
|
||||
|
||||
BoundKind::Tuple { elements } => {
|
||||
|
||||
+47
-28
@@ -78,7 +78,7 @@ impl<E: MacroEvaluator> MacroExpander<E> {
|
||||
fn expand_recursive(&mut self, node: Node<UntypedKind>) -> Result<Node<UntypedKind>, String> {
|
||||
match node.kind {
|
||||
UntypedKind::MacroDecl { name, params, body } => {
|
||||
let p_names: Vec<Rc<str>> = params.into_iter().map(|s| s.name).collect();
|
||||
let p_names = self.extract_param_names(¶ms)?;
|
||||
self.registry.define(name.name, p_names, *body);
|
||||
Ok(Node {
|
||||
identity: node.identity,
|
||||
@@ -90,25 +90,27 @@ impl<E: MacroEvaluator> MacroExpander<E> {
|
||||
UntypedKind::Call { callee, args } => {
|
||||
if let UntypedKind::Identifier(ref sym) = callee.kind
|
||||
&& let Some((params, body)) = self.registry.lookup(&sym.name) {
|
||||
let mut expanded_args = Vec::new();
|
||||
for arg in args {
|
||||
expanded_args.push(self.expand_recursive(arg)?);
|
||||
}
|
||||
let expanded = self.expand_call(node.identity.clone(), &sym.name, params, expanded_args, body)?;
|
||||
let expanded_args = self.expand_recursive(*args)?;
|
||||
|
||||
// Extract argument nodes from the expanded tuple
|
||||
let arg_elements = if let UntypedKind::Tuple { elements } = expanded_args.kind {
|
||||
elements
|
||||
} else {
|
||||
vec![expanded_args]
|
||||
};
|
||||
|
||||
let expanded = self.expand_call(node.identity.clone(), &sym.name, params, arg_elements, body)?;
|
||||
return self.expand_recursive(expanded);
|
||||
}
|
||||
|
||||
let expanded_callee = self.expand_recursive(*callee)?;
|
||||
let mut expanded_args = Vec::new();
|
||||
for arg in args {
|
||||
expanded_args.push(self.expand_recursive(arg)?);
|
||||
}
|
||||
let expanded_args = self.expand_recursive(*args)?;
|
||||
|
||||
Ok(Node {
|
||||
identity: node.identity,
|
||||
kind: UntypedKind::Call {
|
||||
callee: Box::new(expanded_callee),
|
||||
args: expanded_args,
|
||||
args: Box::new(expanded_args),
|
||||
},
|
||||
ty: (),
|
||||
})
|
||||
@@ -131,12 +133,16 @@ impl<E: MacroEvaluator> MacroExpander<E> {
|
||||
|
||||
UntypedKind::Lambda { params, body } => {
|
||||
self.registry.push();
|
||||
let expanded_params = self.expand_recursive(*params)?;
|
||||
let expanded_body = self.expand_recursive(Node::clone(&body))?;
|
||||
self.registry.pop();
|
||||
|
||||
Ok(Node {
|
||||
identity: node.identity,
|
||||
kind: UntypedKind::Lambda { params, body: Rc::new(expanded_body) },
|
||||
kind: UntypedKind::Lambda {
|
||||
params: Box::new(expanded_params),
|
||||
body: Rc::new(expanded_body)
|
||||
},
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
@@ -242,7 +248,11 @@ impl<E: MacroEvaluator> MacroExpander<E> {
|
||||
kind: UntypedKind::Identifier(Symbol::from(name)),
|
||||
ty: (),
|
||||
}),
|
||||
args: bindings.into_values().collect(),
|
||||
args: Box::new(Node {
|
||||
identity: identity.clone(),
|
||||
kind: UntypedKind::Tuple { elements: bindings.into_values().collect() },
|
||||
ty: (),
|
||||
}),
|
||||
},
|
||||
ty: (),
|
||||
};
|
||||
@@ -257,6 +267,20 @@ impl<E: MacroEvaluator> MacroExpander<E> {
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_param_names(&self, node: &Node<UntypedKind>) -> Result<Vec<Rc<str>>, String> {
|
||||
match &node.kind {
|
||||
UntypedKind::Parameter(sym) => Ok(vec![sym.name.clone()]),
|
||||
UntypedKind::Tuple { elements } => {
|
||||
let mut names = Vec::new();
|
||||
for el in elements {
|
||||
names.extend(self.extract_param_names(el)?);
|
||||
}
|
||||
Ok(names)
|
||||
}
|
||||
_ => Err("Invalid parameter pattern in macro declaration".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_template(&self, node: Node<UntypedKind>, state: &mut ExpansionState) -> Result<Node<UntypedKind>, String> {
|
||||
match node.kind {
|
||||
UntypedKind::Identifier(mut sym) => {
|
||||
@@ -265,6 +289,11 @@ impl<E: MacroEvaluator> MacroExpander<E> {
|
||||
Ok(Node { identity: node.identity, kind: UntypedKind::Identifier(sym), ty: () })
|
||||
}
|
||||
|
||||
UntypedKind::Parameter(mut sym) => {
|
||||
sym.context = Some(state.expansion_id.clone());
|
||||
Ok(Node { identity: node.identity, kind: UntypedKind::Parameter(sym), ty: () })
|
||||
}
|
||||
|
||||
UntypedKind::Placeholder(inner) => {
|
||||
// Break out of template for substitution/evaluation
|
||||
if let UntypedKind::Identifier(ref sym) = inner.kind
|
||||
@@ -353,18 +382,10 @@ impl<E: MacroEvaluator> MacroExpander<E> {
|
||||
|
||||
UntypedKind::Call { callee, args } => {
|
||||
let expanded_callee = self.expand_template(*callee, state)?;
|
||||
let mut expanded_args = Vec::new();
|
||||
for arg in args {
|
||||
if let UntypedKind::Splice(ref inner) = arg.kind {
|
||||
let val = self.evaluator.evaluate(inner, state.bindings)?;
|
||||
self.handle_splice_value(val, node.identity.clone(), &mut expanded_args)?;
|
||||
} else {
|
||||
expanded_args.push(self.expand_template(arg, state)?);
|
||||
}
|
||||
}
|
||||
let expanded_args = self.expand_template(*args, state)?;
|
||||
Ok(Node {
|
||||
identity: node.identity,
|
||||
kind: UntypedKind::Call { callee: Box::new(expanded_callee), args: expanded_args },
|
||||
kind: UntypedKind::Call { callee: Box::new(expanded_callee), args: Box::new(expanded_args) },
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
@@ -384,14 +405,12 @@ impl<E: MacroEvaluator> MacroExpander<E> {
|
||||
})
|
||||
}
|
||||
|
||||
UntypedKind::Lambda { mut params, body } => {
|
||||
for p in &mut params {
|
||||
p.context = Some(state.expansion_id.clone());
|
||||
}
|
||||
UntypedKind::Lambda { params, body } => {
|
||||
let expanded_params = self.expand_template(*params, state)?;
|
||||
let body = self.expand_template(Node::clone(&body), state)?;
|
||||
Ok(Node {
|
||||
identity: node.identity,
|
||||
kind: UntypedKind::Lambda { params, body: Rc::new(body) },
|
||||
kind: UntypedKind::Lambda { params: Box::new(expanded_params), body: Rc::new(body) },
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ impl TCO {
|
||||
match node.kind {
|
||||
BoundKind::Call { callee, args } => {
|
||||
let new_callee = Box::new(Self::transform(*callee, false));
|
||||
let new_args: Vec<_> = args.into_iter().map(|arg| Self::transform(arg, false)).collect();
|
||||
let new_args = Box::new(Self::transform(*args, false));
|
||||
|
||||
if is_tail_position {
|
||||
Node {
|
||||
@@ -75,13 +75,13 @@ impl TCO {
|
||||
}
|
||||
},
|
||||
|
||||
BoundKind::Lambda { param_count, upvalues, body } => {
|
||||
BoundKind::Lambda { params, upvalues, body } => {
|
||||
// The body of a lambda is implicitly in tail position when the lambda is called.
|
||||
let new_body = Rc::new(Self::transform((*body).clone(), true));
|
||||
|
||||
Node {
|
||||
kind: BoundKind::Lambda {
|
||||
param_count,
|
||||
params,
|
||||
upvalues,
|
||||
body: new_body,
|
||||
},
|
||||
|
||||
@@ -48,7 +48,7 @@ impl TypeChecker {
|
||||
|
||||
pub fn check(&self, node: BoundNode, arg_types: &[StaticType]) -> Result<TypedNode, String> {
|
||||
match node.kind {
|
||||
BoundKind::Lambda { param_count, upvalues, body } => {
|
||||
BoundKind::Lambda { params, upvalues, body } => {
|
||||
// 1. Determine types of captured variables (Root lambdas have none)
|
||||
let mut upvalue_types = Vec::with_capacity(upvalues.len());
|
||||
for _ in &upvalues {
|
||||
@@ -57,35 +57,33 @@ impl TypeChecker {
|
||||
|
||||
// 2. Create the specialized context
|
||||
let root_ctx = TypeContext::new(0, vec![], None);
|
||||
let mut lambda_ctx = TypeContext::new(param_count + 64, upvalue_types, Some(&root_ctx));
|
||||
let mut lambda_ctx = TypeContext::new(64, upvalue_types, Some(&root_ctx));
|
||||
|
||||
// 3. INJECT specialized argument types into slots
|
||||
for (i, ty) in arg_types.iter().enumerate() {
|
||||
if (i as u32) < param_count {
|
||||
lambda_ctx.set_local_type(i as u32, ty.clone());
|
||||
}
|
||||
}
|
||||
let arg_tuple_ty = if arg_types.is_empty() {
|
||||
StaticType::Any // No specialized info
|
||||
} else {
|
||||
StaticType::Tuple(arg_types.to_vec())
|
||||
};
|
||||
|
||||
let params_typed = self.check_params(*params, &arg_tuple_ty, &mut lambda_ctx)?;
|
||||
|
||||
// 4. Check body with the new types
|
||||
let body_typed = self.check_node((*body).clone(), &mut lambda_ctx)?;
|
||||
let ret_ty = body_typed.ty.clone();
|
||||
|
||||
// 5. Construct specialized function type
|
||||
let final_params = if arg_types.is_empty() {
|
||||
vec![StaticType::Any; param_count as usize]
|
||||
} else {
|
||||
arg_types.to_vec()
|
||||
};
|
||||
let final_params_ty = params_typed.ty.clone();
|
||||
|
||||
let fn_ty = StaticType::Function(Box::new(crate::ast::types::Signature {
|
||||
params: final_params,
|
||||
params: final_params_ty,
|
||||
ret: ret_ty,
|
||||
}));
|
||||
|
||||
Ok(Node {
|
||||
identity: node.identity,
|
||||
kind: BoundKind::Lambda {
|
||||
param_count,
|
||||
params: Box::new(params_typed),
|
||||
upvalues,
|
||||
body: Rc::new(body_typed)
|
||||
},
|
||||
@@ -97,7 +95,11 @@ impl TypeChecker {
|
||||
let virtual_lambda = BoundNode {
|
||||
identity: node.identity.clone(),
|
||||
kind: BoundKind::Lambda {
|
||||
param_count: 0,
|
||||
params: Box::new(Node {
|
||||
identity: node.identity.clone(),
|
||||
kind: BoundKind::Tuple { elements: vec![] },
|
||||
ty: (),
|
||||
}),
|
||||
upvalues: vec![],
|
||||
body: Rc::new(node)
|
||||
},
|
||||
@@ -108,6 +110,38 @@ impl TypeChecker {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_params(&self, node: BoundNode, specialized_ty: &StaticType, ctx: &mut TypeContext) -> Result<TypedNode, String> {
|
||||
let (kind, ty) = match node.kind {
|
||||
BoundKind::Parameter { name, slot } => {
|
||||
ctx.set_local_type(slot, specialized_ty.clone());
|
||||
(BoundKind::Parameter { name, slot }, specialized_ty.clone())
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
let mut typed_elements = Vec::new();
|
||||
let mut elem_types = Vec::new();
|
||||
|
||||
for (i, el) in elements.into_iter().enumerate() {
|
||||
let sub_ty = match specialized_ty {
|
||||
StaticType::Tuple(t) => t.get(i).cloned().unwrap_or(StaticType::Any),
|
||||
StaticType::Vector(inner, _) => (**inner).clone(),
|
||||
_ => StaticType::Any,
|
||||
};
|
||||
let t = self.check_params(el, &sub_ty, ctx)?;
|
||||
elem_types.push(t.ty.clone());
|
||||
typed_elements.push(t);
|
||||
}
|
||||
(BoundKind::Tuple { elements: typed_elements }, StaticType::Tuple(elem_types))
|
||||
}
|
||||
_ => return Err("Invalid node in parameter list".to_string()),
|
||||
};
|
||||
|
||||
Ok(Node {
|
||||
identity: node.identity,
|
||||
kind,
|
||||
ty,
|
||||
})
|
||||
}
|
||||
|
||||
fn check_node(&self, node: BoundNode, ctx: &mut TypeContext) -> Result<TypedNode, String> {
|
||||
let (kind, ty) = match node.kind {
|
||||
BoundKind::Nop => (BoundKind::Nop, StaticType::Void),
|
||||
@@ -117,6 +151,10 @@ impl TypeChecker {
|
||||
(BoundKind::Constant(v), ty)
|
||||
},
|
||||
|
||||
BoundKind::Parameter { name, slot } => {
|
||||
(BoundKind::Parameter { name, slot }, ctx.get_type(Address::Local(slot)))
|
||||
},
|
||||
|
||||
BoundKind::Get { addr, name } => {
|
||||
let ty = if let Address::Global(idx) = addr {
|
||||
self.global_types.borrow().get(&idx).cloned().unwrap_or(StaticType::Any)
|
||||
@@ -194,7 +232,7 @@ impl TypeChecker {
|
||||
(BoundKind::Block { exprs: typed_exprs }, last_ty)
|
||||
},
|
||||
|
||||
BoundKind::Lambda { param_count, upvalues, body } => {
|
||||
BoundKind::Lambda { params, upvalues, body } => {
|
||||
// 1. Determine types of captured variables
|
||||
let mut upvalue_types = Vec::with_capacity(upvalues.len());
|
||||
for &addr in &upvalues {
|
||||
@@ -202,20 +240,21 @@ impl TypeChecker {
|
||||
}
|
||||
|
||||
// 2. Create nested context for lambda body
|
||||
let mut lambda_ctx = TypeContext::new(param_count + 64, upvalue_types, Some(ctx));
|
||||
let mut lambda_ctx = TypeContext::new(64, upvalue_types, Some(ctx));
|
||||
|
||||
// 3. Check body
|
||||
// 3. Check parameters and body
|
||||
let params_typed = self.check_params(*params, &StaticType::Any, &mut lambda_ctx)?;
|
||||
let body_typed = self.check_node((*body).clone(), &mut lambda_ctx)?;
|
||||
let ret_ty = body_typed.ty.clone();
|
||||
|
||||
// 4. Construct function type: fn(any ... any) -> Ret
|
||||
// 4. Construct function type
|
||||
let fn_ty = StaticType::Function(Box::new(crate::ast::types::Signature {
|
||||
params: vec![StaticType::Any; param_count as usize],
|
||||
params: params_typed.ty.clone(),
|
||||
ret: ret_ty,
|
||||
}));
|
||||
|
||||
(BoundKind::Lambda {
|
||||
param_count,
|
||||
params: Box::new(params_typed),
|
||||
upvalues,
|
||||
body: Rc::new(body_typed)
|
||||
}, fn_ty)
|
||||
@@ -223,17 +262,13 @@ impl TypeChecker {
|
||||
|
||||
BoundKind::Call { callee, args } => {
|
||||
let callee_typed = self.check_node(*callee, ctx)?;
|
||||
let mut typed_args = Vec::new();
|
||||
for arg in args {
|
||||
typed_args.push(self.check_node(arg, ctx)?);
|
||||
}
|
||||
let args_typed = self.check_node(*args, ctx)?;
|
||||
|
||||
let arg_types: Vec<_> = typed_args.iter().map(|a| a.ty.clone()).collect();
|
||||
let ret_ty = callee_typed.ty.resolve_call(&arg_types).unwrap_or(StaticType::Any);
|
||||
let ret_ty = callee_typed.ty.resolve_call(&args_typed.ty).unwrap_or(StaticType::Any);
|
||||
|
||||
(BoundKind::Call {
|
||||
callee: Box::new(callee_typed),
|
||||
args: typed_args
|
||||
args: Box::new(args_typed)
|
||||
}, ret_ty)
|
||||
},
|
||||
|
||||
|
||||
@@ -19,6 +19,20 @@ impl UpvalueAnalyzer {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn visit_params(node: &Node<UntypedKind>, scope: &mut HashMap<Symbol, Identity>) {
|
||||
match &node.kind {
|
||||
UntypedKind::Parameter(sym) => {
|
||||
scope.insert(sym.clone(), node.identity.clone());
|
||||
}
|
||||
UntypedKind::Tuple { elements } => {
|
||||
for el in elements {
|
||||
Self::visit_params(el, scope);
|
||||
}
|
||||
}
|
||||
_ => {} // Ignore other nodes in parameter patterns for now
|
||||
}
|
||||
}
|
||||
|
||||
fn visit(
|
||||
node: &Node<UntypedKind>,
|
||||
scopes: &mut Vec<HashMap<Symbol, Identity>>,
|
||||
@@ -50,15 +64,20 @@ impl UpvalueAnalyzer {
|
||||
}
|
||||
UntypedKind::Lambda { params, body } => {
|
||||
let mut new_scope = HashMap::new();
|
||||
for p in params {
|
||||
new_scope.insert(p.clone(), node.identity.clone());
|
||||
}
|
||||
Self::visit_params(params, &mut new_scope);
|
||||
|
||||
scopes.push(new_scope);
|
||||
// The current node is the lambda causing captures in its body
|
||||
Self::visit(body, scopes, capture_map, Some(node.identity.clone()));
|
||||
scopes.pop();
|
||||
}
|
||||
UntypedKind::MacroDecl { params, body, .. } => {
|
||||
let mut new_scope = HashMap::new();
|
||||
Self::visit_params(params, &mut new_scope);
|
||||
scopes.push(new_scope);
|
||||
Self::visit(body, scopes, capture_map, current_lambda);
|
||||
scopes.pop();
|
||||
}
|
||||
UntypedKind::If { cond, then_br, else_br } => {
|
||||
Self::visit(cond, scopes, capture_map, current_lambda.clone());
|
||||
Self::visit(then_br, scopes, capture_map, current_lambda.clone());
|
||||
@@ -72,9 +91,7 @@ impl UpvalueAnalyzer {
|
||||
}
|
||||
UntypedKind::Call { callee, args } => {
|
||||
Self::visit(callee, scopes, capture_map, current_lambda.clone());
|
||||
for arg in args {
|
||||
Self::visit(arg, scopes, capture_map, current_lambda.clone());
|
||||
}
|
||||
Self::visit(args, scopes, capture_map, current_lambda.clone());
|
||||
}
|
||||
UntypedKind::Block { exprs } => {
|
||||
for expr in exprs {
|
||||
@@ -95,10 +112,10 @@ impl UpvalueAnalyzer {
|
||||
UntypedKind::Expansion { call: _, expanded } => {
|
||||
Self::visit(expanded, scopes, capture_map, current_lambda);
|
||||
}
|
||||
UntypedKind::MacroDecl { body, .. } | UntypedKind::Template(body) | UntypedKind::Placeholder(body) | UntypedKind::Splice(body) => {
|
||||
UntypedKind::Template(body) | UntypedKind::Placeholder(body) | UntypedKind::Splice(body) => {
|
||||
Self::visit(body, scopes, capture_map, current_lambda);
|
||||
}
|
||||
UntypedKind::Nop | UntypedKind::Constant(_) | UntypedKind::Extension(_) => {}
|
||||
UntypedKind::Nop | UntypedKind::Constant(_) | UntypedKind::Extension(_) | UntypedKind::Parameter(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user