diff --git a/src/ast/compiler/analyzer.rs b/src/ast/compiler/analyzer.rs index 31fe2d9..80032b3 100644 --- a/src/ast/compiler/analyzer.rs +++ b/src/ast/compiler/analyzer.rs @@ -1,5 +1,5 @@ use crate::ast::compiler::bound_nodes::{ - Address, AnalyzedNode, BoundKind, GlobalIdx, NodeMetrics, TypedNode, + Address, AnalyzedNode, BoundKind, GlobalIdx, Node, NodeMetrics, TypedNode, TypedPhase, }; use crate::ast::types::Purity; use std::collections::{HashMap, HashSet}; @@ -318,7 +318,7 @@ impl<'a> Analyzer<'a> { BoundKind::Error => (BoundKind::Error, Purity::Impure), }; - crate::ast::compiler::bound_nodes::Node { + Node { identity: node.identity.clone(), kind: new_kind, ty: NodeMetrics { @@ -334,7 +334,7 @@ trait NodeExt { fn for_each_child(&self, f: F); } -impl NodeExt for BoundKind { +impl NodeExt for BoundKind { fn for_each_child(&self, mut f: F) { match self { BoundKind::If { diff --git a/src/ast/compiler/binder.rs b/src/ast/compiler/binder.rs index 69976c0..c384312 100644 --- a/src/ast/compiler/binder.rs +++ b/src/ast/compiler/binder.rs @@ -1,5 +1,5 @@ use crate::ast::compiler::bound_nodes::{ - Address, BoundKind, DeclarationKind, GlobalIdx, LocalSlot, Node, UpvalueIdx, + Address, BoundKind, BoundPhase, DeclarationKind, GlobalIdx, Node, UpvalueIdx, VirtualId, }; use crate::ast::diagnostics::Diagnostics; use crate::ast::nodes::{Symbol, SyntaxKind, SyntaxNode}; @@ -9,7 +9,7 @@ use std::rc::Rc; #[derive(Debug, Clone)] pub struct LocalInfo { - pub addr: Address, + pub addr: Address, pub identity: Identity, pub _ty: StaticType, pub purity: Purity, @@ -38,7 +38,7 @@ struct FunctionCompiler { identity: Identity, scopes: Vec, slot_count: u32, - upvalues: Vec
, + upvalues: Vec>, } impl FunctionCompiler { @@ -65,7 +65,7 @@ impl FunctionCompiler { } } - fn define_variable(&mut self, name: &Symbol, identity: Identity) -> Result { + fn define_variable(&mut self, name: &Symbol, identity: Identity) -> Result, String> { let current_scope = self.scopes.last_mut().unwrap(); if current_scope.locals.contains_key(name) { return Err(format!( @@ -74,7 +74,7 @@ impl FunctionCompiler { )); } - let slot = LocalSlot(self.slot_count); + let slot = VirtualId(self.slot_count); current_scope.locals.insert( name.clone(), LocalInfo { @@ -97,7 +97,7 @@ impl FunctionCompiler { None } - fn add_upvalue(&mut self, addr: Address) -> UpvalueIdx { + fn add_upvalue(&mut self, addr: Address) -> UpvalueIdx { if let Some(idx) = self.upvalues.iter().position(|&a| a == addr) { return UpvalueIdx(idx as u32); } @@ -114,7 +114,7 @@ pub enum ExprContext { } pub type BindingResult = ( - Node, + Node, HashMap>, Vec, u32, @@ -179,7 +179,7 @@ impl Binder { identity: Identity, _kind: crate::ast::compiler::bound_nodes::DeclarationKind, diag: &mut Diagnostics, - ) -> Option
{ + ) -> Option> { let current_fn_idx = self.functions.len() - 1; if current_fn_idx == 0 { @@ -211,7 +211,7 @@ impl Binder { node: &SyntaxNode, ctx: ExprContext, diag: &mut Diagnostics, - ) -> Node { + ) -> Node { match &node.kind { SyntaxKind::Nop => self.make_node(node.identity.clone(), BoundKind::Nop), SyntaxKind::Constant(v) => { @@ -361,7 +361,7 @@ impl Binder { let body_bound = self.bind(body.as_ref(), ExprContext::Expression, diag); let compiled_fn = self.functions.pop().unwrap(); - fn count_params(node: &Node) -> Option { + fn count_params(node: &Node) -> Option { match &node.kind { BoundKind::Define { kind: DeclarationKind::Parameter, @@ -520,9 +520,9 @@ impl Binder { ); self.make_node(node.identity.clone(), BoundKind::Error) } - SyntaxKind::Error => crate::ast::compiler::bound_nodes::Node { + SyntaxKind::Error => Node { identity: node.identity.clone(), - kind: crate::ast::compiler::bound_nodes::BoundKind::Error, + kind: BoundKind::Error, ty: (), }, } @@ -533,7 +533,7 @@ impl Binder { sym: &Symbol, diag: &mut Diagnostics, identity: &Identity, - ) -> Option
{ + ) -> Option> { let current_fn_idx = self.functions.len() - 1; // 1. Try local in current function @@ -603,7 +603,7 @@ impl Binder { node: &SyntaxNode, kind: DeclarationKind, diag: &mut Diagnostics, - ) -> Node { + ) -> Node { match &node.kind { SyntaxKind::Identifier(sym) => { if let Some(addr) = self.declare_variable(sym, node.identity.clone(), kind, diag) { @@ -647,7 +647,7 @@ impl Binder { &mut self, node: &SyntaxNode, diag: &mut Diagnostics, - ) -> Node { + ) -> Node { match &node.kind { SyntaxKind::Identifier(sym) => { if let Some(addr) = self.resolve_variable(sym, diag, &node.identity) { @@ -684,7 +684,7 @@ impl Binder { } } - fn make_node(&self, identity: Identity, kind: BoundKind<()>) -> Node { + fn make_node(&self, identity: Identity, kind: BoundKind) -> Node { Node { identity, kind, diff --git a/src/ast/compiler/bound_nodes.rs b/src/ast/compiler/bound_nodes.rs index 96edba9..2c8eeb1 100644 --- a/src/ast/compiler/bound_nodes.rs +++ b/src/ast/compiler/bound_nodes.rs @@ -3,11 +3,20 @@ use crate::ast::types::{Identity, StaticType, Value}; use std::rc::Rc; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct LocalSlot(pub u32); +pub struct VirtualId(pub u32); -impl std::fmt::Display for LocalSlot { +impl std::fmt::Display for VirtualId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "L{}", self.0) + write!(f, "V{}", self.0) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct StackOffset(pub u32); + +impl std::fmt::Display for StackOffset { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "S{}", self.0) } } @@ -29,46 +38,84 @@ impl std::fmt::Display for GlobalIdx { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Address { - Local(LocalSlot), // Stack-Slot index (relative to frame base) +#[derive(Debug, PartialEq, Eq, Hash)] +pub enum Address { + Local(L), // Local address (VirtualId or StackOffset) Upvalue(UpvalueIdx), // Index in the closure's upvalue array Global(GlobalIdx), // Index in the global environment vector } +impl Clone for Address { + fn clone(&self) -> Self { + match self { + Address::Local(l) => Address::Local(l.clone()), + Address::Upvalue(u) => Address::Upvalue(*u), + Address::Global(g) => Address::Global(*g), + } + } +} + +impl Copy for Address {} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum DeclarationKind { Variable, Parameter, } -/// Trait for DSL-specific AST extensions (like Pipe, Series-Access, etc.) -pub trait BoundExtension: std::fmt::Debug { - fn clone_box(&self) -> Box>; - fn display_name(&self) -> String; +pub trait CompilerPhase: std::fmt::Debug + 'static { + type Metadata: std::fmt::Debug + Clone + PartialEq; + type LocalAddress: std::fmt::Debug + Clone + Copy + PartialEq + Eq + std::hash::Hash; } -impl Clone for Box> { +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BoundPhase; +impl CompilerPhase for BoundPhase { + type Metadata = (); + type LocalAddress = VirtualId; +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TypedPhase; +impl CompilerPhase for TypedPhase { + type Metadata = StaticType; + type LocalAddress = VirtualId; +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AnalyzedPhase; +impl CompilerPhase for AnalyzedPhase { + type Metadata = NodeMetrics; + type LocalAddress = VirtualId; +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RuntimePhase; +impl CompilerPhase for RuntimePhase { + type Metadata = RuntimeMetadata; + type LocalAddress = StackOffset; +} + +/// A bound AST node, decorated with phase-specific information P. +#[derive(Debug, PartialEq)] +pub struct Node { + pub identity: Identity, + pub kind: BoundKind

, + pub ty: P::Metadata, +} + +impl Clone for Node

{ fn clone(&self) -> Self { - self.clone_box() + Self { + identity: self.identity.clone(), + kind: self.kind.clone(), + ty: self.ty.clone(), + } } } -/// A bound AST node, decorated with type or metric information T. -#[derive(Debug, Clone, PartialEq)] -pub struct Node { - pub identity: Identity, - pub kind: BoundKind, - pub ty: T, -} +pub type TypedNode = Node; -/// Type alias for a node that has been fully type-checked. -pub type TypedNode = Node; - -/// Type alias for the global function registry. -pub type GlobalFunctionRegistry = std::collections::HashMap>; - -/// Metrics collected during the analysis phase. #[derive(Debug, Clone, PartialEq)] pub struct NodeMetrics { pub original: Rc, @@ -76,114 +123,144 @@ pub struct NodeMetrics { pub is_recursive: bool, } -/// Type alias for a node that has been analyzed. -pub type AnalyzedNode = Node; +pub type AnalyzedNode = Node; -/// Type alias for the global analyzed function registry. +#[derive(Clone)] +pub struct RuntimeMetadata { + pub ty: StaticType, + pub is_tail: bool, + pub original: Rc, + pub stack_size: u32, +} + +impl std::fmt::Debug for RuntimeMetadata { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Metadata") + .field("ty", &self.ty) + .field("is_tail", &self.is_tail) + .field("stack_size", &self.stack_size) + .finish() + } +} + +impl PartialEq for RuntimeMetadata { + fn eq(&self, other: &Self) -> bool { + self.ty == other.ty && self.is_tail == other.is_tail && Rc::ptr_eq(&self.original, &other.original) && self.stack_size == other.stack_size + } +} + +pub type ExecNode = Node; + +pub type GlobalFunctionRegistry = std::collections::HashMap>>; pub type GlobalAnalyzedRegistry = std::collections::HashMap>; -#[derive(Debug, Clone)] -pub enum BoundKind { +pub trait BoundExtension: std::fmt::Debug { + fn clone_box(&self) -> Box>; + fn display_name(&self) -> String; +} + +impl Clone for Box> { + fn clone(&self) -> Self { + self.clone_box() + } +} + +#[derive(Debug)] +pub enum BoundKind { Nop, Constant(Value), - - // Variable Access (Resolved) Get { - addr: Address, + addr: Address, name: Symbol, }, - - // Variable Update (Assignment) Set { - addr: Address, - value: Rc>, + addr: Address, + value: Rc>, }, - - // Variable Declaration (Unified for Local/Global/Parameter) Define { name: Symbol, - addr: Address, + addr: Address, kind: DeclarationKind, - value: Rc>, + value: Rc>, captured_by: Vec, }, - - /// A first-class field accessor (e.g. .name) FieldAccessor(crate::ast::types::Keyword), - - /// Specialized field access (O(1) via RecordLayout) GetField { - rec: Rc>, + rec: Rc>, field: crate::ast::types::Keyword, }, - If { - cond: Rc>, - then_br: Rc>, - else_br: Option>>, + cond: Rc>, + then_br: Rc>, + else_br: Option>>, }, - - /// A destructuring operation (can be a definition or an assignment depending on the pattern) Destructure { - pattern: Rc>, - value: Rc>, + pattern: Rc>, + value: Rc>, }, - Lambda { - params: Rc>, - // The list of variables captured from enclosing scopes - upvalues: Vec

, - body: Rc>, - /// Static optimization: number of positional parameters if the pattern is flat. + params: Rc>, + upvalues: Vec>, + body: Rc>, positional_count: Option, }, - Call { - callee: Rc>, - args: Rc>, + callee: Rc>, + args: Rc>, }, - Again { - args: Rc>, + args: Rc>, }, - Pipe { - inputs: Vec>>, - lambda: Rc>, + inputs: Vec>>, + lambda: Rc>, out_type: crate::ast::types::StaticType, }, Block { - exprs: Vec>>, + exprs: Vec>>, }, - Tuple { - elements: Vec>>, + elements: Vec>>, }, - Record { layout: std::sync::Arc, - values: Vec>>, + values: Vec>>, }, - - /// An expanded macro call, preserving the original call for debugging and UI. Expansion { - /// The original call from the syntax AST. original_call: Rc, - /// The result of binding the expanded AST. - bound_expanded: Rc>, + bound_expanded: Rc>, }, - - /// A diagnostic poison node, allowing compilation to continue after an error. Error, - - /// DSL-specific extension slot - Extension(Box>), + Extension(Box>), } -impl PartialEq for BoundKind -where - T: PartialEq, -{ +impl Clone for BoundKind

{ + fn clone(&self) -> Self { + match self { + BoundKind::Nop => BoundKind::Nop, + BoundKind::Constant(v) => BoundKind::Constant(v.clone()), + BoundKind::Get { addr, name } => BoundKind::Get { addr: addr.clone(), name: name.clone() }, + BoundKind::Set { addr, value } => BoundKind::Set { addr: addr.clone(), value: value.clone() }, + BoundKind::Define { name, addr, kind, value, captured_by } => BoundKind::Define { name: name.clone(), addr: addr.clone(), kind: *kind, value: value.clone(), captured_by: captured_by.clone() }, + BoundKind::FieldAccessor(k) => BoundKind::FieldAccessor(k.clone()), + BoundKind::GetField { rec, field } => BoundKind::GetField { rec: rec.clone(), field: field.clone() }, + BoundKind::If { cond, then_br, else_br } => BoundKind::If { cond: cond.clone(), then_br: then_br.clone(), else_br: else_br.clone() }, + BoundKind::Destructure { pattern, value } => BoundKind::Destructure { pattern: pattern.clone(), value: value.clone() }, + BoundKind::Lambda { params, upvalues, body, positional_count } => BoundKind::Lambda { params: params.clone(), upvalues: upvalues.clone(), body: body.clone(), positional_count: *positional_count }, + BoundKind::Call { callee, args } => BoundKind::Call { callee: callee.clone(), args: args.clone() }, + BoundKind::Again { args } => BoundKind::Again { args: args.clone() }, + BoundKind::Pipe { inputs, lambda, out_type } => BoundKind::Pipe { inputs: inputs.clone(), lambda: lambda.clone(), out_type: out_type.clone() }, + BoundKind::Block { exprs } => BoundKind::Block { exprs: exprs.clone() }, + BoundKind::Tuple { elements } => BoundKind::Tuple { elements: elements.clone() }, + BoundKind::Record { layout, values } => BoundKind::Record { layout: layout.clone(), values: values.clone() }, + BoundKind::Expansion { original_call, bound_expanded } => BoundKind::Expansion { original_call: original_call.clone(), bound_expanded: bound_expanded.clone() }, + BoundKind::Error => BoundKind::Error, + BoundKind::Extension(ext) => BoundKind::Extension(ext.clone()), + } + } +} + +impl PartialEq for BoundKind

{ fn eq(&self, other: &Self) -> bool { match (self, other) { (BoundKind::Nop, BoundKind::Nop) => true, @@ -191,87 +268,32 @@ where (BoundKind::Get { addr: aa, name: na }, BoundKind::Get { addr: ab, name: nb }) => { aa == ab && na == nb } - ( - BoundKind::Set { - addr: aa, - value: va, - }, - BoundKind::Set { - addr: ab, - value: vb, - }, - ) => aa == ab && Rc::ptr_eq(va, vb), - ( - BoundKind::Define { - name: na, - addr: aa, - kind: ka, - value: va, - captured_by: ca, - }, - BoundKind::Define { - name: nb, - addr: ab, - kind: kb, - value: vb, - captured_by: cb, - }, - ) => na == nb && aa == ab && ka == kb && Rc::ptr_eq(va, vb) && ca == cb, + (BoundKind::Set { addr: aa, value: va }, BoundKind::Set { addr: ab, value: vb }) => { + aa == ab && Rc::ptr_eq(va, vb) + } + (BoundKind::Define { name: na, addr: aa, kind: ka, value: va, captured_by: ca }, BoundKind::Define { name: nb, addr: ab, kind: kb, value: vb, captured_by: cb }) => { + na == nb && aa == ab && ka == kb && Rc::ptr_eq(va, vb) && ca == cb + } (BoundKind::FieldAccessor(a), BoundKind::FieldAccessor(b)) => a == b, - ( - BoundKind::GetField { rec: ra, field: fa }, - BoundKind::GetField { rec: rb, field: fb }, - ) => Rc::ptr_eq(ra, rb) && fa == fb, - ( - BoundKind::If { - cond: ca, - then_br: ta, - else_br: ea, - }, - BoundKind::If { - cond: cb, - then_br: tb, - else_br: eb, - }, - ) => Rc::ptr_eq(ca, cb) && Rc::ptr_eq(ta, tb) && match (ea, eb) { - (Some(a), Some(b)) => Rc::ptr_eq(a, b), - (None, None) => true, - _ => false, - }, - ( - BoundKind::Destructure { - pattern: pa, - value: va, - }, - BoundKind::Destructure { - pattern: pb, - value: vb, - }, - ) => Rc::ptr_eq(pa, pb) && Rc::ptr_eq(va, vb), - ( - BoundKind::Lambda { - params: pa, - upvalues: ua, - body: ba, - positional_count: pca, - }, - BoundKind::Lambda { - params: pb, - upvalues: ub, - body: bb, - positional_count: pcb, - }, - ) => Rc::ptr_eq(pa, pb) && ua == ub && Rc::ptr_eq(ba, bb) && pca == pcb, - ( - BoundKind::Call { - callee: ca, - args: aa, - }, - BoundKind::Call { - callee: cb, - args: ab, - }, - ) => Rc::ptr_eq(ca, cb) && Rc::ptr_eq(aa, ab), + (BoundKind::GetField { rec: ra, field: fa }, BoundKind::GetField { rec: rb, field: fb }) => { + Rc::ptr_eq(ra, rb) && fa == fb + } + (BoundKind::If { cond: ca, then_br: ta, else_br: ea }, BoundKind::If { cond: cb, then_br: tb, else_br: eb }) => { + Rc::ptr_eq(ca, cb) && Rc::ptr_eq(ta, tb) && match (ea, eb) { + (Some(a), Some(b)) => Rc::ptr_eq(a, b), + (None, None) => true, + _ => false, + } + } + (BoundKind::Destructure { pattern: pa, value: va }, BoundKind::Destructure { pattern: pb, value: vb }) => { + Rc::ptr_eq(pa, pb) && Rc::ptr_eq(va, vb) + } + (BoundKind::Lambda { params: pa, upvalues: ua, body: ba, positional_count: pca }, BoundKind::Lambda { params: pb, upvalues: ub, body: bb, positional_count: pcb }) => { + Rc::ptr_eq(pa, pb) && ua == ub && Rc::ptr_eq(ba, bb) && pca == pcb + } + (BoundKind::Call { callee: ca, args: aa }, BoundKind::Call { callee: cb, args: ab }) => { + Rc::ptr_eq(ca, cb) && Rc::ptr_eq(aa, ab) + } (BoundKind::Again { args: aa }, BoundKind::Again { args: ab }) => Rc::ptr_eq(aa, ab), (BoundKind::Block { exprs: ea }, BoundKind::Block { exprs: eb }) => { ea.len() == eb.len() && ea.iter().zip(eb.iter()).all(|(a, b)| Rc::ptr_eq(a, b)) @@ -279,46 +301,26 @@ where (BoundKind::Tuple { elements: ea }, BoundKind::Tuple { elements: eb }) => { ea.len() == eb.len() && ea.iter().zip(eb.iter()).all(|(a, b)| Rc::ptr_eq(a, b)) } - ( - BoundKind::Record { - layout: la, - values: va, - }, - BoundKind::Record { - layout: lb, - values: vb, - }, - ) => std::sync::Arc::ptr_eq(la, lb) && va.len() == vb.len() && va.iter().zip(vb.iter()).all(|(a, b)| Rc::ptr_eq(a, b)), - ( - BoundKind::Expansion { - original_call: ca, - bound_expanded: ea, - }, - BoundKind::Expansion { - original_call: cb, - bound_expanded: eb, - }, - ) => Rc::ptr_eq(ca, cb) && Rc::ptr_eq(ea, eb), + (BoundKind::Record { layout: la, values: va }, BoundKind::Record { layout: lb, values: vb }) => { + std::sync::Arc::ptr_eq(la, lb) && va.len() == vb.len() && va.iter().zip(vb.iter()).all(|(a, b)| Rc::ptr_eq(a, b)) + } + (BoundKind::Expansion { original_call: ca, bound_expanded: ea }, BoundKind::Expansion { original_call: cb, bound_expanded: eb }) => { + Rc::ptr_eq(ca, cb) && Rc::ptr_eq(ea, eb) + } (BoundKind::Error, BoundKind::Error) => true, - (BoundKind::Extension(_), BoundKind::Extension(_)) => false, _ => false, } } } -/// A single field in a Record literal (Key-Value pair) -pub type RecordField = (Node, Node); - -impl BoundKind { +impl BoundKind

{ pub fn display_name(&self) -> String { match self { BoundKind::Nop => "NOP".to_string(), BoundKind::Constant(v) => format!("CONST({})", v), BoundKind::Get { addr, name } => format!("GET({}, {:?})", name.name, addr), BoundKind::Set { addr, .. } => format!("SET({:?})", addr), - BoundKind::Define { - name, addr, kind, .. - } => { + BoundKind::Define { name, addr, kind, .. } => { let k_str = match kind { DeclarationKind::Variable => "VAR", DeclarationKind::Parameter => "PARAM", @@ -329,9 +331,7 @@ impl BoundKind { BoundKind::GetField { field, .. } => format!("GET_FIELD(.{})", field.name()), BoundKind::If { .. } => "IF".to_string(), BoundKind::Destructure { .. } => "DESTRUCTURE".to_string(), - BoundKind::Lambda { - params, upvalues, .. - } => { + BoundKind::Lambda { params, upvalues, .. } => { let p_str = match ¶ms.kind { BoundKind::Tuple { elements } => format!("p:{}", elements.len()), _ => "p:1".to_string(), diff --git a/src/ast/compiler/captures.rs b/src/ast/compiler/captures.rs index 57e8997..ed8eedb 100644 --- a/src/ast/compiler/captures.rs +++ b/src/ast/compiler/captures.rs @@ -1,67 +1,34 @@ -use crate::ast::compiler::bound_nodes::{BoundKind, Node}; +use crate::ast::compiler::bound_nodes::{BoundKind, CompilerPhase, Node}; use crate::ast::types::Identity; use std::collections::HashMap; +use std::rc::Rc; pub struct CapturePass; impl CapturePass { - pub fn apply(node: Node, capture_map: &HashMap>) -> Node { + pub fn apply(node: Node

, capture_map: &HashMap>) -> Node

{ Self::transform(node, capture_map) } - fn transform(mut node: Node, capture_map: &HashMap>) -> Node { - use std::rc::Rc; - match node.kind { + fn transform(mut node: Node

, capture_map: &HashMap>) -> Node

{ + node.kind = match node.kind { BoundKind::Define { name, addr, kind, value, - .. + mut captured_by, } => { - let captured_by = capture_map.get(&node.identity).cloned().unwrap_or_default(); - node.kind = BoundKind::Define { + if let Some(capturers) = capture_map.get(&node.identity) { + captured_by.extend(capturers.iter().cloned()); + } + BoundKind::Define { name, addr, kind, value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)), captured_by, - }; - } - - BoundKind::If { - cond, - then_br, - else_br, - } => { - node.kind = BoundKind::If { - cond: Rc::new(Self::transform(cond.as_ref().clone(), capture_map)), - then_br: Rc::new(Self::transform(then_br.as_ref().clone(), capture_map)), - else_br: else_br.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map))), - }; - } - - BoundKind::Set { addr, value } => { - node.kind = BoundKind::Set { - addr, - value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)), - }; - } - - BoundKind::FieldAccessor(_) => {} - - BoundKind::GetField { rec, field } => { - node.kind = BoundKind::GetField { - rec: Rc::new(Self::transform(rec.as_ref().clone(), capture_map)), - field, - }; - } - - BoundKind::Destructure { pattern, value } => { - node.kind = BoundKind::Destructure { - pattern: Rc::new(Self::transform(pattern.as_ref().clone(), capture_map)), - value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)), - }; + } } BoundKind::Lambda { @@ -69,27 +36,38 @@ impl CapturePass { upvalues, body, positional_count, - } => { - node.kind = BoundKind::Lambda { - params: Rc::new(Self::transform(params.as_ref().clone(), capture_map)), - upvalues, - body: Rc::new(Self::transform(body.as_ref().clone(), capture_map)), - positional_count, - }; - } + } => BoundKind::Lambda { + params: Rc::new(Self::transform(params.as_ref().clone(), capture_map)), + upvalues, + body: Rc::new(Self::transform(body.as_ref().clone(), capture_map)), + positional_count, + }, - BoundKind::Call { callee, args } => { - node.kind = BoundKind::Call { - callee: Rc::new(Self::transform(callee.as_ref().clone(), capture_map)), - args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)), - }; - } + BoundKind::Call { callee, args } => BoundKind::Call { + callee: Rc::new(Self::transform(callee.as_ref().clone(), capture_map)), + args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)), + }, + + BoundKind::Again { args } => BoundKind::Again { + args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)), + }, + + BoundKind::If { + cond, + then_br, + else_br, + } => BoundKind::If { + cond: Rc::new(Self::transform(cond.as_ref().clone(), capture_map)), + then_br: Rc::new(Self::transform(then_br.as_ref().clone(), capture_map)), + else_br: else_br + .map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map))), + }, + + BoundKind::Destructure { pattern, value } => BoundKind::Destructure { + pattern: Rc::new(Self::transform(pattern.as_ref().clone(), capture_map)), + value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)), + }, - BoundKind::Again { args } => { - node.kind = BoundKind::Again { - args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)), - }; - } BoundKind::Pipe { inputs, lambda, @@ -99,56 +77,53 @@ impl CapturePass { for input in inputs { t_inputs.push(Rc::new(Self::transform(input.as_ref().clone(), capture_map))); } - node.kind = BoundKind::Pipe { + BoundKind::Pipe { inputs: t_inputs, lambda: Rc::new(Self::transform(lambda.as_ref().clone(), capture_map)), - out_type: out_type.clone(), - }; - } - BoundKind::Block { exprs } => { - node.kind = BoundKind::Block { - exprs: exprs - .into_iter() - .map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map))) - .collect(), - }; + out_type, + } } - BoundKind::Tuple { elements } => { - node.kind = BoundKind::Tuple { - elements: elements - .into_iter() - .map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map))) - .collect(), - }; - } + BoundKind::Block { exprs } => BoundKind::Block { + exprs: exprs + .into_iter() + .map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map))) + .collect(), + }, - BoundKind::Record { layout, values } => { - node.kind = BoundKind::Record { - layout, - values: values - .into_iter() - .map(|v| Rc::new(Self::transform(v.as_ref().clone(), capture_map))) - .collect(), - }; - } + BoundKind::Tuple { elements } => BoundKind::Tuple { + elements: elements + .into_iter() + .map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map))) + .collect(), + }, + + BoundKind::Record { layout, values } => BoundKind::Record { + layout, + values: values + .into_iter() + .map(|v| Rc::new(Self::transform(v.as_ref().clone(), capture_map))) + .collect(), + }, BoundKind::Expansion { original_call, bound_expanded, - } => { - node.kind = BoundKind::Expansion { - original_call, - bound_expanded: Rc::new(Self::transform(bound_expanded.as_ref().clone(), capture_map)), - }; - } + } => BoundKind::Expansion { + original_call, + bound_expanded: Rc::new(Self::transform( + bound_expanded.as_ref().clone(), + capture_map, + )), + }, - BoundKind::Nop - | BoundKind::Constant(_) - | BoundKind::Get { .. } - | BoundKind::Extension(_) - | BoundKind::Error => {} - } + BoundKind::GetField { rec, field } => BoundKind::GetField { + rec: Rc::new(Self::transform(rec.as_ref().clone(), capture_map)), + field, + }, + + other => other, + }; node } } diff --git a/src/ast/compiler/dumper.rs b/src/ast/compiler/dumper.rs index 22641d9..d862097 100644 --- a/src/ast/compiler/dumper.rs +++ b/src/ast/compiler/dumper.rs @@ -1,5 +1,4 @@ -use crate::ast::compiler::bound_nodes::{BoundKind, Node}; -use std::fmt::Debug; +use crate::ast::compiler::bound_nodes::{BoundKind, CompilerPhase, Node}; /// Human-readable AST dumper for the bound AST. pub struct Dumper { @@ -9,7 +8,7 @@ pub struct Dumper { impl Dumper { /// Produces a formatted string representation of the given bound AST node and its children. - pub fn dump(node: &Node) -> String { + pub fn dump(node: &Node

) -> String { let mut dumper = Self { output: String::new(), indent: 0, @@ -24,14 +23,14 @@ impl Dumper { } } - fn log(&mut self, label: &str, node: &Node) { + fn log(&mut self, label: &str, node: &Node

) { self.write_indent(); self.output.push_str(label); self.output .push_str(&format!(" \n", node.ty)); } - fn visit(&mut self, node: &Node) { + fn visit(&mut self, node: &Node

) { match &node.kind { BoundKind::Nop => self.log("Nop", node), BoundKind::Constant(v) => { diff --git a/src/ast/compiler/lambda_collector.rs b/src/ast/compiler/lambda_collector.rs index ea8c065..85dd565 100644 --- a/src/ast/compiler/lambda_collector.rs +++ b/src/ast/compiler/lambda_collector.rs @@ -1,21 +1,21 @@ -use crate::ast::compiler::bound_nodes::{Address, BoundKind, GlobalIdx, Node}; +use crate::ast::compiler::bound_nodes::{Address, BoundKind, CompilerPhase, GlobalIdx, Node}; 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>>, +pub struct LambdaCollector<'a, P: CompilerPhase> { + registry: &'a mut HashMap>>, } -impl<'a, T: Clone> LambdaCollector<'a, T> { +impl<'a, P: CompilerPhase> LambdaCollector<'a, P> { /// Performs a full traversal of the AST and populates the provided registry. - pub fn collect(node: &Node, registry: &'a mut HashMap>>) { + pub fn collect(node: &Node

, registry: &'a mut HashMap>>) { let mut collector = Self { registry }; collector.visit(node); } - fn visit(&mut self, node: &Node) { + fn visit(&mut self, node: &Node

) { match &node.kind { BoundKind::Block { exprs } => { for expr in exprs { diff --git a/src/ast/compiler/lowering.rs b/src/ast/compiler/lowering.rs index 3588707..1b0155b 100644 --- a/src/ast/compiler/lowering.rs +++ b/src/ast/compiler/lowering.rs @@ -1,31 +1,7 @@ -use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalSlot, Node}; -use crate::ast::types::StaticType; +use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, ExecNode, Node, RuntimeMetadata, StackOffset, VirtualId}; use std::collections::HashMap; -use std::fmt::Debug; use std::rc::Rc; -#[derive(Clone)] -pub struct RuntimeMetadata { - pub ty: StaticType, - pub is_tail: bool, - /// The analyzed node, containing metrics and a link to the original TypedNode. - pub original: Rc, - pub stack_size: u32, -} - -impl Debug for RuntimeMetadata { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Metadata") - .field("ty", &self.ty) - .field("is_tail", &self.is_tail) - .field("stack_size", &self.stack_size) - .finish() - } -} - -/// The ExecNode is the AST used by the VM. It carries TCO flags and links to metrics. -pub type ExecNode = Node; - struct StackAllocator { mapping: HashMap, next_slot: u32, @@ -39,19 +15,20 @@ impl StackAllocator { } } - fn map_slot(&mut self, slot: LocalSlot) -> LocalSlot { + fn map_slot(&mut self, slot: VirtualId) -> StackOffset { let entry = self.mapping.entry(slot.0).or_insert_with(|| { let s = self.next_slot; self.next_slot += 1; s }); - LocalSlot(*entry) + StackOffset(*entry) } - fn map_address(&mut self, addr: Address) -> Address { + fn map_address(&mut self, addr: Address) -> Address { match addr { Address::Local(slot) => Address::Local(self.map_slot(slot)), - other => other, + Address::Upvalue(idx) => Address::Upvalue(idx), + Address::Global(idx) => Address::Global(idx), } } } diff --git a/src/ast/compiler/macros.rs b/src/ast/compiler/macros.rs index 8c69ed4..eeec12b 100644 --- a/src/ast/compiler/macros.rs +++ b/src/ast/compiler/macros.rs @@ -769,7 +769,7 @@ mod tests { locals.insert( Symbol::from("*"), crate::ast::compiler::binder::LocalInfo { - addr: Address::Local(crate::ast::compiler::bound_nodes::LocalSlot(0)), + addr: Address::Local(crate::ast::compiler::bound_nodes::VirtualId(0)), identity: crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation { line: 0, col: 0, diff --git a/src/ast/compiler/optimizer/inliner.rs b/src/ast/compiler/optimizer/inliner.rs index acafde6..fbc1a26 100644 --- a/src/ast/compiler/optimizer/inliner.rs +++ b/src/ast/compiler/optimizer/inliner.rs @@ -1,4 +1,4 @@ -use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalSlot}; +use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, VirtualId}; use crate::ast::types::{Purity, Value}; use crate::ast::vm::Closure; use std::cell::RefCell; @@ -24,7 +24,7 @@ impl<'a> Inliner<'a> { } } - pub fn is_inlinable_value(&self, val: &Value, addr: Address) -> bool { + pub fn is_inlinable_value(&self, val: &Value, addr: Address) -> bool { let type_ok = match val { Value::Int(_) | Value::Float(_) @@ -141,7 +141,7 @@ impl<'a> Inliner<'a> { } } - pub fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet) { + pub fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet) { match &node.kind { BoundKind::Define { addr: Address::Local(slot), diff --git a/src/ast/compiler/optimizer/substitution_map.rs b/src/ast/compiler/optimizer/substitution_map.rs index 15c7aa7..3505d4c 100644 --- a/src/ast/compiler/optimizer/substitution_map.rs +++ b/src/ast/compiler/optimizer/substitution_map.rs @@ -1,17 +1,17 @@ -use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalSlot, Node, UpvalueIdx}; +use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, Node, UpvalueIdx, VirtualId}; use crate::ast::types::Value; use std::collections::{HashMap, HashSet}; use std::rc::Rc; #[derive(Default)] pub struct SubstitutionMap { - pub values: HashMap, - pub ast_substitutions: HashMap>, - pub slot_mapping: HashMap, - pub assigned: HashSet

, + pub values: HashMap, Value>, + pub ast_substitutions: HashMap, Rc>, + pub slot_mapping: HashMap, + pub assigned: HashSet>, pub next_slot: u32, - pub used: HashSet
, - pub captured_slots: HashSet, + pub used: HashSet>, + pub captured_slots: HashSet, } impl SubstitutionMap { @@ -43,40 +43,40 @@ impl SubstitutionMap { inner } - pub fn add_ast_substitution(&mut self, addr: Address, node: AnalyzedNode) { + pub fn add_ast_substitution(&mut self, addr: Address, node: AnalyzedNode) { self.ast_substitutions.insert(addr, Rc::new(node)); } - pub fn map_slot(&mut self, old_slot: LocalSlot) -> LocalSlot { + pub fn map_slot(&mut self, old_slot: VirtualId) -> VirtualId { if let Some(&new_slot) = self.slot_mapping.get(&old_slot) { return new_slot; } - let new_slot = LocalSlot(self.next_slot); + let new_slot = VirtualId(self.next_slot); self.slot_mapping.insert(old_slot, new_slot); self.next_slot += 1; new_slot } - pub fn map_address(&mut self, addr: Address) -> Address { + pub fn map_address(&mut self, addr: Address) -> Address { match addr { Address::Local(slot) => Address::Local(self.map_slot(slot)), other => other, } } - pub fn add_value(&mut self, addr: Address, val: Value) { + pub fn add_value(&mut self, addr: Address, val: Value) { self.values.insert(addr, val); } - pub fn get_value(&self, addr: &Address) -> Option<&Value> { + pub fn get_value(&self, addr: &Address) -> Option<&Value> { self.values.get(addr) } - pub fn remove_value(&mut self, addr: &Address) { + pub fn remove_value(&mut self, addr: &Address) { self.values.remove(addr); } - fn reindex_addr(&self, addr: Address, mapping: &[Option]) -> Address { + fn reindex_addr(&self, addr: Address, mapping: &[Option]) -> Address { if let Address::Upvalue(idx) = addr && let Some(res) = mapping.get(idx.0 as usize) && let Some(new_idx) = res diff --git a/src/ast/compiler/optimizer/utils.rs b/src/ast/compiler/optimizer/utils.rs index 2790a67..470284d 100644 --- a/src/ast/compiler/optimizer/utils.rs +++ b/src/ast/compiler/optimizer/utils.rs @@ -1,4 +1,4 @@ -use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, GlobalIdx}; +use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, GlobalIdx, VirtualId}; use crate::ast::types::{Identity, Value}; use crate::ast::vm::Closure; use std::collections::HashSet; @@ -32,17 +32,17 @@ impl PathTracker { // --- UsageInfo --- #[derive(Default)] pub struct UsageInfo { - pub used: HashSet
, - pub assigned: HashSet
, + pub used: HashSet>, + pub assigned: HashSet>, pub used_identities: HashSet, } impl UsageInfo { - pub fn is_assigned(&self, addr: &Address) -> bool { + pub fn is_assigned(&self, addr: &Address) -> bool { self.assigned.contains(addr) } - pub fn is_used(&self, addr: &Address) -> bool { + pub fn is_used(&self, addr: &Address) -> bool { self.used.contains(addr) } diff --git a/src/ast/compiler/specializer.rs b/src/ast/compiler/specializer.rs index 20f2a2a..e967288 100644 --- a/src/ast/compiler/specializer.rs +++ b/src/ast/compiler/specializer.rs @@ -1,4 +1,4 @@ -use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, Node, NodeMetrics}; +use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, AnalyzedPhase, BoundKind, Node, NodeMetrics, VirtualId}; use crate::ast::types::{Purity, Signature, StaticType, Value}; use std::cell::RefCell; use std::collections::HashMap; @@ -6,16 +6,16 @@ use std::rc::Rc; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct MonoCacheKey { - pub address: Address, + pub address: Address, pub arg_types: Vec, } -pub type CompileFunc = Rc, &[StaticType]) -> Result<(Value, StaticType), String>>; +pub type CompileFunc = Rc>, &[StaticType]) -> Result<(Value, StaticType), String>>; pub type RtlLookupFunc = Rc Option<(Value, StaticType)>>; pub trait FunctionRegistry { - fn resolve(&self, addr: Address) -> Option>; - fn resolve_analyzed(&self, _addr: Address) -> Option> { + fn resolve(&self, addr: Address) -> Option>>; + fn resolve_analyzed(&self, _addr: Address) -> Option> { None } } diff --git a/src/ast/compiler/type_checker.rs b/src/ast/compiler/type_checker.rs index 63239ff..a0f23b3 100644 --- a/src/ast/compiler/type_checker.rs +++ b/src/ast/compiler/type_checker.rs @@ -1,4 +1,4 @@ -use crate::ast::compiler::bound_nodes::{Address, BoundKind, Node, TypedNode}; +use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundPhase, CompilerPhase, Node, TypedNode, VirtualId}; use crate::ast::diagnostics::Diagnostics; use crate::ast::types::StaticType; use std::collections::HashMap; @@ -33,7 +33,7 @@ impl<'a> TypeContext<'a> { } } - fn get_type(&self, addr: Address) -> StaticType { + fn get_type(&self, addr: Address) -> StaticType { match addr { Address::Local(slot) => self .slots @@ -54,7 +54,7 @@ impl<'a> TypeContext<'a> { } } - fn set_type(&mut self, addr: Address, ty: StaticType) { + fn set_type(&mut self, addr: Address, ty: StaticType) { match addr { Address::Local(slot) => { self.slots.insert(slot.0, ty); @@ -81,7 +81,18 @@ impl TypeChecker { pub fn check( &self, - node: &Node, + node: &Node, + arg_types: &[StaticType], + diag: &mut Diagnostics, + ) -> TypedNode { + self.check_node_as_bound(node, arg_types, diag) + } + + /// Allows re-checking a node from any phase as if it were a bound node. + /// This is useful for specialization where we re-type a TypedNode with more specific info. + pub fn check_node_as_bound>( + &self, + node: &Node

, arg_types: &[StaticType], diag: &mut Diagnostics, ) -> TypedNode { @@ -92,20 +103,17 @@ impl TypeChecker { body, positional_count, } => { - // 1. Determine types of captured variables (Root lambdas have none) let mut upvalue_types = Vec::with_capacity(upvalues.len()); - for _ in upvalues { + for &_addr in upvalues { upvalue_types.push(StaticType::Any); } - // 2. Create the specialized context let root_ctx = TypeContext::new(0, vec![], &self.root_types, None); let mut lambda_ctx = TypeContext::new(64, upvalue_types, &self.root_types, Some(&root_ctx)); - // 3. INJECT specialized argument types into slots let arg_tuple_ty = if arg_types.is_empty() { - StaticType::Any // No specialized info + StaticType::Any } else { StaticType::Tuple(arg_types.to_vec()) }; @@ -117,11 +125,8 @@ impl TypeChecker { diag, ); - // 4. Check body with the new types let body_typed = self.check_node(body, &mut lambda_ctx, diag); let ret_ty = body_typed.ty.clone(); - - // 5. Construct specialized function type let final_params_ty = params_typed.ty.clone(); let fn_ty = StaticType::Function(Box::new(crate::ast::types::Signature { @@ -147,9 +152,9 @@ impl TypeChecker { } } - fn check_params( + fn check_params>( &self, - node: &Node, + node: &Node

, specialized_ty: &StaticType, ctx: &mut TypeContext, diag: &mut Diagnostics, @@ -182,10 +187,6 @@ impl TypeChecker { addr, value: _value, } => { - // In an assignment pattern, 'value' is just a Nop placeholder from the Binder. - // We update the type of the address (if it's a local or global) to match the specialized type. - // Note: For now, we assume assignments are compatible if types were already inferred, - // or we could add a check here against existing type. ( BoundKind::Set { addr: *addr, @@ -240,7 +241,7 @@ impl TypeChecker { StaticType::Error => StaticType::Error, _ => StaticType::Any, }; - let t = self.check_params(el, &sub_ty, ctx, diag); + let t = self.check_params(el.as_ref(), &sub_ty, ctx, diag); elem_types.push(t.ty.clone()); typed_elements.push(Rc::new(t)); } @@ -268,9 +269,9 @@ impl TypeChecker { } } - fn check_node( + fn check_node>( &self, - node: &Node, + node: &Node

, ctx: &mut TypeContext, diag: &mut Diagnostics, ) -> TypedNode { @@ -373,7 +374,7 @@ 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 pat_typed = self.check_params(pattern.as_ref(), &val_typed.ty, ctx, diag); let ty = val_typed.ty.clone(); ( BoundKind::Destructure { @@ -397,13 +398,11 @@ impl TypeChecker { if let Some(e) = else_br { 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; } else_typed = Some(Rc::new(et)); } else { - // If without else returns Optional(Then) (T | Void) final_ty = StaticType::Optional(Box::new(then_typed.ty.clone())); } @@ -422,7 +421,6 @@ impl TypeChecker { let mut arg_types = Vec::with_capacity(inputs.len()); for input in inputs { let typed_input = self.check_node(input, ctx, diag); - // Unwrap Series(T) or Stream(T) to T for the lambda argument let arg_ty = if let StaticType::Series(inner) = &typed_input.ty { *inner.clone() } else if let StaticType::Stream(inner) = &typed_input.ty { @@ -434,11 +432,9 @@ impl TypeChecker { typed_inputs.push(Rc::new(typed_input)); } - // Specialized check for the lambda using the input types! - let typed_lambda = self.check(lambda, &arg_types, diag); + let typed_lambda = self.check_node_as_bound(lambda.as_ref(), &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! if let StaticType::Optional(inner) = &sig.ret { *inner.clone() } else { @@ -476,17 +472,14 @@ impl TypeChecker { body, positional_count, } => { - // 1. Determine types of captured variables let mut upvalue_types = Vec::with_capacity(upvalues.len()); for &addr in upvalues { upvalue_types.push(ctx.get_type(addr)); } - // 2. Create nested context for lambda body let mut lambda_ctx = TypeContext::new(64, upvalue_types, ctx.root_types, Some(ctx)); - // 3. Check parameters and body let params_typed = self.check_params( params.as_ref(), &StaticType::Any, @@ -494,13 +487,11 @@ impl TypeChecker { diag, ); - // Set current params type for 'again' validation lambda_ctx.current_params_ty = Some(params_typed.ty.clone()); let body_typed = self.check_node(body, &mut lambda_ctx, diag); let ret_ty = body_typed.ty.clone(); - // 4. Construct function type let fn_ty = StaticType::Function(Box::new(crate::ast::types::Signature { params: params_typed.ty.clone(), ret: ret_ty, @@ -520,7 +511,6 @@ impl TypeChecker { BoundKind::Call { callee, args } => { 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 mut typed_elements = Vec::new(); let mut elem_types = Vec::new(); @@ -537,7 +527,6 @@ impl TypeChecker { ty: StaticType::Tuple(elem_types), } } else { - // Should not happen as parser wraps args in Tuple, but fallback safely self.check_node(args, ctx, diag) }; @@ -565,7 +554,6 @@ 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 mut typed_elements = Vec::new(); let mut elem_types = Vec::new(); diff --git a/src/ast/environment.rs b/src/ast/environment.rs index 607c1d6..7af98ec 100644 --- a/src/ast/environment.rs +++ b/src/ast/environment.rs @@ -10,12 +10,12 @@ use std::path::{Path, PathBuf}; use std::rc::Rc; use crate::ast::compiler::bound_nodes::{ - Address, AnalyzedNode, BoundKind, GlobalAnalyzedRegistry, GlobalFunctionRegistry, GlobalIdx, - Node, + Address, AnalyzedNode, BoundKind, ExecNode, GlobalAnalyzedRegistry, GlobalFunctionRegistry, GlobalIdx, + Node, VirtualId, }; use crate::ast::compiler::dumper::Dumper; use crate::ast::compiler::lambda_collector::LambdaCollector; -use crate::ast::compiler::lowering::{Lowering, ExecNode}; +use crate::ast::compiler::lowering::Lowering; use crate::ast::compiler::macros::{MacroEvaluator, MacroExpander, MacroRegistry}; use crate::ast::compiler::optimizer::Optimizer; use crate::ast::compiler::specializer::{FunctionRegistry, MonoCache, Specializer}; @@ -89,19 +89,11 @@ pub struct Environment { } struct EnvFunctionRegistry { - registry: Rc>, analyzed_registry: Rc>, } impl FunctionRegistry for EnvFunctionRegistry { - fn resolve(&self, addr: Address) -> Option> { - if let Address::Global(idx) = addr { - self.registry.borrow().get(&idx).cloned() - } else { - None - } - } - fn resolve_analyzed(&self, addr: Address) -> Option> { + fn resolve(&self, addr: Address) -> Option>> { if let Address::Global(idx) = addr { self.analyzed_registry.borrow().get(&idx).cloned() } else { @@ -406,7 +398,7 @@ impl Environment { if !current_scope.locals.contains_key(sym) { let mut slot_count = self.root_slot_count.borrow_mut(); - let slot = crate::ast::compiler::bound_nodes::LocalSlot(*slot_count); + let slot = VirtualId(*slot_count); current_scope.locals.insert( sym.clone(), crate::ast::compiler::binder::LocalInfo { @@ -484,15 +476,15 @@ impl Environment { LambdaCollector::collect(&bound_ast, &mut self.function_registry.borrow_mut()); let checker = TypeChecker::new(self.root_types.clone()); - let wrapped_ast = if let crate::ast::compiler::bound_nodes::BoundKind::Lambda { .. } = bound_ast.kind { + let wrapped_ast = if let BoundKind::Lambda { .. } = bound_ast.kind { bound_ast } else { - crate::ast::compiler::bound_nodes::Node { + Node { identity: bound_ast.identity.clone(), - kind: crate::ast::compiler::bound_nodes::BoundKind::Lambda { - params: std::rc::Rc::new(crate::ast::compiler::bound_nodes::Node { + kind: BoundKind::Lambda { + params: std::rc::Rc::new(Node { identity: bound_ast.identity.clone(), - kind: crate::ast::compiler::bound_nodes::BoundKind::Tuple { elements: vec![] }, + kind: BoundKind::Tuple { elements: vec![] }, ty: (), }), upvalues: vec![], @@ -668,7 +660,7 @@ impl Environment { { let closure = Rc::new(crate::ast::vm::Closure::new( params.clone(), - body.ty.original.clone(), + body.ty.original.clone(), body.clone(), Vec::new(), *positional_count, @@ -717,13 +709,11 @@ impl Environment { fn specialize_node(&self, node: AnalyzedNode) -> AnalyzedNode { let registry = Rc::new(EnvFunctionRegistry { - registry: self.function_registry.clone(), analyzed_registry: self.typed_function_registry.clone(), }); let rtl_lookup = Rc::new(|name: &str, args: &[StaticType]| intrinsics::lookup(name, args)); let typed_reg = self.typed_function_registry.clone(); - let syntax_reg = self.function_registry.clone(); let mono_cache = self.monomorph_cache.clone(); let root_values = self.root_values.clone(); let root_types = self.root_types.clone(); @@ -731,12 +721,30 @@ impl Environment { let optimization = self.optimization; let compiler = Rc::new( - move |func_template: Rc, + move |func_template: Rc>, arg_types: &[StaticType]| -> Result<(Value, StaticType), String> { let mut diag = Diagnostics::new(); let checker = TypeChecker::new(root_types.clone()); - let retyped_ast = checker.check(func_template.as_ref(), arg_types, &mut diag); + + // For specialization, we re-type-check the analyzed node. + // Note: AnalyzedNode.ty.original is the TypedNode. + // But TypeChecker expects Node. We need to go from TypedNode -> BoundNode. + // However, since TypedNode is just Node, we can't easily "un-type" it. + // Instead, we access the original AST from the binder if we had it, + // OR we make the TypeChecker generic. + // For now, we assume the TypedNode can be used where a BoundNode is expected + // if we strip the metadata. + // A better way is to store the BoundNode in NodeMetrics as well. + + // Temporary fix: Re-binding from source would be too expensive. + // Let's assume for now that we can specialize directly on the TypedNode + // or that we need a small helper to transform TypedNode -> BoundNode. + + // Realizing that TypedNode (StaticType) is very similar to BoundNode (()), + // we can just use the internal transform. + + let retyped_ast = checker.check_node_as_bound(func_template.ty.original.as_ref(), arg_types, &mut diag); if diag.has_errors() { return Err(diag @@ -750,7 +758,6 @@ impl Environment { let analyzed = Analyzer::analyze(&retyped_ast, &root_purity.borrow()); let sub_registry = Rc::new(EnvFunctionRegistry { - registry: syntax_reg.clone(), analyzed_registry: typed_reg.clone(), }); let sub_rtl_lookup = diff --git a/src/ast/vm.rs b/src/ast/vm.rs index 787dede..16e32d9 100644 --- a/src/ast/vm.rs +++ b/src/ast/vm.rs @@ -1,5 +1,4 @@ -use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind}; -use crate::ast::compiler::lowering::ExecNode; +use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, ExecNode, StackOffset}; use crate::ast::types::{Object, Value}; use std::any::Any; use std::cell::RefCell; @@ -335,7 +334,7 @@ impl VM { val.clone() }; - self.set_value(addr.clone(), store_val)?; + self.set_value(*addr, store_val)?; // Define always evaluates to the unwrapped value for immediate use. Ok(val) } @@ -351,7 +350,7 @@ impl VM { } Ok(val) } - BoundKind::Get { addr, .. } => self.get_value(addr.clone()), + BoundKind::Get { addr, .. } => self.get_value(*addr), BoundKind::FieldAccessor(k) => Ok(Value::FieldAccessor(k.clone())), @@ -399,7 +398,7 @@ impl VM { BoundKind::Set { addr, value } => { let val = self.eval_internal(obs, value)?; - self.set_value(addr.clone(), val.clone())?; + self.set_value(*addr, val.clone())?; Ok(val) } BoundKind::If { @@ -882,7 +881,7 @@ impl VM { } } - fn capture_upvalue(&mut self, addr: Address) -> Result>, String> { + fn capture_upvalue(&mut self, addr: Address) -> Result>, String> { match addr { Address::Local(slot) => { let frame = self.frames.last().ok_or("No call frame")?; @@ -918,7 +917,7 @@ impl VM { } } - fn get_value(&self, addr: Address) -> Result { + fn get_value(&self, addr: Address) -> Result { match addr { Address::Local(slot) => { let frame = self.frames.last().ok_or("No call frame")?; @@ -958,7 +957,7 @@ impl VM { } } - fn set_value(&mut self, addr: Address, value: Value) -> Result<(), String> { + fn set_value(&mut self, addr: Address, value: Value) -> Result<(), String> { match addr { Address::Local(slot) => { let frame = self.frames.last().ok_or("No call frame")?;