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
(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 std::collections::{HashMap, HashSet};
use std::rc::Rc;
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.
lambda_stack: Vec<crate::ast::types::Identity>,
/// 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.
recursive_identities: HashSet<crate::ast::types::Identity>,
}
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 {
global_purity,
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::types::{Identity, StaticType};
use std::cell::RefCell;
@@ -7,7 +9,7 @@ use std::rc::Rc;
#[derive(Debug, Clone)]
struct LocalInfo {
slot: u32,
slot: LocalSlot,
identity: Identity,
// Note: Binder doesn't strictly need the type anymore,
// 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) {
return Err(format!(
"Variable '{}' is already defined in this scope level.",
sym.name
));
}
let slot = self.slot_count;
let slot = LocalSlot(self.slot_count);
self.locals.insert(
sym.clone(),
LocalInfo {
@@ -81,7 +83,7 @@ impl FunctionCompiler {
&mut self,
name: &Symbol,
identity: Identity,
globals: &Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
globals: &Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
) -> Result<Address, String> {
match self.kind {
ScopeKind::Root => {
@@ -93,8 +95,8 @@ impl FunctionCompiler {
));
}
let idx = globals_map.len() as u32;
globals_map.insert(name.clone(), (idx, identity));
Ok(Address::Global(idx))
globals_map.insert(name.clone(), (GlobalIdx(idx), identity));
Ok(Address::Global(GlobalIdx(idx)))
}
ScopeKind::Local => {
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) {
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);
idx
}
@@ -116,13 +118,13 @@ impl FunctionCompiler {
pub struct Binder {
functions: Vec<FunctionCompiler>,
// 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
capture_map: HashMap<Identity, std::collections::HashSet<Identity>>,
}
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 {
functions: Vec::new(),
globals,
@@ -130,13 +132,16 @@ impl Binder {
};
binder.functions.push(FunctionCompiler::new(
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
}
pub fn bind_root(
globals: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
globals: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
node: &Node<UntypedKind>,
) -> Result<(BoundNode, HashMap<Identity, Vec<Identity>>), String> {
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 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)]
pub enum Address {
Local(u32), // Stack-Slot index (relative to frame base)
Upvalue(u32), // Index in the closure's upvalue array
Global(u32), // Index in the global environment vector
Local(LocalSlot), // Stack-Slot index (relative to frame base)
Upvalue(UpvalueIdx), // Index in the closure's upvalue array
Global(GlobalIdx), // Index in the global environment vector
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+3 -1
View File
@@ -101,7 +101,9 @@ impl Dumper {
if !captured_by.is_empty() {
for capturer in captured_by {
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!(
"- Capturer: Lambda at line {}, col {}\n",
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;
/// 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<u32, BoundNode<T>>,
registry: &'a mut HashMap<GlobalIdx, 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<u32, BoundNode<T>>) {
pub fn collect(node: &BoundNode<T>, registry: &'a mut HashMap<GlobalIdx, BoundNode<T>>) {
let mut collector = Self { registry };
collector.visit(node);
}
+5 -2
View File
@@ -716,8 +716,11 @@ mod tests {
global_names.insert(
Symbol::from("*"),
(
0,
crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation { line: 0, col: 0 }),
crate::ast::compiler::bound_nodes::GlobalIdx(0),
crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation {
line: 0,
col: 0,
}),
),
);
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::types::{Purity, StaticType, Value};
use crate::ast::vm::Closure;
@@ -10,13 +12,13 @@ pub struct Optimizer {
pub enabled: bool,
max_passes: usize,
pub globals: Option<Rc<RefCell<Vec<Value>>>>,
pub global_purity: Option<Rc<RefCell<HashMap<u32, Purity>>>>,
pub lambda_registry: Option<Rc<RefCell<HashMap<u32, AnalyzedNode>>>>,
pub global_purity: Option<Rc<RefCell<HashMap<GlobalIdx, Purity>>>>,
pub lambda_registry: Option<Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>>,
}
struct PathTracker {
inlining_depth: usize,
inlining_stack: HashSet<u32>,
inlining_stack: HashSet<GlobalIdx>,
identity_stack: HashSet<crate::ast::types::Identity>,
}
@@ -75,12 +77,15 @@ impl Optimizer {
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
}
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
}
@@ -146,7 +151,7 @@ impl Optimizer {
&& let Some(globals_rc) = &self.globals
{
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)
{
return self.make_constant_node(val.clone(), &node);
@@ -304,8 +309,10 @@ impl Optimizer {
{
let mut closure_sub = SubstitutionMap::new();
for (i, cell) in closure.upvalues.iter().enumerate() {
closure_sub
.add_value(Address::Upvalue(i as u32), cell.borrow().clone());
closure_sub.add_value(
Address::Upvalue(UpvalueIdx(i as u32)),
cell.borrow().clone(),
);
}
path.inlining_depth += 1;
@@ -480,7 +487,8 @@ impl Optimizer {
}
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);
} else {
mapping.push(Some(new_upvalues.len() as u32));
@@ -579,7 +587,7 @@ impl Optimizer {
..
} => {
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 } => {
for el in elements {
@@ -684,7 +692,7 @@ impl Optimizer {
BoundKind::Get {
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(),
_ => return None,
};
@@ -737,7 +745,7 @@ impl Optimizer {
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 {
BoundKind::Define {
addr: Address::Local(slot),
@@ -802,7 +810,7 @@ impl Optimizer {
// Ensure local slots are mapped even if no constant/AST is substituted
if let Address::Local(slot) = addr {
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;
}
@@ -895,7 +903,7 @@ impl Optimizer {
// Map used upvalues to parent scope
for addr in &inner_info.used {
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);
}
@@ -903,7 +911,7 @@ impl Optimizer {
// Map assigned upvalues to parent scope
for addr in &inner_info.assigned {
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);
}
@@ -954,11 +962,11 @@ impl Optimizer {
struct SubstitutionMap {
values: HashMap<Address, Value>,
ast_substitutions: HashMap<Address, AnalyzedNode>,
slot_mapping: HashMap<u32, u32>,
slot_mapping: HashMap<LocalSlot, LocalSlot>,
assigned: HashSet<Address>,
next_slot: u32,
used: HashSet<Address>,
captured_slots: HashSet<u32>,
captured_slots: HashSet<LocalSlot>,
}
impl SubstitutionMap {
@@ -978,11 +986,11 @@ impl SubstitutionMap {
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) {
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.next_slot += 1;
new_slot
@@ -1009,10 +1017,10 @@ impl SubstitutionMap {
fn reindex_addr(&self, addr: Address, mapping: &[Option<u32>]) -> Address {
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
{
Address::Upvalue(*new_idx)
Address::Upvalue(UpvalueIdx(*new_idx))
} else {
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::types::StaticType;
use std::collections::HashMap;
@@ -12,7 +12,7 @@ struct TypeContext<'a> {
/// Types of captured variables (passed from outer scope)
upvalue_types: Vec<StaticType>,
/// 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)
current_params_ty: Option<StaticType>,
}
@@ -21,7 +21,7 @@ impl<'a> TypeContext<'a> {
fn new(
slot_count: u32,
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>>,
) -> Self {
Self {
@@ -35,9 +35,9 @@ impl<'a> TypeContext<'a> {
fn get_type(&self, addr: Address) -> StaticType {
match addr {
Address::Local(idx) => self
Address::Local(slot) => self
.slots
.get(idx as usize)
.get(slot.0 as usize)
.cloned()
.unwrap_or(StaticType::Any),
Address::Global(idx) => self
@@ -48,7 +48,7 @@ impl<'a> TypeContext<'a> {
.unwrap_or(StaticType::Any),
Address::Upvalue(idx) => self
.upvalue_types
.get(idx as usize)
.get(idx.0 as usize)
.cloned()
.unwrap_or(StaticType::Any),
}
@@ -56,9 +56,9 @@ impl<'a> TypeContext<'a> {
fn set_type(&mut self, addr: Address, ty: StaticType) {
match addr {
Address::Local(idx) => {
if let Some(slot) = self.slots.get_mut(idx as usize) {
*slot = ty;
Address::Local(slot) => {
if let Some(entry) = self.slots.get_mut(slot.0 as usize) {
*entry = ty;
}
}
Address::Global(idx) => {
@@ -70,11 +70,11 @@ impl<'a> TypeContext<'a> {
}
pub struct TypeChecker {
global_types: Rc<std::cell::RefCell<HashMap<u32, StaticType>>>,
global_types: Rc<std::cell::RefCell<HashMap<GlobalIdx, StaticType>>>,
}
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 }
}
+12 -12
View File
@@ -8,7 +8,7 @@ use std::cell::RefCell;
use std::collections::HashMap;
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::lambda_collector::LambdaCollector;
use crate::ast::compiler::macros::{MacroEvaluator, MacroExpander, MacroRegistry};
@@ -22,21 +22,21 @@ use crate::ast::types::{
};
pub struct Environment {
pub global_names: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
pub global_types: Rc<RefCell<HashMap<u32, StaticType>>>,
pub global_purity: Rc<RefCell<HashMap<u32, Purity>>>,
pub global_names: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
pub global_types: Rc<RefCell<HashMap<GlobalIdx, StaticType>>>,
pub global_purity: Rc<RefCell<HashMap<GlobalIdx, Purity>>>,
pub global_values: Rc<RefCell<Vec<Value>>>,
pub prng: Rc<RefCell<fastrand::Rng>>,
pub function_registry: Rc<RefCell<HashMap<u32, BoundNode>>>,
pub typed_function_registry: Rc<RefCell<HashMap<u32, AnalyzedNode>>>,
pub function_registry: Rc<RefCell<HashMap<GlobalIdx, BoundNode>>>,
pub typed_function_registry: Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>,
pub monomorph_cache: Rc<RefCell<MonoCache>>,
pub debug_mode: bool,
pub optimization: bool,
}
struct EnvFunctionRegistry {
registry: Rc<RefCell<HashMap<u32, BoundNode>>>,
analyzed_registry: Rc<RefCell<HashMap<u32, AnalyzedNode>>>,
registry: Rc<RefCell<HashMap<GlobalIdx, BoundNode>>>,
analyzed_registry: Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>,
}
impl FunctionRegistry for EnvFunctionRegistry {
@@ -57,8 +57,8 @@ impl FunctionRegistry for EnvFunctionRegistry {
}
struct RuntimeMacroEvaluator {
global_names: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
global_types: Rc<RefCell<HashMap<u32, StaticType>>>,
global_names: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
global_types: Rc<RefCell<HashMap<GlobalIdx, StaticType>>>,
global_values: Rc<RefCell<Vec<Value>>>,
}
@@ -133,7 +133,7 @@ impl Environment {
let mut values = self.global_values.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 });
names.insert(Symbol::from(name), (idx, identity));
types.insert(idx, ty);
@@ -164,7 +164,7 @@ impl Environment {
let mut values = self.global_values.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 });
names.insert(Symbol::from(name), (idx, identity));
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> {
match addr {
Address::Local(idx) => {
Address::Local(slot) => {
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 let Value::Cell(cell) = &self.stack[abs_index] {
Ok(cell.clone())
@@ -519,15 +519,15 @@ impl VM {
Ok(cell)
}
} else {
Err(format!("Stack underflow capture local {}", idx))
Err(format!("Stack underflow capture local {}", slot))
}
}
Address::Upvalue(idx) => {
let frame = self.frames.last().ok_or("No call frame")?;
if let Some(closure) = &frame.closure {
let idx = idx as usize;
if idx < closure.upvalues.len() {
Ok(closure.upvalues[idx].clone())
let u_idx = idx.0 as usize;
if u_idx < closure.upvalues.len() {
Ok(closure.upvalues[u_idx].clone())
} else {
Err(format!("Upvalue access out of bounds capture {}", idx))
}
@@ -541,23 +541,23 @@ impl VM {
fn get_value(&self, addr: Address) -> Result<Value, String> {
match addr {
Address::Local(idx) => {
Address::Local(slot) => {
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() {
match &self.stack[abs_index] {
Value::Cell(cell) => Ok(cell.borrow().clone()),
val => Ok(val.clone()),
}
} else {
Err(format!("Stack underflow access local {}", idx))
Err(format!("Stack underflow access local {}", slot))
}
}
Address::Global(idx) => {
let idx = idx as usize;
let g_idx = idx.0 as usize;
let globals = self.globals.borrow();
if idx < globals.len() {
Ok(globals[idx].clone())
if g_idx < globals.len() {
Ok(globals[g_idx].clone())
} else {
Err(format!("Global access out of bounds {}", idx))
}
@@ -565,9 +565,9 @@ impl VM {
Address::Upvalue(idx) => {
let frame = self.frames.last().ok_or("No call frame")?;
if let Some(closure) = &frame.closure {
let idx = idx as usize;
if idx < closure.upvalues.len() {
Ok(closure.upvalues[idx].borrow().clone())
let u_idx = idx.0 as usize;
if u_idx < closure.upvalues.len() {
Ok(closure.upvalues[u_idx].borrow().clone())
} else {
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> {
match addr {
Address::Local(idx) => {
Address::Local(slot) => {
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 let Value::Cell(cell) = &self.stack[abs_index] {
*cell.borrow_mut() = value;
@@ -592,25 +592,25 @@ impl VM {
} else if abs_index == self.stack.len() {
self.stack.push(value);
} else {
return Err(format!("Stack gap write local {}", idx));
return Err(format!("Stack gap write local {}", slot));
}
Ok(())
}
Address::Global(idx) => {
let idx = idx as usize;
let g_idx = idx.0 as usize;
let mut globals = self.globals.borrow_mut();
if idx >= globals.len() {
globals.resize(idx + 1, Value::Void);
if g_idx >= globals.len() {
globals.resize(g_idx + 1, Value::Void);
}
globals[idx] = value;
globals[g_idx] = value;
Ok(())
}
Address::Upvalue(idx) => {
let frame = self.frames.last().ok_or("No call frame")?;
if let Some(closure) = &frame.closure {
let idx = idx as usize;
if idx < closure.upvalues.len() {
*closure.upvalues[idx].borrow_mut() = value;
let u_idx = idx.0 as usize;
if u_idx < closure.upvalues.len() {
*closure.upvalues[u_idx].borrow_mut() = value;
Ok(())
} else {
Err(format!("Upvalue assignment out of bounds {}", idx))
+4 -1
View File
@@ -431,6 +431,9 @@ mod tests {
dump
);
// 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"
);
}
}