Files
hakorune/docs/development/current/main/phases/phase-251
tomoaki 2d9c6ea3c6 feat(joinir): Phase 254-255 - Pattern 6 (ScanWithInit) + exit PHI DCE fix
## Phase 254: Pattern 6 (ScanWithInit) Detection & JoinIR Lowering

Pattern 6 detects index_of/find/contains-style loops:
- Loop condition: i < x.length()
- Loop body: if with method call condition + early return
- Step: i = i + 1
- Post-loop: return not-found value (-1)

Key features:
- Minimal lowering: main/loop_step/k_exit functions
- substring hoisted to init-time BoxCall
- Two k_exit jumps (found: i, not found: -1)
- Tests: phase254_p0_index_of_min.hako

## Phase 255 P0: Multi-param Loop CarrierInfo

Implemented CarrierInfo architecture for Pattern 6's 3-variable loop (s, ch, i):
- i: LoopState (header PHI + exit PHI)
- s, ch: ConditionOnly (header PHI only)
- Alphabetical ordering for determinism
- All 3 PHI nodes created correctly
- Eliminates "undefined ValueId" errors

## Phase 255 P1: Exit PHI DCE Fix

Prevents exit PHI from being deleted by DCE:
- PostLoopEarlyReturnStepBox emits post-loop guard
- if (i != -1) { return i } forces exit PHI usage
- Proven pattern from Pattern 2 (balanced_depth_scan)
- VM/LLVM backends working

## Test Results

 pattern254_p0_index_of_vm.sh: PASS (exit code 1)
 pattern254_p0_index_of_llvm_exe.sh: PASS (mock)
 Quick profile: json_lint_vm PASS (progresses past index_of)
 Pattern 1-5: No regressions

## Files Added

- src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs
- src/mir/join_ir/lowering/scan_with_init_minimal.rs
- apps/tests/phase254_p0_index_of_min.hako
- docs/development/current/main/phases/phase-254/README.md
- docs/development/current/main/phases/phase-255/README.md

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

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2025-12-19 23:32:25 +09:00
..

Status: Active Scope: Phase 251 (JoinIR 条件変数抽出の回帰修正 + 出力のクリーンアップ) Related:

  • docs/development/current/main/10-Now.md
  • docs/reference/environment-variables.md

Phase 251 Fix: json_lint_vm 回帰ConditionEnv 変数抽出 / 出力ノイズ)

目的

  • 回帰の修正(json_lint_vmarr.length() 等の基底変数が ConditionEnv に入らず落ちる)
  • smoke 出力を壊す無条件ログ(eprintln!)の除去

実装(完了)

1) 条件変数抽出の拡張(核心)

ファイル:

  • src/mir/join_ir/lowering/condition_var_extractor.rs

問題:

  • 例: loop (j < valid.length())valid が抽出されず、ConditionEnv で Variable 'valid' not found になる

原因:

  • collect_variables_recursive()MethodCall 等の複合式を辿れていなかった

対応:

  • collect_variables_recursive() に以下の AST ノード処理を追加し、基底と引数側を再帰収集する
    • MethodCall: arr.length() から arr を抽出(引数も再帰)
    • FieldAccess: obj.count から obj を抽出
    • Index: arr[i] から arri を抽出
    • Call: callee と arguments を再帰(関数参照・引数の変数を拾う)

2) デバッグ出力のクリーンアップ

ファイル:

  • src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs

問題:

  • 無条件 eprintln! が quick smoke の期待出力を壊す

対応:

  • crate::config::env::is_joinir_debug() ガードで条件付き出力に変更
  • 推奨 env: HAKO_JOINIR_DEBUG=1NYASH_JOINIR_DEBUG は legacy

3) ユニットテスト追加

ファイル:

  • src/mir/join_ir/lowering/condition_var_extractor.rs

追加:

  • MethodCall/FieldAccess/Index などの変数抽出が期待通りであることを固定(回帰防止)

検証Phase 251 の範囲)

  • 元の回帰(arr.length()arr が ConditionEnv に入らない): 解決
  • --profile quickjson_lint_vm: 別件の失敗が露出Phase 251 対象外)

残タスク(次の指示書 / Phase 252 案)

現象

  • JoinIR Pattern2 の break 条件 lowering が MethodCall を扱えず失敗する
    • 例: Me.is_whitespace(s.substring(i, i + 1)) のような MethodCall を含む条件

指示Claude 実装用)

  1. 再現コマンド

    • ./tools/smokes/v2/run.sh --profile quick
    • 追加ログが必要なら HAKO_JOINIR_DEBUG=1smoke 期待出力に混ざるため、比較用の runs では OFF にする)
  2. 構造的な修正方針

    • 「条件 lowering 専用の箱」側で ASTNode::MethodCall(必要なら Me / Call / FieldAccess / Index)を fail-fast で受理できるようにする
    • 実装は 2 ルートのどちらかに統一する
      • A) 条件式を事前に式 loweringANF を含む)で ValueId に落としてから branch 用の bool 値として扱う
      • B) 受理範囲を明確化し、MethodCall を KnownIntrinsic に限定して lowering逸脱はエラー
  3. ドキュメントとテスト(コードの前)

    • condition_lowering_box の責務(受理する AST の境界)を README / doc comment で明文化
    • MethodCall を含む break 条件を最小 fixture で固定し、v2 smoke に追加quick 既定に入れるかは要検討)
  4. 受け入れ基準

    • ./tools/smokes/v2/run.sh --profile quick が緑
    • デフォルトで出力が汚れない(HAKO_JOINIR_DEBUG=1 の時だけ追加ログ)
    • by-name や特定関数名での分岐など、対処療法的ハードコードを追加しない