diff --git a/.jules/bolt.md b/.jules/bolt.md index e0b49ebc..a1e28467 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -1,6 +1,4 @@ ## 2024-01-20 - Lifetime Refactoring in Parser to Eliminate Token Cloning **Learning:** In the Rust parser (`compiler/parser/src/parser.rs`), methods like `advance()` and `previous()` originally returned a reference tied to `&mut self`. Because `Token` was still borrowing `self` mutably, the parser couldn't call methods like `self.parse_prefix` (which requires another `&mut self` borrow) without first calling `.clone()` on the token to drop the initial borrow. **Action:** By explicitly defining the return lifetime as `&'a Token` (tied to the lifetime of the underlying token slice `&'a [Token]`, rather than the `Parser` instance), the mutable borrow of `self` ends immediately. This elegantly satisfies the borrow checker while removing the overhead of cloning tokens throughout `expressions.rs` and `statements.rs`. Look for similar lifetime constraints elsewhere in the compiler that force unnecessary copies. -## 2024-08-18 - Eliminating instruction cloning in VM executor loop -**Learning:** In the bytecode executor (`runtime/vm/src/executor.rs`), fetching instructions inside the tight `execute_loop` originally involved `inst.clone()`. Since `Instruction` contains a `Vec`, cloning it in the inner execution loop causes massive allocation overhead on every instruction dispatch, severely degrading performance. By maintaining references to instructions from the chunk rather than cloning, we bypassed significant garbage collection and allocation costs. -**Action:** Always scrutinize `.clone()` inside hot execution loops, especially for structs that own heap-allocated fields (like `Vec` or `String`). + diff --git a/runtime/interpreter/src/expressions.rs b/runtime/interpreter/src/expressions.rs index b8305f67..6611d3e0 100644 --- a/runtime/interpreter/src/expressions.rs +++ b/runtime/interpreter/src/expressions.rs @@ -214,7 +214,7 @@ impl AstVisitor for Interpreter { Expression::Index(idx) => { let obj_val = self.visit_expression(&idx.object)?; let idx_val = self.visit_expression(&idx.index)?; - self.eval_index_access(obj_val, idx_val, idx.span) + self.eval_index_access(&obj_val, &idx_val, idx.span) } Expression::New(new_expr) => { // Instantiate model constructor @@ -332,8 +332,8 @@ impl Interpreter { fn eval_index_access( &mut self, - obj_val: RuntimeValue, - idx_val: RuntimeValue, + obj_val: &RuntimeValue, + idx_val: &RuntimeValue, span: techscript_common::Span, ) -> EvalResult { match obj_val { @@ -407,7 +407,7 @@ impl Interpreter { let final_val = if op == "=" { value_val } else { - let current = self.eval_index_access(obj_val.clone(), idx_val.clone(), span)?; + let current = self.eval_index_access(&obj_val, &idx_val, span)?; let basic_op = &op[0..op.len() - 1]; eval_binary(basic_op, current, value_val)? }; @@ -524,7 +524,7 @@ impl Interpreter { Expression::Index(idx) => { // e.g. a ?. [idx] let idx_val = self.visit_expression(&idx.index)?; - self.eval_index_access(left_val, idx_val, idx.span) + self.eval_index_access(&left_val, &idx_val, idx.span) } Expression::Call(call) => { // Method call: e.g. a ?. greet() or a ?. b.greet() diff --git a/runtime/interpreter/src/statements.rs b/runtime/interpreter/src/statements.rs index e372eb00..f6e80545 100644 --- a/runtime/interpreter/src/statements.rs +++ b/runtime/interpreter/src/statements.rs @@ -58,7 +58,7 @@ impl Interpreter { let iter_val = self.visit_expression(&for_stmt.iterable)?; let items = match iter_val { RuntimeValue::List { items, .. } => items.borrow().clone(), - RuntimeValue::Tuple(elements) => elements.clone(), + RuntimeValue::Tuple(elements) => elements, other => { return Err(RuntimeError::new( RuntimeErrorKind::TypeMismatch {