feat(phi): Option C実装 - 箱分割設計でPHIバグ根本修正(基本実装)

## 実装内容

### 新規モジュール(箱理論設計)

1. **LocalScopeInspectorBox** (280行, 13テスト)
   - 各変数がどのブロックで定義されているか追跡
   - is_defined_in_all() で全exit predsでの定義チェック
   - record_snapshot() でスナップショット記録

2. **LoopVarClassBox** (220行, 10テスト)
   - 変数を4種類に分類: Pinned/Carrier/BodyLocalExit/BodyLocalInternal
   - needs_exit_phi() でPHI生成要否判定
   - filter_exit_phi_candidates() で候補フィルタリング

### 既存モジュール修正

3. **loop_snapshot_merge.rs**
   - merge_exit_with_classification() 追加
   - Option Cロジック実装: 全exit predsで定義されていない変数はSKIP
   - デバッグログ追加 (NYASH_OPTION_C_DEBUG)

4. **loopform_builder.rs**
   - build_exit_phis() シグネチャ拡張 (inspector追加)
   - 実際のCFG predecessors使用 (ops.get_block_predecessors)
   - build_exit_phis_for_control() でinspector構築

5. **loop_.rs (JSON bridge)**
   - build_exit_phis() 呼び出し修正
   - header/exit snapshots記録

## 技術的成果

###  理論的に正しい設計確立
- 変数スコープを厳密に追跡
- CFGベースの正確な判定
- 汎用的で拡張性の高い基盤

###  部分的動作確認済み
- 多数のループで BodyLocalInternal が正しくSKIPされる
- ログ: "[Option C] → SKIP exit PHI for 'ch'" 確認
- 23個のユニットテスト全PASS

### ⚠️ 残存課題
- header snapshot記録漏れによる一部誤判定
- 次回修正で完全動作見込み

## 設計哲学(箱理論)

各箱が単一責任を持つ:
- LocalScopeInspectorBox: 変数定義位置の追跡のみ
- LoopVarClassBox: 変数分類のみ
- LoopSnapshotMergeBox: PHI入力生成のみ

→ 保守性・再利用性・テスタビリティの向上

Related: #skip_whitespace PHI bug

🤖 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 11:22:17 +09:00
parent f5e8ed7f2f
commit 2e6e6b61ff
6 changed files with 986 additions and 6 deletions

View File

@ -327,7 +327,24 @@ pub(super) fn lower_loop_stmt(
loopform.seal_phis(&mut ops, latch_bb, &canonical_continue_snaps)?;
// 8) exit PHIheader fallthrough + break スナップショット)
loopform.build_exit_phis(&mut ops, exit_bb, cend, &exit_snaps)?;
// Option C: Build LocalScopeInspectorBox from exit snapshots
use crate::mir::phi_core::local_scope_inspector::LocalScopeInspectorBox;
let mut inspector = LocalScopeInspectorBox::new();
// Record header variable definitions (pinned + carriers)
for pinned in &loopform.pinned {
inspector.record_definition(&pinned.name, loopform.header_id);
}
for carrier in &loopform.carriers {
inspector.record_definition(&carrier.name, loopform.header_id);
}
// Record all variable definitions from exit snapshots
for (block_id, snapshot) in &exit_snaps {
inspector.record_snapshot(*block_id, snapshot);
}
loopform.build_exit_phis(&mut ops, exit_bb, cend, &exit_snaps, &inspector)?;
Ok(exit_bb)
}