Add type aliases for registries

Introduces `GlobalFunctionRegistry` and `GlobalAnalyzedRegistry` type
aliases to clarify the usage of `HashMap`s for storing global functions
and their analyzed versions. This change also refactors the
`LambdaCollector` and `Optimizer` to use these new aliases, improving
code readability and maintainability.
This commit is contained in:
Michael Schimmel
2026-03-03 15:51:47 +01:00
parent a18642fd7b
commit 078b520c37
6 changed files with 94 additions and 82 deletions
+6
View File
@@ -60,6 +60,9 @@ pub type BoundNode<T = ()> = Node<BoundKind<T>, T>;
/// Type alias for a node that has been fully type-checked.
pub type TypedNode = BoundNode<StaticType>;
/// Type alias for the global function registry.
pub type GlobalFunctionRegistry = std::collections::HashMap<GlobalIdx, Rc<BoundNode>>;
/// Metrics collected during the analysis phase.
#[derive(Debug, Clone, PartialEq)]
pub struct NodeMetrics {
@@ -71,6 +74,9 @@ pub struct NodeMetrics {
/// Type alias for a node that has been analyzed.
pub type AnalyzedNode = BoundNode<NodeMetrics>;
/// Type alias for the global analyzed function registry.
pub type GlobalAnalyzedRegistry = std::collections::HashMap<GlobalIdx, Rc<AnalyzedNode>>;
#[derive(Debug, Clone)]
pub enum BoundKind<T = ()> {
Nop,
+5 -4
View File
@@ -1,15 +1,16 @@
use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundNode, GlobalIdx};
use std::collections::HashMap;
use std::rc::Rc;
/// A pass that collects all global function definitions (lambdas) into a registry.
/// This allows the Specializer to retrieve the original AST of a function for monomorphization.
pub struct LambdaCollector<'a, T> {
registry: &'a mut HashMap<GlobalIdx, BoundNode<T>>,
registry: &'a mut HashMap<GlobalIdx, Rc<BoundNode<T>>>,
}
impl<'a, T: Clone> LambdaCollector<'a, T> {
/// Performs a full traversal of the AST and populates the provided registry.
pub fn collect(node: &BoundNode<T>, registry: &'a mut HashMap<GlobalIdx, BoundNode<T>>) {
pub fn collect(node: &BoundNode<T>, registry: &'a mut HashMap<GlobalIdx, Rc<BoundNode<T>>>) {
let mut collector = Self { registry };
collector.visit(node);
}
@@ -32,7 +33,7 @@ impl<'a, T: Clone> LambdaCollector<'a, T> {
if let BoundKind::Lambda { .. } = &current.kind {
self.registry
.insert(*global_index, (*current).as_ref().clone());
.insert(*global_index, Rc::new((**current).clone()));
}
}
self.visit(value);
@@ -48,7 +49,7 @@ impl<'a, T: Clone> LambdaCollector<'a, T> {
if let BoundKind::Lambda { .. } = &current.kind {
self.registry
.insert(*global_index, (*current).as_ref().clone());
.insert(*global_index, Rc::new((**current).clone()));
}
}
self.visit(value);
+5 -3
View File
@@ -1,4 +1,6 @@
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, GlobalIdx, UpvalueIdx};
use crate::ast::compiler::bound_nodes::{
Address, AnalyzedNode, BoundKind, GlobalAnalyzedRegistry, GlobalIdx, UpvalueIdx,
};
use crate::ast::nodes::Node;
use crate::ast::types::{Purity, Value};
use crate::ast::vm::Closure;
@@ -16,7 +18,7 @@ pub struct Optimizer {
max_passes: usize,
pub globals: Option<Rc<RefCell<Vec<Value>>>>,
pub global_purity: Option<Rc<RefCell<HashMap<GlobalIdx, Purity>>>>,
pub lambda_registry: Option<Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>>,
pub lambda_registry: Option<Rc<RefCell<GlobalAnalyzedRegistry>>>,
}
impl Optimizer {
@@ -42,7 +44,7 @@ impl Optimizer {
pub fn with_registry(
mut self,
registry: Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>,
registry: Rc<RefCell<GlobalAnalyzedRegistry>>,
) -> Self {
self.lambda_registry = Some(registry);
self
+3 -3
View File
@@ -11,12 +11,12 @@ pub struct MonoCacheKey {
pub arg_types: Vec<StaticType>,
}
pub type CompileFunc = Rc<dyn Fn(BoundNode, &[StaticType]) -> Result<(Value, StaticType), String>>;
pub type CompileFunc = Rc<dyn Fn(Rc<BoundNode>, &[StaticType]) -> Result<(Value, StaticType), String>>;
pub type RtlLookupFunc = Rc<dyn Fn(&str, &[StaticType]) -> Option<(Value, StaticType)>>;
pub trait FunctionRegistry {
fn resolve(&self, addr: Address) -> Option<BoundNode>;
fn resolve_analyzed(&self, _addr: Address) -> Option<AnalyzedNode> {
fn resolve(&self, addr: Address) -> Option<Rc<BoundNode>>;
fn resolve_analyzed(&self, _addr: Address) -> Option<Rc<AnalyzedNode>> {
None
}
}
+61 -61
View File
@@ -81,11 +81,11 @@ impl TypeChecker {
pub fn check(
&self,
node: BoundNode,
node: &BoundNode,
arg_types: &[StaticType],
diag: &mut Diagnostics,
) -> TypedNode {
match node.kind {
match &node.kind {
BoundKind::Lambda {
params,
upvalues,
@@ -94,7 +94,7 @@ impl TypeChecker {
} => {
// 1. Determine types of captured variables (Root lambdas have none)
let mut upvalue_types = Vec::with_capacity(upvalues.len());
for _ in &upvalues {
for _ in upvalues {
upvalue_types.push(StaticType::Any);
}
@@ -111,14 +111,14 @@ impl TypeChecker {
};
let params_typed = self.check_params(
params.as_ref().clone(),
params.as_ref(),
&arg_tuple_ty,
&mut lambda_ctx,
diag,
);
// 4. Check body with the new types
let body_typed = self.check_node((*body).clone(), &mut lambda_ctx, diag);
let body_typed = self.check_node(body, &mut lambda_ctx, diag);
let ret_ty = body_typed.ty.clone();
// 5. Construct specialized function type
@@ -130,12 +130,12 @@ impl TypeChecker {
}));
Node {
identity: node.identity,
identity: node.identity.clone(),
kind: BoundKind::Lambda {
params: Rc::new(params_typed),
upvalues,
upvalues: upvalues.clone(),
body: Rc::new(body_typed),
positional_count,
positional_count: *positional_count,
},
ty: fn_ty,
}
@@ -151,24 +151,24 @@ impl TypeChecker {
ty: (),
}),
upvalues: vec![],
body: Rc::new(node),
body: Rc::new(node.clone()),
positional_count: Some(0),
},
ty: (),
};
self.check(virtual_lambda, &[], diag)
self.check(&virtual_lambda, &[], diag)
}
}
}
fn check_params(
&self,
node: BoundNode,
node: &BoundNode,
specialized_ty: &StaticType,
ctx: &mut TypeContext,
diag: &mut Diagnostics,
) -> TypedNode {
let (kind, ty) = match node.kind {
let (kind, ty) = match &node.kind {
BoundKind::Define {
name,
addr,
@@ -176,18 +176,18 @@ impl TypeChecker {
captured_by,
..
} => {
ctx.set_type(addr, specialized_ty.clone());
ctx.set_type(*addr, specialized_ty.clone());
(
BoundKind::Define {
name,
addr,
kind: decl_kind,
name: name.clone(),
addr: *addr,
kind: *decl_kind,
value: Box::new(Node {
identity: node.identity.clone(),
kind: BoundKind::Nop,
ty: specialized_ty.clone(),
}),
captured_by,
captured_by: captured_by.clone(),
},
specialized_ty.clone(),
)
@@ -202,7 +202,7 @@ impl TypeChecker {
// or we could add a check here against existing type.
(
BoundKind::Set {
addr,
addr: *addr,
value: Box::new(Node {
identity: node.identity.clone(),
kind: BoundKind::Nop,
@@ -230,7 +230,7 @@ impl TypeChecker {
Some(node.identity.clone()),
);
return Node {
identity: node.identity,
identity: node.identity.clone(),
kind: BoundKind::Error,
ty: StaticType::Error,
};
@@ -240,11 +240,11 @@ impl TypeChecker {
let mut typed_elements = Vec::new();
let mut elem_types = Vec::new();
for (i, el) in elements.into_iter().enumerate() {
for (i, el) in elements.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::Matrix(inner, _) => (**inner).clone(), // Matrix flat iteration or row? Wait. Matrix destructuring logic is tricky. But for now let's focus on Record.
StaticType::Matrix(inner, _) => (**inner).clone(),
StaticType::List(inner) => (**inner).clone(),
StaticType::Record(layout) => layout
.fields
@@ -276,7 +276,7 @@ impl TypeChecker {
};
Node {
identity: node.identity,
identity: node.identity.clone(),
kind,
ty,
}
@@ -284,16 +284,16 @@ impl TypeChecker {
fn check_node(
&self,
node: BoundNode,
node: &BoundNode,
ctx: &mut TypeContext,
diag: &mut Diagnostics,
) -> TypedNode {
let (kind, ty) = match node.kind {
let (kind, ty) = match &node.kind {
BoundKind::Nop => (BoundKind::Nop, StaticType::Void),
BoundKind::Constant(v) => {
let ty = v.static_type();
(BoundKind::Constant(v), ty)
(BoundKind::Constant(v.clone()), ty)
}
BoundKind::Define {
@@ -303,27 +303,27 @@ impl TypeChecker {
value,
captured_by,
} => {
let val_typed = self.check_node(*value, ctx, diag);
let val_typed = self.check_node(value, ctx, diag);
let ty = val_typed.ty.clone();
ctx.set_type(addr, ty.clone());
ctx.set_type(*addr, ty.clone());
(
BoundKind::Define {
name: name.clone(),
addr,
kind: decl_kind,
addr: *addr,
kind: *decl_kind,
value: Box::new(val_typed),
captured_by,
captured_by: captured_by.clone(),
},
ty,
)
}
BoundKind::Get { addr, name } => {
let ty = ctx.get_type(addr);
let ty = ctx.get_type(*addr);
(
BoundKind::Get {
addr,
addr: *addr,
name: name.clone(),
},
ty,
@@ -331,14 +331,14 @@ impl TypeChecker {
}
BoundKind::FieldAccessor(k) => {
(BoundKind::FieldAccessor(k), StaticType::FieldAccessor(k))
(BoundKind::FieldAccessor(*k), StaticType::FieldAccessor(*k))
}
BoundKind::GetField { rec, field } => {
let rec_typed = self.check_node(*rec, ctx, diag);
let rec_typed = self.check_node(rec, ctx, diag);
let field_ty = match &rec_typed.ty {
StaticType::Record(layout) => {
if let Some(idx) = layout.index_of(field) {
if let Some(idx) = layout.index_of(*field) {
layout.fields[idx].1.clone()
} else {
diag.push_error(
@@ -365,20 +365,20 @@ impl TypeChecker {
(
BoundKind::GetField {
rec: Box::new(rec_typed),
field,
field: *field,
},
field_ty,
)
}
BoundKind::Set { addr, value } => {
let val_typed = self.check_node(*value, ctx, diag);
let val_typed = self.check_node(value, ctx, diag);
let ty = val_typed.ty.clone();
ctx.set_type(addr, ty.clone());
ctx.set_type(*addr, ty.clone());
(
BoundKind::Set {
addr,
addr: *addr,
value: Box::new(val_typed),
},
ty,
@@ -386,8 +386,8 @@ impl TypeChecker {
}
BoundKind::Destructure { pattern, value } => {
let val_typed = self.check_node(*value, ctx, diag);
let pat_typed = self.check_params(*pattern, &val_typed.ty, ctx, diag);
let val_typed = self.check_node(value, ctx, diag);
let pat_typed = self.check_params(pattern, &val_typed.ty, ctx, diag);
let ty = val_typed.ty.clone();
(
BoundKind::Destructure {
@@ -403,14 +403,14 @@ impl TypeChecker {
then_br,
else_br,
} => {
let cond_typed = self.check_node(*cond, ctx, diag);
let then_typed = self.check_node(*then_br, ctx, diag);
let cond_typed = self.check_node(cond, ctx, diag);
let then_typed = self.check_node(then_br, ctx, diag);
let mut else_typed = None;
let mut final_ty = then_typed.ty.clone();
if let Some(e) = else_br {
let et = self.check_node(*e, ctx, diag);
let et = self.check_node(e, ctx, diag);
// Basic type promotion: if types differ, fall back to Any
if et.ty != final_ty {
final_ty = StaticType::Any;
@@ -447,7 +447,7 @@ impl TypeChecker {
}
// Specialized check for the lambda using the input types!
let typed_lambda = self.check(*lambda, &arg_types, diag);
let typed_lambda = self.check(lambda, &arg_types, diag);
let ret_ty = if let StaticType::Function(sig) = &typed_lambda.ty {
// If the lambda returns an Optional(T), the pipeline filters Void and stores T!
@@ -490,7 +490,7 @@ impl TypeChecker {
} => {
// 1. Determine types of captured variables
let mut upvalue_types = Vec::with_capacity(upvalues.len());
for &addr in &upvalues {
for &addr in upvalues {
upvalue_types.push(ctx.get_type(addr));
}
@@ -500,7 +500,7 @@ impl TypeChecker {
// 3. Check parameters and body
let params_typed = self.check_params(
params.as_ref().clone(),
params.as_ref(),
&StaticType::Any,
&mut lambda_ctx,
diag,
@@ -509,7 +509,7 @@ impl TypeChecker {
// Set current params type for 'again' validation
lambda_ctx.current_params_ty = Some(params_typed.ty.clone());
let body_typed = self.check_node((*body).clone(), &mut lambda_ctx, diag);
let body_typed = self.check_node(body, &mut lambda_ctx, diag);
let ret_ty = body_typed.ty.clone();
// 4. Construct function type
@@ -521,19 +521,19 @@ impl TypeChecker {
(
BoundKind::Lambda {
params: Rc::new(params_typed),
upvalues,
upvalues: upvalues.clone(),
body: Rc::new(body_typed),
positional_count,
positional_count: *positional_count,
},
fn_ty,
)
}
BoundKind::Call { callee, args } => {
let callee_typed = self.check_node(*callee, ctx, diag);
let callee_typed = self.check_node(callee, ctx, diag);
// Manually check args (Tuple) to prevent Vector/Matrix promotion
let args_typed = if let BoundKind::Tuple { elements } = args.kind {
let args_typed = if let BoundKind::Tuple { elements } = &args.kind {
let mut typed_elements = Vec::new();
let mut elem_types = Vec::new();
for e in elements {
@@ -542,7 +542,7 @@ impl TypeChecker {
typed_elements.push(t);
}
Node {
identity: args.identity,
identity: args.identity.clone(),
kind: BoundKind::Tuple {
elements: typed_elements,
},
@@ -550,7 +550,7 @@ impl TypeChecker {
}
} else {
// Should not happen as parser wraps args in Tuple, but fallback safely
self.check_node(*args, ctx, diag)
self.check_node(args, ctx, diag)
};
let ret_ty = match callee_typed.ty.resolve_call(&args_typed.ty) {
@@ -578,7 +578,7 @@ impl TypeChecker {
BoundKind::Again { args } => {
// Manually check args (Tuple) to prevent Vector/Matrix promotion
let args_typed = if let BoundKind::Tuple { elements } = args.kind {
let args_typed = if let BoundKind::Tuple { elements } = &args.kind {
let mut typed_elements = Vec::new();
let mut elem_types = Vec::new();
for e in elements {
@@ -587,14 +587,14 @@ impl TypeChecker {
typed_elements.push(t);
}
Node {
identity: args.identity,
identity: args.identity.clone(),
kind: BoundKind::Tuple {
elements: typed_elements,
},
ty: StaticType::Tuple(elem_types),
}
} else {
self.check_node(*args, ctx, diag)
self.check_node(args, ctx, diag)
};
if let Some(expected_ty) = &ctx.current_params_ty
@@ -660,7 +660,7 @@ impl TypeChecker {
let mut typed_values = Vec::with_capacity(values.len());
let mut fields_ty = Vec::with_capacity(values.len());
for (i, v) in values.into_iter().enumerate() {
for (i, v) in values.iter().enumerate() {
let vt = self.check_node(v, ctx, diag);
fields_ty.push((layout.fields[i].0, vt.ty.clone()));
typed_values.push(vt);
@@ -680,11 +680,11 @@ impl TypeChecker {
original_call,
bound_expanded,
} => {
let expanded_typed = self.check_node(*bound_expanded, ctx, diag);
let expanded_typed = self.check_node(bound_expanded, ctx, diag);
let ty = expanded_typed.ty.clone();
(
BoundKind::Expansion {
original_call,
original_call: original_call.clone(),
bound_expanded: Box::new(expanded_typed),
},
ty,
@@ -705,7 +705,7 @@ impl TypeChecker {
};
Node {
identity: node.identity,
identity: node.identity.clone(),
kind,
ty,
}
+14 -11
View File
@@ -8,7 +8,10 @@ use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, BoundNode, GlobalIdx};
use crate::ast::compiler::bound_nodes::{
Address, AnalyzedNode, BoundKind, BoundNode, GlobalAnalyzedRegistry, GlobalFunctionRegistry,
GlobalIdx,
};
use crate::ast::compiler::dumper::Dumper;
use crate::ast::compiler::lambda_collector::LambdaCollector;
use crate::ast::compiler::macros::{MacroEvaluator, MacroExpander, MacroRegistry};
@@ -69,8 +72,8 @@ pub struct Environment {
pub global_types: Rc<RefCell<HashMap<GlobalIdx, StaticType>>>,
pub global_purity: Rc<RefCell<HashMap<GlobalIdx, Purity>>>,
pub global_values: Rc<RefCell<Vec<Value>>>,
pub function_registry: Rc<RefCell<HashMap<GlobalIdx, BoundNode>>>,
pub typed_function_registry: Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>,
pub function_registry: Rc<RefCell<GlobalFunctionRegistry>>,
pub typed_function_registry: Rc<RefCell<GlobalAnalyzedRegistry>>,
pub monomorph_cache: Rc<RefCell<MonoCache>>,
pub debug_mode: bool,
pub optimization: bool,
@@ -79,19 +82,19 @@ pub struct Environment {
}
struct EnvFunctionRegistry {
registry: Rc<RefCell<HashMap<GlobalIdx, BoundNode>>>,
analyzed_registry: Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>,
registry: Rc<RefCell<GlobalFunctionRegistry>>,
analyzed_registry: Rc<RefCell<GlobalAnalyzedRegistry>>,
}
impl FunctionRegistry for EnvFunctionRegistry {
fn resolve(&self, addr: Address) -> Option<BoundNode> {
fn resolve(&self, addr: Address) -> Option<Rc<BoundNode>> {
if let Address::Global(idx) = addr {
self.registry.borrow().get(&idx).cloned()
} else {
None
}
}
fn resolve_analyzed(&self, addr: Address) -> Option<AnalyzedNode> {
fn resolve_analyzed(&self, addr: Address) -> Option<Rc<AnalyzedNode>> {
if let Address::Global(idx) = addr {
self.analyzed_registry.borrow().get(&idx).cloned()
} else {
@@ -122,7 +125,7 @@ impl MacroEvaluator for RuntimeMacroEvaluator {
let (bound_ast, captures) = Binder::bind_root(self.global_names.clone(), node, &mut diag)?;
let bound_ast = crate::ast::compiler::captures::CapturePass::apply(bound_ast, &captures);
let checker = TypeChecker::new(self.global_types.clone());
let typed_ast = checker.check(bound_ast, &[], &mut diag);
let typed_ast = checker.check(&bound_ast, &[], &mut diag);
if diag.has_errors() {
return Err(diag
@@ -316,7 +319,7 @@ impl Environment {
LambdaCollector::collect(&bound_ast, &mut self.function_registry.borrow_mut());
let checker = TypeChecker::new(self.global_types.clone());
let typed_ast = checker.check(bound_ast, &[], &mut diagnostics);
let typed_ast = checker.check(&bound_ast, &[], &mut diagnostics);
CompilationResult {
ast: Some(typed_ast),
@@ -420,12 +423,12 @@ impl Environment {
let optimization = self.optimization;
let compiler = Rc::new(
move |func_template: BoundNode,
move |func_template: Rc<BoundNode>,
arg_types: &[StaticType]|
-> Result<(Value, StaticType), String> {
let mut diag = Diagnostics::new();
let checker = TypeChecker::new(global_types.clone());
let retyped_ast = checker.check(func_template, arg_types, &mut diag);
let retyped_ast = checker.check(func_template.as_ref(), arg_types, &mut diag);
if diag.has_errors() {
return Err(diag