From 73425762c2c7b41ff33786b9a880faa60512fd42 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 18 Aug 2026 08:33:21 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Eliminate=20O(N)=20array=20?= =?UTF-8?q?cloning=20in=20loops=20and=20destructuring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit avoids expensive copying in `RuntimeValue::Tuple` destructuring and read-only accesses: - `Statement::For` now takes ownership of the tuple elements instead of calling `.clone()` when iterating. - `eval_index_access` signature was changed to take `&RuntimeValue` by reference. This eliminates deep clones of tuples, lists, and maps at various callsites, reducing O(N) overhead to O(1) reads for variable evaluations and chained member/index access. Co-authored-by: Tcode-Motion <188012755+Tcode-Motion@users.noreply.github.com> --- .jules/bolt.md | 4 ++++ runtime/interpreter/src/expressions.rs | 10 +++++----- runtime/interpreter/src/statements.rs | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index fd89ec5b..1697784f 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -1,3 +1,7 @@ ## 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-05-18 - Eliminating Unnecessary Tuple Clones in Interpreter +**Learning:** Techscript's `RuntimeValue::Tuple` stores an owned `Vec`. Functions like `eval_index_access` previously accepted `obj_val: RuntimeValue`, forcing callers to `.clone()` massive structures just to read an element. Also, `Statement::For` cloned the tuple elements during iteration even when `iter_val` was already owned. +**Action:** Always accept `&RuntimeValue` in read-only interpreter operations like index or member access to prevent deep vector clones. When destructuring an owned `RuntimeValue::Tuple`, match and take ownership of its inner vector directly instead of cloning it. 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 {