From 587aa10bf48865e0d1670a76537340b586e5c915 Mon Sep 17 00:00:00 2001 From: Tim Fennis Date: Thu, 4 Dec 2025 14:45:14 +0100 Subject: [PATCH 1/5] PERF? --- ndc_lib/src/interpreter/environment.rs | 47 +++++++++++++++---------- ndc_lib/src/interpreter/evaluate/mod.rs | 12 +++---- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/ndc_lib/src/interpreter/environment.rs b/ndc_lib/src/interpreter/environment.rs index ce451d1..611ae8a 100644 --- a/ndc_lib/src/interpreter/environment.rs +++ b/ndc_lib/src/interpreter/environment.rs @@ -20,7 +20,7 @@ pub struct RootEnvironment { pub struct Environment { root: Rc>, parent: Option, - values: Vec>>, + values: RefCell>, } impl fmt::Debug for Environment { @@ -29,7 +29,7 @@ impl fmt::Debug for Environment { f, "Environment[has_parent: {:?}, values.len(): {}]", self.parent.is_some(), - self.values.len(), + self.values.borrow().len(), ) } } @@ -62,7 +62,7 @@ impl Environment { let mut env = Self { root: Rc::new(RefCell::new(root)), parent: None, - values: Vec::new(), + values: Default::default(), }; crate::stdlib::register(&mut env); @@ -95,22 +95,23 @@ impl Environment { // This whole mess (special handling for functions) can be removed once we check // types during the resolver pass if let Value::Function(value_to_insert) = &value { - if let Some(existing_value) = self.values.get(slot) { - let existing_value = &*existing_value.borrow(); + if let Some(existing_value) = self.values.borrow().get(slot) { if let Value::Function(func) = existing_value { func.borrow_mut().merge(&mut value_to_insert.borrow_mut()) } } else { - self.values.insert(slot, Rc::new(RefCell::new(value))); + self.values.borrow_mut().insert(slot, value); } return; } - if self.values.len() > slot { - self.values[slot] = Rc::new(RefCell::new(value)) + let values = &mut *self.values.borrow_mut(); + if values.len() > slot { + values[slot] = value } else { - self.values.insert(slot, Rc::new(RefCell::new(value))) + debug_assert!(slot == values.len()); + values.insert(slot, value) // TODO: push should work here right } } @@ -153,24 +154,29 @@ impl Environment { gb.push(OverloadedFunction::from_multiple(vec![new_function])) } - fn get_slot(&self, depth: usize, slot: usize) -> Rc> { + fn get_copy_from_slot(&self, depth: usize, slot: usize) -> Value { if depth == 0 { - self.values[slot].clone() + self.values + .borrow() + .get(slot) + .expect("slot can't be empty") + .clone() } else { self.parent .clone() .expect("expected parent env did not exist") .borrow() - .get_slot(depth - 1, slot) + .get_copy_from_slot(depth - 1, slot) } } + #[must_use] - pub fn get(&self, var: ResolvedVar) -> Rc> { + pub fn get(&self, var: ResolvedVar) -> Value { match var { - ResolvedVar::Captured { depth, slot } => self.get_slot(depth, slot), - ResolvedVar::Global { slot } => Rc::new(RefCell::new(Value::function( - self.root.borrow().global_functions[slot].clone(), - ))), + ResolvedVar::Captured { depth, slot } => self.get_copy_from_slot(depth, slot), + ResolvedVar::Global { slot } => { + Value::function(self.root.borrow().global_functions[slot].clone()) + } } } @@ -179,7 +185,10 @@ impl Environment { pub fn take(&self, var: ResolvedVar) -> Option { match var { ResolvedVar::Captured { depth: 0, slot } => Some(std::mem::replace( - &mut *self.values[slot].borrow_mut(), + self.values + .borrow_mut() + .get_mut(slot) + .expect("slot can't be empty"), Value::unit(), )), ResolvedVar::Captured { depth, slot } => self @@ -200,7 +209,7 @@ impl Environment { Rc::new(RefCell::new(Self { parent: Some(Rc::clone(parent)), root: root_ref, - values: Vec::new(), + values: Default::default(), })) } } diff --git a/ndc_lib/src/interpreter/evaluate/mod.rs b/ndc_lib/src/interpreter/evaluate/mod.rs index 7ecc12c..88e7d81 100644 --- a/ndc_lib/src/interpreter/evaluate/mod.rs +++ b/ndc_lib/src/interpreter/evaluate/mod.rs @@ -40,8 +40,6 @@ pub(crate) fn evaluate_expression( environment .borrow() .get(resolved.expect("identifier was not resolved before execution")) - .borrow() - .clone() } Expression::VariableDeclaration { l_value, value } => { let value = evaluate_expression(value, environment)?; @@ -106,15 +104,14 @@ pub(crate) fn evaluate_expression( while let Some((resolved_op, modified_in_place)) = operations_to_try.next() { let operation = environment.borrow().get(resolved_op); - let operation_val = &*operation.borrow(); - let Value::Function(func) = operation_val else { + let Value::Function(func) = operation else { unreachable!( "the resolver pass should have guaranteed that the operation points to a function" ); }; - let result = match call_function(func, &mut arguments, environment, span) { + let result = match call_function(&func, &mut arguments, environment, span) { Err(FunctionCarrier::FunctionNotFound) if operations_to_try.peek().is_none() => { @@ -162,8 +159,7 @@ pub(crate) fn evaluate_expression( while let Some((resolved_operation, modified_in_place)) = operations_to_try.next() { - let operation = environment.borrow().get(resolved_operation); - let operation_val = &*operation.borrow(); + let operation_val = environment.borrow().get(resolved_operation); let Value::Function(func) = operation_val else { unreachable!( @@ -172,7 +168,7 @@ pub(crate) fn evaluate_expression( }; let result = match call_function( - func, + &func, &mut [value_at_index.clone(), right_value.clone()], environment, span, From 4aaf04e84ab1ce547096c26f9fb0168d16c3c335 Mon Sep 17 00:00:00 2001 From: Tim Fennis Date: Thu, 4 Dec 2025 15:02:03 +0100 Subject: [PATCH 2/5] clean --- ndc_lib/src/stdlib/list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndc_lib/src/stdlib/list.rs b/ndc_lib/src/stdlib/list.rs index a922455..0d2b289 100644 --- a/ndc_lib/src/stdlib/list.rs +++ b/ndc_lib/src/stdlib/list.rs @@ -85,7 +85,7 @@ mod inner { #[function(name = "++")] pub fn list_concat(left: &mut ListRepr, right: &mut ListRepr) -> Value { if Rc::strong_count(left) == 1 { - left.borrow_mut().extend_from_slice(&*right.borrow()); + left.borrow_mut().extend_from_slice(&right.borrow()); Value::Sequence(Sequence::List(left.clone())) } else { From 2bef9218977c299f2f6cbed7aacca65dc742d72e Mon Sep 17 00:00:00 2001 From: Tim Fennis Date: Thu, 4 Dec 2025 15:24:43 +0100 Subject: [PATCH 3/5] Inline EnvironmentRef type for clarity --- ndc_lib/src/interpreter/environment.rs | 6 ++---- ndc_lib/src/interpreter/evaluate/index.rs | 9 +++++---- ndc_lib/src/interpreter/evaluate/mod.rs | 14 +++++++------- ndc_lib/src/interpreter/function.rs | 20 ++++++++++++-------- ndc_lib/src/interpreter/mod.rs | 6 +++--- ndc_macros/src/function.rs | 2 +- 6 files changed, 30 insertions(+), 27 deletions(-) diff --git a/ndc_lib/src/interpreter/environment.rs b/ndc_lib/src/interpreter/environment.rs index 611ae8a..cd97e72 100644 --- a/ndc_lib/src/interpreter/environment.rs +++ b/ndc_lib/src/interpreter/environment.rs @@ -9,8 +9,6 @@ use std::fmt::Formatter; use std::io::{Stdout, Write, stdout}; use std::rc::Rc; -pub type EnvironmentRef = Rc>; - pub struct RootEnvironment { pub output: Box, // These are global values @@ -19,7 +17,7 @@ pub struct RootEnvironment { pub struct Environment { root: Rc>, - parent: Option, + parent: Option>>, values: RefCell>, } @@ -204,7 +202,7 @@ impl Environment { } } - pub fn new_scope(parent: &EnvironmentRef) -> EnvironmentRef { + pub fn new_scope(parent: &Rc>) -> Rc> { let root_ref = Rc::clone(&parent.borrow().root); Rc::new(RefCell::new(Self { parent: Some(Rc::clone(parent)), diff --git a/ndc_lib/src/interpreter/evaluate/index.rs b/ndc_lib/src/interpreter/evaluate/index.rs index b4b5544..b25f96d 100644 --- a/ndc_lib/src/interpreter/evaluate/index.rs +++ b/ndc_lib/src/interpreter/evaluate/index.rs @@ -10,16 +10,17 @@ //! +----------------+-----+----+----+----+----+----+----+----+----+----+ use super::{EvaluationError, IntoEvaluationResult, evaluate_expression}; +use crate::interpreter::environment::Environment; use crate::{ ast::{Expression, ExpressionLocation}, - interpreter::{ - environment::EnvironmentRef, function::FunctionCarrier, sequence::Sequence, value::Value, - }, + interpreter::{function::FunctionCarrier, sequence::Sequence, value::Value}, lexer::Span, }; use itertools::Itertools; +use std::cell::RefCell; use std::cmp::min; use std::ops::IndexMut; +use std::rc::Rc; #[derive(Clone)] pub enum EvaluatedIndex { @@ -63,7 +64,7 @@ impl EvaluatedIndex { pub(crate) fn evaluate_as_index( expression_location: &ExpressionLocation, - environment: &mut EnvironmentRef, + environment: &mut Rc>, ) -> Result { let (range_start, range_end, inclusive) = match expression_location.expression { Expression::RangeExclusive { diff --git a/ndc_lib/src/interpreter/evaluate/mod.rs b/ndc_lib/src/interpreter/evaluate/mod.rs index 88e7d81..1c26358 100644 --- a/ndc_lib/src/interpreter/evaluate/mod.rs +++ b/ndc_lib/src/interpreter/evaluate/mod.rs @@ -1,6 +1,6 @@ use crate::ast::{Expression, ExpressionLocation, ForBody, ForIteration, LogicalOperator, Lvalue}; use crate::hash_map::HashMap; -use crate::interpreter::environment::{Environment, EnvironmentRef}; +use crate::interpreter::environment::Environment; use crate::interpreter::function::{Function, FunctionBody, FunctionCarrier, OverloadedFunction}; use crate::interpreter::int::Int; use crate::interpreter::iterator::mut_value_to_iterator; @@ -22,7 +22,7 @@ mod index; #[allow(clippy::too_many_lines)] pub(crate) fn evaluate_expression( expression_location: &ExpressionLocation, - environment: &mut EnvironmentRef, + environment: &mut Rc>, ) -> EvaluationResult { let span = expression_location.span; let literal: Value = match &expression_location.expression { @@ -651,7 +651,7 @@ pub(crate) fn evaluate_expression( fn produce_default_value( default: &Value, - environment: &EnvironmentRef, + environment: &Rc>, span: Span, ) -> EvaluationResult { match default { @@ -674,7 +674,7 @@ fn produce_default_value( fn declare_or_assign_variable( l_value: &Lvalue, value: Value, - environment: &mut EnvironmentRef, + environment: &mut Rc>, span: Span, ) -> EvaluationResult { match l_value { @@ -866,7 +866,7 @@ where fn call_function( function: &Rc>, evaluated_args: &mut [Value], - environment: &EnvironmentRef, + environment: &Rc>, span: Span, ) -> EvaluationResult { let result = function.borrow().call(evaluated_args, environment); @@ -887,7 +887,7 @@ fn call_function( fn execute_body( body: &ForBody, - environment: &mut EnvironmentRef, + environment: &mut Rc>, result: &mut Vec, ) -> EvaluationResult { match body { @@ -920,7 +920,7 @@ fn execute_for_iterations( iterations: &[ForIteration], body: &ForBody, out_values: &mut Vec, - environment: &mut EnvironmentRef, + environment: &mut Rc>, span: Span, ) -> Result { let Some((cur, tail)) = iterations.split_first() else { diff --git a/ndc_lib/src/interpreter/function.rs b/ndc_lib/src/interpreter/function.rs index e63e16b..578661f 100644 --- a/ndc_lib/src/interpreter/function.rs +++ b/ndc_lib/src/interpreter/function.rs @@ -1,6 +1,6 @@ use crate::ast::{ExpressionLocation, ResolvedVar}; use crate::hash_map::{DefaultHasher, HashMap}; -use crate::interpreter::environment::{Environment, EnvironmentRef}; +use crate::interpreter::environment::Environment; use crate::interpreter::evaluate::{ ErrorConverter, EvaluationError, EvaluationResult, evaluate_expression, }; @@ -19,7 +19,7 @@ use std::rc::Rc; /// easy to have an executable function as a method signature in the standard library pub struct Callable<'a> { pub function: Rc>, - pub environment: &'a EnvironmentRef, + pub environment: &'a Rc>, } impl Callable<'_> { @@ -79,7 +79,7 @@ pub enum FunctionBody { Closure { parameter_names: Vec, body: ExpressionLocation, - environment: EnvironmentRef, + environment: Rc>, }, NumericUnaryOp { body: fn(number: Number) -> Number, @@ -89,7 +89,7 @@ pub enum FunctionBody { }, GenericFunction { type_signature: TypeSignature, - function: fn(&mut [Value], &EnvironmentRef) -> EvaluationResult, + function: fn(&mut [Value], &Rc>) -> EvaluationResult, }, Memoized { cache: RefCell>, @@ -111,7 +111,7 @@ impl FunctionBody { } pub fn generic( type_signature: TypeSignature, - function: fn(&mut [Value], &EnvironmentRef) -> EvaluationResult, + function: fn(&mut [Value], &Rc>) -> EvaluationResult, ) -> Self { Self::GenericFunction { type_signature, @@ -140,7 +140,7 @@ impl FunctionBody { } } - pub fn call(&self, args: &mut [Value], env: &EnvironmentRef) -> EvaluationResult { + pub fn call(&self, args: &mut [Value], env: &Rc>) -> EvaluationResult { match self { Self::Closure { body, environment, .. @@ -327,7 +327,7 @@ impl OverloadedFunction { } } - pub fn call(&self, args: &mut [Value], env: &EnvironmentRef) -> EvaluationResult { + pub fn call(&self, args: &mut [Value], env: &Rc>) -> EvaluationResult { let types: Vec = args.iter().map(ValueType::from).collect(); let mut best_function_match = None; @@ -356,7 +356,11 @@ impl OverloadedFunction { }) } - fn call_vectorized(&self, args: &mut [Value], env: &EnvironmentRef) -> EvaluationResult { + fn call_vectorized( + &self, + args: &mut [Value], + env: &Rc>, + ) -> EvaluationResult { let [left, right] = args else { // Vectorized application only works in cases where there are two tuple arguments return Err(FunctionCarrier::FunctionNotFound); diff --git a/ndc_lib/src/interpreter/mod.rs b/ndc_lib/src/interpreter/mod.rs index c22a2e6..19d0451 100644 --- a/ndc_lib/src/interpreter/mod.rs +++ b/ndc_lib/src/interpreter/mod.rs @@ -2,7 +2,7 @@ use std::cell::RefCell; use std::rc::Rc; use crate::ast::ExpressionLocation; -use crate::interpreter::environment::{Environment, EnvironmentRef, InterpreterOutput}; +use crate::interpreter::environment::{Environment, InterpreterOutput}; use crate::interpreter::evaluate::{EvaluationError, evaluate_expression}; use crate::interpreter::function::FunctionCarrier; use crate::interpreter::resolve::{LexicalData, resolve_pass}; @@ -22,7 +22,7 @@ pub mod sequence; pub mod value; pub struct Interpreter { - environment: EnvironmentRef, + environment: Rc>, lexical_data: LexicalData, } @@ -43,7 +43,7 @@ impl Interpreter { } #[must_use] - pub fn environment(self) -> EnvironmentRef { + pub fn environment(self) -> Rc> { self.environment } diff --git a/ndc_macros/src/function.rs b/ndc_macros/src/function.rs index 7f3860e..4887c03 100644 --- a/ndc_macros/src/function.rs +++ b/ndc_macros/src/function.rs @@ -181,7 +181,7 @@ fn wrap_single( let function_declaration = quote! { pub fn #identifier ( values: &mut [crate::interpreter::value::Value], - environment: &crate::interpreter::environment::EnvironmentRef + environment: &std::rc::Rc> ) -> crate::interpreter::evaluate::EvaluationResult { // Define the inner function that has the rust type signature #[inline] From c6440b312482fd85cbbd3762d5619d9334d4b0af Mon Sep 17 00:00:00 2001 From: Tim Fennis Date: Thu, 4 Dec 2025 16:25:34 +0100 Subject: [PATCH 4/5] not really a refactor but easier to read code --- ndc_lib/src/interpreter/environment.rs | 8 ++++---- ndc_lib/src/interpreter/evaluate/index.rs | 2 +- ndc_lib/src/interpreter/evaluate/mod.rs | 20 ++++++++++---------- ndc_lib/src/interpreter/function.rs | 6 +++--- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ndc_lib/src/interpreter/environment.rs b/ndc_lib/src/interpreter/environment.rs index cd97e72..8570edf 100644 --- a/ndc_lib/src/interpreter/environment.rs +++ b/ndc_lib/src/interpreter/environment.rs @@ -202,13 +202,13 @@ impl Environment { } } - pub fn new_scope(parent: &Rc>) -> Rc> { + pub fn new_scope(parent: &Rc>) -> Self { let root_ref = Rc::clone(&parent.borrow().root); - Rc::new(RefCell::new(Self { - parent: Some(Rc::clone(parent)), + Self { + parent: Some(parent.clone()), root: root_ref, values: Default::default(), - })) + } } } diff --git a/ndc_lib/src/interpreter/evaluate/index.rs b/ndc_lib/src/interpreter/evaluate/index.rs index b25f96d..690dcd6 100644 --- a/ndc_lib/src/interpreter/evaluate/index.rs +++ b/ndc_lib/src/interpreter/evaluate/index.rs @@ -64,7 +64,7 @@ impl EvaluatedIndex { pub(crate) fn evaluate_as_index( expression_location: &ExpressionLocation, - environment: &mut Rc>, + environment: &Rc>, ) -> Result { let (range_start, range_end, inclusive) = match expression_location.expression { Expression::RangeExclusive { diff --git a/ndc_lib/src/interpreter/evaluate/mod.rs b/ndc_lib/src/interpreter/evaluate/mod.rs index 1c26358..33fed2b 100644 --- a/ndc_lib/src/interpreter/evaluate/mod.rs +++ b/ndc_lib/src/interpreter/evaluate/mod.rs @@ -22,7 +22,7 @@ mod index; #[allow(clippy::too_many_lines)] pub(crate) fn evaluate_expression( expression_location: &ExpressionLocation, - environment: &mut Rc>, + environment: &Rc>, ) -> EvaluationResult { let span = expression_location.span; let literal: Value = match &expression_location.expression { @@ -214,11 +214,11 @@ pub(crate) fn evaluate_expression( } } Expression::Block { statements } => { - let mut local_scope = Environment::new_scope(environment); + let local_scope = Rc::new(RefCell::new(Environment::new_scope(environment))); let mut value = Value::unit(); for stm in statements { - value = evaluate_expression(stm, &mut local_scope)?; + value = evaluate_expression(stm, &local_scope)?; } drop(local_scope); @@ -674,7 +674,7 @@ fn produce_default_value( fn declare_or_assign_variable( l_value: &Lvalue, value: Value, - environment: &mut Rc>, + environment: &Rc>, span: Span, ) -> EvaluationResult { match l_value { @@ -887,7 +887,7 @@ fn call_function( fn execute_body( body: &ForBody, - environment: &mut Rc>, + environment: &Rc>, result: &mut Vec, ) -> EvaluationResult { match body { @@ -920,7 +920,7 @@ fn execute_for_iterations( iterations: &[ForIteration], body: &ForBody, out_values: &mut Vec, - environment: &mut Rc>, + environment: &Rc>, span: Span, ) -> Result { let Some((cur, tail)) = iterations.split_first() else { @@ -940,17 +940,17 @@ fn execute_for_iterations( // ``` // With the current implementation with a new scope declared for every iteration this produces 10 functions // each with their own scope and their own version of `i`, this might potentially be a bit slower though - let mut scope = Environment::new_scope(environment); - declare_or_assign_variable(l_value, r_value, &mut scope, span)?; + let scope = Rc::new(RefCell::new(Environment::new_scope(environment))); + declare_or_assign_variable(l_value, r_value, &scope, span)?; if tail.is_empty() { - match execute_body(body, &mut scope, out_values) { + match execute_body(body, &scope, out_values) { Err(FunctionCarrier::Continue) => {} Err(error) => return Err(error), Ok(_value) => {} } } else { - execute_for_iterations(tail, body, out_values, &mut scope, span)?; + execute_for_iterations(tail, body, out_values, &scope, span)?; } } } diff --git a/ndc_lib/src/interpreter/function.rs b/ndc_lib/src/interpreter/function.rs index 578661f..37f5161 100644 --- a/ndc_lib/src/interpreter/function.rs +++ b/ndc_lib/src/interpreter/function.rs @@ -148,12 +148,11 @@ impl FunctionBody { let mut local_scope = Environment::new_scope(environment); { - let mut env = local_scope.borrow_mut(); for (position, value) in args.iter().enumerate() { // NOTE: stores a copy of the value in the environment (which is fine?) // NOTE: we just assume here that the arguments are slotted in order starting at 0 // because why not? Is this a call convention? - env.set( + local_scope.set( ResolvedVar::Captured { depth: 0, slot: position, @@ -163,7 +162,8 @@ impl FunctionBody { } } - match evaluate_expression(body, &mut local_scope) { + let local_scope = Rc::new(RefCell::new(local_scope)); + match evaluate_expression(body, &local_scope) { Err(FunctionCarrier::Return(v)) => Ok(v), r => r, } From 8bc958ccde171d5ffcf24fb31f3262ff88d5412e Mon Sep 17 00:00:00 2001 From: Tim Fennis Date: Thu, 4 Dec 2025 16:37:08 +0100 Subject: [PATCH 5/5] Make more simple --- ndc_lib/src/interpreter/environment.rs | 34 +++++++++++--------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/ndc_lib/src/interpreter/environment.rs b/ndc_lib/src/interpreter/environment.rs index 8570edf..4455e4b 100644 --- a/ndc_lib/src/interpreter/environment.rs +++ b/ndc_lib/src/interpreter/environment.rs @@ -18,7 +18,7 @@ pub struct RootEnvironment { pub struct Environment { root: Rc>, parent: Option>>, - values: RefCell>, + values: Vec, } impl fmt::Debug for Environment { @@ -27,7 +27,7 @@ impl fmt::Debug for Environment { f, "Environment[has_parent: {:?}, values.len(): {}]", self.parent.is_some(), - self.values.borrow().len(), + self.values.len(), ) } } @@ -93,23 +93,24 @@ impl Environment { // This whole mess (special handling for functions) can be removed once we check // types during the resolver pass if let Value::Function(value_to_insert) = &value { - if let Some(existing_value) = self.values.borrow().get(slot) { + if let Some(existing_value) = self.values.get(slot) { if let Value::Function(func) = existing_value { func.borrow_mut().merge(&mut value_to_insert.borrow_mut()) } } else { - self.values.borrow_mut().insert(slot, value); + self.values.push(value); + // self.values.insert(slot, value); } return; } - let values = &mut *self.values.borrow_mut(); - if values.len() > slot { - values[slot] = value + if self.values.len() > slot { + self.values[slot] = value } else { - debug_assert!(slot == values.len()); - values.insert(slot, value) // TODO: push should work here right + debug_assert!(slot == self.values.len()); + // self.values.insert(slot, value) // TODO: push should work here right + self.values.push(value); } } @@ -154,11 +155,7 @@ impl Environment { fn get_copy_from_slot(&self, depth: usize, slot: usize) -> Value { if depth == 0 { - self.values - .borrow() - .get(slot) - .expect("slot can't be empty") - .clone() + self.values[slot].clone() } else { self.parent .clone() @@ -180,20 +177,17 @@ impl Environment { /// Takes the named variable from memory and leaves `Value::unit()` in its place #[must_use] - pub fn take(&self, var: ResolvedVar) -> Option { + pub fn take(&mut self, var: ResolvedVar) -> Option { match var { ResolvedVar::Captured { depth: 0, slot } => Some(std::mem::replace( - self.values - .borrow_mut() - .get_mut(slot) - .expect("slot can't be empty"), + self.values.get_mut(slot).expect("slot can't be empty"), Value::unit(), )), ResolvedVar::Captured { depth, slot } => self .parent .clone() .expect("expected parent env did not exist") - .borrow() + .borrow_mut() .take(ResolvedVar::Captured { depth: depth - 1, slot,