diff --git a/docs/development/current/main/joinir-architecture-overview.md b/docs/development/current/main/joinir-architecture-overview.md index 335d4ec4..da452b6d 100644 --- a/docs/development/current/main/joinir-architecture-overview.md +++ b/docs/development/current/main/joinir-architecture-overview.md @@ -12,6 +12,8 @@ このファイルは情報量が多いので、「何を知りたいか」で読む場所を分けると楽だよ: +- 注意: 本文のセクション番号は歴史的経緯で重複することがあるため、参照は「見出し名」を基本にする。 + - **不変条件だけ押さえたいとき** - セクション `1. 不変条件(Invariants)` を読む。 - JoinIR 全体で絶対に守るルール(ValueId 領域 / PHI 契約 / Fail‑Fast)がここにまとまっている。 @@ -21,13 +23,12 @@ - LoopPattern / Pattern lowerer / ConditionEnv / ExitLine / Boundary など、構造箱・判定箱・生成箱の入口がここ。 - **Normalized JoinIR(JoinIR→JoinIR 正規化)を知りたいとき** - - セクション `3. JoinIR 第1章:基盤完成サマリ` と - 小節 `3.1–3.3`(Structured vs Normalized の関係)を読む。 + - 見出し `JoinIR 第1章:基盤完成サマリ` と + 小節 `Structured vs Normalized`(Structured/Normalized の関係)を読む。 - 詳細な完成サマリは `PHASE_43_245B_NORMALIZED_COMPLETION.md` にまとまっているので、そちらも合わせて参照してね。 - **JsonParser/selfhost のループ状況を見たいとき** - - セクション `7. JoinIR 第1章:基盤完成サマリ` 内の JsonParser/selfhost 表と、 - `phase181-jsonparser-loop-roadmap.md` を見る。 + - 見出し `JoinIR 第1章:基盤完成サマリ` 内の JsonParser/selfhost 表と、`phase181-jsonparser-loop-roadmap.md` を見る。 - **各 Phase ごとの細かい経緯・ログが欲しいとき** - `docs/development/current/main/phase*-*.md` 系の Phase ドキュメントを読む。 @@ -35,13 +36,24 @@ --- +## 0.1 用語(routing / fallback / fail-fast) + +本文では「fallback」という単語が文脈でぶれやすいので、先に用語を固定する: + +- **routing**: JoinIR Core 内での正規な経路選択(Pattern ルータ、if-sum mode ↔ legacy P3 など)。理由が分かる形でログ/タグを付ける。 +- **soft fallback**: 任意箱(pre-validation / optional optimizer)が失敗したときに、同等意味論の別 JoinIR 経路へ退避すること(JoinIR Core 外へは出ない)。 +- **prohibited fallback**: 非 JoinIR への退避(例: LoopBuilder)・サイレント退避・契約違反の握りつぶし。 + ## 1. 不変条件(Invariants) JoinIR ラインで守るべきルールを先に書いておくよ: 1. **JoinIR 内部は JoinIR ValueId だけ** - - JoinIR lowering が発行する `JoinInst` の `ValueId` は、すべて `alloc_value()` で割り当てるローカル ID。 - - Rust/MIR 側の ValueId(`builder.variable_map` に入っている ID)は、JoinIR には直接持ち込まない。 + - JoinIR lowering が発行する `JoinInst` の `ValueId` は JoinValueSpace の領域(Param/Local)のみを使う。 + - `alloc_param()`(Param 100–999)/ `alloc_local()`(Local 1000+)が SSOT。 + - 多くの lowerer が使う `alloc_value()` は「実装上の別名(Local を増やす)」として扱う(概念上は `alloc_local()`)。 + - Rust/MIR 側の ValueId(例: `builder.variable_map`)は、JoinInst の operand としては持ち込まない。 + - 例外(ただし JoinInst には出現しない): LoopHeader PHI dst は merge(MIR) 段階で確保される MIR ValueId で、JoinValueSpace は `reserve_phi()` で衝突防止のマークだけを行う。 2. **host ↔ join の橋渡しは JoinInlineBoundary 系だけ** - host から JoinIR への入力(ループ変数 / 条件専用変数)は @@ -94,14 +106,14 @@ JoinIR ラインで守るべきルールを先に書いておくよ: - `ExprResult`: ループ戻り値 → exit_phi_builder で処理 10. **JoinIR Core は常時 ON** - - LoopBuilder は物理削除済み。JoinIR を OFF にする経路やフォールバックは存在しない。 + - LoopBuilder は物理削除済み。JoinIR を OFF にする経路や prohibited fallback(非 JoinIR への退避)は存在しない。 - `NYASH_JOINIR_CORE` は deprecated(0 を指定しても警告して無視)。JoinIR の OFF トグルは提供しない。 11. **Phase 220: P3 if-sum の ConditionEnv 統合完了** - **LoopBuilder へのフォールバック経路は Phase 187 で完全削除済み**。 - **P3 if-sum には ConditionPatternBox + ConditionEnv が必須**: - 単純条件(`var CmpOp literal`)のみ if-sum mode へルーティング(Phase 219-fix)。 - - 複雑条件(`i % 2 == 1` 等)は legacy P3 経路へフォールバック。 + - 複雑条件(`i % 2 == 1` 等)は legacy P3 経路へ routing(JoinIR Core 内)。 - **Phase 220-D で loop 条件の変数サポート完了**: - `loop(i < len)` のような変数条件を ConditionEnv で解決。 - `extract_loop_condition()` を ValueOrLiteral 対応に拡張。 @@ -109,6 +121,27 @@ JoinIR ラインで守るべきルールを先に書いておくよ: - ExprResultResolver Box で expr_result routing を統一化(Phase 221-R)。 - phase212_if_sum_min.hako → RC=2 達成。 +12. **Lexical scope(Phase 68)を host 側で実在化する** + - JoinIR の正当性は「上流の束縛モデル」が壊れていないことを前提にする。 + - MIR builder は `{...}`(Program)/ `ScopeBox` を lexical scope として扱い、`local` shadowing を正しく復元する。 + - “未宣言名への代入はエラー” を SSOT(quick-reference/LANGUAGE_REFERENCE)に揃えて Fail-Fast 化する。 + - 参照: + - `docs/development/current/main/phase67-mir-var-identity-survey.md` + - `docs/reference/language/variables-and-scope.md` + - 次の焦点(Phase 73+): + - JoinIR lowering の `ScopeManager` を「名前ベース」から「BindingId ベース」に段階移行し、shadowing/束縛同一性を JoinIR 側でも安全に扱えるようにする。 + - 参照: `docs/development/current/main/phase73-scope-manager-design.md` + +13. **Ownership/Relay(Phase 56–71): carrier/capture/relay を契約化する(dev-only)** + - `OwnershipPlan` は carriers/captures/relay_writes の SSOT として使い、混線を Fail-Fast で検出する。 + - multihop relay は plan 層では受理できるが、runtime 側で未対応の間は標準タグで Fail-Fast に落とす(Phase 70-A)。 + - 標準タグ: `[ownership/relay:runtime_unsupported]` + - `OwnershipPlanValidator` を箱として隔離し、Pattern lowerer 側から再利用できる形にする(Phase 71-Pre)。 + - 参照: + - `docs/development/current/main/phase56-ownership-relay-design.md` + - `docs/development/current/main/phase65-ownership-relay-multihop-design.md` + - `docs/development/current/main/phase70-relay-runtime-guard.md` + --- ## 1.9 ValueId Space Management (Phase 201) @@ -120,21 +153,22 @@ JoinIR の ValueId 割り当ては **JoinValueSpace** で一元管理され、3 ``` JoinValueSpace Memory Layout: - 0 100 1000 u32::MAX + 0 100 1000 LOCAL_MAX ├──────────┼──────────┼──────────────────────────┤ │ PHI │ Param │ Local │ │ Reserved│ Region │ Region │ └──────────┴──────────┴──────────────────────────┘ PHI Reserved (0-99): - - LoopHeader PHI dst 用の予約領域 - - reserve_phi(id) で特定 ID をマーク + - LoopHeader PHI dst 用の「予約マーカー領域」(JoinValueSpace 側の契約) + - `reserve_phi(dst)` で「この MIR ValueId を PHI dst として予約した」というマークだけを行う(JoinValueSpace は PHI dst を割り当てない) + - 注: 実際の PHI dst は host MirBuilder が割り当てるため、0-99 に入ることは “保証” ではない(Phase 72 観測: `docs/development/current/main/phase72-phi-reserved-observation.md`) Param Region (100-999): - alloc_param() で割り当て - 使用箇所: ConditionEnv, CarrierInfo.join_id, CapturedEnv -Local Region (1000+): +Local Region (1000..=LOCAL_MAX): - alloc_local() で割り当て - 使用箇所: Pattern lowerers (Const, BinOp, etc.) ``` @@ -145,6 +179,7 @@ Local Region (1000+): - **領域分離**: Param ID、Local ID、PHI dst が決して重複しない - **契約検証**: デバッグモードで違反を早期検出 - **後方互換性**: 既存 API は継続動作 +- 注意: `LOCAL_MAX` はデバッグ/検証用の上限。概念上は Local 領域は拡張可能で、必要なら上限を増やす。 ### 1.9.3 各コンポーネントと ValueId 領域の対応表 @@ -167,8 +202,9 @@ Local Region (1000+): - アロケータ間の調整不要 2. **reserve_phi() vs alloc_phi()** - - PHI dst ID は MirBuilder(host 側)から来るため、JoinValueSpace は割り当てない - - `reserve_phi()` はマーカーのみ(「この ID を上書きするな」という契約) + - LoopHeader PHI dst は merge(MIR) 段階で確保される **MIR 側 ValueId**(JoinInst の ValueId とは世界が違う)。 + - JoinValueSpace は PHI dst を「割り当てる」箱ではなく、`reserve_phi(dst)` で予約マークし、JoinIR Param/Local の割当と衝突しないようにする。 + - `reserve_phi()` はマーカーのみ(「この ID を JoinIR 側で上書き/再利用するな」という契約)。 3. **value_id_ranges.rs との関係** - `value_id_ranges.rs`: **モジュールレベルの分離**(min_loop, skip_ws 等の各モジュールに大きな固定範囲を割り当て) @@ -195,7 +231,9 @@ Local Region (1000+): - 検証項目: - 全ての `boundary.join_inputs` が Param 領域(100-999)にいる - 全ての `condition_bindings[].join_value` が Param 領域にいる - - 全ての `carrier_phis[].phi_dst` が有効範囲(<= LOCAL_MAX)内 + - 全ての `carrier_phis[].phi_dst` が「JoinIR 側の Param/Local と衝突しない」こと + - 現状は “偶発的な非衝突(MirBuilder が低番、JoinValueSpace が 100+)” で安定している(Phase 72 観測: `docs/development/current/main/phase72-phi-reserved-observation.md`)。 + - 検証強化の是非は Phase 72 を SSOT にして別フェーズで決める(ここで前提を固定しない)。 4. **明示的な領域定数** ```rust @@ -321,7 +359,7 @@ Local Region (1000+): - 複雑条件(`i % 2 == 1`、MethodCall 等)は false を返し、legacy P3 経路へルーティング。 - 使用箇所: - PatternPipelineContext.is_if_sum_pattern() で条件複雑度をチェック。 - - P3 if-sum mode は単純比較のみ受理、複雑条件は PoC lowerer へフォールバック。 + - P3 if-sum mode は単純比較のみ受理し、複雑条件は legacy P3 route へ routing(JoinIR Core 内)。 - **MethodCallLowerer(Phase 224-B / 224-C / 225 実装完了)** - ファイル: `src/mir/join_ir/lowering/method_call_lowerer.rs` @@ -357,7 +395,7 @@ Local Region (1000+): - 責務: - ループ条件(header/break/continue)に出てくる LoopBodyLocal を carrier に昇格する統一 API。 - Pattern 2/Pattern 4 両対応の薄いコーディネーター箱(detection は専門 Promoter に委譲)。 - - **Phase 224: Two-tier strategy** - A-3 Trim → A-4 DigitPos フォールバック型オーケストレーション。 + - **Phase 224: Two-tier strategy** - A-3 Trim → A-4 DigitPos の二段階 routing(昇格箱の順序を固定)。 - Phase 223-3 実装内容: - `extract_continue_condition()`: body 内の if 文から continue 条件を抽出。 - `try_promote_for_condition()`: LoopBodyCarrierPromoter を使った昇格処理。 @@ -374,12 +412,12 @@ Local Region (1000+): - **Pattern2ScopeManager**: Pattern2 専用の薄いラッパー(promoted_loopbodylocals 対応含む)。 - **ExprLowerer**: 式 lowering を1箇所に集約(Phase 231: Condition context のみ、General context は将来実装)。 - Phase 231 実装内容: - - Pattern2 break 条件の **pre-validation** として ExprLowerer を試行(fallback 完備)。 - - 簡単な条件式(`i >= 5` など)を正常に検証、複雑なパターンは UnsupportedNode エラーで legacy path へ fallback。 + - Pattern2 break 条件の **pre-validation** として ExprLowerer を試行(soft fallback を前提にした検証ルート)。 + - 簡単な条件式(`i >= 5` など)を正常に検証し、未対応は UnsupportedNode エラーとして上位 router が legacy route を選ぶ。 - 箱化・モジュール化の原則に準拠(ScopeManager は trait、ExprLowerer は再利用可能)。 - 設計原則: - **Box-First**: ScopeManager は trait-based "box" で変数解決を抽象化。 - - **Fail-Safe**: 未対応 AST ノードは明示的エラーで fallback 可能(実行時エラーにしない)。 + - **Fail-Safe**: 未対応 AST ノードは明示的エラーとして扱い、JoinIR Core 内の routing で安全に退避する。 - **Incremental Adoption**: Phase 231 は検証専用、Phase 232+ で実際の lowering 置き換え予定。 - 使用箇所: - `pattern2_with_break.rs` の break 条件 lowering 前に pre-validation として実行。 @@ -416,7 +454,7 @@ Local Region (1000+): - Phase 224 実装内容: - `try_promote()`: A-4 パターン検出 & bool carrier 昇格(`digit_pos` → `is_digit_pos`)。 - **Unit tests**: 6/6 PASS(basic pattern, non-indexOf rejection, no dependency rejection, comparison operators, equality rejection)。 - - **Integration**: LoopBodyCondPromoter から A-3 Trim フォールバック後に呼ばれる。 + - **Integration**: LoopBodyCondPromoter から A-3 Trim 失敗後の二段階 routing で呼ばれる。 - 設計原則: - **One Box, One Question**: A-4 DigitPos パターン専用(A-3 Trim は LoopBodyCarrierPromoter に残す)。 - **Separation of Concerns**: Trim(equality-based)と DigitPos(comparison-based)を分離。 @@ -1315,7 +1353,7 @@ Normalized JoinIR を 1 段挟むと、開発の手触りがどう変わるか ### 3.18 Phase 41-NORM-CANON-P2-CORE – Pattern2 コアケースの canonical Normalized 化 -- Pattern2 のコアセット(P2 ミニ + JsonParser skip_ws/atoi ミニ/real)について、JoinIR→MIR Bridge の既定を Normalized→MIR に寄せ、Structured→MIR は比較テスト用/フォールバック用の位置づけにする(Fail‑Fast ポリシーは維持)。 +- Pattern2 のコアセット(P2 ミニ + JsonParser skip_ws/atoi ミニ/real)について、JoinIR→MIR Bridge の既定を Normalized→MIR に寄せ、Structured→MIR は比較テスト用/soft fallback(JoinIR 内)の位置づけにする(Fail‑Fast ポリシーは維持)。 - `shape_guard` で「Normalized 対応と宣言した P2 コアループ」は常に Normalized 経路を通すようにし、Normalized 側の invariant 破損は dev では panic、本番では明示エラーで早期検出する設計に寄せる。 ### 3.19 Phase 42-NORM-P2-INVENTORY – P2 コア/ミドル/ヘビーの棚卸し @@ -1331,7 +1369,7 @@ Normalized JoinIR を 1 段挟むと、開発の手触りがどう変わるか - **P2-Heavy**(複数 MethodCall / 複雑キャリアを持つもの) - JsonParser: `_parse_string`, `_parse_array`, `_parse_object`, `_unescape_string` - P2/P3/P4 が混在し、複雑なキャリアや MethodCall 多数のため、Phase 43 以降の後続フェーズで設計する。 -- P2-Core については Phase 41 で canonical Normalized 化が完了しており、Structured→MIR は比較テスト用 / フォールバック用の経路として扱う。 +- P2-Core については Phase 41 で canonical Normalized 化が完了しており、Structured→MIR は比較テスト用 / soft fallback(JoinIR 内)の経路として扱う。 - P2-Mid のうち、Phase 43 ではまず `_parse_number` を第 1 候補、`_atoi` 本体を第 2 候補として扱い、Normalized→MIR(direct) に必要な追加インフラ(EnvLayout 拡張 / JpInst パターン拡張)を段階的に入れていく前提を整理した。 ### 3.20 Phase 43-NORM-CANON-P2-MID – JsonParser 本命 P2(_parse_number/_atoi)への適用 ✅ COMPLETE diff --git a/docs/development/current/main/phase67-mir-var-identity-survey.md b/docs/development/current/main/phase67-mir-var-identity-survey.md index 16170452..8a14475f 100644 --- a/docs/development/current/main/phase67-mir-var-identity-survey.md +++ b/docs/development/current/main/phase67-mir-var-identity-survey.md @@ -58,9 +58,9 @@ shadowing の有無は言語中枢なので放置できない)。 追加したスモーク: - `tools/smokes/v2/profiles/quick/core/phase215/scope_shadow_vm.sh` - - 観測: `local x=1 { local x=2 } return x` が `rc=2`(inner `local x` が outer を上書きして leak) + - 観測(Phase 67 当時): `local x=1 { local x=2 } return x` が `rc=2`(inner `local x` が outer を上書きして leak) - `tools/smokes/v2/profiles/quick/core/phase215/scope_assign_creates_local_vm.sh` - - 観測: `{ y = 42 } return y` が `rc=42`(束縛が block 外へ leak) + - 観測(Phase 67 当時): `{ y = 42 } return y` が `rc=42`(束縛が block 外へ leak) 再現コマンド: @@ -85,3 +85,25 @@ shadowing の有無は言語中枢なので放置できない)。 Phase 67 の観測(de facto 実装)から、Phase 68 はまず A(MIR スコープフレーム)に進むのが安全。 その上で、Ownership/Relay が shadowing を正確に扱う必要が出た箇所だけを Phase 69+ で BindingId 化するのが最小差分になる。 + +--- + +## Status update(Phase 68/69 反映) + +この設計分岐は実際に Phase 68/69 で完了した。 + +- Phase 68: MIR 側で lexical scope を実装 + - `{...}`(Program)/ `ScopeBox` を lexical scope として扱い、`local` shadowing を正しく復元。 + - “未宣言名への代入はエラー” を SSOT(quick-reference/LANGUAGE_REFERENCE)に揃えて Fail-Fast 化。 + - プローブは仕様固定へ更新: + - `scope_shadow_vm` の期待は `rc=1`(outer が保持される) + - `scope_assign_creates_local_vm` は “未宣言代入はエラー” を検証 + - `scope_loop_body_local_vm` を追加(loop body local が外へ leak しない) +- Phase 69: Ownership/Relay 側で shadowing-aware 化 + - `AstOwnershipAnalyzer` を内部 `BindingId` で分離し、ネスト block local が loop carriers/relay に混線しないように修正。 + +次の焦点は、Phase 65 で定義した merge relay / 本番 multihop の実行導線(Phase 70+)に戻る。 + +関連: +- Phase 68(lexical scope 実装): `src/mir/builder/vars/lexical_scope.rs`(commit `1fae4f16`) +- Phase 69(shadowing-aware ownership): `src/mir/join_ir/ownership/ast_analyzer.rs`(commit `795d68ec`)