# Phase 118: Loop + If-Only Merge Parity (VM + LLVM EXE) ## 目的 Phase 117で if-only nested-if + call merge が固まったので、Phase 118では "loop + if-only + merge" を固定する。loop側のPHI生成と if-only条件付き更新を組み合わせたパターンを確立し、VM + LLVM EXE の両方で動作保証する。 ## 背景 - **Phase 117**: if-only nested-if + call merge parity 確立(`phase117_if_only_nested_if_call_merge_min.hako`) - **Phase 118**: loop + if-only merge の組み合わせテスト - ループ内で if-only により条件付き変数更新 - Pattern3 (if-sum) の活用(既存実装を利用) - ループ継続条件での PHI 生成 - Exit での merge 処理 ## テストケース ### Fixture **ファイル**: `apps/tests/phase118_loop_nested_if_merge_min.hako` ```hako // Phase 118: loop + if-only merge parity test // Expected output: 2 (numeric line) // Calculation: i=0: x=0 (skip), i=1: x=0+1→1, i=2: x=1+1→2 static box Main { main() { local i = 0 local x = 0 loop(i < 3) { if i > 0 { x = x + 1 } i = i + 1 } print(x) return "OK" } } ``` **期待出力**: `2`(数値1行) **計算ロジック**: - `i=0`: `i > 0` は false、x は 0 のまま(skip) - `i=1`: `i > 0` は true、`x = 0 + 1 → 1`(if ブランチ) - `i=2`: `i > 0` は true、`x = 1 + 1 → 2`(if ブランチ) - ループ終了後に `x` の値 `2` を出力 **Pattern3 活用**: このパターンは Pattern3 (if-sum) で既に対応済み。if-only で条件付き加算を行うパターンとして、既存実装を活用。 ### スモークテスト #### VM Smoke Test **ファイル**: `tools/smokes/v2/profiles/integration/apps/phase118_loop_nested_if_merge_vm.sh` ```bash #!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../../../lib/output_validator.sh" FIXTURE="apps/tests/phase118_loop_nested_if_merge_min.hako" echo "[phase118_loop_nested_if_merge_vm] Testing loop + if-else merge parity (VM)..." # VM execution with STRICT mode OUTPUT=$(NYASH_DISABLE_PLUGINS=1 HAKO_JOINIR_STRICT=1 ./target/release/hakorune --backend vm "$FIXTURE" 2>&1) || { echo "❌ VM execution failed" echo "$OUTPUT" exit 1 } # Validate: expect 1 line with value 2 validate_numeric_output 1 "2" "$OUTPUT" echo "✅ [phase118_loop_nested_if_merge_vm] PASS" ``` **実行条件**: - `NYASH_DISABLE_PLUGINS=1`: プラグイン無効(core経路のみ) - `HAKO_JOINIR_STRICT=1`: JoinIR STRICT モード有効 - `--backend vm`: Rust VM バックエンド **検証**: `validate_numeric_output 1 "2" "$OUTPUT"`(1行、値2) #### LLVM EXE Smoke Test **ファイル**: `tools/smokes/v2/profiles/integration/apps/phase118_loop_nested_if_merge_llvm_exe.sh` ```bash #!/bin/bash # Phase 118: loop + if-else merge parity (LLVM EXE) source "$(dirname "$0")/../../../lib/test_runner.sh" source "$(dirname "$0")/../../../lib/llvm_exe_runner.sh" export SMOKES_USE_PYVM=0 require_env || exit 2 llvm_exe_preflight_or_skip || exit 0 # Phase 97/98/100 SSOT: plugin dlopen check → build only if needed → dlopen recheck. FILEBOX_SO="$NYASH_ROOT/plugins/nyash-filebox-plugin/libnyash_filebox_plugin.so" MAPBOX_SO="$NYASH_ROOT/plugins/nyash-map-plugin/libnyash_map_plugin.so" STRINGBOX_SO="$NYASH_ROOT/plugins/nyash-string-plugin/libnyash_string_plugin.so" CONSOLEBOX_SO="$NYASH_ROOT/plugins/nyash-console-plugin/libnyash_console_plugin.so" INTEGERBOX_SO="$NYASH_ROOT/plugins/nyash-integer-plugin/libnyash_integer_plugin.so" LLVM_REQUIRED_PLUGINS=( "FileBox|$FILEBOX_SO|nyash-filebox-plugin" "MapBox|$MAPBOX_SO|nyash-map-plugin" "StringBox|$STRINGBOX_SO|nyash-string-plugin" "ConsoleBox|$CONSOLEBOX_SO|nyash-console-plugin" "IntegerBox|$INTEGERBOX_SO|nyash-integer-plugin" ) LLVM_PLUGIN_BUILD_LOG="/tmp/phase118_loop_nested_if_merge_plugin_build.log" llvm_exe_ensure_plugins_or_fail || exit 1 INPUT_HAKO="$NYASH_ROOT/apps/tests/phase118_loop_nested_if_merge_min.hako" OUTPUT_EXE="$NYASH_ROOT/tmp/phase118_loop_nested_if_merge_llvm_exe" EXPECTED='2' EXPECTED_LINES=1 LLVM_BUILD_LOG="/tmp/phase118_loop_nested_if_merge_build.log" if llvm_exe_build_and_run_numeric_smoke; then test_pass "phase118_loop_nested_if_merge_llvm_exe: output matches expected (2)" else exit 1 fi ``` **必要プラグイン**: FileBox, MapBox, StringBox, ConsoleBox, IntegerBox(Phase 117と同じセット) **検証**: `llvm_exe_build_and_run_numeric_smoke`(EXPECTED='2', EXPECTED_LINES=1) ## 検証コマンド ### VM Smoke Test ```bash bash tools/smokes/v2/profiles/integration/apps/phase118_loop_nested_if_merge_vm.sh ``` ### LLVM EXE Smoke Test ```bash bash tools/smokes/v2/profiles/integration/apps/phase118_loop_nested_if_merge_llvm_exe.sh ``` ### 回帰テスト(Phase 117) ```bash bash tools/smokes/v2/profiles/integration/apps/phase117_if_only_nested_if_call_merge_llvm_exe.sh ``` ## 成果物 1. **Fixture**: `apps/tests/phase118_loop_nested_if_merge_min.hako` 2. **VM Smoke**: `tools/smokes/v2/profiles/integration/apps/phase118_loop_nested_if_merge_vm.sh` 3. **LLVM EXE Smoke**: `tools/smokes/v2/profiles/integration/apps/phase118_loop_nested_if_merge_llvm_exe.sh` 4. **ドキュメント**: `docs/development/current/main/phases/phase-118/README.md`(本ファイル) ## 追加(Phase 118 follow-up):Pattern3 carrier PHI contract 固定 Pattern3(if-sum)の exit carrier PHI が欠けると、後段で `Carrier '' not found in carrier_phis` が発生する。 このフェーズでは fixture + VM/LLVM EXE smoke で再現と回帰固定を行い、さらに Fail-Fast の契約チェックを追加した。 ### Fixture - `apps/tests/phase118_pattern3_if_sum_min.hako`(期待: `12`) ### Smoke - VM: `tools/smokes/v2/profiles/integration/apps/phase118_pattern3_if_sum_vm.sh` - LLVM EXE: `tools/smokes/v2/profiles/integration/apps/phase118_pattern3_if_sum_llvm_exe.sh` ### 契約(SSOT) - `exit_bindings` に含まれる `LoopState` carrier は、必ず exit PHI(`exit_carrier_phis`)を持つこと。 - 違反時は `[joinir/phase118/exit_phi/missing_carrier_phi] ... Hint: ...` で Fail-Fast。 ## Phase 118 完了条件 - ✅ VM Smoke Test PASS - ✅ LLVM EXE Smoke Test PASS - ✅ Phase 117 回帰テスト PASS - ✅ ドキュメント更新(10-Now.md, 01-JoinIR-Selfhost-INDEX.md) ## 次のステップ Phase 119以降で更に複雑なループ + 制御フロー構造のテストケースを追加予定。