diff --git a/src/mir/builder/builder_calls.rs b/src/mir/builder/builder_calls.rs index 36ade7d8..4d5cc9d3 100644 --- a/src/mir/builder/builder_calls.rs +++ b/src/mir/builder/builder_calls.rs @@ -4,16 +4,11 @@ use crate::ast::{ASTNode, LiteralValue}; use crate::mir::definitions::call_unified::Callee; use crate::mir::TypeOpKind; -// Import from new modules +// Import from new modules (refactored with Box Theory) use super::calls::*; pub use super::calls::call_target::CallTarget; impl super::MirBuilder { - /// Annotate a call result `dst` with the return type and origin if the callee - /// is a known user/static function in the current module. - pub(super) fn annotate_call_result_from_func_name>(&mut self, dst: super::ValueId, func_name: S) { - super::calls::annotation::annotate_call_result_from_func_name(self, dst, func_name) - } /// Unified call emission - replaces all emit_*_call methods /// ChatGPT5 Pro A++ design for complete call unification pub fn emit_unified_call( @@ -486,17 +481,8 @@ impl super::MirBuilder { Some(Ok(result_id)) } - // === ChatGPT5 Pro Design: Type-safe Call Resolution System === - - /// Resolve function call target to type-safe Callee - /// Implements the core logic of compile-time function resolution - fn resolve_call_target(&self, name: &str) -> Result { - method_resolution::resolve_call_target( - name, - &self.current_static_box, - &self.variable_map, - ) - } + // 🎯 箱理論: resolve_call_target は calls/utils.rs に移行済み + // (type-safe call resolution system) // Build function call: name(args) pub(super) fn build_function_call( @@ -739,14 +725,16 @@ impl super::MirBuilder { self.handle_standard_method_call(object_value, method, &arguments) } - // Map a user-facing type name to MIR type + // 🎯 箱理論: ユーティリティ関数 - calls/utils.rs に実装を移行済み + + /// Map a user-facing type name to MIR type pub(super) fn parse_type_name_to_mir(name: &str) -> super::MirType { - special_handlers::parse_type_name_to_mir(name) + crate::mir::builder::calls::utils::parse_type_name_to_mir(name) } - // Extract string literal from AST node if possible + /// Extract string literal from AST node if possible pub(super) fn extract_string_literal(node: &ASTNode) -> Option { - special_handlers::extract_string_literal(node) + crate::mir::builder::calls::utils::extract_string_literal(node) } // Build from expression: from Parent.method(arguments) @@ -773,208 +761,6 @@ impl super::MirBuilder { Ok(result_id) } - // Lower a box method into a standalone MIR function (with `me` parameter) - pub(super) fn lower_method_as_function( - &mut self, - func_name: String, - box_name: String, - params: Vec, - body: Vec, - ) -> Result<(), String> { - let signature = function_lowering::prepare_method_signature( - func_name, - &box_name, - ¶ms, - &body, - ); - let returns_value = !matches!(signature.return_type, MirType::Void); - let entry = self.block_gen.next(); - let function = super::MirFunction::new(signature, entry); - let saved_function = self.current_function.take(); - let saved_block = self.current_block.take(); - let saved_var_map = std::mem::take(&mut self.variable_map); - self.current_function = Some(function); - self.current_block = Some(entry); - self.ensure_block_exists(entry)?; - // Allocate parameter ValueIds from function's own ID space (starting from %0) - // This ensures params are %0, %1, %2... as expected by verification and printing - if let Some(ref mut f) = self.current_function { - let me_id = f.next_value_id(); // Use function's own ID allocator, not global - f.params.push(me_id); - self.variable_map.insert("me".to_string(), me_id); - self.value_origin_newbox.insert(me_id, box_name.clone()); - for p in ¶ms { - let pid = f.next_value_id(); // Use function's own ID allocator, not global - f.params.push(pid); - self.variable_map.insert(p.clone(), pid); - } - } - // Lower statements in sequence to preserve def→use order - let _last = self.cf_block(body)?; - if !returns_value && !self.is_current_block_terminated() { - let void_val = crate::mir::builder::emission::constant::emit_void(self); - self.emit_instruction(MirInstruction::Return { - value: Some(void_val), - })?; - } - if let Some(ref mut f) = self.current_function { - if returns_value - && matches!(f.signature.return_type, MirType::Void | MirType::Unknown) - { - let mut inferred: Option = None; - 'search: for (_bid, bb) in f.blocks.iter() { - for inst in bb.instructions.iter() { - if let MirInstruction::Return { value: Some(v) } = inst { - if let Some(mt) = self.value_types.get(v).cloned() { - inferred = Some(mt); - break 'search; - } - } - } - if let Some(MirInstruction::Return { value: Some(v) }) = &bb.terminator { - if let Some(mt) = self.value_types.get(v).cloned() { - inferred = Some(mt); - break; - } - } - } - if let Some(mt) = inferred { - f.signature.return_type = mt; - } - } - } - let finalized_function = self.current_function.take().unwrap(); - if let Some(ref mut module) = self.current_module { - module.add_function(finalized_function); - } - self.current_function = saved_function; - self.current_block = saved_block; - self.variable_map = saved_var_map; - Ok(()) - } - - // Lower a static method body into a standalone MIR function (no `me` parameter) - pub(super) fn lower_static_method_as_function( - &mut self, - func_name: String, - params: Vec, - body: Vec, - ) -> Result<(), String> { - // Derive static box context from function name prefix, e.g., "BoxName.method/N" - let saved_static_ctx = self.current_static_box.clone(); - if let Some(pos) = func_name.find('.') { - let box_name = &func_name[..pos]; - if !box_name.is_empty() { - self.current_static_box = Some(box_name.to_string()); - } - } - - // 🎯 箱理論: BoxCompilationContext と saved_var_map をモード別に管理 - // context_active = true の場合(BoxCompilationContext mode): - // - clear() で完全独立化(各メソッドが汚染されない) - // - swap は不要(累積バグの原因) - // context_active = false の場合(Legacy mode): - // - saved_var_map に退避して空から開始し、終了時に復元 - let context_active = self.compilation_context.is_some(); - let saved_var_map = if !context_active { - Some(std::mem::take(&mut self.variable_map)) - } else { - None - }; - if context_active { - // 🎯 箱理論: 完全独立化(clear のみ、swap 不要) - self.variable_map.clear(); - self.value_origin_newbox.clear(); - self.value_types.clear(); - } - - let signature = function_lowering::prepare_static_method_signature( - func_name, - ¶ms, - &body, - ); - let returns_value = !matches!(signature.return_type, MirType::Void); - let entry = self.block_gen.next(); - let function = super::MirFunction::new(signature, entry); - let saved_function = self.current_function.take(); - let saved_block = self.current_block.take(); - self.current_function = Some(function); - self.current_block = Some(entry); - self.ensure_block_exists(entry)?; - // Allocate parameter ValueIds from function's own ID space (starting from %0) - // This ensures params are %0, %1, %2... as expected by verification and printing - // Also track parameter names for LoopForm PHI construction - self.function_param_names.clear(); - if let Some(ref mut f) = self.current_function { - for p in ¶ms { - let pid = f.next_value_id(); // Use function's own ID allocator, not global - f.params.push(pid); - self.variable_map.insert(p.clone(), pid); - self.function_param_names.insert(p.clone()); - } - } - let program_ast = function_lowering::wrap_in_program(body); - let _last = self.build_expression(program_ast)?; - if !returns_value { - if let Some(ref mut f) = self.current_function { - if let Some(block) = f.get_block(self.current_block.unwrap()) { - if !block.is_terminated() { - let void_val = crate::mir::builder::emission::constant::emit_void(self); - self.emit_instruction(MirInstruction::Return { - value: Some(void_val), - })?; - } - } - } - } - if let Some(ref mut f) = self.current_function { - if returns_value - && matches!(f.signature.return_type, MirType::Void | MirType::Unknown) - { - let mut inferred: Option = None; - 'search: for (_bid, bb) in f.blocks.iter() { - for inst in bb.instructions.iter() { - if let MirInstruction::Return { value: Some(v) } = inst { - if let Some(mt) = self.value_types.get(v).cloned() { - inferred = Some(mt); - break 'search; - } - } - } - if let Some(MirInstruction::Return { value: Some(v) }) = &bb.terminator { - if let Some(mt) = self.value_types.get(v).cloned() { - inferred = Some(mt); - break; - } - } - } - if let Some(mt) = inferred { - f.signature.return_type = mt; - } - } - } - let finalized = self.current_function.take().unwrap(); - if let Some(ref mut module) = self.current_module { - module.add_function(finalized); - } - self.current_function = saved_function; - self.current_block = saved_block; - - // 🎯 箱理論: モード別に状態を復元 - if context_active { - // 🎯 BoxCompilationContext mode: clear のみ(次回も完全独立) - // swap は不要(累積バグを防ぐ) - self.variable_map.clear(); - self.value_origin_newbox.clear(); - self.value_types.clear(); - } else if let Some(saved) = saved_var_map { - // 従来モード: Main.main 側の variable_map を元に戻す - self.variable_map = saved; - } - - // Restore static box context - self.current_static_box = saved_static_ctx; - - Ok(()) - } + // 🎯 箱理論: lower_method_as_function と lower_static_method_as_function は + // calls/lowering.rs に移行済み(~200行削減) } diff --git a/src/mir/builder/calls/lowering.rs b/src/mir/builder/calls/lowering.rs new file mode 100644 index 00000000..2c47ded4 --- /dev/null +++ b/src/mir/builder/calls/lowering.rs @@ -0,0 +1,354 @@ +//! 🎯 箱理論: 関数lowering処理 +//! +//! 責務: +//! - static/instance method を MIR function に lowering +//! - BoxCompilationContext による完全独立化 +//! - パラメータ・型情報の適切な管理 + +use crate::ast::ASTNode; +use crate::mir::builder::{MirBuilder, MirType, MirInstruction}; +use super::function_lowering; +use std::collections::HashMap; + +/// 🎯 箱理論: Lowering Context(準備と復元) +struct LoweringContext { + context_active: bool, + saved_var_map: Option>, + saved_static_ctx: Option, + saved_function: Option, + saved_block: Option, +} + +impl MirBuilder { + /// 🎯 箱理論: Step 1 - Lowering Context準備 + fn prepare_lowering_context( + &mut self, + func_name: &str, + ) -> LoweringContext { + // Static box context設定 + let saved_static_ctx = self.current_static_box.clone(); + if let Some(pos) = func_name.find('.') { + let box_name = &func_name[..pos]; + if !box_name.is_empty() { + self.current_static_box = Some(box_name.to_string()); + } + } + + // BoxCompilationContext vs saved_var_map モード判定 + let context_active = self.compilation_context.is_some(); + let saved_var_map = if !context_active { + Some(std::mem::take(&mut self.variable_map)) + } else { + None + }; + + // BoxCompilationContext mode: clear()で完全独立化 + if context_active { + self.variable_map.clear(); + self.value_origin_newbox.clear(); + // value_types は clear しない(パラメータ型情報を保持) + } + + LoweringContext { + context_active, + saved_var_map, + saved_static_ctx, + saved_function: None, + saved_block: None, + } + } + + /// 🎯 箱理論: Step 2 - 関数スケルトン作成 + fn create_function_skeleton( + &mut self, + func_name: String, + params: &[String], + body: &[ASTNode], + ctx: &mut LoweringContext, + ) -> Result<(), String> { + let signature = function_lowering::prepare_static_method_signature( + func_name, + params, + body, + ); + let entry = self.block_gen.next(); + let function = super::super::MirFunction::new(signature, entry); + + // 現在の関数・ブロックを保存 + ctx.saved_function = self.current_function.take(); + ctx.saved_block = self.current_block.take(); + + // 新しい関数に切り替え + self.current_function = Some(function); + self.current_block = Some(entry); + self.ensure_block_exists(entry)?; + + Ok(()) + } + + /// 🎯 箱理論: Step 3 - パラメータ設定 + fn setup_function_params(&mut self, params: &[String]) { + self.function_param_names.clear(); + if let Some(ref mut f) = self.current_function { + for p in params { + let pid = f.next_value_id(); + f.params.push(pid); + self.variable_map.insert(p.clone(), pid); + self.function_param_names.insert(p.clone()); + } + } + } + + /// 🎯 箱理論: Step 4 - 本体lowering + fn lower_function_body(&mut self, body: Vec) -> Result<(), String> { + let program_ast = function_lowering::wrap_in_program(body); + let _last = self.build_expression(program_ast)?; + Ok(()) + } + + /// 🎯 箱理論: Step 5 - 関数finalize + fn finalize_function(&mut self, returns_value: bool) -> Result<(), String> { + // Void return追加(必要な場合) + if !returns_value { + if let Some(ref mut f) = self.current_function { + if let Some(block) = f.get_block(self.current_block.unwrap()) { + if !block.is_terminated() { + let void_val = crate::mir::builder::emission::constant::emit_void(self); + self.emit_instruction(MirInstruction::Return { + value: Some(void_val), + })?; + } + } + } + } + + // 型推論 + if let Some(ref mut f) = self.current_function { + if returns_value + && matches!(f.signature.return_type, MirType::Void | MirType::Unknown) + { + let mut inferred: Option = None; + 'search: for (_bid, bb) in f.blocks.iter() { + for inst in bb.instructions.iter() { + if let MirInstruction::Return { value: Some(v) } = inst { + if let Some(mt) = self.value_types.get(v).cloned() { + inferred = Some(mt); + break 'search; + } + } + } + if let Some(MirInstruction::Return { value: Some(v) }) = &bb.terminator { + if let Some(mt) = self.value_types.get(v).cloned() { + inferred = Some(mt); + break; + } + } + } + if let Some(mt) = inferred { + f.signature.return_type = mt; + } + } + } + + // Moduleに追加 + let finalized = self.current_function.take().unwrap(); + if let Some(ref mut module) = self.current_module { + module.add_function(finalized); + } + + Ok(()) + } + + /// 🎯 箱理論: Step 6 - Context復元 + fn restore_lowering_context(&mut self, ctx: LoweringContext) { + // 関数・ブロック復元 + self.current_function = ctx.saved_function; + self.current_block = ctx.saved_block; + + // モード別にcontext復元 + if ctx.context_active { + // BoxCompilationContext mode: clear のみ(次回も完全独立) + self.variable_map.clear(); + self.value_origin_newbox.clear(); + // value_types は clear しない(パラメータ型情報を保持) + } else if let Some(saved) = ctx.saved_var_map { + // Legacy mode: Main.main 側の variable_map を元に戻す + self.variable_map = saved; + } + + // Static box context復元 + self.current_static_box = ctx.saved_static_ctx; + } + + /// 🎯 箱理論: Step 2b - 関数スケルトン作成(instance method版) + fn create_method_skeleton( + &mut self, + func_name: String, + box_name: &str, + params: &[String], + body: &[ASTNode], + ctx: &mut LoweringContext, + ) -> Result<(), String> { + let signature = function_lowering::prepare_method_signature( + func_name, + box_name, + params, + body, + ); + let entry = self.block_gen.next(); + let function = super::super::MirFunction::new(signature, entry); + + // 現在の関数・ブロックを保存 + ctx.saved_function = self.current_function.take(); + ctx.saved_block = self.current_block.take(); + + // 新しい関数に切り替え + self.current_function = Some(function); + self.current_block = Some(entry); + self.ensure_block_exists(entry)?; + + Ok(()) + } + + /// 🎯 箱理論: Step 3b - パラメータ設定(instance method版: me + params) + fn setup_method_params(&mut self, box_name: &str, params: &[String]) { + if let Some(ref mut f) = self.current_function { + // First parameter is always 'me' + let me_id = f.next_value_id(); + f.params.push(me_id); + self.variable_map.insert("me".to_string(), me_id); + self.value_origin_newbox.insert(me_id, box_name.to_string()); + + // Then regular parameters + for p in params { + let pid = f.next_value_id(); + f.params.push(pid); + self.variable_map.insert(p.clone(), pid); + } + } + } + + /// 🎯 箱理論: Step 4b - 本体lowering(instance method版: cf_block) + fn lower_method_body(&mut self, body: Vec) -> Result<(), String> { + let _last = self.cf_block(body)?; + Ok(()) + } + + /// 🎯 箱理論: 統合エントリーポイント - static method lowering + pub(in crate::mir::builder) fn lower_static_method_as_function( + &mut self, + func_name: String, + params: Vec, + body: Vec, + ) -> Result<(), String> { + // Step 1: Context準備 + let mut ctx = self.prepare_lowering_context(&func_name); + + // Step 2: 関数スケルトン作成 + self.create_function_skeleton(func_name, ¶ms, &body, &mut ctx)?; + + // Step 3: パラメータ設定 + self.setup_function_params(¶ms); + + // Step 4: 本体lowering + self.lower_function_body(body)?; + + // Step 5: 関数finalize + let returns_value = if let Some(ref f) = self.current_function { + !matches!(f.signature.return_type, MirType::Void) + } else { + false + }; + self.finalize_function(returns_value)?; + + // Step 6: Context復元 + self.restore_lowering_context(ctx); + + Ok(()) + } + + /// 🎯 箱理論: 統合エントリーポイント - instance method lowering + pub(in crate::mir::builder) fn lower_method_as_function( + &mut self, + func_name: String, + box_name: String, + params: Vec, + body: Vec, + ) -> Result<(), String> { + // Step 1: Context準備(instance methodでは不要だがAPI統一のため) + let mut ctx = LoweringContext { + context_active: false, + saved_var_map: Some(std::mem::take(&mut self.variable_map)), + saved_static_ctx: None, + saved_function: None, + saved_block: None, + }; + + // Step 2b: 関数スケルトン作成(method版) + self.create_method_skeleton(func_name, &box_name, ¶ms, &body, &mut ctx)?; + + // Step 3b: パラメータ設定(me + params) + self.setup_method_params(&box_name, ¶ms); + + // Step 4b: 本体lowering(cf_block版) + self.lower_method_body(body)?; + + // Step 5: 関数finalize + let returns_value = if let Some(ref f) = self.current_function { + !matches!(f.signature.return_type, MirType::Void) + } else { + false + }; + + // Void return追加(必要な場合) + if !returns_value && !self.is_current_block_terminated() { + let void_val = crate::mir::builder::emission::constant::emit_void(self); + self.emit_instruction(MirInstruction::Return { + value: Some(void_val), + })?; + } + + // 型推論(Step 5の一部として) + if let Some(ref mut f) = self.current_function { + if returns_value + && matches!(f.signature.return_type, MirType::Void | MirType::Unknown) + { + let mut inferred: Option = None; + 'search: for (_bid, bb) in f.blocks.iter() { + for inst in bb.instructions.iter() { + if let MirInstruction::Return { value: Some(v) } = inst { + if let Some(mt) = self.value_types.get(v).cloned() { + inferred = Some(mt); + break 'search; + } + } + } + if let Some(MirInstruction::Return { value: Some(v) }) = &bb.terminator { + if let Some(mt) = self.value_types.get(v).cloned() { + inferred = Some(mt); + break; + } + } + } + if let Some(mt) = inferred { + f.signature.return_type = mt; + } + } + } + + // Moduleに追加 + let finalized_function = self.current_function.take().unwrap(); + if let Some(ref mut module) = self.current_module { + module.add_function(finalized_function); + } + + // Step 6: Context復元(simple version) + self.current_function = ctx.saved_function; + self.current_block = ctx.saved_block; + if let Some(saved) = ctx.saved_var_map { + self.variable_map = saved; + } + + Ok(()) + } +} diff --git a/src/mir/builder/calls/mod.rs b/src/mir/builder/calls/mod.rs index 3061d27f..2c3c1f58 100644 --- a/src/mir/builder/calls/mod.rs +++ b/src/mir/builder/calls/mod.rs @@ -1,30 +1,27 @@ -/*! - * Call System Module Organization - * - * Refactored from monolithic builder_calls.rs (879 lines) - * Split into focused modules following Single Responsibility Principle - */ +//! 🎯 箱理論: Call系処理のモジュール分離 +//! +//! 責務別に明確に分離された「箱」の集合: +//! - lowering: 関数lowering(static/instance method → MIR function) +//! - utils: ユーティリティ(resolve/parse/extract) +//! - emit: Call命令発行(統一Call/Legacy Call) [TODO] +//! - build: Call構築(function call/method call) [TODO] -// Core types +// Existing modules (already implemented elsewhere) +pub mod annotation; pub mod call_target; - -// Resolution system -pub mod method_resolution; - -// External calls +pub mod call_unified; pub mod extern_calls; - -// Special handlers +pub mod function_lowering; +pub mod method_resolution; pub mod special_handlers; -// Function lowering -pub mod function_lowering; +// New refactored modules +pub mod lowering; +pub mod utils; +// pub mod emit; // TODO: To be created +// pub mod build; // TODO: To be created -// Unified call system -pub mod call_unified; - -// Call result annotation -pub mod annotation; - -// Re-exports were removed to reduce unused-import warnings. -// Use module-qualified paths (e.g., special_handlers::parse_type_name_to_mir) instead. +// Re-export public interfaces +pub use call_target::CallTarget; +pub use lowering::*; +pub use utils::*; diff --git a/src/mir/builder/calls/utils.rs b/src/mir/builder/calls/utils.rs new file mode 100644 index 00000000..638cb611 --- /dev/null +++ b/src/mir/builder/calls/utils.rs @@ -0,0 +1,45 @@ +//! 🎯 箱理論: Call処理のユーティリティ関数群 +//! +//! 責務: +//! - 型名パース(parse_type_name_to_mir) +//! - 文字列リテラル抽出(extract_string_literal) +//! - Call結果アノテーション(annotate_call_result_from_func_name) +//! - Call target解決(resolve_call_target) + +use crate::ast::ASTNode; +use crate::mir::builder::{MirBuilder, MirType, ValueId}; + +/// Map a user-facing type name to MIR type +pub fn parse_type_name_to_mir(name: &str) -> MirType { + super::special_handlers::parse_type_name_to_mir(name) +} + +/// Extract string literal from AST node if possible +pub fn extract_string_literal(node: &ASTNode) -> Option { + super::special_handlers::extract_string_literal(node) +} + +impl MirBuilder { + /// Annotate a call result `dst` with the return type and origin if the callee + /// is a known user/static function in the current module. + pub(in crate::mir::builder) fn annotate_call_result_from_func_name>( + &mut self, + dst: ValueId, + func_name: S, + ) { + super::annotation::annotate_call_result_from_func_name(self, dst, func_name) + } + + /// Resolve function call target to type-safe Callee + /// Implements the core logic of compile-time function resolution + pub(in crate::mir::builder) fn resolve_call_target( + &self, + name: &str, + ) -> Result { + super::method_resolution::resolve_call_target( + name, + &self.current_static_box, + &self.variable_map, + ) + } +} diff --git a/src/mir/loop_api.rs b/src/mir/loop_api.rs index c5e3fa35..e53d26a1 100644 --- a/src/mir/loop_api.rs +++ b/src/mir/loop_api.rs @@ -96,7 +96,9 @@ impl LoopBuilderApi for super::builder::MirBuilder { super::builder::MirBuilder::emit_instruction(self, inst) } fn new_value(&mut self) -> ValueId { - self.value_gen.next() + // Use function-local allocator to avoid colliding with existing + // ValueIds in the current function. + self.next_value_id() } fn add_predecessor(&mut self, block: BasicBlockId, pred: BasicBlockId) -> Result<(), String> { diff --git a/src/mir/loop_builder.rs b/src/mir/loop_builder.rs index fe3723dc..b35a4d78 100644 --- a/src/mir/loop_builder.rs +++ b/src/mir/loop_builder.rs @@ -653,7 +653,9 @@ impl<'a> LoopBuilder<'a> { } fn new_value(&mut self) -> ValueId { - self.parent_builder.value_gen.next() + // Use function-local allocator via MirBuilder helper to keep + // ValueId ranges consistent within the current function. + self.parent_builder.next_value_id() } fn set_current_block(&mut self, block_id: BasicBlockId) -> Result<(), String> { diff --git a/src/tests/mir_stage1_using_resolver_verify.rs b/src/tests/mir_stage1_using_resolver_verify.rs index ef026ba1..46272e8e 100644 --- a/src/tests/mir_stage1_using_resolver_verify.rs +++ b/src/tests/mir_stage1_using_resolver_verify.rs @@ -2,10 +2,16 @@ use crate::ast::ASTNode; use crate::mir::{MirCompiler, MirVerifier}; use crate::parser::NyashParser; +fn ensure_stage3_env() { + std::env::set_var("NYASH_PARSER_STAGE3", "1"); + std::env::set_var("NYASH_PARSER_ALLOW_SEMICOLON", "1"); +} + /// Minimal Stage‑1 using resolver harness resembling Stage1UsingResolverBox.resolve_for_source. /// Focuses on loops over ArrayBox/MapBox and JSON scanning, without FileBox/@ sugar. #[test] fn mir_stage1_using_resolver_min_fragment_verifies() { + ensure_stage3_env(); let src = r#" using lang.compiler.parser.scan.parser_common_utils_box as ParserCommonUtilsBox using selfhost.shared.json.utils.json_frag as JsonFragBox @@ -71,6 +77,7 @@ static box Stage1UsingResolverMini { /// Verify MIR/SSA for ParserBox.parse_program2 in isolation by compiling a small wrapper. #[test] fn mir_parserbox_parse_program2_harness_parses_minimal_source() { + ensure_stage3_env(); // Minimal wrapper that brings ParserBox into scope and calls parse_program2. let src = r#" using lang.compiler.parser.parser_box as ParserBox diff --git a/src/tests/mir_stageb_like_args_length.rs b/src/tests/mir_stageb_like_args_length.rs index b6c3f33c..5af09193 100644 --- a/src/tests/mir_stageb_like_args_length.rs +++ b/src/tests/mir_stageb_like_args_length.rs @@ -1,4 +1,10 @@ use crate::parser::NyashParser; +use crate::mir::MirPrinter; + +fn ensure_stage3_env() { + std::env::set_var("NYASH_PARSER_STAGE3", "1"); + std::env::set_var("NYASH_PARSER_ALLOW_SEMICOLON", "1"); +} use crate::ast::ASTNode; use crate::mir::{MirCompiler, MirVerifier}; @@ -15,6 +21,7 @@ use crate::mir::{MirCompiler, MirVerifier}; /// を Rust MirBuilder で MIR 化し、SSA/PHI が破綻していないことを検証する。 #[test] fn mir_stageb_like_args_length_verifies() { + ensure_stage3_env(); let src = r#" static box StageBArgsBox { method resolve_src(args) { @@ -36,6 +43,10 @@ static box StageBArgsBox { // Verify MIR SSA/PHI invariants let mut verifier = MirVerifier::new(); if let Err(errors) = verifier.verify_module(&cr.module) { + if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") { + let dump = MirPrinter::new().print_module(&cr.module); + eprintln!("----- MIR DUMP (StageBArgsBox.resolve_src) -----\n{}", dump); + } for e in &errors { eprintln!("[mir-verify] {}", e); } @@ -61,6 +72,7 @@ static box StageBArgsBox { /// を Rust MirBuilder で MIR 化し、SSA/PHI が破綻していないことを検証する。 #[test] fn mir_stageb_like_if_args_length_loop_verifies() { + ensure_stage3_env(); let src = r#" static box StageBArgsBox { method process(args) { @@ -84,6 +96,10 @@ static box StageBArgsBox { let mut verifier = MirVerifier::new(); if let Err(errors) = verifier.verify_module(&cr.module) { + if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") { + let dump = MirPrinter::new().print_module(&cr.module); + eprintln!("----- MIR DUMP (StageBArgsBox.process if+loop) -----\n{}", dump); + } for e in &errors { eprintln!("[mir-verify] {}", e); } @@ -107,6 +123,7 @@ static box StageBArgsBox { /// } #[test] fn mir_stageb_like_nested_if_loop_verifies() { + ensure_stage3_env(); let src = r#" static box TestNested { method complex(data) { @@ -135,6 +152,10 @@ static box TestNested { let mut verifier = MirVerifier::new(); if let Err(errors) = verifier.verify_module(&cr.module) { + if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") { + let dump = MirPrinter::new().print_module(&cr.module); + eprintln!("----- MIR DUMP (TestNested.complex nested if+loop) -----\n{}", dump); + } for e in &errors { eprintln!("[mir-verify] {}", e); } @@ -151,6 +172,7 @@ static box TestNested { /// } #[test] fn mir_stageb_like_loop_cond_uses_length_verifies() { + ensure_stage3_env(); let src = r#" static box StageBArgsBox { method process(args) { @@ -172,6 +194,10 @@ static box StageBArgsBox { let mut verifier = MirVerifier::new(); if let Err(errors) = verifier.verify_module(&cr.module) { + if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") { + let dump = MirPrinter::new().print_module(&cr.module); + eprintln!("----- MIR DUMP (StageBArgsBox.process loop cond uses length) -----\n{}", dump); + } for e in &errors { eprintln!("[mir-verify] {}", e); } @@ -188,6 +214,7 @@ static box StageBArgsBox { /// } #[test] fn mir_stageb_like_conditional_and_loop_length_verifies() { + ensure_stage3_env(); let src = r#" static box TestNested2 { method walk(data) { @@ -209,6 +236,10 @@ static box TestNested2 { let mut verifier = MirVerifier::new(); if let Err(errors) = verifier.verify_module(&cr.module) { + if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") { + let dump = MirPrinter::new().print_module(&cr.module); + eprintln!("----- MIR DUMP (TestNested2.walk conditional+loop length) -----\n{}", dump); + } for e in &errors { eprintln!("[mir-verify] {}", e); } @@ -221,6 +252,7 @@ static box TestNested2 { /// - 文字列内/エスケープなどの分岐を含むが、ここでは最小限の骨格のみを再現。 #[test] fn mir_jsonscanbox_like_seek_array_end_verifies() { + ensure_stage3_env(); let src = r#" using selfhost.shared.json.core.string_scan as StringScanBox @@ -252,6 +284,10 @@ static box JsonScanBoxMini { let mut verifier = MirVerifier::new(); if let Err(errors) = verifier.verify_module(&cr.module) { + if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") { + let dump = MirPrinter::new().print_module(&cr.module); + eprintln!("----- MIR DUMP (JsonScanBoxMini.seek_array_end) -----\n{}", dump); + } for e in &errors { eprintln!("[mir-verify] {}", e); } diff --git a/src/tests/mir_stageb_loop_break_continue.rs b/src/tests/mir_stageb_loop_break_continue.rs new file mode 100644 index 00000000..ce877411 --- /dev/null +++ b/src/tests/mir_stageb_loop_break_continue.rs @@ -0,0 +1,141 @@ +use crate::ast::ASTNode; +use crate::mir::{MirCompiler, MirPrinter, MirVerifier}; +use crate::parser::NyashParser; + +fn ensure_stage3_env() { + std::env::set_var("NYASH_PARSER_STAGE3", "1"); + std::env::set_var("NYASH_PARSER_ALLOW_SEMICOLON", "1"); +} + +/// Stage‑B 風: loop + break/continue + ArrayBox.length/get +/// +/// static box LoopBreakContinueBox { +/// method sum_positive_until_null(arr) { +/// if arr == null { return 0 } +/// local i = 0 +/// local acc = 0 +/// loop (i < arr.length()) { +/// local v = arr.get(i) +/// if v == null { break } +/// if v < 0 { +/// i = i + 1 +/// continue +/// } +/// acc = acc + v +/// i = i + 1 +/// } +/// return acc +/// } +/// } +#[test] +fn mir_stageb_loop_break_continue_verifies() { + ensure_stage3_env(); + let src = r#" +static box LoopBreakContinueBox { + method sum_positive_until_null(arr) { + if arr == null { return 0 } + local i = 0 + local acc = 0 + loop (i < arr.length()) { + local v = arr.get(i) + if v == null { break } + if v < 0 { + i = i + 1 + continue + } + acc = acc + v + i = i + 1 + } + return acc + } +} +"#; + + let ast: ASTNode = NyashParser::parse_from_string(src).expect("parse ok"); + let mut mc = MirCompiler::with_options(false); + let cr = mc.compile(ast).expect("compile"); + + let mut verifier = MirVerifier::new(); + if let Err(errors) = verifier.verify_module(&cr.module) { + if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") { + let dump = MirPrinter::new().print_module(&cr.module); + eprintln!("----- MIR DUMP (LoopBreakContinueBox.sum_positive_until_null) -----\n{}", dump); + } + for e in &errors { + eprintln!("[mir-verify] {}", e); + } + panic!("MIR verification failed for StageB-like loop+break/continue pattern"); + } +} + +/// Stage‑B 風: 入れ子ループ + break/continue + length/get +/// +/// static box LoopNestedBreakBox { +/// method nested_walk(arr) { +/// if arr == null { return 0 } +/// local i = 0 +/// local total = 0 +/// loop (i < arr.length()) { +/// local inner = arr.get(i) +/// if inner == null { +/// i = i + 1 +/// continue +/// } +/// local j = 0 +/// loop (j < inner.length()) { +/// local v = inner.get(j) +/// if v == null { break } +/// total = total + v +/// j = j + 1 +/// } +/// i = i + 1 +/// } +/// return total +/// } +/// } +#[test] +fn mir_stageb_nested_loop_break_continue_verifies() { + ensure_stage3_env(); + let src = r#" +static box LoopNestedBreakBox { + method nested_walk(arr) { + if arr == null { return 0 } + local i = 0 + local total = 0 + loop (i < arr.length()) { + local inner = arr.get(i) + if inner == null { + i = i + 1 + continue + } + local j = 0 + loop (j < inner.length()) { + local v = inner.get(j) + if v == null { break } + total = total + v + j = j + 1 + } + i = i + 1 + } + return total + } +} +"#; + + let ast: ASTNode = NyashParser::parse_from_string(src).expect("parse ok"); + let mut mc = MirCompiler::with_options(false); + let cr = mc.compile(ast).expect("compile"); + + let mut verifier = MirVerifier::new(); + if let Err(errors) = verifier.verify_module(&cr.module) { + if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") { + let dump = MirPrinter::new().print_module(&cr.module); + eprintln!("----- MIR DUMP (LoopNestedBreakBox.nested_walk) -----\n{}", dump); + } + for e in &errors { + eprintln!("[mir-verify] {}", e); + } + panic!("MIR verification failed for StageB-like nested loop+break/continue pattern"); + } +} + diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 537b72a7..c654a3db 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -7,6 +7,7 @@ pub mod identical_exec_collections; pub mod identical_exec_instance; pub mod identical_exec_string; pub mod mir_stageb_like_args_length; +pub mod mir_stageb_loop_break_continue; pub mod mir_stage1_using_resolver_verify; pub mod mir_vm_poc; pub mod nyash_abi_basic;