Files
hakorune/docs/development/refactoring/MASTER_REFACTORING_PLAN.md
Selfhosting Dev c8063c9e41 pyvm: split op handlers into ops_core/ops_box/ops_ctrl; add ops_flow + intrinsic; delegate vm.py without behavior change
net-plugin: modularize constants (consts.rs) and sockets (sockets.rs); remove legacy commented socket code; fix unused imports
mir: move instruction unit tests to tests/mir_instruction_unit.rs (file lean-up); no semantic changes
runner/pyvm: ensure using pre-strip; misc docs updates

Build: cargo build ok; legacy cfg warnings remain as before
2025-09-21 08:53:00 +09:00

55 KiB
Raw Blame History

Nyash言語 マスターリファクタリング計画

Version: 1.0
Date: 2025-09-20
Status: 実行準備完了
Duration: 13-18週間約3-4ヶ月
Scope: JIT Cranelift除外、根本品質重視の統一化

🎯 戦略概要

核心原則

  • Progressive Unification: 段階的統一による安全な移行
  • Fundamentals First: 80/20回避、根本からの品質構築
  • Reference Integrity: PyVMを品質保証の参照点として維持
  • Non-Destructive: 既存安定機能の破壊を避ける

現状の問題構造

現在の混在状態:
├── Rust VM (Legacy) - 部分的実装、不安定
├── Python PyVM - 参照実装、安定稼働
├── MIR Interpreter - 最小実装
├── JIT Cranelift - 【除外対象】
└── Mini-VM (Nyash) - 開発中、将来の統一目標

目標アーキテクチャ

統一後の構造:
├── Unified VM Interface
│   ├── PyVM Backend (Reference)
│   ├── Nyash VM Backend (Primary)
│   └── MIR Interpreter Backend (Debug)
├── Common Entry Resolution
├── Unified Control Flow
├── Type Safety Layer
└── Single Macro Expansion Path

📋 Phase 1: VM Core統一 (4-6週間)

🎯 Phase 1 目標

「3つのVM実装を統一インターフェースで管理し、段階的にNyash VMに移行する基盤を確立」

Step 1.1: PyVM機能完全化 (Week 1-2)

実装対象

# src/llvm_py/pyvm/vm_enhanced.py
class EnhancedPyVM:
    def __init__(self):
        self.nested_function_handler = NestedFunctionHandler()
        self.control_flow_manager = ControlFlowManager()
        self.scope_boundary_tracker = ScopeBoundaryTracker()
        
    def execute_with_nested_functions(self, mir_module):
        # ネスト関数の自動リフト処理
        lifted_module = self.nested_function_handler.lift_nested_functions(mir_module)
        return self.execute_standard(lifted_module)
        
    def handle_break_continue(self, instruction, context):
        # Box境界を跨ぐbreak/continue の安全処理
        if context.is_box_boundary():
            return self.control_flow_manager.handle_box_boundary_control(instruction, context)
        return self.control_flow_manager.handle_standard_control(instruction, context)

ネスト関数リフト機能

class NestedFunctionHandler:
    def lift_nested_functions(self, mir_module):
        """
        function outer() {
            function inner() { return 42; }  // ← これを自動リフト
            return inner();
        }
        
        ↓ 変換後
        
        function _lifted_inner_123() { return 42; }
        function outer() {
            return _lifted_inner_123();
        }
        """
        lifted_functions = []
        for func in mir_module.functions:
            nested_funcs, cleaned_func = self.extract_nested_functions(func)
            lifted_functions.extend(nested_funcs)
            lifted_functions.append(cleaned_func)
        
        return MirModule(functions=lifted_functions)
        
    def extract_nested_functions(self, function):
        # 関数内の function 宣言を検出・抽出
        # gensym による名前生成(衝突回避)
        # 呼び出し箇所を生成された名前に置換
        pass

制御フロー強化

class ControlFlowManager:
    def __init__(self):
        self.loop_stack = []  # ループコンテキストスタック
        self.function_stack = []  # 関数コンテキストスタック
        
    def enter_loop(self, loop_id, break_target, continue_target):
        self.loop_stack.append({
            'id': loop_id,
            'break_target': break_target,
            'continue_target': continue_target,
            'in_box_method': self.is_in_box_method()
        })
        
    def handle_break(self, instruction):
        if not self.loop_stack:
            raise RuntimeError("break outside of loop")
            
        current_loop = self.loop_stack[-1]
        if current_loop['in_box_method']:
            # Boxメソッド内のbreak特別処理
            return self.handle_box_method_break(current_loop)
        return self.jump_to_target(current_loop['break_target'])
        
    def handle_continue(self, instruction):
        if not self.loop_stack:
            raise RuntimeError("continue outside of loop")
            
        current_loop = self.loop_stack[-1]
        if current_loop['in_box_method']:
            # Boxメソッド内のcontinue特別処理
            return self.handle_box_method_continue(current_loop)
        return self.jump_to_target(current_loop['continue_target'])

受け入れ基準

# テストスイート
./tools/test/phase1/pyvm_nested_functions.sh
./tools/test/phase1/pyvm_control_flow.sh
./tools/test/phase1/pyvm_box_boundaries.sh

# 期待する成功例
echo 'function outer() { function inner() { return 42; } return inner(); }' | python pyvm_enhanced.py
# → 出力: 42

echo 'loop(i < 10) { if (i == 5) break; i = i + 1; }' | python pyvm_enhanced.py
# → 正常終了、i = 5

Step 1.2: Unified VM Interface設計 (Week 2-3)

共通インターフェース実装

// src/backend/vm_unified.rs
use std::collections::HashMap;

#[derive(Debug, Clone)]
pub struct VMResult {
    pub return_value: Option<VMValue>,
    pub stdout: String,
    pub stderr: String,
    pub execution_stats: VMStats,
}

#[derive(Debug, Clone)]
pub struct VMStats {
    pub instructions_executed: u64,
    pub memory_used: usize,
    pub execution_time_ms: u64,
    pub function_calls: u64,
}

pub trait UnifiedVM {
    fn name(&self) -> &str;
    fn execute_mir(&mut self, module: &MirModule) -> Result<VMResult, VMError>;
    fn set_debug_mode(&mut self, enabled: bool);
    fn set_trace_mode(&mut self, enabled: bool);
    fn get_current_stats(&self) -> VMStats;
    fn reset_stats(&mut self);
}

// PyVM実装
pub struct PyVMBackend {
    python_path: PathBuf,
    debug_enabled: bool,
    trace_enabled: bool,
    stats: VMStats,
}

impl UnifiedVM for PyVMBackend {
    fn name(&self) -> &str { "PyVM" }
    
    fn execute_mir(&mut self, module: &MirModule) -> Result<VMResult, VMError> {
        // 1. MIRをJSON形式でシリアライズ
        let mir_json = serde_json::to_string(module)?;
        
        // 2. Python VMを呼び出し
        let mut cmd = Command::new("python");
        cmd.arg(&self.python_path)
           .arg("--mode").arg("execute")
           .stdin(Stdio::piped())
           .stdout(Stdio::piped())
           .stderr(Stdio::piped());
           
        if self.debug_enabled {
            cmd.arg("--debug");
        }
        if self.trace_enabled {
            cmd.arg("--trace");
        }
        
        let mut child = cmd.spawn()?;
        child.stdin.as_mut().unwrap().write_all(mir_json.as_bytes())?;
        
        let output = child.wait_with_output()?;
        
        // 3. 結果をパース
        let result: VMResult = serde_json::from_slice(&output.stdout)?;
        self.stats = result.execution_stats.clone();
        
        Ok(result)
    }
    
    fn set_debug_mode(&mut self, enabled: bool) {
        self.debug_enabled = enabled;
    }
    
    fn set_trace_mode(&mut self, enabled: bool) {
        self.trace_enabled = enabled;
    }
    
    fn get_current_stats(&self) -> VMStats {
        self.stats.clone()
    }
    
    fn reset_stats(&mut self) {
        self.stats = VMStats::default();
    }
}

// Nyash VM実装将来
pub struct NyashVMBackend {
    vm_script_path: PathBuf,
    debug_enabled: bool,
    interpreter: NyashInterpreter,
}

impl UnifiedVM for NyashVMBackend {
    fn name(&self) -> &str { "NyashVM" }
    
    fn execute_mir(&mut self, module: &MirModule) -> Result<VMResult, VMError> {
        // Nyashスクリプトで実装されたVMを実行
        // 現在は開発中、将来的にはこれが主力
        todo!("Nyash VM implementation in progress")
    }
    
    // ... 他のメソッド実装
}

VM管理システム

// src/backend/vm_manager.rs
pub struct VMManager {
    backends: HashMap<String, Box<dyn UnifiedVM>>,
    default_backend: String,
    fallback_chain: Vec<String>,
}

impl VMManager {
    pub fn new() -> Self {
        let mut manager = VMManager {
            backends: HashMap::new(),
            default_backend: "PyVM".to_string(),
            fallback_chain: vec!["PyVM".to_string(), "MIRInterpreter".to_string()],
        };
        
        // 利用可能なバックエンドを登録
        manager.register_backend("PyVM", Box::new(PyVMBackend::new()));
        manager.register_backend("MIRInterpreter", Box::new(MIRInterpreterBackend::new()));
        
        // NyashVMが利用可能な場合のみ登録
        if NyashVMBackend::is_available() {
            manager.register_backend("NyashVM", Box::new(NyashVMBackend::new()));
            manager.default_backend = "NyashVM".to_string();
        }
        
        manager
    }
    
    pub fn execute_with_fallback(&mut self, module: &MirModule) -> Result<VMResult, VMError> {
        // デフォルトバックエンドを試行
        if let Some(backend) = self.backends.get_mut(&self.default_backend) {
            match backend.execute_mir(module) {
                Ok(result) => return Ok(result),
                Err(e) => {
                    eprintln!("Warning: {} failed: {}", self.default_backend, e);
                }
            }
        }
        
        // フォールバックチェーンを順次試行
        for backend_name in &self.fallback_chain {
            if backend_name == &self.default_backend {
                continue; // 既に試行済み
            }
            
            if let Some(backend) = self.backends.get_mut(backend_name) {
                match backend.execute_mir(module) {
                    Ok(result) => {
                        eprintln!("Fallback to {} succeeded", backend_name);
                        return Ok(result);
                    }
                    Err(e) => {
                        eprintln!("Warning: {} failed: {}", backend_name, e);
                    }
                }
            }
        }
        
        Err(VMError::AllBackendsFailed)
    }
    
    pub fn register_backend(&mut self, name: &str, backend: Box<dyn UnifiedVM>) {
        self.backends.insert(name.to_string(), backend);
    }
    
    pub fn list_available_backends(&self) -> Vec<String> {
        self.backends.keys().cloned().collect()
    }
    
    pub fn get_backend_stats(&self, name: &str) -> Option<VMStats> {
        self.backends.get(name).map(|b| b.get_current_stats())
    }
}

Step 1.3: Mini-VM段階的拡張 (Week 3-4)

JSON v0 完全ローダー

// apps/selfhost/vm/json_loader.nyash
static box JSONLoader {
    parse_mir_module(json_string: StringBox) -> MirModuleBox {
        local root = me.parse_json_object(json_string)
        
        local functions = new ArrayBox()
        local funcs_array = root.get("functions")
        local i = 0
        loop(i < funcs_array.length()) {
            local func_data = funcs_array.get(i)
            local parsed_func = me.parse_function(func_data)
            functions.push(parsed_func)
            i = i + 1
        }
        
        local module = new MirModuleBox()
        module.set_functions(functions)
        return module
    }
    
    parse_function(func_obj: MapBox) -> FunctionBox {
        local name = func_obj.get("name")
        local params = me.parse_parameters(func_obj.get("parameters"))
        local body = me.parse_basic_blocks(func_obj.get("body"))
        
        local function = new FunctionBox()
        function.set_name(name)
        function.set_parameters(params)
        function.set_body(body)
        return function
    }
    
    parse_basic_blocks(blocks_array: ArrayBox) -> ArrayBox {
        local blocks = new ArrayBox()
        local i = 0
        loop(i < blocks_array.length()) {
            local block_data = blocks_array.get(i)
            local block = me.parse_basic_block(block_data)
            blocks.push(block)
            i = i + 1
        }
        return blocks
    }
    
    parse_basic_block(block_obj: MapBox) -> BasicBlockBox {
        local id = block_obj.get("id")
        local instructions = me.parse_instructions(block_obj.get("instructions"))
        
        local block = new BasicBlockBox()
        block.set_id(id)
        block.set_instructions(instructions)
        return block
    }
    
    parse_instructions(instrs_array: ArrayBox) -> ArrayBox {
        local instructions = new ArrayBox()
        local i = 0
        loop(i < instrs_array.length()) {
            local instr_data = instrs_array.get(i)
            local instruction = me.parse_instruction(instr_data)
            instructions.push(instruction)
            i = i + 1
        }
        return instructions
    }
    
    parse_instruction(instr_obj: MapBox) -> InstructionBox {
        local opcode = instr_obj.get("opcode")
        local operands = instr_obj.get("operands")
        
        local instruction = new InstructionBox()
        instruction.set_opcode(opcode)
        instruction.set_operands(operands)
        return instruction
    }
}

MIR14命令完全実装

// apps/selfhost/vm/mir_executor.nyash
static box MirExecutor {
    stack: ArrayBox
    heap: MapBox
    call_stack: ArrayBox
    current_function: FunctionBox
    current_block: IntegerBox
    instruction_pointer: IntegerBox
    
    birth() {
        me.stack = new ArrayBox()
        me.heap = new MapBox()
        me.call_stack = new ArrayBox()
        me.current_block = 0
        me.instruction_pointer = 0
    }
    
    execute_module(module: MirModuleBox) -> VMResultBox {
        // エントリポイント解決
        local entry_func = me.resolve_entry_point(module)
        
        // メイン実行ループ
        me.current_function = entry_func
        local result = me.execute_function(entry_func, new ArrayBox())
        
        local vm_result = new VMResultBox()
        vm_result.set_return_value(result)
        vm_result.set_stdout(me.get_stdout())
        vm_result.set_stderr(me.get_stderr())
        return vm_result
    }
    
    execute_function(function: FunctionBox, args: ArrayBox) -> ValueBox {
        // 関数フレーム設定
        me.push_call_frame(function, args)
        
        // 基本ブロック実行
        local blocks = function.get_body()
        me.current_block = 0
        
        loop(me.current_block < blocks.length()) {
            local block = blocks.get(me.current_block)
            local result = me.execute_basic_block(block)
            
            if (result.is_return()) {
                me.pop_call_frame()
                return result.get_value()
            }
            
            if (result.is_jump()) {
                me.current_block = result.get_target_block()
            } else {
                me.current_block = me.current_block + 1
            }
        }
        
        // デフォルトリターン
        return new NullValueBox()
    }
    
    execute_basic_block(block: BasicBlockBox) -> ExecutionResultBox {
        local instructions = block.get_instructions()
        local i = 0
        
        loop(i < instructions.length()) {
            local instruction = instructions.get(i)
            local result = me.execute_instruction(instruction)
            
            if (result.is_control_flow()) {
                return result
            }
            
            i = i + 1
        }
        
        return new ContinueResultBox()
    }
    
    execute_instruction(instruction: InstructionBox) -> ExecutionResultBox {
        local opcode = instruction.get_opcode()
        local operands = instruction.get_operands()
        
        return peek opcode {
            "const" => me.execute_const(operands),
            "load" => me.execute_load(operands),
            "store" => me.execute_store(operands),
            "binop" => me.execute_binop(operands),
            "compare" => me.execute_compare(operands),
            "branch" => me.execute_branch(operands),
            "jump" => me.execute_jump(operands),
            "call" => me.execute_call(operands),
            "ret" => me.execute_ret(operands),
            "phi" => me.execute_phi(operands),
            "newbox" => me.execute_newbox(operands),
            "boxcall" => me.execute_boxcall(operands),
            "externcall" => me.execute_externcall(operands),
            "typeop" => me.execute_typeop(operands),
            else => panic("Unknown instruction: " + opcode)
        }
    }
    
    // 各命令の実装
    execute_const(operands: ArrayBox) -> ExecutionResultBox {
        local value = operands.get(0)
        local target = operands.get(1)
        me.set_variable(target, value)
        return new ContinueResultBox()
    }
    
    execute_binop(operands: ArrayBox) -> ExecutionResultBox {
        local op = operands.get(0)
        local left = me.get_variable(operands.get(1))
        local right = me.get_variable(operands.get(2))
        local target = operands.get(3)
        
        local result = peek op {
            "add" => left + right,
            "sub" => left - right,
            "mul" => left * right,
            "div" => left / right,
            else => panic("Unknown binary operation: " + op)
        }
        
        me.set_variable(target, result)
        return new ContinueResultBox()
    }
    
    execute_call(operands: ArrayBox) -> ExecutionResultBox {
        local func_name = operands.get(0)
        local args = me.evaluate_arguments(operands.slice(1))
        local target = operands.get_last()
        
        local function = me.resolve_function(func_name)
        local result = me.execute_function(function, args)
        me.set_variable(target, result)
        
        return new ContinueResultBox()
    }
    
    execute_ret(operands: ArrayBox) -> ExecutionResultBox {
        local value = if (operands.length() > 0) {
            me.get_variable(operands.get(0))
        } else {
            new NullValueBox()
        }
        
        local result = new ReturnResultBox()
        result.set_value(value)
        return result
    }
    
    execute_branch(operands: ArrayBox) -> ExecutionResultBox {
        local condition = me.get_variable(operands.get(0))
        local true_block = operands.get(1)
        local false_block = operands.get(2)
        
        local target_block = if (condition.to_boolean()) {
            true_block
        } else {
            false_block
        }
        
        local result = new JumpResultBox()
        result.set_target_block(target_block)
        return result
    }
    
    execute_jump(operands: ArrayBox) -> ExecutionResultBox {
        local target_block = operands.get(0)
        local result = new JumpResultBox()
        result.set_target_block(target_block)
        return result
    }
}

受け入れ基準

# Mini-VM基本機能テスト
./tools/test/phase1/mini_vm_basic.sh

# PyVMとの出力比較テスト
./tools/test/phase1/mini_vm_vs_pyvm.sh

# 期待する成功例
echo '{"functions": [{"name": "main", "body": [...]}]}' | ./mini_vm.nyash
# → PyVMと同一の出力

📋 Phase 2: エントリ解決統一 (2-3週間)

🎯 Phase 2 目標

「すべてのVM実装で一貫したエントリポイント解決を実現し、実行時の不整合を根絶」

Step 2.1: エントリ解決ライブラリ設計 (Week 1)

統一エントリ解決システム

// src/runtime/entry_resolver.rs
use std::collections::HashMap;

#[derive(Debug, Clone)]
pub struct EntryResolverConfig {
    pub allow_toplevel_main: bool,
    pub strict_main_signature: bool,
    pub fallback_to_first_function: bool,
    pub require_main_box: bool,
}

impl Default for EntryResolverConfig {
    fn default() -> Self {
        Self {
            allow_toplevel_main: false,  // デフォルトは strict
            strict_main_signature: true,
            fallback_to_first_function: false,
            require_main_box: true,
        }
    }
}

pub struct EntryResolver {
    config: EntryResolverConfig,
}

impl EntryResolver {
    pub fn new(config: EntryResolverConfig) -> Self {
        Self { config }
    }
    
    pub fn resolve_entry_point(&self, module: &MirModule) -> Result<FunctionId, EntryResolutionError> {
        // 1. Main.main() を最優先で検索
        if let Some(func_id) = self.find_main_box_main(module) {
            return Ok(func_id);
        }
        
        // 2. 設定によってtoplevel main() を試行
        if self.config.allow_toplevel_main {
            if let Some(func_id) = self.find_toplevel_main(module) {
                return Ok(func_id);
            }
        }
        
        // 3. フォールバック戦略
        if self.config.fallback_to_first_function && !module.functions.is_empty() {
            return Ok(FunctionId(0));
        }
        
        // 4. エラー
        Err(EntryResolutionError::NoValidEntryPoint {
            found_functions: self.list_available_functions(module),
            config: self.config.clone(),
        })
    }
    
    fn find_main_box_main(&self, module: &MirModule) -> Option<FunctionId> {
        for (i, function) in module.functions.iter().enumerate() {
            // "Main.main" または "main" (Boxメソッドとして)
            if function.name == "Main.main" || 
               (function.is_box_method && function.name == "main" && function.box_name == Some("Main")) {
                
                if self.config.strict_main_signature {
                    if self.validate_main_signature(function) {
                        return Some(FunctionId(i));
                    }
                } else {
                    return Some(FunctionId(i));
                }
            }
        }
        None
    }
    
    fn find_toplevel_main(&self, module: &MirModule) -> Option<FunctionId> {
        for (i, function) in module.functions.iter().enumerate() {
            if function.name == "main" && !function.is_box_method {
                if self.config.strict_main_signature {
                    if self.validate_main_signature(function) {
                        return Some(FunctionId(i));
                    }
                } else {
                    return Some(FunctionId(i));
                }
            }
        }
        None
    }
    
    fn validate_main_signature(&self, function: &MirFunction) -> bool {
        // main() または main(args: ArrayBox) を受け入れ
        match function.parameters.len() {
            0 => true,  // main()
            1 => {
                // main(args: ArrayBox) を確認
                function.parameters[0].type_hint.as_ref()
                    .map(|t| t == "ArrayBox")
                    .unwrap_or(true)  // 型ヒントない場合は許可
            }
            _ => false,  // 2個以上の引数は不正
        }
    }
    
    fn list_available_functions(&self, module: &MirModule) -> Vec<String> {
        module.functions.iter()
            .map(|f| format!("{}({})", f.name, f.parameters.len()))
            .collect()
    }
    
    pub fn create_detailed_error_message(&self, error: &EntryResolutionError) -> String {
        match error {
            EntryResolutionError::NoValidEntryPoint { found_functions, config } => {
                let mut msg = String::new();
                msg.push_str("No valid entry point found.\n\n");
                
                msg.push_str("Expected entry points (in order of preference):\n");
                if config.require_main_box {
                    msg.push_str("  1. static box Main { main() { ... } }  (最推奨)\n");
                    msg.push_str("  2. static box Main { main(args) { ... } }\n");
                }
                if config.allow_toplevel_main {
                    msg.push_str("  3. function main() { ... }  (非推奨)\n");
                    msg.push_str("  4. function main(args) { ... }\n");
                }
                
                msg.push_str("\nFound functions:\n");
                for func in found_functions {
                    msg.push_str(&format!("  - {}\n", func));
                }
                
                msg.push_str("\nRecommendation:\n");
                msg.push_str("Add this to your code:\n");
                msg.push_str("static box Main {\n");
                msg.push_str("    main() {\n");
                msg.push_str("        // Your code here\n");
                msg.push_str("    }\n");
                msg.push_str("}\n");
                
                msg
            }
        }
    }
}

#[derive(Debug, Clone)]
pub enum EntryResolutionError {
    NoValidEntryPoint {
        found_functions: Vec<String>,
        config: EntryResolverConfig,
    },
}

impl std::fmt::Display for EntryResolutionError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            EntryResolutionError::NoValidEntryPoint { .. } => {
                write!(f, "No valid entry point found")
            }
        }
    }
}

impl std::error::Error for EntryResolutionError {}

環境変数による設定

// src/runtime/entry_resolver.rs に追加
impl EntryResolverConfig {
    pub fn from_environment() -> Self {
        Self {
            allow_toplevel_main: std::env::var("NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN")
                .map(|v| v == "1" || v.to_lowercase() == "true")
                .unwrap_or(false),
            strict_main_signature: std::env::var("NYASH_ENTRY_STRICT_SIGNATURE")
                .map(|v| v == "1" || v.to_lowercase() == "true")
                .unwrap_or(true),
            fallback_to_first_function: std::env::var("NYASH_ENTRY_FALLBACK_FIRST")
                .map(|v| v == "1" || v.to_lowercase() == "true")
                .unwrap_or(false),
            require_main_box: std::env::var("NYASH_ENTRY_REQUIRE_MAIN_BOX")
                .map(|v| v != "0" && v.to_lowercase() != "false")
                .unwrap_or(true),
        }
    }
}

Step 2.2: 全バックエンド統合 (Week 2-3)

Unified VM Interface に統合

// src/backend/vm_unified.rs に追加
impl UnifiedVM for PyVMBackend {
    fn execute_mir(&mut self, module: &MirModule) -> Result<VMResult, VMError> {
        // 1. エントリ解決
        let resolver = EntryResolver::new(EntryResolverConfig::from_environment());
        let entry_func_id = resolver.resolve_entry_point(module)
            .map_err(|e| VMError::EntryResolution(e))?;
        
        // 2. モジュールにエントリ情報を付加
        let mut module_with_entry = module.clone();
        module_with_entry.entry_point = Some(entry_func_id);
        
        // 3. PyVMに渡すJSONに含める
        let mir_json = serde_json::to_string(&module_with_entry)?;
        
        // ... 以下既存のPyVM実行ロジック
    }
}

PyVM側での対応

# src/llvm_py/pyvm/vm_enhanced.py に追加
class EnhancedPyVM:
    def execute_mir_module(self, mir_json_str):
        mir_module = json.loads(mir_json_str)
        
        # エントリポイント解決Rust側で解決済みを使用
        if 'entry_point' in mir_module:
            entry_func_id = mir_module['entry_point']
            entry_function = mir_module['functions'][entry_func_id]
        else:
            # フォールバック(古い形式互換性)
            entry_function = self.resolve_entry_legacy(mir_module)
        
        # メイン実行
        result = self.execute_function(entry_function, [])
        
        return {
            'return_value': result,
            'stdout': self.get_stdout(),
            'stderr': self.get_stderr(),
            'execution_stats': self.get_stats(),
        }
    
    def resolve_entry_legacy(self, mir_module):
        """レガシー互換性のためのエントリ解決"""
        # "Main.main" を検索
        for func in mir_module['functions']:
            if func['name'] == 'Main.main':
                return func
        
        # "main" を検索
        for func in mir_module['functions']:
            if func['name'] == 'main':
                return func
        
        # 最初の関数を使用
        if mir_module['functions']:
            return mir_module['functions'][0]
        
        raise RuntimeError("No executable function found")

受け入れ基準

# エントリ解決統一テスト
./tools/test/phase2/entry_resolution_unified.sh

# 設定による動作変更テスト
NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN=1 ./tools/test/phase2/toplevel_main_test.sh
NYASH_ENTRY_STRICT_SIGNATURE=0 ./tools/test/phase2/relaxed_signature_test.sh

# 期待する成功例
# Main.main() パターン
echo 'static box Main { main() { print("Hello"); } }' | ./nyash --backend pyvm
# → Hello

# toplevel main() パターン(環境変数有効時)
NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN=1 echo 'function main() { print("World"); }' | ./nyash --backend pyvm
# → World

# エラーメッセージ品質確認
echo 'function foo() { print("test"); }' | ./nyash --backend pyvm
# → 詳細なエラーメッセージと修正提案

📋 Phase 3: 制御フロー根本修正 (3-4週間)

🎯 Phase 3 目標

「ネスト関数、break/continue、Box境界での制御フローを完全に統一し、言語機能の完全性を達成」

Step 3.1: 制御フロー統一設計 (Week 1)

制御フローコンテキスト管理

// src/mir/control_flow_unified.rs
use std::collections::HashMap;

#[derive(Debug, Clone)]
pub struct ControlFlowContext {
    loop_stack: Vec<LoopContext>,
    function_stack: Vec<FunctionContext>,
    break_targets: HashMap<LoopId, BasicBlockId>,
    continue_targets: HashMap<LoopId, BasicBlockId>,
    scope_stack: Vec<ScopeContext>,
}

#[derive(Debug, Clone)]
pub struct LoopContext {
    id: LoopId,
    break_label: String,
    continue_label: String,
    is_in_box_method: bool,
    parent_function: FunctionId,
}

#[derive(Debug, Clone)]
pub struct FunctionContext {
    id: FunctionId,
    name: String,
    is_box_method: bool,
    box_name: Option<String>,
    return_type: Option<String>,
}

#[derive(Debug, Clone)]
pub struct ScopeContext {
    scope_type: ScopeType,
    depth: usize,
    parent_function: FunctionId,
    variables: HashMap<String, VariableInfo>,
}

#[derive(Debug, Clone)]
pub enum ScopeType {
    Function,
    Loop,
    If,
    Block,
    BoxMethod,
}

impl ControlFlowContext {
    pub fn new() -> Self {
        Self {
            loop_stack: Vec::new(),
            function_stack: Vec::new(),
            break_targets: HashMap::new(),
            continue_targets: HashMap::new(),
            scope_stack: Vec::new(),
        }
    }
    
    pub fn enter_function(&mut self, func_id: FunctionId, name: String, is_box_method: bool) {
        let context = FunctionContext {
            id: func_id,
            name,
            is_box_method,
            box_name: None, // TODO: 実際のBox名を取得
            return_type: None,
        };
        
        self.function_stack.push(context);
        
        self.scope_stack.push(ScopeContext {
            scope_type: if is_box_method { ScopeType::BoxMethod } else { ScopeType::Function },
            depth: self.scope_stack.len(),
            parent_function: func_id,
            variables: HashMap::new(),
        });
    }
    
    pub fn exit_function(&mut self) {
        self.function_stack.pop();
        
        // 関数スコープを削除
        while let Some(scope) = self.scope_stack.last() {
            if matches!(scope.scope_type, ScopeType::Function | ScopeType::BoxMethod) {
                self.scope_stack.pop();
                break;
            }
            self.scope_stack.pop();
        }
    }
    
    pub fn enter_loop(&mut self, loop_id: LoopId, break_target: BasicBlockId, continue_target: BasicBlockId) {
        let current_function = self.current_function_id();
        let is_in_box_method = self.is_in_box_method();
        
        let context = LoopContext {
            id: loop_id,
            break_label: format!("break_{}", loop_id.0),
            continue_label: format!("continue_{}", loop_id.0),
            is_in_box_method,
            parent_function: current_function,
        };
        
        self.loop_stack.push(context);
        self.break_targets.insert(loop_id, break_target);
        self.continue_targets.insert(loop_id, continue_target);
        
        self.scope_stack.push(ScopeContext {
            scope_type: ScopeType::Loop,
            depth: self.scope_stack.len(),
            parent_function: current_function,
            variables: HashMap::new(),
        });
    }
    
    pub fn exit_loop(&mut self) {
        if let Some(loop_context) = self.loop_stack.pop() {
            self.break_targets.remove(&loop_context.id);
            self.continue_targets.remove(&loop_context.id);
        }
        
        // ループスコープを削除
        while let Some(scope) = self.scope_stack.last() {
            if matches!(scope.scope_type, ScopeType::Loop) {
                self.scope_stack.pop();
                break;
            }
            self.scope_stack.pop();
        }
    }
    
    pub fn handle_break(&self) -> Result<BasicBlockId, ControlFlowError> {
        let current_loop = self.loop_stack.last()
            .ok_or(ControlFlowError::BreakOutsideLoop)?;
        
        if current_loop.is_in_box_method {
            // Boxメソッド内のbreak特別処理
            self.handle_box_method_break(current_loop)
        } else {
            // 通常のbreak処理
            Ok(self.break_targets[&current_loop.id])
        }
    }
    
    pub fn handle_continue(&self) -> Result<BasicBlockId, ControlFlowError> {
        let current_loop = self.loop_stack.last()
            .ok_or(ControlFlowError::ContinueOutsideLoop)?;
        
        if current_loop.is_in_box_method {
            // Boxメソッド内のcontinue特別処理
            self.handle_box_method_continue(current_loop)
        } else {
            // 通常のcontinue処理
            Ok(self.continue_targets[&current_loop.id])
        }
    }
    
    fn handle_box_method_break(&self, loop_context: &LoopContext) -> Result<BasicBlockId, ControlFlowError> {
        // Boxメソッド境界を超えるbreak処理
        // TODO: Boxメソッドの特別な制御フロー処理
        Ok(self.break_targets[&loop_context.id])
    }
    
    fn handle_box_method_continue(&self, loop_context: &LoopContext) -> Result<BasicBlockId, ControlFlowError> {
        // Boxメソッド境界を超えるcontinue処理
        // TODO: Boxメソッドの特別な制御フロー処理
        Ok(self.continue_targets[&loop_context.id])
    }
    
    pub fn current_function_id(&self) -> FunctionId {
        self.function_stack.last()
            .map(|ctx| ctx.id)
            .unwrap_or(FunctionId(0))
    }
    
    pub fn is_in_box_method(&self) -> bool {
        self.function_stack.last()
            .map(|ctx| ctx.is_box_method)
            .unwrap_or(false)
    }
    
    pub fn validate_break(&self) -> Result<(), ControlFlowError> {
        if self.loop_stack.is_empty() {
            return Err(ControlFlowError::BreakOutsideLoop);
        }
        
        // Box境界チェック
        let current_loop = &self.loop_stack[self.loop_stack.len() - 1];
        let current_function = self.current_function_id();
        
        if current_loop.parent_function != current_function {
            return Err(ControlFlowError::BreakAcrossFunctionBoundary);
        }
        
        Ok(())
    }
    
    pub fn validate_continue(&self) -> Result<(), ControlFlowError> {
        if self.loop_stack.is_empty() {
            return Err(ControlFlowError::ContinueOutsideLoop);
        }
        
        // Box境界チェック
        let current_loop = &self.loop_stack[self.loop_stack.len() - 1];
        let current_function = self.current_function_id();
        
        if current_loop.parent_function != current_function {
            return Err(ControlFlowError::ContinueAcrossFunctionBoundary);
        }
        
        Ok(())
    }
}

#[derive(Debug, Clone)]
pub enum ControlFlowError {
    BreakOutsideLoop,
    ContinueOutsideLoop,
    BreakAcrossFunctionBoundary,
    ContinueAcrossFunctionBoundary,
    InvalidLoopNesting,
}

impl std::fmt::Display for ControlFlowError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ControlFlowError::BreakOutsideLoop => 
                write!(f, "break statement outside of loop"),
            ControlFlowError::ContinueOutsideLoop => 
                write!(f, "continue statement outside of loop"),
            ControlFlowError::BreakAcrossFunctionBoundary => 
                write!(f, "break statement cannot cross function boundary"),
            ControlFlowError::ContinueAcrossFunctionBoundary => 
                write!(f, "continue statement cannot cross function boundary"),
            ControlFlowError::InvalidLoopNesting => 
                write!(f, "invalid loop nesting structure"),
        }
    }
}

impl std::error::Error for ControlFlowError {}

Step 3.2: ネスト関数リフト標準化 (Week 2)

ネスト関数自動リフトシステム

// src/mir/nested_function_lifter.rs
use std::collections::{HashMap, HashSet};

pub struct NestedFunctionLifter {
    symbol_generator: SymbolGenerator,
    captured_variables: HashMap<FunctionId, HashSet<String>>,
}

impl NestedFunctionLifter {
    pub fn new() -> Self {
        Self {
            symbol_generator: SymbolGenerator::new(),
            captured_variables: HashMap::new(),
        }
    }
    
    pub fn lift_nested_functions(&mut self, module: &mut MirModule) -> Result<(), LiftError> {
        let mut lifted_functions = Vec::new();
        let mut modified_functions = Vec::new();
        
        for (func_id, function) in module.functions.iter().enumerate() {
            let (nested_funcs, modified_func) = self.extract_and_lift_nested(
                FunctionId(func_id), 
                function
            )?;
            
            lifted_functions.extend(nested_funcs);
            modified_functions.push(modified_func);
        }
        
        // 元の関数を置換
        module.functions = modified_functions;
        
        // リフトされた関数を追加
        module.functions.extend(lifted_functions);
        
        Ok(())
    }
    
    fn extract_and_lift_nested(&mut self, parent_id: FunctionId, function: &MirFunction) 
        -> Result<(Vec<MirFunction>, MirFunction), LiftError> {
        
        let mut nested_functions = Vec::new();
        let mut modified_body = function.body.clone();
        let mut replacement_map = HashMap::new();
        
        // 1. ネスト関数を検出・抽出
        for (block_idx, block) in function.body.iter().enumerate() {
            for (instr_idx, instruction) in block.instructions.iter().enumerate() {
                if let Some(nested_func) = self.extract_function_declaration(instruction)? {
                    // 新しい関数名を生成
                    let lifted_name = self.symbol_generator.generate_function_name(
                        &function.name, 
                        &nested_func.name
                    );
                    
                    // キャプチャ変数を分析
                    let captured = self.analyze_captured_variables(&nested_func, function)?;
                    
                    if !captured.is_empty() {
                        return Err(LiftError::CapturedVariablesNotSupported {
                            function_name: nested_func.name.clone(),
                            captured_variables: captured,
                        });
                    }
                    
                    // リフトされた関数を作成
                    let mut lifted_func = nested_func.clone();
                    lifted_func.name = lifted_name.clone();
                    lifted_func.is_nested = false;
                    
                    nested_functions.push(lifted_func);
                    
                    // 置換マップに記録
                    replacement_map.insert(nested_func.name.clone(), lifted_name);
                    
                    // 元の命令を削除no-op に置換)
                    modified_body[block_idx].instructions[instr_idx] = MirInstruction::noop();
                }
            }
        }
        
        // 2. 関数呼び出しを置換
        self.replace_function_calls(&mut modified_body, &replacement_map)?;
        
        let mut modified_function = function.clone();
        modified_function.body = modified_body;
        
        Ok((nested_functions, modified_function))
    }
    
    fn extract_function_declaration(&self, instruction: &MirInstruction) 
        -> Result<Option<MirFunction>, LiftError> {
        
        match instruction {
            MirInstruction::FunctionDeclaration { function } => {
                Ok(Some(function.clone()))
            }
            _ => Ok(None)
        }
    }
    
    fn analyze_captured_variables(&self, nested_func: &MirFunction, parent_func: &MirFunction) 
        -> Result<HashSet<String>, LiftError> {
        
        let mut captured = HashSet::new();
        let mut nested_locals = HashSet::new();
        let parent_locals = self.collect_local_variables(parent_func);
        
        // ネスト関数のローカル変数を収集
        for param in &nested_func.parameters {
            nested_locals.insert(param.name.clone());
        }
        
        // ネスト関数内で使用される変数を分析
        for block in &nested_func.body {
            for instruction in &block.instructions {
                for used_var in self.extract_used_variables(instruction) {
                    if !nested_locals.contains(&used_var) && parent_locals.contains(&used_var) {
                        captured.insert(used_var);
                    }
                }
            }
        }
        
        Ok(captured)
    }
    
    fn collect_local_variables(&self, function: &MirFunction) -> HashSet<String> {
        let mut locals = HashSet::new();
        
        // パラメータを追加
        for param in &function.parameters {
            locals.insert(param.name.clone());
        }
        
        // ローカル変数宣言を検索
        for block in &function.body {
            for instruction in &block.instructions {
                if let Some(var_name) = self.extract_local_declaration(instruction) {
                    locals.insert(var_name);
                }
            }
        }
        
        locals
    }
    
    fn extract_used_variables(&self, instruction: &MirInstruction) -> Vec<String> {
        match instruction {
            MirInstruction::Load { source, .. } => vec![source.clone()],
            MirInstruction::Store { target, source } => vec![target.clone(), source.clone()],
            MirInstruction::BinOp { left, right, .. } => vec![left.clone(), right.clone()],
            MirInstruction::Call { args, .. } => args.clone(),
            // ... 他の命令タイプ
            _ => Vec::new(),
        }
    }
    
    fn extract_local_declaration(&self, instruction: &MirInstruction) -> Option<String> {
        match instruction {
            MirInstruction::LocalDeclaration { name } => Some(name.clone()),
            _ => None,
        }
    }
    
    fn replace_function_calls(&self, body: &mut Vec<BasicBlock>, replacement_map: &HashMap<String, String>) 
        -> Result<(), LiftError> {
        
        for block in body.iter_mut() {
            for instruction in block.instructions.iter_mut() {
                if let MirInstruction::Call { function_name, .. } = instruction {
                    if let Some(new_name) = replacement_map.get(function_name) {
                        *function_name = new_name.clone();
                    }
                }
            }
        }
        
        Ok(())
    }
}

pub struct SymbolGenerator {
    counter: u32,
}

impl SymbolGenerator {
    pub fn new() -> Self {
        Self { counter: 0 }
    }
    
    pub fn generate_function_name(&mut self, parent_name: &str, nested_name: &str) -> String {
        self.counter += 1;
        format!("_lifted_{}_{}__{}", parent_name, nested_name, self.counter)
    }
}

#[derive(Debug, Clone)]
pub enum LiftError {
    CapturedVariablesNotSupported {
        function_name: String,
        captured_variables: HashSet<String>,
    },
    InvalidNestedFunction {
        function_name: String,
        reason: String,
    },
}

impl std::fmt::Display for LiftError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            LiftError::CapturedVariablesNotSupported { function_name, captured_variables } => {
                write!(f, 
                    "Nested function '{}' captures variables from parent scope: {:?}. \
                    Closure capture is not yet supported. \
                    Please move the function to top level or box level.",
                    function_name, captured_variables
                )
            }
            LiftError::InvalidNestedFunction { function_name, reason } => {
                write!(f, "Invalid nested function '{}': {}", function_name, reason)
            }
        }
    }
}

impl std::error::Error for LiftError {}

Step 3.3: Box境界制御フロー (Week 3-4)

Box境界制御フロー処理

// src/mir/box_boundary_control_flow.rs
use std::collections::HashMap;

pub struct BoxBoundaryControlFlowHandler {
    box_method_contexts: HashMap<FunctionId, BoxMethodContext>,
    active_loops: HashMap<LoopId, LoopBoundaryInfo>,
}

#[derive(Debug, Clone)]
pub struct BoxMethodContext {
    box_name: String,
    method_name: String,
    parent_scope: Option<ScopeId>,
    boundary_type: BoxBoundaryType,
}

#[derive(Debug, Clone)]
pub enum BoxBoundaryType {
    Isolated,        // Box内で完結
    Transparent,     // 外部ループを継承
    Controlled,      // 特別な制御が必要
}

#[derive(Debug, Clone)]
pub struct LoopBoundaryInfo {
    loop_id: LoopId,
    parent_function: FunctionId,
    crossing_box_methods: Vec<FunctionId>,
    break_handling: BreakHandlingStrategy,
    continue_handling: ContinueHandlingStrategy,
}

#[derive(Debug, Clone)]
pub enum BreakHandlingStrategy {
    Standard,           // 通常のbreak処理
    BoxMethodExit,      // Boxメソッド終了として処理
    ParentLoopBreak,    // 親ループのbreakに変換
    Error,              // エラーとして処理
}

#[derive(Debug, Clone)]
pub enum ContinueHandlingStrategy {
    Standard,           // 通常のcontinue処理
    BoxMethodReturn,    // Boxメソッドreturnとして処理
    ParentLoopContinue, // 親ループのcontinueに変換
    Error,              // エラーとして処理
}

impl BoxBoundaryControlFlowHandler {
    pub fn new() -> Self {
        Self {
            box_method_contexts: HashMap::new(),
            active_loops: HashMap::new(),
        }
    }
    
    pub fn register_box_method(&mut self, func_id: FunctionId, box_name: String, method_name: String) {
        let context = BoxMethodContext {
            box_name,
            method_name,
            parent_scope: None,
            boundary_type: self.determine_boundary_type(&box_name),
        };
        
        self.box_method_contexts.insert(func_id, context);
    }
    
    fn determine_boundary_type(&self, box_name: &str) -> BoxBoundaryType {
        // Boxの種類に応じて境界タイプを決定
        match box_name {
            // システムBoxは透明
            "ArrayBox" | "StringBox" | "MapBox" => BoxBoundaryType::Transparent,
            
            // ユーザー定義Boxは分離
            _ => BoxBoundaryType::Isolated,
        }
    }
    
    pub fn handle_loop_entry(&mut self, loop_id: LoopId, current_function: FunctionId) {
        let boundary_info = LoopBoundaryInfo {
            loop_id,
            parent_function: current_function,
            crossing_box_methods: Vec::new(),
            break_handling: self.determine_break_strategy(current_function),
            continue_handling: self.determine_continue_strategy(current_function),
        };
        
        self.active_loops.insert(loop_id, boundary_info);
    }
    
    pub fn handle_loop_exit(&mut self, loop_id: LoopId) {
        self.active_loops.remove(&loop_id);
    }
    
    fn determine_break_strategy(&self, function_id: FunctionId) -> BreakHandlingStrategy {
        if let Some(context) = self.box_method_contexts.get(&function_id) {
            match context.boundary_type {
                BoxBoundaryType::Isolated => BreakHandlingStrategy::BoxMethodExit,
                BoxBoundaryType::Transparent => BreakHandlingStrategy::Standard,
                BoxBoundaryType::Controlled => BreakHandlingStrategy::ParentLoopBreak,
            }
        } else {
            BreakHandlingStrategy::Standard
        }
    }
    
    fn determine_continue_strategy(&self, function_id: FunctionId) -> ContinueHandlingStrategy {
        if let Some(context) = self.box_method_contexts.get(&function_id) {
            match context.boundary_type {
                BoxBoundaryType::Isolated => ContinueHandlingStrategy::BoxMethodReturn,
                BoxBoundaryType::Transparent => ContinueHandlingStrategy::Standard,
                BoxBoundaryType::Controlled => ContinueHandlingStrategy::ParentLoopContinue,
            }
        } else {
            ContinueHandlingStrategy::Standard
        }
    }
    
    pub fn process_break(&self, loop_id: LoopId, current_function: FunctionId) 
        -> Result<ControlFlowAction, BoxBoundaryError> {
        
        let loop_info = self.active_loops.get(&loop_id)
            .ok_or(BoxBoundaryError::LoopNotFound(loop_id))?;
        
        match loop_info.break_handling {
            BreakHandlingStrategy::Standard => {
                Ok(ControlFlowAction::Break(loop_info.loop_id))
            }
            
            BreakHandlingStrategy::BoxMethodExit => {
                // Boxメソッド終了として処理
                Ok(ControlFlowAction::Return(None))
            }
            
            BreakHandlingStrategy::ParentLoopBreak => {
                // 親ループのbreakに変換
                if loop_info.parent_function != current_function {
                    self.find_parent_loop_break(current_function)
                } else {
                    Ok(ControlFlowAction::Break(loop_info.loop_id))
                }
            }
            
            BreakHandlingStrategy::Error => {
                Err(BoxBoundaryError::BreakAcrossBoundary {
                    loop_id,
                    function_id: current_function,
                })
            }
        }
    }
    
    pub fn process_continue(&self, loop_id: LoopId, current_function: FunctionId) 
        -> Result<ControlFlowAction, BoxBoundaryError> {
        
        let loop_info = self.active_loops.get(&loop_id)
            .ok_or(BoxBoundaryError::LoopNotFound(loop_id))?;
        
        match loop_info.continue_handling {
            ContinueHandlingStrategy::Standard => {
                Ok(ControlFlowAction::Continue(loop_info.loop_id))
            }
            
            ContinueHandlingStrategy::BoxMethodReturn => {
                // Boxメソッドreturnとして処理継続的な意味で
                Ok(ControlFlowAction::Return(None))
            }
            
            ContinueHandlingStrategy::ParentLoopContinue => {
                // 親ループのcontinueに変換
                if loop_info.parent_function != current_function {
                    self.find_parent_loop_continue(current_function)
                } else {
                    Ok(ControlFlowAction::Continue(loop_info.loop_id))
                }
            }
            
            ContinueHandlingStrategy::Error => {
                Err(BoxBoundaryError::ContinueAcrossBoundary {
                    loop_id,
                    function_id: current_function,
                })
            }
        }
    }
    
    fn find_parent_loop_break(&self, current_function: FunctionId) 
        -> Result<ControlFlowAction, BoxBoundaryError> {
        
        // 現在の関数の親スコープでアクティブなループを検索
        for (loop_id, loop_info) in &self.active_loops {
            if loop_info.crossing_box_methods.contains(&current_function) {
                return Ok(ControlFlowAction::Break(*loop_id));
            }
        }
        
        Err(BoxBoundaryError::NoParentLoop(current_function))
    }
    
    fn find_parent_loop_continue(&self, current_function: FunctionId) 
        -> Result<ControlFlowAction, BoxBoundaryError> {
        
        // 現在の関数の親スコープでアクティブなループを検索
        for (loop_id, loop_info) in &self.active_loops {
            if loop_info.crossing_box_methods.contains(&current_function) {
                return Ok(ControlFlowAction::Continue(*loop_id));
            }
        }
        
        Err(BoxBoundaryError::NoParentLoop(current_function))
    }
}

#[derive(Debug, Clone)]
pub enum ControlFlowAction {
    Break(LoopId),
    Continue(LoopId),
    Return(Option<String>),
    Jump(BasicBlockId),
}

#[derive(Debug, Clone)]
pub enum BoxBoundaryError {
    LoopNotFound(LoopId),
    BreakAcrossBoundary { loop_id: LoopId, function_id: FunctionId },
    ContinueAcrossBoundary { loop_id: LoopId, function_id: FunctionId },
    NoParentLoop(FunctionId),
    InvalidBoundaryType(String),
}

impl std::fmt::Display for BoxBoundaryError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            BoxBoundaryError::LoopNotFound(loop_id) => 
                write!(f, "Loop {} not found in active loop contexts", loop_id.0),
            BoxBoundaryError::BreakAcrossBoundary { loop_id, function_id } => 
                write!(f, "break statement in function {} cannot reach loop {}", function_id.0, loop_id.0),
            BoxBoundaryError::ContinueAcrossBoundary { loop_id, function_id } => 
                write!(f, "continue statement in function {} cannot reach loop {}", function_id.0, loop_id.0),
            BoxBoundaryError::NoParentLoop(function_id) => 
                write!(f, "No parent loop found for function {}", function_id.0),
            BoxBoundaryError::InvalidBoundaryType(msg) => 
                write!(f, "Invalid box boundary type: {}", msg),
        }
    }
}

impl std::error::Error for BoxBoundaryError {}

受け入れ基準

# 制御フロー統合テスト
./tools/test/phase3/control_flow_unified.sh

# ネスト関数リフトテスト
./tools/test/phase3/nested_function_lift.sh

# Box境界制御フローテスト
./tools/test/phase3/box_boundary_control_flow.sh

# 期待する成功例
# ネスト関数の自動リフト
echo 'function outer() { function inner() { return 42; } return inner(); }' | ./nyash --backend pyvm
# → 42 (正常実行)

# Box境界でのbreak/continue
echo 'local arr = new ArrayBox(); loop(i < 10) { arr.forEach(x => { if (x > 5) break; }); }' | ./nyash --backend pyvm
# → 適切なエラーメッセージまたは正常実行

# 複雑な制御フロー
echo 'loop(i < 10) { loop(j < 5) { if (condition) continue; if (other) break; } }' | ./nyash --backend pyvm
# → 正常実行

この詳細計画により、Nyashの根本的な品質問題を段階的に解決し、真の意味でのセルフホスティング成功への基盤を確立できます。