Files
hakorune/docs/development/current/main/joinir-architecture-overview.md
nyash-codex a3df5ecc7a feat(joinir): Phase 183 LoopBodyLocal role separation
Implements role-based separation of LoopBodyLocal variables to prevent
inappropriate Trim promotion for body-only local variables.

## Changes

### Task 183-1: Design Documentation
- Created `phase183-loopbodylocal-role-separation.md` with role taxonomy:
  - Condition LoopBodyLocal: Used in loop conditions → Trim promotion target
  - Body-only LoopBodyLocal: Only in body → No promotion needed
- Documented architectural approach and implementation strategy

### Task 183-2: Implementation
- Added `TrimLoopLowerer::is_var_used_in_condition()` helper
  - Recursively checks if variable appears in condition AST
  - Handles BinaryOp, UnaryOp, MethodCall node types
- Updated `try_lower_trim_like_loop()` to filter condition LoopBodyLocal
  - Only processes LoopBodyLocal that appear in break conditions
  - Skips body-only LoopBodyLocal (returns Ok(None) early)
- Added 5 unit tests for variable detection logic

### Task 183-3: Test Files
- Created `phase183_body_only_loopbodylocal.hako`
  - Demonstrates body-only LoopBodyLocal (`temp`) not triggering Trim
  - Verified trace output: "No LoopBodyLocal detected, skipping Trim lowering"
- Created additional test files (phase183_p1_match_literal, phase183_p2_atoi, phase183_p2_parse_number)

### Task 183-4: Documentation Updates
- Updated `joinir-architecture-overview.md` with Phase 183 results
- Updated `CURRENT_TASK.md` with Phase 183 completion status

## Results

 LoopBodyLocal role separation complete
 Body-only LoopBodyLocal skips Trim promotion
 5 unit tests passing
 Trace verification successful

## Next Steps (Phase 184+)

- Body-local variable MIR lowering support
- String concatenation filter relaxation
- Full _parse_number/_atoi implementation

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-08 23:14:10 +09:00

403 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# JoinIR Architecture Overview (20251208)
このドキュメントは、JoinIR ライン全体Loop/If lowering, ExitLine, Boundary, 条件式 lowering
「箱」と「契約」を横串でまとめた設計図だよ。selfhost / JsonParser / hako_check など、
どの呼び出し元から見てもここを見れば「JoinIR 層の責務と流れ」が分かるようにしておく。
変更があったら、Phase ドキュメントではなく **このファイルを随時更新する** 方針。
---
## 1. 不変条件Invariants
JoinIR ラインで守るべきルールを先に書いておくよ:
1. **JoinIR 内部は JoinIR ValueId だけ**
- JoinIR lowering が発行する `JoinInst``ValueId` は、すべて `alloc_value()` で割り当てるローカル ID。
- Rust/MIR 側の ValueId`builder.variable_map` に入っている IDは、JoinIR には直接持ち込まない。
2. **host ↔ join の橋渡しは JoinInlineBoundary 系だけ**
- host から JoinIR への入力(ループ変数 / 条件専用変数)は
- `JoinInlineBoundary.join_inputs + host_inputs`
- `JoinInlineBoundary.condition_bindings`ConditionBinding
だけで接続する。
- 出力(キャリアの出口)は `JoinInlineBoundary.exit_bindings` に一本化する。
3. **LoopHeader PHI を SSA の単一源泉にする**
- ループ変数とキャリアは **LoopHeaderPhiBuilder/LoopHeaderPhiInfo** でヘッダ PHI を作り、これを「現在値」の SSOT にする。
- exit 用の PHI も組むが、変数再接続や expr_result 収集はヘッダ PHI を経由して行うSSAundef 防止)。
4. **式としての戻り値とキャリア更新を分離する**
- 「ループが式として値を返す」ケース(例: `let r = loop_min_while(...)`)の出口は **exit_phi_builder** が扱う。
- 「ループが状態更新だけする」ケース(例: `trim``start/end`)の出口は **ExitLineExitMeta / ExitBinding / ExitLineReconnector** だけが扱う。
5. **ループ制御 vs 条件式の分離**
- ループの「形」Pattern14, LoopFeaturesは control-flow 専用の箱が担当。
- 条件式(`i < len && (ch == " " || ch == "\t")` 等)は **BoolExprLowerer / condition_to_joinir** が担当し、
ループパターンは boolean ValueId だけを受け取る。
6. **FailFast**
- JoinIR が対応していないループパターン / if パターンは、必ず `[joinir/freeze]` 等で明示的にエラーにする。
- LoopBuilder 等へのサイレントフォールバックは禁止。
7. **Param の役割ParamRoleを分ける**
- JoinIR 側で扱うパラメータは概念的に 3 種類に分かれる:
- 条件専用Condition param: 継続条件や break 条件だけに使う値
- キャリアCarrier param: ループ状態pos/result など)として更新される値
- 式結果Expr param: ループが式として返す値
- ExitLine / Header PHI / InstructionRewriter は **Carrier param だけ** を対象にし、Condition/Expr param は上書きしない。
- 現状は ConditionBinding/ExitMeta/JoinFragmentMeta で役割を区別しており、将来 ParamRole enum として明示する予定。
8. **LoopHeader PHI dst は予約領域(上書き禁止)**
- LoopHeaderPhiBuilder が生成したヘッダ PHI の dst ValueId は「現在のループ値」の SSOT として扱い、
BoundaryInjector や InstructionRewriter が `Copy` などで二度書きすることを禁止する。
- merge ラインでは「ヘッダ PHI dst に対する新しい定義が出てきたら debug モードで panic する」ことで契約違反を早期検出する。
---
## 2. 主な箱と責務
### 2.1 Loop 構造・検出ライン
- **LoopFeatures / LoopPatternKind / router**
- ファイル:
- `src/mir/loop_pattern_detection.rs`
- `src/mir/builder/control_flow/joinir/patterns/router.rs`
- `src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs`
- 責務:
- AST から break/continue/ifelse PHI などの特徴を抽出ast_feature_extractor
- `classify(&LoopFeatures)` で Pattern14 に分類し、テーブル駆動の `LOOP_PATTERNS` でルーティング。
- ルータ順序は P4(continue) → P3(ifphi) → P1(simple) → P2(break) で固定(優先度フィールドはデバッグ用)。
- **Pattern Lowerers (Pattern14)**
- ファイル例:
- `pattern1_minimal.rs`Simple while
- `pattern2_with_break.rs`break 付き / Trim 昇格パスを含む)
- `pattern3_with_if_phi.rs`ifphi キャリア)
- `pattern4_with_continue.rs`continue を Select で表現)
- 責務:
- LoopScopeShape / AST / LoopFeatures を入力として JoinIR の `JoinModule` を構築。
- `JoinFragmentMeta{ expr_result, exit_meta }` を返し、出口情報を ExitLine に渡す。
- host/MIR の ValueId は一切扱わないJoinIR ローカルの ValueId のみ)。
- **Scope / Env Builders**
- `loop_scope_shape_builder.rs`: ループ本体ローカルの収集、LoopScopeShape 統一生成。
- `condition_env_builder.rs`: 条件専用変数の環境と ConditionBinding を一括構築。
- **CommonPatternInitializer** (Phase 33-22)
- ファイル: `src/mir/builder/control_flow/joinir/patterns/common_init.rs`
- 責務:
- 全 Pattern 共通の初期化ロジック統一化(ループ変数抽出 + CarrierInfo 構築)。
- 全パターンで boundary.loop_var_name を確実に設定し、SSAundef を防ぐ。
- **PatternPipelineContext** (Phase 179-B)
- ファイル: `src/mir/builder/control_flow/joinir/patterns/pattern_pipeline.rs`
- 責務:
- 全 Pattern の前処理結果を格納する「解析済みコンテキスト箱」。
- CommonPatternInitializer + LoopScopeShapeBuilder の結果を統一的に保持。
- Pattern 1-4 の共通データloop_var_name, loop_var_id, carrier_info, loop_scopeを提供。
- Pattern 2/4 専用データcondition_env, carrier_updates, trim_helperは Option<T> で保持。
- **Analyzer-only dependencies**: 解析ロジックのみ依存、JoinIR emission ロジックは含まない。
- **TrimLoopLowerer (P5 Dedicated Module)** (Phase 180)
- ファイル: `src/mir/builder/control_flow/joinir/patterns/trim_loop_lowering.rs`
- 責務:
- Trim/CharComparison (Pattern 5) 専用の lowering ロジックを一箇所に集約。
- Pattern2/4 から呼ばれ、LoopBodyLocal 変数を carrier に昇格し、Trim パターンの break 条件を置き換える。
- TrimPatternValidator/TrimPatternLowerer を内部で利用し、carrier 初期化コード生成・条件式置換を実行。
- 入力:
- MirBuilder, LoopScopeShape, loop_cond, break_cond, body, loop_var_name, carrier_info, alloc_join_value
- 出力:
- `Some(TrimLoweringResult)`: Trim パターン検出・lowering 成功時(置換後条件、更新 carrier_info、condition_bindings
- `None`: Trim パターンでない場合(通常ループ処理に戻る)
- `Err`: Trim パターン検出したが lowering 失敗時
- 使用元:
- Pattern2 (pattern2_with_break.rs): Trim/P5 ロジックを完全委譲(~160 行削減)
- Pattern4 (pattern4_with_continue.rs): 将来の Phase 172+ で Trim lowering 実装時に利用予定
- デザイン原則:
- Pure analysis container前処理結果のみ保持、emission なし)
- Pattern-specific variantsOption<T> でパターン固有データ管理)
- Single source of truth全パターンが同じ前処理経路を使用
- **JoinIRConversionPipeline** (Phase 33-22)
- ファイル: `src/mir/builder/control_flow/joinir/patterns/conversion_pipeline.rs`
- 責務:
- JoinIR → MIR 変換フロー統一化JoinModule → MirModule → merge_joinir_mir_blocks
- JoinIR/MIR の関数数・ブロック数をログ出力し、全パターンが同じ入口でマージする。
### 2.2 条件式ライン(式の箱)
- **BoolExprLowerer / condition_to_joinir**
- ファイル:
- `src/mir/join_ir/lowering/bool_expr_lowerer.rs`
- `src/mir/join_ir/lowering/condition_to_joinir.rs`
- 責務:
- 通常の if/while 条件を MIR Compare/BinOp/UnaryOp へ lowering。
- ループ lowerer 用の「AST 条件 → JoinIR Compute 命令列」を ConditionEnv とセットで構築。
- **ConditionEnv/ConditionBinding + ConditionEnvBuilder**
- ファイル:
- `src/mir/join_ir/lowering/condition_env.rs`
- `src/mir/builder/control_flow/joinir/patterns/condition_env_builder.rs`
- 責務:
- 変数名→JoinIR ValueId の環境を組み立て、host↔join の橋渡しを ConditionBinding に明示する。
- Pattern 2 では break 条件の全変数をスキャンし、JoinInlineBoundary.condition_bindings に渡す。
- **LoopConditionScopeBoxPhase 170-D 実装済み)**
- ファイル: `src/mir/loop_pattern_detection/loop_condition_scope.rs`
- 責務:
- 条件式の各変数を LoopParam / OuterLocal / LoopBodyLocal に分類。
- 関数パラメータ誤分類バグは `condition_var_analyzer.rs` の修正で解消済みOuterLocal として扱う)。
- **LoopBodyCarrierPromoterPhase 171-C-2 実装済み)**
- ファイル: `src/mir/loop_pattern_detection/loop_body_carrier_promoter.rs`
- 責務:
- LoopBodyLocal を Trim パターンとして bool キャリアへ昇格substring + equality 連鎖を検出)。
- 昇格成功 → CarrierInfo に統合し Pattern 2/4 へ橋渡し。昇格失敗は FailFast。
- Pattern 2 は安全な Trim なら実際に前処理substring 生成 + 空白比較の初期化)を emit してから JoinIR lowering。
- Pattern 4 は Trim 昇格が起きた場合はガード付きでエラーにし、未実装を明示FailFast
- 汎用性:
- Phase 173 で `_skip_whitespace`JsonParserが Trim パターンで動作確認済み。
- Phase 174 で `_parse_string` 最小化版(終端クォート検出)でも動作確認済み。
- → 空白文字以外の文字比較ループにも対応可能TrimLoopHelper の汎用性実証)。
- **ContinueBranchNormalizer / LoopUpdateAnalyzer**
- ファイル:
- `src/mir/join_ir/lowering/continue_branch_normalizer.rs`
- `src/mir/join_ir/lowering/loop_update_analyzer.rs`
- 責務:
- else-continue を then-continue へ正規化し、Select ベースの continue を簡潔にする。
- ループ本体で実際に更新されるキャリアだけを抽出Pattern 4 で不要キャリアを排除)。
### 2.3 キャリア / Exit / Boundary ライン
- **CarrierInfo / LoopUpdateAnalyzer**
- ファイル:
- `src/mir/join_ir/lowering/carrier_info.rs`
- `src/mir/join_ir/lowering/loop_update_analyzer.rs`
- 責務:
- ループで更新される変数carrierを検出し、UpdateExpr を保持。
- Pattern 4 では実際に更新されるキャリアだけを残す。
- **ExitMeta / JoinFragmentMeta**
- ファイル: `carrier_info.rs`
- 責務:
- JoinIR lowerer が出口の JoinIR ValueId を記録expr_result とキャリアを明確に分離)。
- **LoopHeader PHI Builder**
- ファイル:
- `src/mir/builder/control_flow/joinir/merge/loop_header_phi_info.rs`
- `loop_header_phi_builder.rs`
- 責務:
- ループ変数とキャリアの PHI をヘッダブロックに生成し、entry/latch の 2 入力で SSA を確立。
- instruction_rewriter が latch 側を埋めた後に finalize して挿入する。
- **JoinInlineBoundary**
- ファイル: `src/mir/join_ir/lowering/inline_boundary.rs`
- 主フィールド:
- `join_inputs / host_inputs`:ループパラメータの橋渡し
- `condition_bindings`条件専用変数の橋渡しJoinIR ValueId を明示)
- `exit_bindings`キャリア出口の橋渡しcarrier 名を明示)
- `expr_result` / `loop_var_name`expr result / ヘッダ PHI 生成用のメタ情報
- 責務:
- 「host ↔ JoinIR」の境界情報の SSOT。各パターン lowerer がここに全て詰めてから merge する。
- **BoundaryInjector**
- ファイル: `src/mir/builder/joinir_inline_boundary_injector.rs`
- 責務:
- `join_inputs``condition_bindings` を entry block に Copy で注入し、JoinIR ローカル ID と host ID を接続。
- **ExitLine (ExitMetaCollector / ExitLineReconnector / ExitLineOrchestrator)**
- ファイル:
- `src/mir/builder/control_flow/joinir/merge/exit_line/mod.rs`
- `exit_line/meta_collector.rs`
- `exit_line/reconnector.rs`
- 責務:
- ExitMeta から exit_bindings を構築Collector
- 変数再接続はヘッダ PHI の dst を使って `builder.variable_map` を更新Reconnector
- expr 用の PHI には一切触れないcarrier 専用ライン)。
- **JoinInlineBoundaryBuilderPhase 200-2 / Phase 201 完了)**
- ファイル: `src/mir/join_ir/lowering/inline_boundary_builder.rs`
- 責務:
- JoinInlineBoundary の構築を Builder パターンで統一化。
- フィールド直書きの散乱を防ぎ、inputs/outputs/condition_bindings/exit_bindings/loop_var_name/expr_result の設定を fluent API で実施。
- **Phase 201 で Pattern1/2/3/4 全てに適用完了**(境界情報組み立てを 1 箇所に集約)。
- **JoinIRVerifierPhase 200-3 追加)**
- ファイル: `src/mir/builder/control_flow/joinir/merge/mod.rs`debug_assertions 専用関数)
- 責務:
- LoopHeader PHI / ExitLine 契約をデバッグビルドで検証する門番。
- `verify_loop_header_phis()`: loop_var_name がある場合にヘッダ PHI が存在するか確認。
- `verify_exit_line()`: exit_bindings が exit block に対応しているか確認。
- `verify_joinir_contracts()`: merge_joinir_mir_blocks() の最後で全契約を一括チェック。
- release ビルドでは完全に除去される(`#[cfg(debug_assertions)]`)。
### 2.4 expr result ライン(式としての戻り値)
- **exit_phi_builder**
- ファイル: `src/mir/builder/control_flow/joinir/merge/exit_phi_builder.rs`
- 責務:
- JoinIR fragment が `expr_result` を持つときに exit ブロックへ PHI を生成。
- carrier_inputs も受け取り exit ブロックに PHI を作るが、再接続の SSOT は LoopHeader PHIExitLine はヘッダ PHI を使用)。
- **InstructionRewriter**
- ファイル: `instruction_rewriter.rs`
- 責務:
- continuation 関数k_exitをスキップし、Return → exit ブロック Jump に変換。
- `JoinFragmentMeta.expr_result` と exit_bindings をヘッダ PHI 経由で収集し、`exit_phi_inputs` / `carrier_inputs` を復活させたSSAundef 修正済み)。
- tail call を Branch/Jump に書き換えつつ、LoopHeaderPhiInfo に latch 入力を記録する。
---
## 3. JoinIR → MIR 統合の全体フロー
1. Pattern router が AST/LoopFeatures から Pattern14 を選択し、各 lowerer が
`(JoinModule, JoinFragmentMeta)` を生成。
2. `JoinInlineBoundary` が:
- ループ入力join_inputs/host_inputs
- 条件変数condition_bindings
- キャリア出口exit_bindings
を保持。
3. `merge_joinir_mir_blocks` が(マルチ関数対応):
- 全関数の BlockID を再割り当てblock_allocatorし、ValueId はパラメータを除外して収集。
- Boundary の condition/exit Bindings の JoinIR ValueId も remap 対象に追加。
- LoopHeader PHI を生成loop_header_phi_builderし、latch 側は instruction_rewriter が埋める。
- instruction_rewriter で関数をマージしつつ Call→Jump に変換、k_exit 関数はスキップ。
- BoundaryInjector で entry block に Copy を注入join_inputs + condition_bindings
- Header PHI を finalize → exit_phi_builder で expr_result/carrier の exit PHI を構築。
- ExitLineOrchestrator がヘッダ PHI dst を使って variable_map を更新。
- host の現在ブロックから JoinIR entry へ jump を張り、exit ブロックに切り替える。
この全体フローの詳細は `src/mir/builder/control_flow/joinir/merge/mod.rs`
`phase-189-multi-function-mir-merge/README.md` を参照。
---
## 4. JsonParser / Trim / P5 ループの現在地Phase 170181
### 4.1 JsonParser ループ空間と P1P5
Phase 181 で JsonParserBox 内の 11 ループを棚卸しした結果、
構造的には **すべて JoinIR Pattern14 (+ P5) で扱える** ことが分かったよ。
- 既に JoinIR 経路で動作しているもの:
- `_skip_whitespace`P2 + P5 Trim
- `_trim` leading/trailingP2 + P5 Trim
- **Phase 182 で P1/P2 パターン検証完了** ✅:
- Pattern1 (Simple): `_match_literal` 系ループで動作確認済みapps/tests/phase182_p1_match_literal.hako
- Pattern2 (Break): 整数演算ループで動作確認済みapps/tests/phase182_p2_break_integer.hako
- **ブロッカー発見**: 実際の JsonParser ループには 2 つの制約が必要:
1. LoopBodyLocal 変数の扱い(`ch`, `digit_pos`, `pos` など)
- 現状は Trim pattern 専用の carrier 昇格を試みてエラーになる
- P1/P2 では純粋なローカル変数(昇格不要)として扱うべき
2. 文字列連結フィルタPhase 178
- `num_str = num_str + ch` のような string concat を保守的に reject
- JsonParser では必須の操作なので段階的に有効化が必要
- **Phase 183 で LoopBodyLocal 役割分離完了** ✅:
- **設計**: LoopBodyLocal を 2 カテゴリに分類:
- **Condition LoopBodyLocal**: ループ条件header/break/continueで使用 → Trim 昇格対象
- **Body-only LoopBodyLocal**: ループ本体のみで使用 → 昇格不要、pure local 扱い
- **実装**: TrimLoopLowerer に `is_var_used_in_condition()` ヘルパー追加
- 条件で使われていない LoopBodyLocal は Trim 昇格スキップ
- 5 つの unit test で変数検出ロジックを検証
- **テスト**: `apps/tests/phase183_body_only_loopbodylocal.hako` で動作確認
- `[TrimLoopLowerer] No LoopBodyLocal detected` トレース出力で body-only 判定成功
- **次の課題**: body-local 変数の MIR lowering 対応(`local temp` in loop body
- Phase 183 では "Trim promotion しない" 判定まで完了
- 実際の MIR 生成は Phase 184+ で対応予定
- 構造的に P1P4 で対応可能(代表例):
- `_parse_number` / `_atoi`P2 Break- Phase 182 でブロッカー特定済み
- `_match_literal`P1 Simple while- Phase 182 で動作確認済み ✅
- `_parse_string` / `_parse_array` / `_parse_object`
P2 + P4 + P5 の組み合わせで表現可能なことを設計上確認済み)
- 低優先度だが理論上は P1P4 からの拡張で吸収可能:
- `_unescape_string` など、複雑な continue / 条件付き更新を含むループ
方針:
- **ループの「形」は P1P4 から増やさない**。
複雑さLoopBodyLocal 条件、OR chain、continue 多用など)は BoolExprLowerer /
ContinueBranchNormalizer / TrimLoopLowerer (P5) といった補助箱側で吸収する。
- JsonParser 側の P5 適用Trim / `_skip_whitespace` / `_parse_string` 最小版)は実証済み。
残りのループは Phase 17x18x で、P1P4+P5 の組み合わせとして段階的に実装していく。
---
## 5. selfhost / .hako JoinIR Frontend との関係
JoinIR は Rust 側だけでなく、将来的に .hako selfhost コンパイラ側でも生成・解析される予定だよ:
- .hako 側の JsonParser/分析箱は、Program JSON / MIR JSON v1 を読んで JoinIR/MIR を解析する。
- Rust 側 JoinIR ラインの設計変更(特に ValueId/ExitLine/Boundary 周り)は、
**必ずこのファイルを更新してから** .hako 側にも段階的に反映する方針。
「JoinIR の仕様」「箱の責務」「境界の契約」は、このファイルを SSOT として運用していく。
---
## 5. 関連ドキュメント
- `docs/development/current/main/10-Now.md`
- 全体の「いまどこ」を短くまとめたダッシュボード。
- `docs/private/roadmap2/phases/phase-180-joinir-unification-before-selfhost/README.md`
- JoinIR 統一フェーズ全体のロードマップと進捗。
- 各 Phase 詳細:
- 185188: Strict mode / LoopBuilder 削除 / Pattern14 基盤
- 189193: Multi-function merge / Select bridge / ExitLine 箱化
- 171172 / 3310/13: ConditionEnv, ConditionBinding, JoinFragmentMeta, ExitLineRefactor 等
- `docs/development/current/main/loop_pattern_space.md`
- JoinIR ループパターン空間の整理メモ。
どの軸(継続条件 / break / continue / PHI / 条件変数スコープ / 更新パターン)でパターンを分けるか、
そして P1P4 / Trim(P5) の位置づけと、今後追加候補のパターン一覧がまとまっている。
---
## 6. RoadmapJoinIR の今後のゴール)
ここから先の JoinIR の「目指す形」を、箱レベルでざっくり書いておくよ。フェーズ詳細は各 phase ドキュメントに分散させて、このセクションは常に最新の方向性だけを保つ。
### 6.1 直近Phase 176-177 まわり)
- **P5Trim/JsonParser 系)ループの複数キャリア対応** ✅ Phase 176 完了 (2025-12-08)
- 完了内容:
- Pattern2 lowerer を全キャリア対応に拡張(ヘッダ PHI / ループ更新 / ExitLine
- CarrierUpdateLowerer ヘルパで UpdateExpr → JoinIR 変換を統一。
- 2キャリアpos + resultE2E テスト完全成功。
- 技術的成果:
- CarrierInfo / ExitMeta / ExitLine / LoopHeaderPhiBuilder の multi-carrier 対応を Pattern2 lowerer で完全活用。
- Trim pattern の「キャリア = ループ変数」という誤解を解消loop_var は特殊キャリア)。
- 次のステップ (Phase 177):
- JsonParser `_parse_string` 本体を P2+P5 で通すpos + result の 2 キャリアで実ループ動作確認)。
### 6.2 中期selfhost depth2 / JsonParser 本体)
- **JsonParserBox / Trim 系ループの本線化**
- 目標:
- `_trim` / `_skip_whitespace` / `_parse_string` / `_parse_array` などの主要ループが、すべて JoinIR Pattern14 + P5 で通ること。
- LoopConditionScopeBox + LoopBodyCarrierPromoter + TrimLoopHelper の上で安全に正規化できるループを広げていく。
- 方針:
- 「ループの形」は P1P4 から増やさず、複雑さは BoolExprLowerer / ContinueBranchNormalizer / P5 系の補助箱で吸収する。
- LoopPatternSpace の P6/P7/P12 候補break+continue 同時 / 複数キャリア条件更新 / early returnは、実アプリで必要になった順に小さく足す。
- **selfhost depth2.hako JoinIR/MIR Frontend**
- 目標:
- `.hako → JsonParserBox → Program/MIR JSON → MirAnalyzerBox/JoinIrAnalyzerBox → VM/LLVM` の深度 2 ループを、日常的に回せるようにする。
- Rust 側の JoinIR は「JSON を受け取って実行・検証するランナー層」、.hako 側が「JoinIR/MIR を構築・解析する言語側 SSOT」という役割分担に近づける。
- 前提:
- 本ドキュメントjoinir-architecture-overview.mdを .hako 側の JoinIR 実装の参照設計として維持し、仕様変更は必ずここを更新してから .hako にも反映する。
### 6.3 当面やらないことNonGoals
- ループパターンを闇雲に増やすこと
- P1P4構造 P5bodylocal 条件を昇格する補助パス)を「骨格」とみなし、
新しいパターンが必要になったときは LoopPatternSpace に追記してから、小さな箱で補う方針。
- LoopBuilder の復活や、JoinIR 以外の別ラインによるループ lowering
- LoopBuilder 系は Phase 186187 で完全に削除済み。
ループに関する新しい要件はすべて JoinIR 側のパターン/箱の拡張で扱う。
- JoinIR の中に言語固有のハードコード(特定 Box 名や変数名)を戻すこと
- Trim/JsonParser 系は、構造パターンと補助箱Promoter/Helperで扱い、
「sum」「ch」など名前ベースの判定は LoopUpdateSummary / TrimLoopHelper の内部に閉じ込める。
この Roadmap は、JoinIR 層の変更や selfhost 深度を進めるたびに更新していくよ。