Refactor address types to use newtype wrappers

This commit introduces newtype wrappers for `LocalSlot`, `UpvalueIdx`,
and `GlobalIdx` to improve type safety and clarity. These wrappers
replace direct use of `u32` for indices, making the code more robust and
easier to understand.

The changes include:
- Defining `LocalSlot`, `UpvalueIdx`, and `GlobalIdx` structs.
- Implementing `Display` for these new types to provide user-friendly
  output.
- Updating the `Address` enum to use these new types.
- Modifying various compiler components (analyzer, binder, optimizer,
  type checker, environment, VM) to use the new types.
- Adjusting tests to accommodate the changes.
This commit is contained in:
Michael Schimmel
2026-02-25 18:24:26 +01:00
parent 11f6115fc1
commit c64902726b
12 changed files with 153 additions and 98 deletions
+2
View File
@@ -1,3 +1,5 @@
;; Benchmark: 129ns
;; Benchmark-Repeat: 15510
(do (do
(macro wrap [f] `(fn [x] (~f x))) (macro wrap [f] `(fn [x] (~f x)))
+9 -4
View File
@@ -1,20 +1,25 @@
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, NodeMetrics, TypedNode}; use crate::ast::compiler::bound_nodes::{
Address, AnalyzedNode, BoundKind, GlobalIdx, NodeMetrics, TypedNode,
};
use crate::ast::types::Purity; use crate::ast::types::Purity;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::rc::Rc; use std::rc::Rc;
pub struct Analyzer<'a> { pub struct Analyzer<'a> {
global_purity: &'a HashMap<u32, Purity>, global_purity: &'a HashMap<GlobalIdx, Purity>,
/// Stack of currently visiting lambdas to detect direct recursion. /// Stack of currently visiting lambdas to detect direct recursion.
lambda_stack: Vec<crate::ast::types::Identity>, lambda_stack: Vec<crate::ast::types::Identity>,
/// Map of global index to its Lambda identity if known. /// Map of global index to its Lambda identity if known.
globals_to_lambdas: HashMap<u32, crate::ast::types::Identity>, globals_to_lambdas: HashMap<GlobalIdx, crate::ast::types::Identity>,
/// Set of identities that were found to be recursive. /// Set of identities that were found to be recursive.
recursive_identities: HashSet<crate::ast::types::Identity>, recursive_identities: HashSet<crate::ast::types::Identity>,
} }
impl<'a> Analyzer<'a> { impl<'a> Analyzer<'a> {
pub fn analyze(node: &TypedNode, global_purity: &'a HashMap<u32, Purity>) -> AnalyzedNode { pub fn analyze(
node: &TypedNode,
global_purity: &'a HashMap<GlobalIdx, Purity>,
) -> AnalyzedNode {
let mut analyzer = Self { let mut analyzer = Self {
global_purity, global_purity,
lambda_stack: Vec::new(), lambda_stack: Vec::new(),
+19 -14
View File
@@ -1,4 +1,6 @@
use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundNode, DeclarationKind}; use crate::ast::compiler::bound_nodes::{
Address, BoundKind, BoundNode, DeclarationKind, GlobalIdx, LocalSlot, UpvalueIdx,
};
use crate::ast::nodes::{Node, Symbol, UntypedKind}; use crate::ast::nodes::{Node, Symbol, UntypedKind};
use crate::ast::types::{Identity, StaticType}; use crate::ast::types::{Identity, StaticType};
use std::cell::RefCell; use std::cell::RefCell;
@@ -7,7 +9,7 @@ use std::rc::Rc;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct LocalInfo { struct LocalInfo {
slot: u32, slot: LocalSlot,
identity: Identity, identity: Identity,
// Note: Binder doesn't strictly need the type anymore, // Note: Binder doesn't strictly need the type anymore,
// but it might be useful for built-ins during resolution. // but it might be useful for built-ins during resolution.
@@ -29,14 +31,14 @@ impl CompilerScope {
} }
} }
fn define(&mut self, sym: &Symbol, identity: Identity) -> Result<u32, String> { fn define(&mut self, sym: &Symbol, identity: Identity) -> Result<LocalSlot, String> {
if self.locals.contains_key(sym) { if self.locals.contains_key(sym) {
return Err(format!( return Err(format!(
"Variable '{}' is already defined in this scope level.", "Variable '{}' is already defined in this scope level.",
sym.name sym.name
)); ));
} }
let slot = self.slot_count; let slot = LocalSlot(self.slot_count);
self.locals.insert( self.locals.insert(
sym.clone(), sym.clone(),
LocalInfo { LocalInfo {
@@ -81,7 +83,7 @@ impl FunctionCompiler {
&mut self, &mut self,
name: &Symbol, name: &Symbol,
identity: Identity, identity: Identity,
globals: &Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>, globals: &Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
) -> Result<Address, String> { ) -> Result<Address, String> {
match self.kind { match self.kind {
ScopeKind::Root => { ScopeKind::Root => {
@@ -93,8 +95,8 @@ impl FunctionCompiler {
)); ));
} }
let idx = globals_map.len() as u32; let idx = globals_map.len() as u32;
globals_map.insert(name.clone(), (idx, identity)); globals_map.insert(name.clone(), (GlobalIdx(idx), identity));
Ok(Address::Global(idx)) Ok(Address::Global(GlobalIdx(idx)))
} }
ScopeKind::Local => { ScopeKind::Local => {
let slot = self.scope.define(name, identity)?; let slot = self.scope.define(name, identity)?;
@@ -103,11 +105,11 @@ impl FunctionCompiler {
} }
} }
fn add_upvalue(&mut self, addr: Address) -> u32 { fn add_upvalue(&mut self, addr: Address) -> UpvalueIdx {
if let Some(idx) = self.upvalues.iter().position(|&a| a == addr) { if let Some(idx) = self.upvalues.iter().position(|&a| a == addr) {
return idx as u32; return UpvalueIdx(idx as u32);
} }
let idx = self.upvalues.len() as u32; let idx = UpvalueIdx(self.upvalues.len() as u32);
self.upvalues.push(addr); self.upvalues.push(addr);
idx idx
} }
@@ -116,13 +118,13 @@ impl FunctionCompiler {
pub struct Binder { pub struct Binder {
functions: Vec<FunctionCompiler>, functions: Vec<FunctionCompiler>,
// Globals mapping: Symbol -> (Index, DefinitionIdentity) // Globals mapping: Symbol -> (Index, DefinitionIdentity)
globals: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>, globals: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
// Map of Declaration Identity -> List of Lambda Identities that capture it // Map of Declaration Identity -> List of Lambda Identities that capture it
capture_map: HashMap<Identity, std::collections::HashSet<Identity>>, capture_map: HashMap<Identity, std::collections::HashSet<Identity>>,
} }
impl Binder { impl Binder {
pub fn new(globals: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>) -> Self { pub fn new(globals: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>) -> Self {
let mut binder = Self { let mut binder = Self {
functions: Vec::new(), functions: Vec::new(),
globals, globals,
@@ -130,13 +132,16 @@ impl Binder {
}; };
binder.functions.push(FunctionCompiler::new( binder.functions.push(FunctionCompiler::new(
ScopeKind::Root, ScopeKind::Root,
crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation { line: 0, col: 0 }), crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation {
line: 0,
col: 0,
}),
)); ));
binder binder
} }
pub fn bind_root( pub fn bind_root(
globals: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>, globals: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
node: &Node<UntypedKind>, node: &Node<UntypedKind>,
) -> Result<(BoundNode, HashMap<Identity, Vec<Identity>>), String> { ) -> Result<(BoundNode, HashMap<Identity, Vec<Identity>>), String> {
let mut binder = Self::new(globals); let mut binder = Self::new(globals);
+30 -3
View File
@@ -2,11 +2,38 @@ use crate::ast::nodes::{Node, Symbol};
use crate::ast::types::{Identity, StaticType, Value}; use crate::ast::types::{Identity, StaticType, Value};
use std::rc::Rc; use std::rc::Rc;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct LocalSlot(pub u32);
impl std::fmt::Display for LocalSlot {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "L{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct UpvalueIdx(pub u32);
impl std::fmt::Display for UpvalueIdx {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "U{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct GlobalIdx(pub u32);
impl std::fmt::Display for GlobalIdx {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "G{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Address { pub enum Address {
Local(u32), // Stack-Slot index (relative to frame base) Local(LocalSlot), // Stack-Slot index (relative to frame base)
Upvalue(u32), // Index in the closure's upvalue array Upvalue(UpvalueIdx), // Index in the closure's upvalue array
Global(u32), // Index in the global environment vector Global(GlobalIdx), // Index in the global environment vector
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+3 -1
View File
@@ -101,7 +101,9 @@ impl Dumper {
if !captured_by.is_empty() { if !captured_by.is_empty() {
for capturer in captured_by { for capturer in captured_by {
self.write_indent(); self.write_indent();
let loc = capturer.location.unwrap_or(crate::ast::types::SourceLocation { line: 0, col: 0 }); let loc = capturer
.location
.unwrap_or(crate::ast::types::SourceLocation { line: 0, col: 0 });
self.output.push_str(&format!( self.output.push_str(&format!(
"- Capturer: Lambda at line {}, col {}\n", "- Capturer: Lambda at line {}, col {}\n",
loc.line, loc.col loc.line, loc.col
+3 -3
View File
@@ -1,15 +1,15 @@
use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundNode}; use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundNode, GlobalIdx};
use std::collections::HashMap; use std::collections::HashMap;
/// A pass that collects all global function definitions (lambdas) into a registry. /// 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. /// This allows the Specializer to retrieve the original AST of a function for monomorphization.
pub struct LambdaCollector<'a, T> { pub struct LambdaCollector<'a, T> {
registry: &'a mut HashMap<u32, BoundNode<T>>, registry: &'a mut HashMap<GlobalIdx, BoundNode<T>>,
} }
impl<'a, T: Clone> LambdaCollector<'a, T> { impl<'a, T: Clone> LambdaCollector<'a, T> {
/// Performs a full traversal of the AST and populates the provided registry. /// Performs a full traversal of the AST and populates the provided registry.
pub fn collect(node: &BoundNode<T>, registry: &'a mut HashMap<u32, BoundNode<T>>) { pub fn collect(node: &BoundNode<T>, registry: &'a mut HashMap<GlobalIdx, BoundNode<T>>) {
let mut collector = Self { registry }; let mut collector = Self { registry };
collector.visit(node); collector.visit(node);
} }
+5 -2
View File
@@ -716,8 +716,11 @@ mod tests {
global_names.insert( global_names.insert(
Symbol::from("*"), Symbol::from("*"),
( (
0, crate::ast::compiler::bound_nodes::GlobalIdx(0),
crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation { line: 0, col: 0 }), crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation {
line: 0,
col: 0,
}),
), ),
); );
let globals = Rc::new(RefCell::new(global_names)); let globals = Rc::new(RefCell::new(global_names));
+30 -22
View File
@@ -1,4 +1,6 @@
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, NodeMetrics}; use crate::ast::compiler::bound_nodes::{
Address, AnalyzedNode, BoundKind, GlobalIdx, LocalSlot, NodeMetrics, UpvalueIdx,
};
use crate::ast::nodes::Node; use crate::ast::nodes::Node;
use crate::ast::types::{Purity, StaticType, Value}; use crate::ast::types::{Purity, StaticType, Value};
use crate::ast::vm::Closure; use crate::ast::vm::Closure;
@@ -10,13 +12,13 @@ pub struct Optimizer {
pub enabled: bool, pub enabled: bool,
max_passes: usize, max_passes: usize,
pub globals: Option<Rc<RefCell<Vec<Value>>>>, pub globals: Option<Rc<RefCell<Vec<Value>>>>,
pub global_purity: Option<Rc<RefCell<HashMap<u32, Purity>>>>, pub global_purity: Option<Rc<RefCell<HashMap<GlobalIdx, Purity>>>>,
pub lambda_registry: Option<Rc<RefCell<HashMap<u32, AnalyzedNode>>>>, pub lambda_registry: Option<Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>>,
} }
struct PathTracker { struct PathTracker {
inlining_depth: usize, inlining_depth: usize,
inlining_stack: HashSet<u32>, inlining_stack: HashSet<GlobalIdx>,
identity_stack: HashSet<crate::ast::types::Identity>, identity_stack: HashSet<crate::ast::types::Identity>,
} }
@@ -75,12 +77,15 @@ impl Optimizer {
self self
} }
pub fn with_purity(mut self, purity: Rc<RefCell<HashMap<u32, Purity>>>) -> Self { pub fn with_purity(mut self, purity: Rc<RefCell<HashMap<GlobalIdx, Purity>>>) -> Self {
self.global_purity = Some(purity); self.global_purity = Some(purity);
self self
} }
pub fn with_registry(mut self, registry: Rc<RefCell<HashMap<u32, AnalyzedNode>>>) -> Self { pub fn with_registry(
mut self,
registry: Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>,
) -> Self {
self.lambda_registry = Some(registry); self.lambda_registry = Some(registry);
self self
} }
@@ -146,7 +151,7 @@ impl Optimizer {
&& let Some(globals_rc) = &self.globals && let Some(globals_rc) = &self.globals
{ {
let globals = globals_rc.borrow(); let globals = globals_rc.borrow();
if let Some(val) = globals.get(idx as usize) if let Some(val) = globals.get(idx.0 as usize)
&& self.is_inlinable_value(val, addr) && self.is_inlinable_value(val, addr)
{ {
return self.make_constant_node(val.clone(), &node); return self.make_constant_node(val.clone(), &node);
@@ -304,8 +309,10 @@ impl Optimizer {
{ {
let mut closure_sub = SubstitutionMap::new(); let mut closure_sub = SubstitutionMap::new();
for (i, cell) in closure.upvalues.iter().enumerate() { for (i, cell) in closure.upvalues.iter().enumerate() {
closure_sub closure_sub.add_value(
.add_value(Address::Upvalue(i as u32), cell.borrow().clone()); Address::Upvalue(UpvalueIdx(i as u32)),
cell.borrow().clone(),
);
} }
path.inlining_depth += 1; path.inlining_depth += 1;
@@ -480,7 +487,8 @@ impl Optimizer {
} }
if let Some(val) = inlined_val { if let Some(val) = inlined_val {
next_inner_subs.add_value(Address::Upvalue(old_idx as u32), val); next_inner_subs
.add_value(Address::Upvalue(UpvalueIdx(old_idx as u32)), val);
mapping.push(None); mapping.push(None);
} else { } else {
mapping.push(Some(new_upvalues.len() as u32)); mapping.push(Some(new_upvalues.len() as u32));
@@ -579,7 +587,7 @@ impl Optimizer {
.. ..
} => { } => {
sub.slot_mapping.insert(*slot, *slot); sub.slot_mapping.insert(*slot, *slot);
sub.next_slot = sub.next_slot.max(*slot + 1); sub.next_slot = sub.next_slot.max(slot.0 + 1);
} }
BoundKind::Tuple { elements } => { BoundKind::Tuple { elements } => {
for el in elements { for el in elements {
@@ -684,7 +692,7 @@ impl Optimizer {
BoundKind::Get { BoundKind::Get {
addr: Address::Global(idx), addr: Address::Global(idx),
.. ..
} => self.globals.as_ref()?.borrow().get(*idx as usize)?.clone(), } => self.globals.as_ref()?.borrow().get(idx.0 as usize)?.clone(),
BoundKind::Constant(val) => val.clone(), BoundKind::Constant(val) => val.clone(),
_ => return None, _ => return None,
}; };
@@ -737,7 +745,7 @@ impl Optimizer {
Some(self.visit_node(body, sub, path)) Some(self.visit_node(body, sub, path))
} }
fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet<u32>) { fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet<LocalSlot>) {
match &node.kind { match &node.kind {
BoundKind::Define { BoundKind::Define {
addr: Address::Local(slot), addr: Address::Local(slot),
@@ -802,7 +810,7 @@ impl Optimizer {
// Ensure local slots are mapped even if no constant/AST is substituted // Ensure local slots are mapped even if no constant/AST is substituted
if let Address::Local(slot) = addr { if let Address::Local(slot) = addr {
sub.slot_mapping.insert(*slot, *slot); sub.slot_mapping.insert(*slot, *slot);
sub.next_slot = sub.next_slot.max(*slot + 1); sub.next_slot = sub.next_slot.max(slot.0 + 1);
} }
*offset += 1; *offset += 1;
} }
@@ -895,7 +903,7 @@ impl Optimizer {
// Map used upvalues to parent scope // Map used upvalues to parent scope
for addr in &inner_info.used { for addr in &inner_info.used {
if let Address::Upvalue(idx) = addr if let Address::Upvalue(idx) = addr
&& let Some(parent_addr) = upvalues.get(*idx as usize) && let Some(parent_addr) = upvalues.get(idx.0 as usize)
{ {
info.used.insert(*parent_addr); info.used.insert(*parent_addr);
} }
@@ -903,7 +911,7 @@ impl Optimizer {
// Map assigned upvalues to parent scope // Map assigned upvalues to parent scope
for addr in &inner_info.assigned { for addr in &inner_info.assigned {
if let Address::Upvalue(idx) = addr if let Address::Upvalue(idx) = addr
&& let Some(parent_addr) = upvalues.get(*idx as usize) && let Some(parent_addr) = upvalues.get(idx.0 as usize)
{ {
info.assigned.insert(*parent_addr); info.assigned.insert(*parent_addr);
} }
@@ -954,11 +962,11 @@ impl Optimizer {
struct SubstitutionMap { struct SubstitutionMap {
values: HashMap<Address, Value>, values: HashMap<Address, Value>,
ast_substitutions: HashMap<Address, AnalyzedNode>, ast_substitutions: HashMap<Address, AnalyzedNode>,
slot_mapping: HashMap<u32, u32>, slot_mapping: HashMap<LocalSlot, LocalSlot>,
assigned: HashSet<Address>, assigned: HashSet<Address>,
next_slot: u32, next_slot: u32,
used: HashSet<Address>, used: HashSet<Address>,
captured_slots: HashSet<u32>, captured_slots: HashSet<LocalSlot>,
} }
impl SubstitutionMap { impl SubstitutionMap {
@@ -978,11 +986,11 @@ impl SubstitutionMap {
self.ast_substitutions.insert(addr, node); self.ast_substitutions.insert(addr, node);
} }
fn map_slot(&mut self, old_slot: u32) -> u32 { fn map_slot(&mut self, old_slot: LocalSlot) -> LocalSlot {
if let Some(&new_slot) = self.slot_mapping.get(&old_slot) { if let Some(&new_slot) = self.slot_mapping.get(&old_slot) {
return new_slot; return new_slot;
} }
let new_slot = self.next_slot; let new_slot = LocalSlot(self.next_slot);
self.slot_mapping.insert(old_slot, new_slot); self.slot_mapping.insert(old_slot, new_slot);
self.next_slot += 1; self.next_slot += 1;
new_slot new_slot
@@ -1009,10 +1017,10 @@ impl SubstitutionMap {
fn reindex_addr(&self, addr: Address, mapping: &[Option<u32>]) -> Address { fn reindex_addr(&self, addr: Address, mapping: &[Option<u32>]) -> Address {
if let Address::Upvalue(idx) = addr if let Address::Upvalue(idx) = addr
&& let Some(res) = mapping.get(idx as usize) && let Some(res) = mapping.get(idx.0 as usize)
&& let Some(new_idx) = res && let Some(new_idx) = res
{ {
Address::Upvalue(*new_idx) Address::Upvalue(UpvalueIdx(*new_idx))
} else { } else {
addr addr
} }
+11 -11
View File
@@ -1,4 +1,4 @@
use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundNode, TypedNode}; use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundNode, GlobalIdx, TypedNode};
use crate::ast::nodes::Node; use crate::ast::nodes::Node;
use crate::ast::types::StaticType; use crate::ast::types::StaticType;
use std::collections::HashMap; use std::collections::HashMap;
@@ -12,7 +12,7 @@ struct TypeContext<'a> {
/// Types of captured variables (passed from outer scope) /// Types of captured variables (passed from outer scope)
upvalue_types: Vec<StaticType>, upvalue_types: Vec<StaticType>,
/// Access to global types for unified resolution /// Access to global types for unified resolution
global_types: &'a std::cell::RefCell<HashMap<u32, StaticType>>, global_types: &'a std::cell::RefCell<HashMap<GlobalIdx, StaticType>>,
/// The expected parameters of the current function (for 'again' validation) /// The expected parameters of the current function (for 'again' validation)
current_params_ty: Option<StaticType>, current_params_ty: Option<StaticType>,
} }
@@ -21,7 +21,7 @@ impl<'a> TypeContext<'a> {
fn new( fn new(
slot_count: u32, slot_count: u32,
upvalue_types: Vec<StaticType>, upvalue_types: Vec<StaticType>,
global_types: &'a std::cell::RefCell<HashMap<u32, StaticType>>, global_types: &'a std::cell::RefCell<HashMap<GlobalIdx, StaticType>>,
parent: Option<&'a TypeContext<'a>>, parent: Option<&'a TypeContext<'a>>,
) -> Self { ) -> Self {
Self { Self {
@@ -35,9 +35,9 @@ impl<'a> TypeContext<'a> {
fn get_type(&self, addr: Address) -> StaticType { fn get_type(&self, addr: Address) -> StaticType {
match addr { match addr {
Address::Local(idx) => self Address::Local(slot) => self
.slots .slots
.get(idx as usize) .get(slot.0 as usize)
.cloned() .cloned()
.unwrap_or(StaticType::Any), .unwrap_or(StaticType::Any),
Address::Global(idx) => self Address::Global(idx) => self
@@ -48,7 +48,7 @@ impl<'a> TypeContext<'a> {
.unwrap_or(StaticType::Any), .unwrap_or(StaticType::Any),
Address::Upvalue(idx) => self Address::Upvalue(idx) => self
.upvalue_types .upvalue_types
.get(idx as usize) .get(idx.0 as usize)
.cloned() .cloned()
.unwrap_or(StaticType::Any), .unwrap_or(StaticType::Any),
} }
@@ -56,9 +56,9 @@ impl<'a> TypeContext<'a> {
fn set_type(&mut self, addr: Address, ty: StaticType) { fn set_type(&mut self, addr: Address, ty: StaticType) {
match addr { match addr {
Address::Local(idx) => { Address::Local(slot) => {
if let Some(slot) = self.slots.get_mut(idx as usize) { if let Some(entry) = self.slots.get_mut(slot.0 as usize) {
*slot = ty; *entry = ty;
} }
} }
Address::Global(idx) => { Address::Global(idx) => {
@@ -70,11 +70,11 @@ impl<'a> TypeContext<'a> {
} }
pub struct TypeChecker { pub struct TypeChecker {
global_types: Rc<std::cell::RefCell<HashMap<u32, StaticType>>>, global_types: Rc<std::cell::RefCell<HashMap<GlobalIdx, StaticType>>>,
} }
impl TypeChecker { impl TypeChecker {
pub fn new(global_types: Rc<std::cell::RefCell<HashMap<u32, StaticType>>>) -> Self { pub fn new(global_types: Rc<std::cell::RefCell<HashMap<GlobalIdx, StaticType>>>) -> Self {
Self { global_types } Self { global_types }
} }
+12 -12
View File
@@ -8,7 +8,7 @@ use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, BoundNode}; use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, BoundNode, GlobalIdx};
use crate::ast::compiler::dumper::Dumper; use crate::ast::compiler::dumper::Dumper;
use crate::ast::compiler::lambda_collector::LambdaCollector; use crate::ast::compiler::lambda_collector::LambdaCollector;
use crate::ast::compiler::macros::{MacroEvaluator, MacroExpander, MacroRegistry}; use crate::ast::compiler::macros::{MacroEvaluator, MacroExpander, MacroRegistry};
@@ -22,21 +22,21 @@ use crate::ast::types::{
}; };
pub struct Environment { pub struct Environment {
pub global_names: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>, pub global_names: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
pub global_types: Rc<RefCell<HashMap<u32, StaticType>>>, pub global_types: Rc<RefCell<HashMap<GlobalIdx, StaticType>>>,
pub global_purity: Rc<RefCell<HashMap<u32, Purity>>>, pub global_purity: Rc<RefCell<HashMap<GlobalIdx, Purity>>>,
pub global_values: Rc<RefCell<Vec<Value>>>, pub global_values: Rc<RefCell<Vec<Value>>>,
pub prng: Rc<RefCell<fastrand::Rng>>, pub prng: Rc<RefCell<fastrand::Rng>>,
pub function_registry: Rc<RefCell<HashMap<u32, BoundNode>>>, pub function_registry: Rc<RefCell<HashMap<GlobalIdx, BoundNode>>>,
pub typed_function_registry: Rc<RefCell<HashMap<u32, AnalyzedNode>>>, pub typed_function_registry: Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>,
pub monomorph_cache: Rc<RefCell<MonoCache>>, pub monomorph_cache: Rc<RefCell<MonoCache>>,
pub debug_mode: bool, pub debug_mode: bool,
pub optimization: bool, pub optimization: bool,
} }
struct EnvFunctionRegistry { struct EnvFunctionRegistry {
registry: Rc<RefCell<HashMap<u32, BoundNode>>>, registry: Rc<RefCell<HashMap<GlobalIdx, BoundNode>>>,
analyzed_registry: Rc<RefCell<HashMap<u32, AnalyzedNode>>>, analyzed_registry: Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>,
} }
impl FunctionRegistry for EnvFunctionRegistry { impl FunctionRegistry for EnvFunctionRegistry {
@@ -57,8 +57,8 @@ impl FunctionRegistry for EnvFunctionRegistry {
} }
struct RuntimeMacroEvaluator { struct RuntimeMacroEvaluator {
global_names: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>, global_names: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
global_types: Rc<RefCell<HashMap<u32, StaticType>>>, global_types: Rc<RefCell<HashMap<GlobalIdx, StaticType>>>,
global_values: Rc<RefCell<Vec<Value>>>, global_values: Rc<RefCell<Vec<Value>>>,
} }
@@ -133,7 +133,7 @@ impl Environment {
let mut values = self.global_values.borrow_mut(); let mut values = self.global_values.borrow_mut();
let mut purity = self.global_purity.borrow_mut(); let mut purity = self.global_purity.borrow_mut();
let idx = values.len() as u32; let idx = GlobalIdx(values.len() as u32);
let identity = NodeIdentity::new(SourceLocation { line: 0, col: 0 }); let identity = NodeIdentity::new(SourceLocation { line: 0, col: 0 });
names.insert(Symbol::from(name), (idx, identity)); names.insert(Symbol::from(name), (idx, identity));
types.insert(idx, ty); types.insert(idx, ty);
@@ -164,7 +164,7 @@ impl Environment {
let mut values = self.global_values.borrow_mut(); let mut values = self.global_values.borrow_mut();
let mut purity = self.global_purity.borrow_mut(); let mut purity = self.global_purity.borrow_mut();
let idx = values.len() as u32; let idx = GlobalIdx(values.len() as u32);
let identity = NodeIdentity::new(SourceLocation { line: 0, col: 0 }); let identity = NodeIdentity::new(SourceLocation { line: 0, col: 0 });
names.insert(Symbol::from(name), (idx, identity)); names.insert(Symbol::from(name), (idx, identity));
types.insert(idx, ty); types.insert(idx, ty);
+25 -25
View File
@@ -506,9 +506,9 @@ impl VM {
fn capture_upvalue(&mut self, addr: Address) -> Result<Rc<RefCell<Value>>, String> { fn capture_upvalue(&mut self, addr: Address) -> Result<Rc<RefCell<Value>>, String> {
match addr { match addr {
Address::Local(idx) => { Address::Local(slot) => {
let frame = self.frames.last().ok_or("No call frame")?; let frame = self.frames.last().ok_or("No call frame")?;
let abs_index = frame.stack_base + (idx as usize); let abs_index = frame.stack_base + (slot.0 as usize);
if abs_index < self.stack.len() { if abs_index < self.stack.len() {
if let Value::Cell(cell) = &self.stack[abs_index] { if let Value::Cell(cell) = &self.stack[abs_index] {
Ok(cell.clone()) Ok(cell.clone())
@@ -519,15 +519,15 @@ impl VM {
Ok(cell) Ok(cell)
} }
} else { } else {
Err(format!("Stack underflow capture local {}", idx)) Err(format!("Stack underflow capture local {}", slot))
} }
} }
Address::Upvalue(idx) => { Address::Upvalue(idx) => {
let frame = self.frames.last().ok_or("No call frame")?; let frame = self.frames.last().ok_or("No call frame")?;
if let Some(closure) = &frame.closure { if let Some(closure) = &frame.closure {
let idx = idx as usize; let u_idx = idx.0 as usize;
if idx < closure.upvalues.len() { if u_idx < closure.upvalues.len() {
Ok(closure.upvalues[idx].clone()) Ok(closure.upvalues[u_idx].clone())
} else { } else {
Err(format!("Upvalue access out of bounds capture {}", idx)) Err(format!("Upvalue access out of bounds capture {}", idx))
} }
@@ -541,23 +541,23 @@ impl VM {
fn get_value(&self, addr: Address) -> Result<Value, String> { fn get_value(&self, addr: Address) -> Result<Value, String> {
match addr { match addr {
Address::Local(idx) => { Address::Local(slot) => {
let frame = self.frames.last().ok_or("No call frame")?; let frame = self.frames.last().ok_or("No call frame")?;
let abs_index = frame.stack_base + (idx as usize); let abs_index = frame.stack_base + (slot.0 as usize);
if abs_index < self.stack.len() { if abs_index < self.stack.len() {
match &self.stack[abs_index] { match &self.stack[abs_index] {
Value::Cell(cell) => Ok(cell.borrow().clone()), Value::Cell(cell) => Ok(cell.borrow().clone()),
val => Ok(val.clone()), val => Ok(val.clone()),
} }
} else { } else {
Err(format!("Stack underflow access local {}", idx)) Err(format!("Stack underflow access local {}", slot))
} }
} }
Address::Global(idx) => { Address::Global(idx) => {
let idx = idx as usize; let g_idx = idx.0 as usize;
let globals = self.globals.borrow(); let globals = self.globals.borrow();
if idx < globals.len() { if g_idx < globals.len() {
Ok(globals[idx].clone()) Ok(globals[g_idx].clone())
} else { } else {
Err(format!("Global access out of bounds {}", idx)) Err(format!("Global access out of bounds {}", idx))
} }
@@ -565,9 +565,9 @@ impl VM {
Address::Upvalue(idx) => { Address::Upvalue(idx) => {
let frame = self.frames.last().ok_or("No call frame")?; let frame = self.frames.last().ok_or("No call frame")?;
if let Some(closure) = &frame.closure { if let Some(closure) = &frame.closure {
let idx = idx as usize; let u_idx = idx.0 as usize;
if idx < closure.upvalues.len() { if u_idx < closure.upvalues.len() {
Ok(closure.upvalues[idx].borrow().clone()) Ok(closure.upvalues[u_idx].borrow().clone())
} else { } else {
Err(format!("Upvalue access out of bounds {}", idx)) Err(format!("Upvalue access out of bounds {}", idx))
} }
@@ -580,9 +580,9 @@ 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 { match addr {
Address::Local(idx) => { Address::Local(slot) => {
let frame = self.frames.last().ok_or("No call frame")?; let frame = self.frames.last().ok_or("No call frame")?;
let abs_index = frame.stack_base + (idx as usize); let abs_index = frame.stack_base + (slot.0 as usize);
if abs_index < self.stack.len() { if abs_index < self.stack.len() {
if let Value::Cell(cell) = &self.stack[abs_index] { if let Value::Cell(cell) = &self.stack[abs_index] {
*cell.borrow_mut() = value; *cell.borrow_mut() = value;
@@ -592,25 +592,25 @@ impl VM {
} else if abs_index == self.stack.len() { } else if abs_index == self.stack.len() {
self.stack.push(value); self.stack.push(value);
} else { } else {
return Err(format!("Stack gap write local {}", idx)); return Err(format!("Stack gap write local {}", slot));
} }
Ok(()) Ok(())
} }
Address::Global(idx) => { Address::Global(idx) => {
let idx = idx as usize; let g_idx = idx.0 as usize;
let mut globals = self.globals.borrow_mut(); let mut globals = self.globals.borrow_mut();
if idx >= globals.len() { if g_idx >= globals.len() {
globals.resize(idx + 1, Value::Void); globals.resize(g_idx + 1, Value::Void);
} }
globals[idx] = value; globals[g_idx] = value;
Ok(()) Ok(())
} }
Address::Upvalue(idx) => { Address::Upvalue(idx) => {
let frame = self.frames.last().ok_or("No call frame")?; let frame = self.frames.last().ok_or("No call frame")?;
if let Some(closure) = &frame.closure { if let Some(closure) = &frame.closure {
let idx = idx as usize; let u_idx = idx.0 as usize;
if idx < closure.upvalues.len() { if u_idx < closure.upvalues.len() {
*closure.upvalues[idx].borrow_mut() = value; *closure.upvalues[u_idx].borrow_mut() = value;
Ok(()) Ok(())
} else { } else {
Err(format!("Upvalue assignment out of bounds {}", idx)) Err(format!("Upvalue assignment out of bounds {}", idx))
+4 -1
View File
@@ -431,6 +431,9 @@ mod tests {
dump dump
); );
// The definitions add1, add2, w1, w2 should be gone after dead code elimination // The definitions add1, add2, w1, w2 should be gone after dead code elimination
assert!(!dump.contains("Define Variable"), "Definitions should be removed by DCE"); assert!(
!dump.contains("Define Variable"),
"Definitions should be removed by DCE"
);
} }
} }