feat(joinir): Phase 92完了 - ConditionalStep + body-local変数サポート

## Phase 92全体の成果

**Phase 92 P0-P2**: ConditionalStep JoinIR生成とbody-local変数サポート
- ConditionalStep(条件付きキャリア更新)のJoinIR生成実装
- Body-local変数(ch等)の条件式での参照サポート
- 変数解決優先度: ConditionEnv → LoopBodyLocalEnv

**Phase 92 P3**: BodyLocalPolicyBox + 安全ガード
- BodyLocalPolicyDecision実装(Accept/Reject判定)
- BodyLocalSlot + DualValueRewriter(JoinIR/MIR二重書き込み)
- Fail-Fast契約(Cannot promote LoopBodyLocal検出)

**Phase 92 P4**: E2E固定+回帰最小化 (本コミット)
- Unit test 3本追加(body-local変数解決検証)
- Integration smoke追加(phase92_pattern2_baseline.sh、2ケースPASS)
- P4-E2E-PLAN.md、P4-COMPLETION.md作成

## 主要な実装

### ConditionalStep(条件付きキャリア更新)
- `conditional_step_emitter.rs`: JoinIR Select命令生成
- `loop_with_break_minimal.rs`: ConditionalStep検出と統合
- `loop_with_continue_minimal.rs`: Pattern4対応

### Body-local変数サポート
- `condition_lowerer.rs`: body-local変数解決機能
  - `lower_condition_to_joinir`: body_local_env パラメータ追加
  - 変数解決優先度実装(ConditionEnv優先)
  - Unit test 3本追加: 変数解決/優先度/エラー
- `header_break_lowering.rs`: break条件でbody-local変数参照
- 7ファイルで後方互換ラッパー(lower_condition_to_joinir_no_body_locals)

### Body-local Policy & Safety
- `body_local_policy.rs`: BodyLocalPolicyDecision(Accept/Reject)
- `body_local_slot.rs`: JoinIR/MIR二重書き込み
- `dual_value_rewriter.rs`: ValueId書き換えヘルパー

## テスト体制

### Unit Tests (+3)
- `test_body_local_variable_resolution`: body-local変数解決
- `test_variable_resolution_priority`: 変数解決優先度(ConditionEnv優先)
- `test_undefined_variable_error`: 未定義変数エラー
- 全7テストPASS(cargo test --release condition_lowerer::tests)

### Integration Smoke (+1)
- `phase92_pattern2_baseline.sh`:
  - Case A: loop_min_while.hako (Pattern2 baseline)
  - Case B: phase92_conditional_step_minimal.hako (条件付きインクリメント)
  - 両ケースPASS、integration profileで発見可能

### 退行確認
-  既存Pattern2Breakテスト正常(退行なし)
-  Phase 135 smoke正常(MIR検証PASS)

## アーキテクチャ設計

### 変数解決メカニズム
```rust
// Priority 1: ConditionEnv (loop params, captured)
if let Some(value_id) = env.get(name) { return Ok(value_id); }
// Priority 2: LoopBodyLocalEnv (body-local like `ch`)
if let Some(body_env) = body_local_env {
    if let Some(value_id) = body_env.get(name) { return Ok(value_id); }
}
```

### Fail-Fast契約
- Delta equality check (conditional_step_emitter.rs)
- Variable resolution error messages (ConditionEnv)
- Body-local promotion rejection (BodyLocalPolicyDecision::Reject)

## ドキュメント

- `P4-E2E-PLAN.md`: 3レベルテスト戦略(Level 1-2完了、Level 3延期)
- `P4-COMPLETION.md`: Phase 92完了報告
- `README.md`: Phase 92全体のまとめ

## 将来の拡張(Phase 92スコープ外)

- Body-local promotionシステム拡張
- P5bパターン認識の汎化(flagベース条件サポート)
- 完全なP5b E2Eテスト(body-local promotion実装後)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-16 21:37:07 +09:00
parent 568619df89
commit d2972c1437
43 changed files with 2615 additions and 980 deletions

View File

@ -54,13 +54,16 @@ pub(in crate::mir::builder) fn choose_pattern_kind(
panic!("{}", msg);
} else {
// debug mode: ログのみ
eprintln!("{}", msg);
trace::trace().dev("choose_pattern_kind/parity", &msg);
}
} else {
// Patterns match - success!
eprintln!(
"[choose_pattern_kind/PARITY] OK: canonical and actual agree on {:?}",
canonical_choice
trace::trace().dev(
"choose_pattern_kind/parity",
&format!(
"[choose_pattern_kind/PARITY] OK: canonical and actual agree on {:?}",
canonical_choice
),
);
}
@ -218,38 +221,47 @@ impl MirBuilder {
match canonicalize_loop_expr(&loop_ast) {
Ok((skeleton, decision)) => {
eprintln!("[loop_canonicalizer] Function: {}", func_name);
eprintln!(
"[loop_canonicalizer] Skeleton steps: {}",
skeleton.steps.len()
trace::trace().dev(
"loop_canonicalizer",
&format!("Function: {}", func_name),
);
eprintln!(
"[loop_canonicalizer] Carriers: {}",
skeleton.carriers.len()
trace::trace().dev(
"loop_canonicalizer",
&format!(" Skeleton steps: {}", skeleton.steps.len()),
);
eprintln!(
"[loop_canonicalizer] Has exits: {}",
skeleton.exits.has_any_exit()
trace::trace().dev(
"loop_canonicalizer",
&format!(" Carriers: {}", skeleton.carriers.len()),
);
eprintln!(
"[loop_canonicalizer] Decision: {}",
if decision.is_success() {
"SUCCESS"
} else {
"FAIL_FAST"
}
trace::trace().dev(
"loop_canonicalizer",
&format!(" Has exits: {}", skeleton.exits.has_any_exit()),
);
trace::trace().dev(
"loop_canonicalizer",
&format!(
" Decision: {}",
if decision.is_success() {
"SUCCESS"
} else {
"FAIL_FAST"
}
),
);
if let Some(pattern) = decision.chosen {
eprintln!("[loop_canonicalizer] Chosen pattern: {:?}", pattern);
trace::trace().dev(
"loop_canonicalizer",
&format!(" Chosen pattern: {:?}", pattern),
);
}
eprintln!(
"[loop_canonicalizer] Missing caps: {:?}",
decision.missing_caps
trace::trace().dev(
"loop_canonicalizer",
&format!(" Missing caps: {:?}", decision.missing_caps),
);
if decision.is_fail_fast() {
eprintln!(
"[loop_canonicalizer] Reason: {}",
decision.notes.join("; ")
trace::trace().dev(
"loop_canonicalizer",
&format!(" Reason: {}", decision.notes.join("; ")),
);
}
@ -268,8 +280,8 @@ impl MirBuilder {
}
}
Err(e) => {
eprintln!("[loop_canonicalizer] Function: {}", func_name);
eprintln!("[loop_canonicalizer] Error: {}", e);
trace::trace().dev("loop_canonicalizer", &format!("Function: {}", func_name));
trace::trace().dev("loop_canonicalizer", &format!(" Error: {}", e));
}
}
}