- Create 3-stage pipeline architecture to solve borrow checker conflicts - Stage 1 (ScanBox): Read-only scanning, identify rewrites (170 lines) - Stage 2 (PlanBox): Pure transformation, generate new blocks (85 lines) - Stage 3 (ApplyBox): Builder mutation only (75 lines) New Files: - scan_box.rs: Stage 1 - Read-only scan for RewritePlan - plan_box.rs: Stage 2 - Transform RewritePlan → RewrittenBlocks - apply_box.rs: Stage 3 - Apply RewrittenBlocks to MirBuilder - C-2.1-pipeline-refactoring.md: Implementation guide Data Structures: - RewritePlan: Describes WHAT to rewrite (tail calls, returns, PHI, params) - RewrittenBlocks: Contains HOW to rewrite (new blocks, replacements, inputs) Benefits: - Borrow checker safety: No overlapping mutable/immutable borrows - Single responsibility: Each stage has one clear purpose - Testability: Each stage can be unit tested independently - Maintainability: Clear data flow, isolated changes Status: - Scaffolding: ✅ Complete (structure defined, stub implementations) - Integration: ⏳ Next step (refactor merge_and_rewrite to use 3 stages) Build: cargo build --release ✅ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Phase 286: JoinIR Line Absorption(JoinIR→CorePlan/Frag 収束)
Status: In Progress (P0, P1, P3, 286C-2 COMPLETE; P2 pending)
Goal
移行期間に残っている「2本の lowering」を、構造で 1 本に収束させる。
- Plan line(Pattern6/7):
CorePlan → Frag(compose) → emit_frag()が SSOT - JoinIR line(Pattern1–5,9):
JoinIR → bridge → mergeが SSOT
Phase 286 では JoinIR line を “第2の lowerer” として放置せず、Plan/Frag SSOT へ吸収する道筋を固定する。
Why(なぜ今)
returnのような「大きな出口語彙」は、責務が分散すると実装場所が揺れて事故りやすい- 移行期間の弱点は「同じASTでも経路により意味論が割れる可能性がある」こと
- pattern を溶かしていく思想の最後の壁が “JoinIR line の残存” になりやすい
SSOT(Phase 286 で守る憲法)
- SSOT=extract(Phase 282): 検出は extract の成功でのみ決める。
pattern_kindは O(1) safety valve のみ。 - CFG/terminator SSOT(Phase 280/281):
Frag + compose::* + emit_frag()が唯一の terminator 生成点。 - Fail-Fast: close-but-unsupported を
Ok(None)で黙殺しない(silent reroute 禁止)。
Responsibility Map(どこを触るか)
- JoinIR line の共通入口(現状):
src/mir/builder/control_flow/joinir/patterns/conversion_pipeline.rssrc/mir/join_ir_vm_bridge/bridge.rssrc/mir/builder/control_flow/joinir/merge/mod.rs
- Plan/Frag SSOT(収束先):
src/mir/builder/control_flow/plan/*src/mir/builder/control_flow/edgecfg/api/compose.rssrc/mir/builder/control_flow/edgecfg/api/emit.rs
Scope(提案)
P0(docs-only)✅ COMPLETE (2025-12-25)
完了内容:
- SSOT ドキュメント作成:
docs/development/current/main/design/joinir-plan-frag-ssot.mdを作成 - 8章構成で固定:
- Scope / Non-goals - 対象範囲の明確化
- 用語(Terms) - JoinIR line, Plan, Frag, Boundary, ExitKind, Freeze point, SSOT の定義
- 責務(Responsibilities) - Planが決めること・決めないこと / Fragが保持すること・保持しないこと
- 禁止事項(Prohibitions) - Planでの実行・名前解決・最適化・ルール実装の禁止 等
- 凍結点(Freeze Points) - PlanFreeze / BoundaryFreeze / ValueIdAllocate / MergeComplete
- 不変条件(Invariants / Fail-Fast) - Plan段階(V1-V7) / Boundary段階(B1-B2, C1-C2) / Merge段階(M1-M4)
- 2本コンパイラ根治の合流点 - 共通パス・分岐点・差分許容場所/非許容場所
- デバッグ導線 - NYASH_CLI_VERBOSE, HAKO_JOINIR_DEBUG, NYASH_TRACE_VARMAP 等
重要な設計決定:
- JoinIR line を AST → MIR 全体ではなく、「(Pattern detection/Plan) → (Frag+Boundary) → (MIR merge) の限定されたパイプライン」として定義
- 禁止事項の「例外なし」表現を削除し、「診断専用の扱い(debugタグ付き・既定OFF)」という運用ルールに変更
- ValueId 100-999 固定範囲を「host ValueId と衝突しない領域」という原則に変更(具体数値は実装詳細として注記)
成果物:
docs/development/current/main/design/joinir-plan-frag-ssot.md(新規)- コード変更なし(docs-only)
P1 (contract_checks 導入 + 実バグ修正) ✅ COMPLETE (2025-12-25)
完了内容:
- contract_checks.rs に検証関数追加:
verify_boundary_contract_at_creation()- B1検証: join_inputs が Param 領域にあること
- C2検証: condition_bindings が Param 領域にあること
- merge/mod.rs に検証呼び出し追加: merge開始時にFail-Fast検証
- 実バグ3件修正: Pattern2/4/5 で
alloc_local()を誤って使っていた箇所をalloc_param()に修正
成果物:
src/mir/builder/control_flow/joinir/merge/contract_checks.rs(変更)src/mir/builder/control_flow/joinir/merge/mod.rs(変更)src/mir/join_ir/lowering/loop_with_break_minimal.rs(変更)src/mir/join_ir/lowering/loop_with_continue_minimal.rs(変更)src/mir/builder/control_flow/joinir/patterns/pattern5_infinite_early_exit.rs(変更)
発見された問題:
- 各 pattern の lowering で関数パラメータに
alloc_local()を使っていた(本来はalloc_param()) - これにより join_inputs に Local ValueId (1000+) が混入し、検証エラーになっていた
改善の示唆(Post-P1 Polish 実施済み):
- API名の曖昧さが誤用を招いていたため、
alloc_join_param()/alloc_join_local()の導入が検討されている - エラーメッセージの「原因特定」強化として context パラメータの追加が検討されている
Post-P1 Polish 追加 (2025-12-25):
- 新API追加:
JoinValueSpace::alloc_join_param()/alloc_join_local()(薄いラッパー) - エラーメッセージ改善:
verify_boundary_contract_at_creation()にcontext: &strパラメータ追加 - docs反映: SSOTドキュメントに脚注形式で数値記載、新API使用の明記
P2(PoC)
- 代表 1 パターン(例: Pattern4)を "JoinIR 生成 → CorePlan/Frag" に変換する PoC
- 目的: merge を通さずに
emit_frag()経由で終端が生成できることの証明
- 目的: merge を通さずに
P3 (error context enrichment) ✅ COMPLETE (2025-12-25)
完了内容:
- P2: host_fn をエラーコンテキストに追加(関数名での特定を容易に)
- P3: join-side 情報(continuation数・boundaryサマリ)をエラーコンテキストに追加
[conts=X exits=Y conds=Z]形式のサマリを追加- 固定キー名で解析容易に
成果物:
src/mir/builder/control_flow/joinir/merge/mod.rs(変更)- 最終エラーフォーマット:
[merge_joinir_mir_blocks host=X join=Y [conts=A exits=B conds=C]]
286C-2 (instruction_rewriter.rs 箱化) ✅ COMPLETE (2025-12-25)
完了内容:
- instruction_rewriter.rs の箱化・意味論不変: 1400行ファイルに責務リストコメントを追加し、4つの箱モジュールを抽出
- InstructionFilterBox: Skip判定ロジック(純粋関数)
should_skip_copy_overwriting_phi()- CopyがPHI dstを上書きするか判定should_skip_function_name_const()- Const String(関数名)のスキップ判定should_skip_boundary_input_const()- Boundary input Constのスキップ判定
- ReturnConverterBox: Return→Jump変換ヘルパー
should_keep_return()- 非スキップ可能継続のReturn保持判定remap_return_value()- Return値のremapヘルパー
- TailCallDetectorBox: テイルコール検出ヘルパー
is_recursive_call()- 再帰呼び出し判定is_loop_entry_call()- ループエントリ呼び出し判定should_skip_param_binding()- パラメータ束縛スキップ判定call_type_description()- 呼び出しタイプの説明文字列取得
- ParameterBindingBox: パラメータ束縛ヘルパー
should_skip_phi_param()- PHI dstパラメータのスキップ判定carrier_param_count()- キャリアパラメータ数取得has_more_carrier_args()- キャリア引数残確認carrier_arg_index()- キャリア引数インデックス計算
- InstructionFilterBox: Skip判定ロジック(純粋関数)
成果物:
src/mir/builder/control_flow/joinir/merge/rewriter/instruction_filter_box.rs(新規)src/mir/builder/control_flow/joinir/merge/rewriter/return_converter_box.rs(新規)src/mir/builder/control_flow/joinir/merge/rewriter/tail_call_detector_box.rs(新規)src/mir/builder/control_flow/joinir/merge/rewriter/parameter_binding_box.rs(新規)src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs(変更: 責務リストコメント追加 + 箱使用)src/mir/builder/control_flow/joinir/merge/rewriter/mod.rs(変更: モジュール追加)
注意点:
- 意味論は完全不変(既存のinlineロジックを箱関数呼び出しに置換)
- ファイル行数は1454行に増加(コメント・import追加により)
- 核ロジックは main loop に密結合しているため、完全な分離にはさらなるリファクタリングが必要
- スモークテスト: 既存FAILなし(1件のemit失敗は本変更と無関係)
Acceptance(P0)
- 2本の lowering が “設計として” どこで 1 本に収束するかが明文化されている
- Phase 284(Return)/ Phase 285(GC)と矛盾しない