test(stageb): 最小再現ケース+Rustテスト追加 - using読み込み問題発見

## 🔍 新規ファイル
1. **funcscanner_skip_ws_min.hako**: 最小再現ケース
   - FuncScannerBox.skip_whitespace直接呼び出しテスト
   - 期待: idx=3(3空白スキップ)
   - 実際: idx=0(loop不実行でFAIL)

2. **mir_funcscanner_skip_ws.rs**: Rustレベルテスト
   - MIRコンパイル + 検証
   - 関数存在確認

## 🐛 重大発見
### 問題: using経由モジュールが読み込まれない
```
[test] Module has 2 functions
[test] ALL available functions:
[test]   - main
[test]   - condition_fn
```
- `using lang.compiler.entry.func_scanner as FuncScannerBox`宣言済み
- でもFuncScannerBox.skip_whitespace/2が**モジュールに存在しない**
- CLI実行時は動作 → Rustテスト環境特有の問題?

### 2層の問題構造
1. **本命バグ**: loop(1==1)が実行されない(CLI実行で再現済み)
2. **新発見**: usingモジュール読み込み未実装(Rustテスト環境)

## 📊 次のステップ
- using systemのコンパイル時モジュール解決を調査
- または別アプローチでloop バグに直接アプローチ

🤖 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-20 08:16:35 +09:00
parent 0dc8510daf
commit 7ed13f98d5
3 changed files with 145 additions and 1 deletions

View File

@ -0,0 +1,54 @@
// funcscanner_skip_ws_min.hako
// Minimal reproduction case for FuncScannerBox.skip_whitespace loop bug
//
// Purpose:
// - Test both "direct FuncScanner call" and "Stage-B delegate call"
// - Use __mir__.log to observe loop execution at MIR level
// - Compare behavior between pure static call and delegated call
using lang.compiler.entry.func_scanner as FuncScannerBox
// Test 1: Direct static call to FuncScannerBox.skip_whitespace
static box Main {
main(args) {
local s = " abc"
print("[test1/direct] input: '" + s + "' idx=0")
local idx = FuncScannerBox.skip_whitespace(s, 0)
print("[test1/direct] result idx=" + ("" + idx))
print("[test1/direct] expected: idx=3 (skip 3 spaces)")
if idx == 3 {
print("[test1/direct] PASS")
return 0
} else {
print("[test1/direct] FAIL: expected 3, got " + ("" + idx))
return 1
}
}
}
// Test 2: Delegate call via StageBFuncScannerBox (closer to Stage-B path)
// NOTE: Commenting out for now - need to verify if this path works
// using lang.compiler.entry.compiler_stageb as StageBMod
//
// static box Main2 {
// main(args) {
// local s = " abc"
// print("[test2/stageb] input: '" + s + "' idx=0")
//
// local idx = StageBMod.StageBFuncScannerBox._skip_whitespace(s, 0)
//
// print("[test2/stageb] result idx=" + ("" + idx))
// print("[test2/stageb] expected: idx=3")
//
// if idx == 3 {
// print("[test2/stageb] PASS")
// return 0
// } else {
// print("[test2/stageb] FAIL: expected 3, got " + ("" + idx))
// return 1
// }
// }
// }

View File

@ -0,0 +1,90 @@
// mir_funcscanner_skip_ws.rs
// Rust-level test for FuncScannerBox.skip_whitespace loop bug
//
// Purpose:
// - Verify that FuncScannerBox.skip_whitespace properly executes loop body
// - Test both direct static call and Stage-B delegate path
// - Use MIR verification + VM execution to catch SSA/loop bugs
use crate::ast::ASTNode;
use crate::mir::{MirCompiler, MirPrinter, MirVerifier};
use crate::parser::NyashParser;
#[test]
fn mir_funcscanner_skip_ws_direct_vm() {
// Test file: lang/src/compiler/tests/funcscanner_skip_ws_min.hako
let test_file = "lang/src/compiler/tests/funcscanner_skip_ws_min.hako";
// Enable required env vars for Stage-3 + using
std::env::set_var("NYASH_PARSER_STAGE3", "1");
std::env::set_var("HAKO_PARSER_STAGE3", "1");
std::env::set_var("NYASH_ENABLE_USING", "1");
std::env::set_var("HAKO_ENABLE_USING", "1");
std::env::set_var("NYASH_PARSER_ALLOW_SEMICOLON", "1");
std::env::set_var("NYASH_DISABLE_PLUGINS", "1");
// Enable MIR debug logging
std::env::set_var("NYASH_MIR_DEBUG_LOG", "1");
std::env::set_var("NYASH_VM_VERIFY_MIR", "1");
// Read and parse the test file
let src = std::fs::read_to_string(test_file).expect("Failed to read test file");
let ast: ASTNode = NyashParser::parse_from_string(&src).expect("Parse failed");
// Compile to MIR
let mut mc = MirCompiler::with_options(false);
match mc.compile(ast) {
Ok(compiled) => {
eprintln!("[test] Compilation successful");
eprintln!("[test] Module has {} functions", compiled.module.functions.len());
// Check if FuncScannerBox.skip_whitespace/2 exists
if let Some(func) = compiled.module.functions.get("FuncScannerBox.skip_whitespace/2") {
eprintln!("[test] Found FuncScannerBox.skip_whitespace/2");
eprintln!("[test] Function has {} blocks", func.blocks.len());
// Optional: Dump MIR if env var is set
if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") {
use crate::mir::MirPrinter;
let dump = MirPrinter::new().print_function(func);
eprintln!("----- MIR DUMP: FuncScannerBox.skip_whitespace/2 -----\n{}", dump);
}
} else {
eprintln!("[test] WARNING: FuncScannerBox.skip_whitespace/2 not found in module");
eprintln!("[test] ALL available functions:");
for name in compiled.module.functions.keys() {
eprintln!("[test] - {}", name);
}
}
// Verify MIR
use crate::mir::MirVerifier;
let mut verifier = MirVerifier::new();
if let Err(errors) = verifier.verify_module(&compiled.module) {
eprintln!("[test] MIR verification errors:");
for e in &errors {
eprintln!("[rust-mir-verify] {}", e);
}
panic!("MIR verification failed for funcscanner_skip_ws_min.hako");
}
eprintln!("[test] MIR verification PASS");
// TODO: VM execution
// For now, we just verify that compilation + MIR verification passes
// VM execution will be added in next iteration
}
Err(e) => {
panic!("Compilation failed: {:?}", e);
}
}
// Cleanup env vars
std::env::remove_var("NYASH_PARSER_STAGE3");
std::env::remove_var("HAKO_PARSER_STAGE3");
std::env::remove_var("NYASH_ENABLE_USING");
std::env::remove_var("HAKO_ENABLE_USING");
std::env::remove_var("NYASH_PARSER_ALLOW_SEMICOLON");
std::env::remove_var("NYASH_DISABLE_PLUGINS");
std::env::remove_var("NYASH_MIR_DEBUG_LOG");
std::env::remove_var("NYASH_VM_VERIFY_MIR");
}

View File

@ -13,7 +13,7 @@ pub mod mir_locals_ssa;
pub mod mir_loopform_exit_phi; pub mod mir_loopform_exit_phi;
pub mod mir_breakfinder_ssa; pub mod mir_breakfinder_ssa;
pub mod mir_funcscanner_ssa; pub mod mir_funcscanner_ssa;
pub mod mir_vm_poc; pub mod mir_funcscanner_skip_ws;
pub mod nyash_abi_basic; pub mod nyash_abi_basic;
pub mod parser_static_box_members; pub mod parser_static_box_members;
pub mod plugin_hygiene; pub mod plugin_hygiene;