diff --git a/src/boxes/array/mod.rs b/src/boxes/array/mod.rs index 63842a9b..4065f09f 100644 --- a/src/boxes/array/mod.rs +++ b/src/boxes/array/mod.rs @@ -4,11 +4,11 @@ use crate::box_trait::{NyashBox, StringBox, BoolBox, IntegerBox, BoxCore, BoxBase}; use std::any::Any; -use std::sync::RwLock; +use std::sync::{Arc, RwLock}; use std::fmt::Display; pub struct ArrayBox { - pub items: RwLock>>, + pub items: Arc>>>, // Arc追加 base: BoxBase, } @@ -16,7 +16,7 @@ impl ArrayBox { /// 新しいArrayBoxを作成 pub fn new() -> Self { ArrayBox { - items: RwLock::new(Vec::new()), + items: Arc::new(RwLock::new(Vec::new())), // Arc::new追加 base: BoxBase::new(), } } @@ -24,7 +24,7 @@ impl ArrayBox { /// 要素を持つArrayBoxを作成 pub fn new_with_elements(elements: Vec>) -> Self { ArrayBox { - items: RwLock::new(elements), + items: Arc::new(RwLock::new(elements)), // Arc::new追加 base: BoxBase::new(), } } @@ -238,11 +238,16 @@ impl ArrayBox { // Clone implementation for ArrayBox (needed since RwLock doesn't auto-derive Clone) impl Clone for ArrayBox { fn clone(&self) -> Self { - let items = self.items.read().unwrap(); - let cloned_items: Vec> = items.iter() - .map(|item| item.clone_box()) + // ディープコピー(独立インスタンス) + let items_guard = self.items.read().unwrap(); + let cloned_items: Vec> = items_guard.iter() + .map(|item| item.clone_box()) // 要素もディープコピー .collect(); - ArrayBox::new_with_elements(cloned_items) + + ArrayBox { + items: Arc::new(RwLock::new(cloned_items)), // 新しいArc + base: BoxBase::new(), + } } } @@ -283,9 +288,13 @@ impl NyashBox for ArrayBox { Box::new(self.clone()) } - /// 仮実装: clone_boxと同じ(後で修正) + /// 🎯 状態共有の核心実装 fn share_box(&self) -> Box { - self.clone_box() + let new_instance = ArrayBox { + items: Arc::clone(&self.items), // Arcクローンで状態共有 + base: BoxBase::new(), // 新しいID + }; + Box::new(new_instance) } fn to_string_box(&self) -> StringBox { diff --git a/src/interpreter/expressions.rs b/src/interpreter/expressions.rs index f1cb46be..fb0e87db 100644 --- a/src/interpreter/expressions.rs +++ b/src/interpreter/expressions.rs @@ -105,7 +105,7 @@ impl NyashInterpreter { name: name.clone(), span: expression.span() })?; - Ok((*shared_var).clone_box()) // Convert for external interface + Ok((*shared_var).share_box()) // 🎯 State-sharing instead of cloning } ASTNode::BinaryOp { operator, left, right, .. } => { diff --git a/test_phase_b_validation.nyash b/test_phase_b_validation.nyash new file mode 100644 index 00000000..60d61e47 --- /dev/null +++ b/test_phase_b_validation.nyash @@ -0,0 +1,24 @@ +// 🚨 ArrayBox状態保持テスト - コアとなる問題を検証 +static box Main { + init { result1, result2, result3 } + main() { + // テスト1: 基本的な状態保持 + local arr1 + arr1 = new ArrayBox() + arr1.push("hello") + me.result1 = arr1.length() // 期待値: 1 + + // テスト2: 複数操作の状態保持 + local arr2 + arr2 = new ArrayBox() + arr2.push("first") + arr2.push("second") + me.result2 = arr2.length() // 期待値: 2 + + // テスト3: 変数再利用での状態保持 + arr1.push("world") + me.result3 = arr1.length() // 期待値: 2 + + return me.result1 + } +} \ No newline at end of file diff --git a/tests/array_state_sharing_test.rs b/tests/array_state_sharing_test.rs new file mode 100644 index 00000000..073cd413 --- /dev/null +++ b/tests/array_state_sharing_test.rs @@ -0,0 +1,70 @@ +#[cfg(test)] +mod array_state_sharing_tests { + use crate::interpreter::Interpreter; + use crate::boxes::array::ArrayBox; + use crate::box_trait::{NyashBox, IntegerBox, StringBox}; + + #[test] + fn test_arraybox_state_sharing_bug_fix() { + // 🚨 問題再現テスト + let mut interpreter = Interpreter::new(); + let program = r#" + static box Main { + init { result } + main() { + local arr + arr = new ArrayBox() + arr.push("hello") + me.result = arr.length() + return me.result + } + } + "#; + + let result = interpreter.execute_program(program).unwrap(); + let int_result = result.as_any().downcast_ref::().unwrap(); + assert_eq!(int_result.value, 1); // 🎯 0ではなく1を返すべき + } + + #[test] + fn test_share_box_vs_clone_box_semantics() { + let arr1 = ArrayBox::new(); + arr1.push(Box::new(StringBox::new("hello"))); + + // share_box: 状態共有 + let arr2 = arr1.share_box(); + let arr2_array = arr2.as_any().downcast_ref::().unwrap(); + assert_eq!(arr2_array.len(), 1); // 共有されている + + // clone_box: 独立 + let arr3 = arr1.clone_box(); + let arr3_array = arr3.as_any().downcast_ref::().unwrap(); + arr1.push(Box::new(StringBox::new("world"))); + assert_eq!(arr3_array.len(), 1); // 影響を受けない + assert_eq!(arr1.len(), 2); // 元は2要素 + assert_eq!(arr2_array.len(), 2); // 共有されているので2要素 + } + + #[test] + fn test_multiple_operations_state_preservation() { + let mut interpreter = Interpreter::new(); + let program = r#" + static box Main { + init { result } + main() { + local arr + arr = new ArrayBox() + arr.push("first") + arr.push("second") + arr.push("third") + me.result = arr.length() + return me.result + } + } + "#; + + let result = interpreter.execute_program(program).unwrap(); + let int_result = result.as_any().downcast_ref::().unwrap(); + assert_eq!(int_result.value, 3); // 3要素が正しく保持されるべき + } +} \ No newline at end of file