From 1eea40454fd28a21f1226489073a7541a40c1118 Mon Sep 17 00:00:00 2001 From: nyash-codex Date: Wed, 26 Nov 2025 11:38:45 +0900 Subject: [PATCH] =?UTF-8?q?test(joinir):=20L-2.2=20Step-3=20-=20Add=20Stag?= =?UTF-8?q?e-B=20JoinIR=E2=86=92MIR=20structure=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Expose `convert_joinir_to_mir` as `pub(crate)` for test access - Add `joinir_stageb_body_structure_test` and `joinir_stageb_funcscanner_structure_test` to `joinir_json_min.rs` - Verify: JoinIR lowering succeeds, JoinIR→MIR conversion succeeds, function count matches, each function has >= 1 block - Result: Both Stage-B functions produce 2 JoinIR functions (entry + loop_step) with appropriate block counts - Note: Current tests verify handwritten JoinIR→MIR bridge layer, not full MIR→JoinIR roundtrip (future improvement) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/mir/join_ir_vm_bridge.rs | 4 +- src/tests/joinir_json_min.rs | 185 +++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 1 deletion(-) diff --git a/src/mir/join_ir_vm_bridge.rs b/src/mir/join_ir_vm_bridge.rs index dc2acae9..bcde2a15 100644 --- a/src/mir/join_ir_vm_bridge.rs +++ b/src/mir/join_ir_vm_bridge.rs @@ -144,7 +144,9 @@ pub fn run_joinir_via_vm( } /// Phase 30.x: JoinIR → MIR 変換器 -fn convert_joinir_to_mir(join_module: &JoinModule) -> Result { +/// +/// Phase 32 L-2.2 Step-3: テストから呼び出し可能に `pub(crate)` 化 +pub(crate) fn convert_joinir_to_mir(join_module: &JoinModule) -> Result { let mut mir_module = MirModule::new("joinir_bridge".to_string()); // Convert all JoinIR functions to MIR (entry function becomes "skip" or similar) diff --git a/src/tests/joinir_json_min.rs b/src/tests/joinir_json_min.rs index 6fea1fb6..0041afb8 100644 --- a/src/tests/joinir_json_min.rs +++ b/src/tests/joinir_json_min.rs @@ -444,3 +444,188 @@ fn joinir_json_v0_stageb_body_min_matches_fixture() { fn joinir_json_v0_stageb_funcscanner_min_matches_fixture() { run_snapshot_test(SnapshotCase::StagebFuncscannerMin); } + +// ============================================================================ +// Phase 32 L-2.2 Step-3: JoinIR → MIR 構造テスト +// ============================================================================ +// +// 目的: +// - JoinIR lowering と JoinIR→MIR 変換が Stage-B ループ構造を壊さないことを検証 +// - Route B (JoinIR→MIR) の健全性チェック +// +// 実行方法: +// cargo test --release joinir_stageb_structure -- --nocapture + +use crate::mir::join_ir_vm_bridge::convert_joinir_to_mir; + +/// Stage-B Body: JoinIR lowering + JoinIR→MIR 変換の構造テスト +#[test] +fn joinir_stageb_body_structure_test() { + // Stage-3 parser を有効化 + std::env::set_var("NYASH_PARSER_STAGE3", "1"); + std::env::set_var("HAKO_PARSER_STAGE3", "1"); + + let src = match std::fs::read_to_string("apps/tests/stageb_body_extract_minimal.hako") { + Ok(s) => s, + Err(_) => { + eprintln!("[joinir/stageb_body] Source file not found, skipping"); + return; + } + }; + + let ast: ASTNode = match NyashParser::parse_from_string(&src) { + Ok(a) => a, + Err(e) => { + eprintln!("[joinir/stageb_body] Parse failed: {:?}", e); + return; + } + }; + + let mut mc = MirCompiler::with_options(false); + let compiled = match mc.compile(ast) { + Ok(c) => c, + Err(e) => { + eprintln!("[joinir/stageb_body] MIR compile failed: {:?}", e); + return; + } + }; + + // Step 1: JoinIR lowering + let join_module = match lower_stageb_body_to_joinir(&compiled.module) { + Some(jm) => jm, + None => { + eprintln!("[joinir/stageb_body] lowering returned None, skipping"); + return; + } + }; + + eprintln!( + "[joinir/stageb_body] ✅ JoinIR lowering succeeded: {} functions", + join_module.functions.len() + ); + + // 構造チェック: 2 関数(entry + loop_step) + assert!( + join_module.functions.len() >= 2, + "Stage-B Body should have at least 2 JoinIR functions" + ); + + // Step 2: JoinIR → MIR 変換 + let mir_module = match convert_joinir_to_mir(&join_module) { + Ok(m) => m, + Err(e) => { + eprintln!("[joinir/stageb_body] JoinIR→MIR conversion failed: {:?}", e); + panic!("JoinIR→MIR conversion should succeed"); + } + }; + + eprintln!( + "[joinir/stageb_body] ✅ JoinIR→MIR conversion succeeded: {} functions", + mir_module.functions.len() + ); + + // 構造チェック: MIR 関数数が JoinIR 関数数と一致 + assert_eq!( + mir_module.functions.len(), + join_module.functions.len(), + "MIR function count should match JoinIR function count" + ); + + // 各関数の Block 数をチェック + for (name, func) in &mir_module.functions { + let block_count = func.blocks.len(); + eprintln!( + "[joinir/stageb_body] Function '{}': {} blocks", + name, block_count + ); + assert!(block_count >= 1, "Each function should have at least 1 block"); + } + + eprintln!("[joinir/stageb_body] ✅ Structure test passed"); +} + +/// Stage-B FuncScanner: JoinIR lowering + JoinIR→MIR 変換の構造テスト +#[test] +fn joinir_stageb_funcscanner_structure_test() { + // Stage-3 parser を有効化 + std::env::set_var("NYASH_PARSER_STAGE3", "1"); + std::env::set_var("HAKO_PARSER_STAGE3", "1"); + + let src = match std::fs::read_to_string("apps/tests/stageb_funcscanner_scan_boxes_minimal.hako") { + Ok(s) => s, + Err(_) => { + eprintln!("[joinir/stageb_funcscanner] Source file not found, skipping"); + return; + } + }; + + let ast: ASTNode = match NyashParser::parse_from_string(&src) { + Ok(a) => a, + Err(e) => { + eprintln!("[joinir/stageb_funcscanner] Parse failed: {:?}", e); + return; + } + }; + + let mut mc = MirCompiler::with_options(false); + let compiled = match mc.compile(ast) { + Ok(c) => c, + Err(e) => { + eprintln!("[joinir/stageb_funcscanner] MIR compile failed: {:?}", e); + return; + } + }; + + // Step 1: JoinIR lowering + let join_module = match lower_stageb_funcscanner_to_joinir(&compiled.module) { + Some(jm) => jm, + None => { + eprintln!("[joinir/stageb_funcscanner] lowering returned None, skipping"); + return; + } + }; + + eprintln!( + "[joinir/stageb_funcscanner] ✅ JoinIR lowering succeeded: {} functions", + join_module.functions.len() + ); + + // 構造チェック: 2 関数(entry + loop_step) + assert!( + join_module.functions.len() >= 2, + "Stage-B FuncScanner should have at least 2 JoinIR functions" + ); + + // Step 2: JoinIR → MIR 変換 + let mir_module = match convert_joinir_to_mir(&join_module) { + Ok(m) => m, + Err(e) => { + eprintln!("[joinir/stageb_funcscanner] JoinIR→MIR conversion failed: {:?}", e); + panic!("JoinIR→MIR conversion should succeed"); + } + }; + + eprintln!( + "[joinir/stageb_funcscanner] ✅ JoinIR→MIR conversion succeeded: {} functions", + mir_module.functions.len() + ); + + // 構造チェック: MIR 関数数が JoinIR 関数数と一致 + assert_eq!( + mir_module.functions.len(), + join_module.functions.len(), + "MIR function count should match JoinIR function count" + ); + + // 各関数の Block 数をチェック + for (name, func) in &mir_module.functions { + let block_count = func.blocks.len(); + eprintln!( + "[joinir/stageb_funcscanner] Function '{}': {} blocks", + name, block_count + ); + assert!(block_count >= 1, "Each function should have at least 1 block"); + } + + eprintln!("[joinir/stageb_funcscanner] ✅ Structure test passed"); +}