feat(joinir): Phase 259 P0 complete - Pattern8 final fixes + docs (pre-block-params migration)

Phase 259 P0: Pattern8 (BoolPredicateScan) 完全完了
is_integer/1 を Pattern8 で受理し、VM/LLVM EXE 両方で動作確認完了。
次の大工事(block-parameterized CFG への移行)前のマイルストーンとして記録。

## Key Fixes Applied

1. **skipped_entry_redirects** (instruction_rewriter.rs)
   - k_exit のスキップ時、entry block 参照を exit_block_id へリダイレクト
   - BasicBlockId not found エラーを根治

2. **loop_var_name** (pattern8_scan_bool_predicate.rs)
   - merge_entry_block 選択に使用(`Some(parts.loop_var.clone())`)
   - 未設定時の誤った entry block 選択を修正

3. **loop_invariants** (pattern8_scan_bool_predicate.rs)
   - PHI-free 不変量パラメータ(`[(me, me_host), (s, s_host)]`)
   - loop_var_name 設定時、BoundaryInjector が join_inputs Copy を全スキップするため必要
   - Pattern6 と同じ設計(header PHI で不変量を保持)

4. **expr_result** (pattern8_scan_bool_predicate.rs)
   - k_exit からの返り値を明示設定(`Some(join_exit_value)`)
   - Pattern7 style(推測ではなく明示)

5. **Smoke test scripts**
   - set +e パターンで exit code 7 をキャプチャ
   - LLVM EXE スクリプトにコメント追加(tools/build_llvm.sh 経由の明記)

## Contract Documentation

- join-explicit-cfg-construction.md に Pattern8 契約の具体例を追加
  - "pattern増でも推測増にしない" の実例として記録
  - loop_var_name / loop_invariants / expr_result / jump_args_layout の契約を明示
- 20-Decisions.md に正規化(Semantic/Plumbing)の分離方針を追記
- DOCS_LAYOUT.md に重要ドキュメントへの参照を追加

## Test Results

-  VM smoke test: `[PASS] phase259_p0_is_integer_vm` (exit 7)
-  LLVM EXE: tools/build_llvm.sh 経由で exit 7 確認
-  --verify: PASS

## Next FAIL (Phase 260+)

- Function: `Main.main/0` in `apps/examples/json_lint/main.hako`
- Error: `[cf_loop/pattern2] Failed to extract break condition from loop body`
- Pattern: Nested loop(外側 loop + 内側 loop with break)

🚀 次の大工事: block-parameterized CFG への移行を開始します。

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-21 03:21:22 +09:00
parent a767f0f3a9
commit 4496b6243d
9 changed files with 253 additions and 19 deletions

View File

@ -506,13 +506,34 @@ impl MirBuilder {
promoted_bindings: std::collections::BTreeMap::new(),
};
// Phase 259 P0 FIX: Create loop_invariants for me and s
// These are passed to the loop but don't change across iterations.
// Order MUST match JoinModule loop_step params: [i, me, s]
// carrier_order is built as: [loop_var] + loop_invariants
let loop_invariants = vec![
("me".to_string(), me_host), // me (receiver) → JoinIR param 1
(parts.haystack.clone(), s_host), // s (haystack) → JoinIR param 2
];
if debug {
trace.debug(
"pattern8/lower",
&format!(
"Phase 259 P0: CarrierInfo with loop_var only (i: LoopState), {} loop_invariants (me, s)",
loop_invariants.len()
),
);
}
// Phase 259 P0: expr_result = join_exit_value (Pattern7 style)
// Pattern8 returns boolean from k_exit, not loop variable
let boundary = JoinInlineBoundaryBuilder::new()
.with_inputs(join_inputs, host_inputs)
.with_loop_invariants(loop_invariants) // Phase 259 P0 FIX: Add loop invariants for me and s
.with_exit_bindings(exit_bindings)
.with_carrier_info(carrier_info)
.with_loop_var_name(Some(parts.loop_var.clone())) // Phase 259 P0 FIX: Required for merge entry selection
.with_expr_result(Some(join_exit_value)) // ✅ CRITICAL: Set expr_result to k_exit param
.build();