Files
hakorune/docs/development/current/main/phases/phase-259
tomoaki 4496b6243d 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>
2025-12-21 03:21:22 +09:00
..

Status: P0 Complete Scope: StringUtils.is_integer/1nested-if + loopを JoinIR で受理して --profile quick を進める。 Related:

  • Now: docs/development/current/main/10-Now.md
  • Phase 258: docs/development/current/main/phases/phase-258/README.md
  • Design goal: docs/development/current/main/design/join-explicit-cfg-construction.md

Phase 259: StringUtils.is_integer/1 (nested-if + loop)

P0 Result (2025-12-21)

  • is_integer/1: Pattern8 で認識・実行成功
  • VM smoke test: [PASS] phase259_p0_is_integer_vm
  • Exit code: 7is_integer("123") == true
  • json_lint_vm: まだ FAIL別問題: nested-loop with break / Pattern2

Key Fixes Applied

  1. expr_result = Some(join_exit_value) - Pattern7 style で明示設定
  2. loop_var_name = Some(parts.loop_var.clone()) - merge_entry_block 選択用
  3. loop_invariants = [(me, me_host), (s, s_host)] - PHI-free 不変量パラメータ
  4. skipped_entry_redirects - k_exit のスキップ時ブロック参照リダイレクト

Current Status (SSOT)

  • is_integer/1 は Pattern8 で解決
  • json_lint_vm は別の nested-loop with break パターンで失敗中Phase 260+

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(i < cases.length()) 内で内側 loop(j < valid.length()) + break
  • AST Structure:
    loop(i < cases.length()) {
      local s = ...
      local ok = 0
      local j = 0
      loop(j < valid.length()) {  // ← 内側ループ
        if (s == valid.get(j)) {
          ok = 1
          break  // ← Pattern2 が抽出失敗
        }
        j = j + 1
      }
      if (ok == 1) { print("OK") } else { print("ERROR") }
      i = i + 1
    }
    
  • Reproduce:
    ./target/release/hakorune --backend vm apps/examples/json_lint/main.hako
    
  • Shape summaryログ由来:
    • prelude: nested-if to compute start (handles leading "-")
    • loop: loop(i < s.length()) { if not this.is_digit(s.substring(i, i+1)) { return false } i = i + 1 }
    • post: return true
    • caps: If,Loop,NestedIf,Return

Goal

  • StringUtils.is_integer/1 を JoinIR で受理し、quick の first FAIL を次へ進める

Proposed Approach (P0)

P0 Design Decision: Pattern8新規採用

Why Pattern8?

Pattern6index_of系は "見つける" scan返り値: 整数 i or -1で、is_integer は "全部検証する" predicate scan返り値: 真偽値 true/false。役割が異なるため、Pattern8 として分離した。

Pattern8 vs Pattern6

Pattern6 (index_of系) Pattern8 (is_integer系)
役割 "見つける" scan "全部検証する" predicate scan
Match形 substring(...) == needle not predicate(ch) → early exit
返り値 Integer (i or -1) Boolean (true/false)
Exit PHI i(ループ状態変数) ret_bool(検証結果)
Carriers [i] (LoopState) [] (empty, expr_result のみ)

JoinIR Contract

  • jump_args_layout: ExprResultPlusCarrierscarriers=0
  • expr_result: Some(join_exit_value) - ret_bool from k_exit (pipeline handling)
  • exit_bindings: Emptycarriers なし)
  • SSOT: join_inputs = entry_func.params.clone()
  • Me receiver: Passed as param [i, me, s] (by-name 禁止)

受理形P0固定

loop(i < s.length()) {
    if not this.is_digit(s.substring(i, i + 1)) {
        return false
    }
    i = i + 1
}
return true
  • prelude の start 計算は許可(ただし i_init = start で渡す)
  • predicate は Me method callthis.is_digitのみ
  • step は 1 固定