Files
hakorune/docs/development/current/main/phases/phase-284/P1-INSTRUCTIONS.md
tomoaki 661bbe1ab7 feat(phase284): P1 Complete - Return in Loop with Block Remap Fix
## Summary

Completed Phase 284 P1: Enable return statements in Pattern4/5 loops via
JoinInst::Ret infrastructure (100% pre-existing, no new infrastructure needed).

**Critical Bug Fix**: Block ID remap priority
- Fixed: local_block_map must take precedence over skipped_entry_redirects
- Root cause: Function-local block IDs can collide with global remap entries
  (example: loop_step:bb4 vs k_exit:bb4 after merge allocation)
- Impact: Conditional Jump else branches were incorrectly redirected to exit
- Solution: Check local_block_map FIRST, then skipped_entry_redirects

## Implementation

### New Files
- `src/mir/join_ir/lowering/return_collector.rs` - Return detection SSOT (top-level only, P1 scope)
- `apps/tests/phase284_p1_return_in_loop_min.hako` - Test fixture (exit code 7)
- Smoke test scripts (VM/LLVM)

### Modified Files
- `loop_with_continue_minimal.rs`: Return condition check + Jump generation
- `pattern4_with_continue.rs`: K_RETURN registration in continuation_funcs
- `canonical_names.rs`: K_RETURN constant
- `instruction_rewriter.rs`: Fixed Branch remap priority (P1 fix)
- `terminator.rs`: Fixed Jump/Branch remap priority (P1 fix)
- `conversion_pipeline.rs`: Return normalization support

## Testing

 VM: exit=7 PASS
 LLVM: exit=7 PASS
 Baseline: 46 PASS, 1 FAIL (pre-existing emit issue)
 Zero regression

## Design Notes

- JoinInst::Ret infrastructure was 100% complete before P1
- Bridge automatically converts JoinInst::Ret → MIR Return terminator
- Pattern4/5 now properly merge k_return as non-skippable continuation
- Correct semantics: true condition → return, false → continue loop

## Next Phase (P2+)

- Refactor: Block remap SSOT (block_remapper.rs)
- Refactor: Return jump emitter extraction
- Scope: Nested if/loop returns, multiple returns
- Design: Standardize early exit pattern (return/break/continue as Jump with cond)

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

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2025-12-23 14:21:27 +09:00

5.3 KiB
Raw Blame History

Phase 284 P1code: Return as ExitKind SSOT実装

目的: return を pattern 固有の特例にせず、ExitKind::Returncompose::* / emit_frag() へ収束させる。

前提SSOTP0:

  • docs/development/current/main/phases/phase-284/README.md
  • Phase 282 の境界ルールSSOT=extract / close-but-unsupported=Err: docs/development/current/main/phases/phase-282/README.md

実装方針(最小)

1) 返り値の運搬ExitKind::Return + args

  • return <expr>ExitKind::Return の edgeとして表現する。
  • Return edge が持つ値は EdgeArgs で運ぶReturn terminator の operand
  • terminator は emit_frag() が生成するpattern/box が直に Return 命令を生やさない)。

2) 「移行期間の穴」を消す

現状は Pattern4/5 などが returnErr(close-but-unsupported) にしている。 P1 のゴールは:

  • return を含む loop-body が “別パターンへ静かに流れる” 状態をなくす
  • SSOT 経路で ExitKind::Return に落ちるようにする

補足(設計意図):

  • Phase 284 は “return だけ” を特別扱いするのではなく、Exit の語彙ExitKindを SSOT 化するフェーズでもある。
  • return を「条件付き Jump の一種」として扱えるようにしておくと、将来の break/continue / throw も同じ導線に乗る。

実装タスク(推奨順)

Step 1: 現状の return ハンドリングを棚卸しread-only

  • joinir patterns extractors:

    • src/mir/builder/control_flow/joinir/patterns/extractors/pattern4.rs
    • src/mir/builder/control_flow/joinir/patterns/extractors/pattern5.rs
    • return を Err にしている箇所close-but-unsupported の根拠)を列挙する
  • control-flow lowering:

    • emit_frag() が Return edge をどう生成しているか確認するtarget=None の Return wire/exit
    • compose::cleanup() の Return wiring が想定どおりか確認する

成果物: docs/development/current/main/phases/phase-284/P1-NOTES.md短い箇条書きでOK

Step 2: return を ExitKind に落とす “単一入口” を作るroot fix

狙い:

  • loop body のどの位置でも return が現れたら ExitKind::Return で外へ出せること
  • これを 1 箇所に寄せるpattern 側に増やさない)

重要: Phase 284 で一番の迷子ポイントは「どこに寄せるか」なので、先に経路を固定する。

A) Plan line と JoinIR line を混同しない(必須)

  • Plan linePattern6/7: src/mir/builder/control_flow/plan/normalizer.rs が Frag 構築 SSOT。 compose::cleanup() / emit_frag() へ寄せるのが正しい。
  • JoinIR linePattern15,9: src/mir/builder/control_flow/joinir/patterns/conversion_pipeline.rs が共通入口。 Pattern4/5 の root fix を plan/normalizer.rs に寄せても効かない(経路が違う)。

B) root fix の候補JoinIR line を対象にする)

実装候補(どれか 1 つに決める):

  • B1) JoinIR lowerer 側に “Return collector” を 1 箇所だけ追加し、Pattern4/5 はそれを呼ぶだけにする
    • 方針: まずは fixture で使う形だけを対応(例: top-level if の then に return
    • 未対応形は Errsilent fallback 禁止)
  • B2) JoinIR→MIR 変換/merge の共通入口へ正規化を寄せる(conversion_pipeline.rs / merge/mod.rs
    • ただし、JoinModule に return 相当が無いと後段で作れないので、ここは “最後に出る地点” にしないこと
    • 実体としては B1 が先に必要になるケースが多い

重要:

  • conversion_pipeline.rs は JoinModule 構築の後で走るため、「return の配線を作る」責務をそこへ押し込むと設計が歪みやすい。 P1 の最小は「JoinIR lowering の入口JoinInst を作る地点)で return を ExitKind に落とす」こと。

要件:

  • Fail-Fast: “表現できない return” は Errsilent fallback 禁止)
  • 既定挙動は変えないreturn を含む既存 fixture があれば、その期待値は明示して更新)

Step 3: extractor の return ポリシーを更新(穴を埋める)

P1 で Return SSOT が通るようになったら、以下を更新する:

  • Pattern4/5 の extractor で returnErr にしないclose-but-unsupported ではなくなるため)
  • ただし “return があるせいでパターン形状が曖昧になる” 場合は Err を維持Fail-Fast

Step 4: fixture + smokeVM/LLVMで SSOT を固定

最小 fixture の要件:

  • return が loop の then/else どちらかに現れる
  • exit code が安定stdout 抑制の LLVM でも確認できる)

例(案):

  • apps/tests/phase284_p1_return_in_loop_min.hako
    • loop 内で条件により return 7 / continue
    • 最終 exit code を 7 に固定

smoke:

  • VM: stdout/exit code を検証
  • LLVM: exit code + harness の Result: <code> を検証stdout が出ない想定)

受け入れ基準

  • return を含む loop fixture が VM/LLVM で同一動作
  • pattern 側に “return の特例 if” が増えていないroot fix のみ)
  • Ok(None) / Err の境界が崩れていないsilent fallback なし)