diff --git a/examples/optimizer_collision_repro.myc b/examples/optimizer_collision_repro.myc index 89ddb3c..5edf62b 100644 --- a/examples/optimizer_collision_repro.myc +++ b/examples/optimizer_collision_repro.myc @@ -1,3 +1,5 @@ +;; Benchmark: 129ns +;; Benchmark-Repeat: 15510 (do (macro wrap [f] `(fn [x] (~f x))) diff --git a/src/ast/compiler/analyzer.rs b/src/ast/compiler/analyzer.rs index ddcce82..fc9ef33 100644 --- a/src/ast/compiler/analyzer.rs +++ b/src/ast/compiler/analyzer.rs @@ -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, + global_purity: &'a HashMap, /// Stack of currently visiting lambdas to detect direct recursion. lambda_stack: Vec, /// Map of global index to its Lambda identity if known. - globals_to_lambdas: HashMap, + globals_to_lambdas: HashMap, /// Set of identities that were found to be recursive. recursive_identities: HashSet, } impl<'a> Analyzer<'a> { - pub fn analyze(node: &TypedNode, global_purity: &'a HashMap) -> AnalyzedNode { + pub fn analyze( + node: &TypedNode, + global_purity: &'a HashMap, + ) -> AnalyzedNode { let mut analyzer = Self { global_purity, lambda_stack: Vec::new(), diff --git a/src/ast/compiler/binder.rs b/src/ast/compiler/binder.rs index 386aee4..60f320a 100644 --- a/src/ast/compiler/binder.rs +++ b/src/ast/compiler/binder.rs @@ -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 { + fn define(&mut self, sym: &Symbol, identity: Identity) -> Result { 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>>, + globals: &Rc>>, ) -> Result { 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, // Globals mapping: Symbol -> (Index, DefinitionIdentity) - globals: Rc>>, + globals: Rc>>, // Map of Declaration Identity -> List of Lambda Identities that capture it capture_map: HashMap>, } impl Binder { - pub fn new(globals: Rc>>) -> Self { + pub fn new(globals: Rc>>) -> 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>>, + globals: Rc>>, node: &Node, ) -> Result<(BoundNode, HashMap>), String> { let mut binder = Self::new(globals); diff --git a/src/ast/compiler/bound_nodes.rs b/src/ast/compiler/bound_nodes.rs index 61bc0d2..50048e7 100644 --- a/src/ast/compiler/bound_nodes.rs +++ b/src/ast/compiler/bound_nodes.rs @@ -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)] diff --git a/src/ast/compiler/dumper.rs b/src/ast/compiler/dumper.rs index 8931f07..159c987 100644 --- a/src/ast/compiler/dumper.rs +++ b/src/ast/compiler/dumper.rs @@ -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 diff --git a/src/ast/compiler/lambda_collector.rs b/src/ast/compiler/lambda_collector.rs index 53cff95..80e5ef1 100644 --- a/src/ast/compiler/lambda_collector.rs +++ b/src/ast/compiler/lambda_collector.rs @@ -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>, + registry: &'a mut HashMap>, } impl<'a, T: Clone> LambdaCollector<'a, T> { /// Performs a full traversal of the AST and populates the provided registry. - pub fn collect(node: &BoundNode, registry: &'a mut HashMap>) { + pub fn collect(node: &BoundNode, registry: &'a mut HashMap>) { let mut collector = Self { registry }; collector.visit(node); } diff --git a/src/ast/compiler/macros.rs b/src/ast/compiler/macros.rs index 9a19259..77fe8d9 100644 --- a/src/ast/compiler/macros.rs +++ b/src/ast/compiler/macros.rs @@ -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)); diff --git a/src/ast/compiler/optimizer.rs b/src/ast/compiler/optimizer.rs index 79d7805..c890a03 100644 --- a/src/ast/compiler/optimizer.rs +++ b/src/ast/compiler/optimizer.rs @@ -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>>>, - pub global_purity: Option>>>, - pub lambda_registry: Option>>>, + pub global_purity: Option>>>, + pub lambda_registry: Option>>>, } struct PathTracker { inlining_depth: usize, - inlining_stack: HashSet, + inlining_stack: HashSet, identity_stack: HashSet, } @@ -75,12 +77,15 @@ impl Optimizer { self } - pub fn with_purity(mut self, purity: Rc>>) -> Self { + pub fn with_purity(mut self, purity: Rc>>) -> Self { self.global_purity = Some(purity); self } - pub fn with_registry(mut self, registry: Rc>>) -> Self { + pub fn with_registry( + mut self, + registry: Rc>>, + ) -> 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) { + fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet) { 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, ast_substitutions: HashMap, - slot_mapping: HashMap, + slot_mapping: HashMap, assigned: HashSet
, next_slot: u32, used: HashSet
, - captured_slots: HashSet, + captured_slots: HashSet, } 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]) -> 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 } diff --git a/src/ast/compiler/type_checker.rs b/src/ast/compiler/type_checker.rs index 5b20680..7d3ad96 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, 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, /// Access to global types for unified resolution - global_types: &'a std::cell::RefCell>, + global_types: &'a std::cell::RefCell>, /// The expected parameters of the current function (for 'again' validation) current_params_ty: Option, } @@ -21,7 +21,7 @@ impl<'a> TypeContext<'a> { fn new( slot_count: u32, upvalue_types: Vec, - global_types: &'a std::cell::RefCell>, + global_types: &'a std::cell::RefCell>, 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>>, + global_types: Rc>>, } impl TypeChecker { - pub fn new(global_types: Rc>>) -> Self { + pub fn new(global_types: Rc>>) -> Self { Self { global_types } } diff --git a/src/ast/environment.rs b/src/ast/environment.rs index 12d1f14..17a48d0 100644 --- a/src/ast/environment.rs +++ b/src/ast/environment.rs @@ -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>>, - pub global_types: Rc>>, - pub global_purity: Rc>>, + pub global_names: Rc>>, + pub global_types: Rc>>, + pub global_purity: Rc>>, pub global_values: Rc>>, pub prng: Rc>, - pub function_registry: Rc>>, - pub typed_function_registry: Rc>>, + pub function_registry: Rc>>, + pub typed_function_registry: Rc>>, pub monomorph_cache: Rc>, pub debug_mode: bool, pub optimization: bool, } struct EnvFunctionRegistry { - registry: Rc>>, - analyzed_registry: Rc>>, + registry: Rc>>, + analyzed_registry: Rc>>, } impl FunctionRegistry for EnvFunctionRegistry { @@ -57,8 +57,8 @@ impl FunctionRegistry for EnvFunctionRegistry { } struct RuntimeMacroEvaluator { - global_names: Rc>>, - global_types: Rc>>, + global_names: Rc>>, + global_types: Rc>>, global_values: Rc>>, } @@ -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); diff --git a/src/ast/vm.rs b/src/ast/vm.rs index c5f83ed..d6279ed 100644 --- a/src/ast/vm.rs +++ b/src/ast/vm.rs @@ -506,9 +506,9 @@ impl VM { fn capture_upvalue(&mut self, addr: Address) -> 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] { 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 { 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)) diff --git a/src/integration_test.rs b/src/integration_test.rs index ffec9ad..8418b1d 100644 --- a/src/integration_test.rs +++ b/src/integration_test.rs @@ -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" + ); } }