fix(mir/exit_phi): Pass branch_source_block to build_exit_phis()

## Problem
Exit PHI generation was using header_id as predecessor, but when
build_expression(condition) creates new blocks, the actual branch
instruction is emitted from a different block, causing:
"phi pred mismatch: no input for predecessor BasicBlockId(X)"

## Solution
- Modified build_exit_phis() to accept branch_source_block parameter
- Capture actual block after condition evaluation in loop_builder.rs
- Use branch_source_block instead of header_id for PHI inputs

## Progress
- Error changed from ValueId(5941)/BasicBlockId(4674) to
  ValueId(5927)/BasicBlockId(4672), showing partial fix
- Added comprehensive test suite in mir_loopform_exit_phi.rs
- Added debug logging to trace condition block creation

## Status
Partial fix - unit tests pass, but Test 2 (Stage-B compilation) still
has errors. Needs further investigation of complex nested compilation
scenarios.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-18 04:26:50 +09:00
parent 5bb094d58f
commit f92779cfe8
3 changed files with 251 additions and 6 deletions

View File

@ -0,0 +1,174 @@
/*!
* Unit tests for LoopForm v2 exit PHI generation
*
* Tests the build_exit_phis() implementation in loopform_builder.rs
* Focus: predecessor tracking and PHI input generation for break statements
*/
use crate::parser::NyashParser;
use crate::mir::{MirCompiler, MirVerifier};
#[test]
fn test_loopform_exit_phi_single_break() {
// Enable LoopForm PHI v2 and MIR verification
std::env::set_var("NYASH_LOOPFORM_PHI_V2", "1");
std::env::set_var("NYASH_VM_VERIFY_MIR", "1");
std::env::set_var("NYASH_LOOPFORM_DEBUG", "1");
std::env::set_var("NYASH_PARSER_STAGE3", "1");
std::env::set_var("NYASH_PARSER_ALLOW_SEMICOLON", "1");
let src = r#"
static box TestExitPhi {
test() {
local i = 0
loop(i < 10) {
if i == 5 { break }
i = i + 1
}
return i
}
}
"#;
println!("=== Test: Single break statement ===");
// Parse
let ast = NyashParser::parse_from_string(src)
.expect("parse failed");
// Compile
let mut mc = MirCompiler::with_options(false);
let cr = mc.compile(ast).expect("compile failed");
// MIR verification
let mut verifier = MirVerifier::new();
if let Err(errors) = verifier.verify_module(&cr.module) {
for err in &errors {
eprintln!("❌ MIR verification error: {}", err);
}
panic!("❌ MIR verification failed with {} errors", errors.len());
}
println!("✅ MIR verification passed");
}
#[test]
fn test_loopform_exit_phi_multiple_breaks() {
std::env::set_var("NYASH_LOOPFORM_PHI_V2", "1");
std::env::set_var("NYASH_VM_VERIFY_MIR", "1");
std::env::set_var("NYASH_LOOPFORM_DEBUG", "1");
std::env::set_var("NYASH_PARSER_STAGE3", "1");
std::env::set_var("NYASH_PARSER_ALLOW_SEMICOLON", "1");
let src = r#"
static box TestMultiBreak {
test() {
local i = 0
loop(i < 10) {
if i == 3 { break }
if i == 5 { break }
i = i + 1
}
return i
}
}
"#;
println!("=== Test: Multiple break statements ===");
let ast = NyashParser::parse_from_string(src).expect("parse failed");
let mut mc = MirCompiler::with_options(false);
let cr = mc.compile(ast).expect("compile failed");
let mut verifier = MirVerifier::new();
if let Err(errors) = verifier.verify_module(&cr.module) {
for err in &errors {
eprintln!("❌ MIR verification error: {}", err);
}
panic!("❌ MIR verification failed with {} errors", errors.len());
}
println!("✅ MIR verification passed");
}
#[test]
fn test_loopform_exit_phi_nested_if_break() {
std::env::set_var("NYASH_LOOPFORM_PHI_V2", "1");
std::env::set_var("NYASH_VM_VERIFY_MIR", "1");
std::env::set_var("NYASH_LOOPFORM_DEBUG", "1");
std::env::set_var("NYASH_PARSER_STAGE3", "1");
std::env::set_var("NYASH_PARSER_ALLOW_SEMICOLON", "1");
let src = r#"
static box TestNestedBreak {
test() {
local i = 0
local found = 0
loop(i < 10) {
if i > 5 {
if i == 7 {
found = 1
break
}
}
i = i + 1
}
return found
}
}
"#;
println!("=== Test: Nested if with break ===");
let ast = NyashParser::parse_from_string(src).expect("parse failed");
let mut mc = MirCompiler::with_options(false);
let cr = mc.compile(ast).expect("compile failed");
let mut verifier = MirVerifier::new();
if let Err(errors) = verifier.verify_module(&cr.module) {
for err in &errors {
eprintln!("❌ MIR verification error: {}", err);
}
panic!("❌ MIR verification failed with {} errors", errors.len());
}
println!("✅ MIR verification passed");
}
#[test]
fn test_loopform_exit_phi_multiple_vars() {
std::env::set_var("NYASH_LOOPFORM_PHI_V2", "1");
std::env::set_var("NYASH_VM_VERIFY_MIR", "1");
std::env::set_var("NYASH_LOOPFORM_DEBUG", "1");
std::env::set_var("NYASH_PARSER_STAGE3", "1");
std::env::set_var("NYASH_PARSER_ALLOW_SEMICOLON", "1");
let src = r#"
static box TestMultiVars {
test() {
local i = 0
local sum = 0
local product = 1
loop(i < 10) {
if sum > 20 { break }
sum = sum + i
product = product * 2
i = i + 1
}
return sum
}
}
"#;
println!("=== Test: Multiple variables with break ===");
let ast = NyashParser::parse_from_string(src).expect("parse failed");
let mut mc = MirCompiler::with_options(false);
let cr = mc.compile(ast).expect("compile failed");
let mut verifier = MirVerifier::new();
if let Err(errors) = verifier.verify_module(&cr.module) {
for err in &errors {
eprintln!("❌ MIR verification error: {}", err);
}
panic!("❌ MIR verification failed with {} errors", errors.len());
}
println!("✅ MIR verification passed");
}