From ddee395f524fd6802f2e8b30fb9b589d3cb8a176 Mon Sep 17 00:00:00 2001 From: MaYu Date: Thu, 25 Jun 2026 23:41:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=A0=E9=80=92=E5=BC=8Fupvalue=20?= =?UTF-8?q?=E2=80=94=203=E5=B1=82=E5=B5=8C=E5=A5=97=E9=97=AD=E5=8C=85?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - upvalues改为Rc>>支持跨编译器共享 - resolve_upvalue: 通过grandparent_upvalues级联创建upvalue链 - compile_nested_function: 传递完整enclosing_locals链 + grandparent_upvalues - Upvalue添加name字段用于传递式查找 测试: 327/327 通过 | Benchmark: 18/18 全部通过 --- aster-core/src/vm/compiler.rs | 126 +++++++++++++++++++++++++++++----- 1 file changed, 107 insertions(+), 19 deletions(-) diff --git a/aster-core/src/vm/compiler.rs b/aster-core/src/vm/compiler.rs index 99d77b7..cbf26e4 100644 --- a/aster-core/src/vm/compiler.rs +++ b/aster-core/src/vm/compiler.rs @@ -97,11 +97,16 @@ struct LoopContext { pub struct Compiler { function: FunctionProto, locals: Vec, - upvalues: Vec, - /// Direct parent's locals (for upvalue resolution) + /// Upvalues for this function (shared with child for transitive resolution) + upvalues: Rc>>, + /// ALL enclosing locals from entire chain (merged, direct parent first) enclosing_locals: Option>, - /// Direct parent's upvalues (for transitive upvalue resolution) - enclosing_upvalues: Option>, + /// Direct parent's own locals count (to distinguish parent locals from deeper ones) + parent_locals_count: usize, + /// Direct parent's upvalues (shared) + enclosing_upvalues: Option>>>, + /// Grandparent's upvalues (shared, for 3-level transitive capture) + grandparent_upvalues: Option>>>, scope_depth: u8, loop_stack: Vec, } @@ -111,9 +116,11 @@ impl Compiler { Self { function: FunctionProto::new(name), locals: Vec::new(), - upvalues: Vec::new(), + upvalues: Rc::new(RefCell::new(Vec::new())), enclosing_locals: None, + parent_locals_count: 0, enclosing_upvalues: None, + grandparent_upvalues: None, scope_depth: 0, loop_stack: Vec::new(), } @@ -685,8 +692,15 @@ impl Compiler { /// Compile a nested function (lambda or function declaration) and return its proto. fn compile_nested_function(&mut self, name: Option, params: &[String], body: &[Stmt]) -> Result { let mut child = Compiler::new(name); - child.enclosing_locals = Some(self.locals.clone()); - child.enclosing_upvalues = Some(self.upvalues.clone()); + // Build merged enclosing locals: our locals + our enclosing chain + let mut all_locals = self.locals.clone(); + if let Some(ref enc) = self.enclosing_locals { + all_locals.extend(enc.iter().cloned()); + } + child.enclosing_locals = Some(all_locals); + child.parent_locals_count = self.locals.len(); + child.enclosing_upvalues = Some(Rc::clone(&self.upvalues)); + child.grandparent_upvalues = self.enclosing_upvalues.clone(); // Add params as locals without StoreLocal — they're already on stack from Call for param in params { @@ -709,7 +723,7 @@ impl Compiler { child.emit_op(OpCode::Return); // Mark captured locals in the parent compiler - for uv in &child.upvalues { + for uv in child.upvalues.borrow().iter() { if uv.is_local { if let Some(local) = self.locals.get_mut(uv.index as usize) { local.is_captured = true; @@ -717,8 +731,10 @@ impl Compiler { } } - child.function.upvalue_count = child.upvalues.len() as u8; - child.function.upvalues = child.upvalues.iter().map(|uv| (uv.is_local, uv.index)).collect(); + let child_upvalues = child.upvalues.borrow(); + child.function.upvalue_count = child_upvalues.len() as u8; + child.function.upvalues = child_upvalues.iter().map(|uv| (uv.is_local, uv.index)).collect(); + drop(child_upvalues); Ok(child.function) } @@ -771,27 +787,99 @@ impl Compiler { /// Returns the upvalue index in this function's upvalues list. fn resolve_upvalue(&mut self, name: &str) -> Option { // Check if already captured - for (j, uv) in self.upvalues.iter().enumerate() { + for (j, uv) in self.upvalues.borrow().iter().enumerate() { if uv.name == name { return Some(j as u8); } } - // Check enclosing function's locals + // Check all enclosing locals (merged chain: parent, grandparent, ...) if let Some(ref enclosing_locals) = self.enclosing_locals { for (i, local) in enclosing_locals.iter().enumerate().rev() { if local.name == name && local.depth > 0 { - let idx = self.upvalues.len() as u8; - self.upvalues.push(Upvalue { index: i as u8, is_local: true, name: name.to_string() }); - return Some(idx); + if i < self.parent_locals_count { + // Found in direct parent's locals → capture as upvalue + let idx = self.upvalues.borrow().len() as u8; + self.upvalues.borrow_mut().push(Upvalue { + index: i as u8, is_local: true, name: name.to_string() + }); + return Some(idx); + } else { + // Found deeper than parent. Need to create upvalue chain. + if let Some(ref enc_upvalues) = self.enclosing_upvalues { + // Check if parent already has this upvalue + let parent_uv_idx = { + let enc = enc_upvalues.borrow(); + enc.iter().position(|uv| uv.name == name).map(|p| p as u8) + }; + let parent_idx = match parent_uv_idx { + Some(idx) => idx, + None => { + // Add upvalue to parent's list. + // The parent sees this variable at a certain index in its own + // enclosing_locals. That index is (i - self.parent_locals_count) + // in the merged list, which corresponds to the same variable + // in the parent's enclosing_locals. + let enc_idx = enc_upvalues.borrow().len() as u8; + + // If we have grandparent_upvalues, the parent's upvalue + // should be transitive (is_local=false), and we need to + // ensure grandparent has it too. + if let Some(ref gp_upvalues) = self.grandparent_upvalues { + // Ensure grandparent has the upvalue first + let gp_idx = { + let gp = gp_upvalues.borrow(); + gp.iter().position(|uv| uv.name == name).map(|p| p as u8) + }; + let gp_idx = match gp_idx { + Some(idx) => idx, + None => { + let idx = gp_upvalues.borrow().len() as u8; + // Grandparent captures this as a local + // (it's directly in the grandparent's enclosing scope) + gp_upvalues.borrow_mut().push(Upvalue { + index: (i - self.parent_locals_count) as u8, + is_local: true, + name: name.to_string() + }); + idx + } + }; + // Parent's upvalue is transitive through grandparent + enc_upvalues.borrow_mut().push(Upvalue { + index: gp_idx, + is_local: false, + name: name.to_string() + }); + } else { + // No grandparent — parent captures directly as local + enc_upvalues.borrow_mut().push(Upvalue { + index: (i - self.parent_locals_count) as u8, + is_local: true, + name: name.to_string() + }); + } + enc_idx + } + }; + // Add transitive upvalue in self pointing to parent's + let idx = self.upvalues.borrow().len() as u8; + self.upvalues.borrow_mut().push(Upvalue { + index: parent_idx, is_local: false, name: name.to_string() + }); + return Some(idx); + } + } } } } - // Check enclosing function's upvalues (transitive) + // Check parent's upvalues (transitive closure over 2 levels) if let Some(ref enclosing_upvalues) = self.enclosing_upvalues { - for uv in enclosing_upvalues.iter().rev() { + for uv in enclosing_upvalues.borrow().iter().rev() { if uv.name == name { - let idx = self.upvalues.len() as u8; - self.upvalues.push(Upvalue { index: uv.index, is_local: false, name: name.to_string() }); + let idx = self.upvalues.borrow().len() as u8; + self.upvalues.borrow_mut().push(Upvalue { + index: uv.index, is_local: false, name: name.to_string() + }); return Some(idx); } }