feat(joinir): Phase 231 - ExprLowerer/ScopeManager pilot implementation

Pilot implementation of unified expression lowering for Pattern2 break conditions:

New files:
- scope_manager.rs (280 lines) - ScopeManager trait + Pattern2ScopeManager
- expr_lowerer.rs (455 lines) - ExprLowerer with Condition context support

Features:
- Unified variable lookup across ConditionEnv/LoopBodyLocalEnv/CapturedEnv/CarrierInfo
- Pre-validation of condition AST before lowering
- Fail-safe design with fallback to legacy path
- 8 new unit tests (all pass)

Integration:
- Pattern2 break condition uses ExprLowerer for pre-validation
- Existing proven lowering path preserved
- Zero impact on existing functionality (890/897 tests pass)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-10 22:48:45 +09:00
parent b07329b37f
commit 13a676d406
10 changed files with 1427 additions and 75 deletions

View File

@ -0,0 +1,231 @@
# Phase 231: ExprLowerer パイロット実装Pattern2 条件式限定)
## 概要
Phase 231 は、Phase 230 で設計した ExprLowerer / ScopeManager アーキテクチャの実現可能性を検証するパイロット実装。
Pattern2 の break 条件式に限定し、新しい変数解決システムが既存コードに素直に統合できることを確認した。
## 実装内容
### 1. ScopeManager trait変数解決の統一インターフェース
**ファイル**: `src/mir/join_ir/lowering/scope_manager.rs`
**責務**:
- 変数参照を統一的に扱う trait
- ConditionEnv / LoopBodyLocalEnv / CapturedEnv / CarrierInfo を統合
- 変数のスコープ種別LoopVar / Carrier / LoopBodyLocal / Capturedを判定
**設計原則**:
- **Box-First**: trait-based "box" で変数解決を抽象化
- **Single Responsibility**: 変数解決のみ担当AST lowering や ValueId 割り当ては別箱)
- **Testable**: 独立してテスト可能
### 2. Pattern2ScopeManagerPattern2 専用実装)
**ファイル**: `src/mir/join_ir/lowering/scope_manager.rs`
**責務**:
- Pattern2 のすべての環境を統合ConditionEnv, LoopBodyLocalEnv, CapturedEnv, CarrierInfo
- promoted_loopbodylocals の名前解決(`digit_pos``is_digit_pos` 変換)
**Lookup 順序**:
1. ConditionEnvループ変数、キャリア、条件専用変数
2. LoopBodyLocalEnvボディローカル変数
3. CapturedEnvキャプチャされた外部変数
4. Promoted LoopBodyLocal昇格された変数の名前変換
### 3. ExprLowerer式 lowering の統一 API
**ファイル**: `src/mir/join_ir/lowering/expr_lowerer.rs`
**責務**:
- AST 式を JoinIR ValueId へ lowering
- ScopeManager を使った変数解決
- サポートされていない AST ノードの検出と fallback
**Phase 231 スコープ**:
- **Context**: Condition のみloop/break 条件)
- **Supported**: リテラル、変数、比較演算(<, >, ==, !=, <=, >=、論理演算and, or, not
- **Not Supported**: MethodCall, NewBox, 複雑な式
**設計原則**:
- **Fail-Safe**: 未対応ノードは明示的エラー(実行時エラーにしない)
- **Thin Wrapper**: 既存の condition_lowerer を活用Phase 231 は API 統一が目的)
- **Incremental Adoption**: 検証専用、実際の lowering 置き換えは Phase 232+
### 4. Pattern2 統合pre-validation
**ファイル**: `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs`
**統合方法**:
- break 条件 lowering の **前** に ExprLowerer で検証
- 成功: ログ出力(`[pattern2/phase231] ✓ ExprLowerer successfully validated`
- UnsupportedNode: fallback ログ(期待される動作)
- 予期しないエラー: 警告ログlegacy path が処理)
**重要**: Phase 231 は **検証専用**。実際の lowering は従来通り `lower_loop_with_break_minimal` が実行。
## テスト結果
### 単体テスト
```
test mir::join_ir::lowering::scope_manager::tests::test_pattern2_scope_manager_loop_var ... ok
test mir::join_ir::lowering::scope_manager::tests::test_pattern2_scope_manager_carrier ... ok
test mir::join_ir::lowering::scope_manager::tests::test_pattern2_scope_manager_promoted_variable ... ok
test mir::join_ir::lowering::scope_manager::tests::test_pattern2_scope_manager_body_local ... ok
test mir::join_ir::lowering::expr_lowerer::tests::test_expr_lowerer_simple_comparison ... ok
test mir::join_ir::lowering::expr_lowerer::tests::test_expr_lowerer_variable_not_found ... ok
test mir::join_ir::lowering::expr_lowerer::tests::test_expr_lowerer_unsupported_node ... ok
test mir::join_ir::lowering::expr_lowerer::tests::test_is_supported_condition ... ok
```
### 統合テスト
```
test result: PASSED. 890 passed; 7 failed; 64 ignored
```
- 890 PASS変更前と同じ
- 7 FAILpre-existing issues、Phase 231 とは無関係)
- Pattern2 関連テスト全て PASS
### E2E テスト
```bash
$ ./target/release/hakorune /tmp/test_phase231.hako 2>&1 | grep phase231
[pattern2/phase231] ✓ ExprLowerer successfully validated break condition
```
テストプログラム:
```nyash
static box Phase231Test {
main() {
local i = 0
local sum = 0
loop(i < 10) {
if i >= 5 { break }
sum = sum + i
i = i + 1
}
return sum // RC: 0正常動作
}
}
```
## ファイル変更
### 新規ファイル2ファイル
1. `src/mir/join_ir/lowering/scope_manager.rs`280 lines
- ScopeManager trait
- Pattern2ScopeManager 実装
- VarScopeKind enum
- 4 unit tests
2. `src/mir/join_ir/lowering/expr_lowerer.rs`455 lines
- ExprLowerer struct
- ExprContext / ExprLoweringError enum
- 3 unit tests
### 変更ファイル3ファイル
1. `src/mir/join_ir/lowering/mod.rs`+2 lines
- scope_manager, expr_lowerer モジュール追加
2. `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs`+36 lines
- ExprLowerer pre-validation 追加
3. `docs/development/current/main/joinir-architecture-overview.md`+20 lines
- Phase 231 実装内容を追加
## 設計上の重要な判断
### 1. Pre-validation アプローチ
**Why**: 既存の proven lowering path を保持しつつ、新 API の検証データを収集。
**Benefits**:
- 既存コードへの影響ゼロfallback 完備)
- 段階的移行が可能Phase 232+ で実際の lowering 置き換え)
- どのパターンが動くか/動かないかのデータ収集
### 2. ScopeManager を trait に
**Why**: Pattern2 以外にも拡張しやすくするため。
**Benefits**:
- Pattern1/Pattern3/Pattern4 で異なる実装を提供可能
- テスト時にモック実装が作れる
- 将来の拡張General context 対応)がしやすい
### 3. ExprLowerer は condition_lowerer を再利用
**Why**: Phase 231 は API 統一が目的、ロジック reimplementation は不要。
**Benefits**:
- 実装コストが低いthin wrapper
- 既存の proven logic を活用(品質保証済み)
- ScopeManager → ConditionEnv 変換だけに集中
## 箱化・モジュール化の原則
### Box-First
- **ScopeManager**: 変数解決を統一的に扱う trait-based "box"
- **ExprLowerer**: 式 lowering を1箇所に集約パイロット段階
### Single Responsibility
- **ScopeManager**: 変数解決のみAST lowering や ValueId 割り当ては別箱)
- **ExprLowerer**: 式 lowering のみ(変数環境管理は ScopeManager に委譲)
### Fail-Safe
- 未対応 AST ードは明示的エラーUnsupportedNode
- フォールバック経路を必ず用意legacy path が処理)
- 実行時エラーにしない(コンパイル時に検出)
### Testability
- ScopeManager は trait なので独立してテスト可能
- ExprLowerer は MirBuilder に依存するが、最小限の stub で動作
- 単体テストで各種エラーケースを網羅
## 次のステップ
### Phase 232: Pattern1/Pattern3 への拡大
- Pattern1ScopeManager, Pattern3ScopeManager 実装
- loop 条件も ExprLowerer で pre-validation
- データ収集: どのパターンが動くか確認
### Phase 233: 実際の lowering 置き換え
- ExprLowerer を実際の lowering path として使用
- fallback path を削除ExprLowerer が完全に置き換え)
- テスト: すべての Pattern2/Pattern1/Pattern3 で動作確認
### Phase 234+: General context 対応
- ExprContext::General 実装
- MethodCall, NewBox, 複雑な式のサポート
- 完全な式 lowering 統一
## まとめ
Phase 231 は ExprLowerer / ScopeManager アーキテクチャの実現可能性を実証した。
**成功要因**:
- Pre-validation アプローチで既存コードへの影響ゼロ
- ScopeManager trait で変数解決を統一的に抽象化
- Box-First / Fail-Safe 原則の徹底
**次の課題**:
- Pattern1/Pattern3 への拡大Phase 232
- 実際の lowering 置き換えPhase 233
- General context 対応Phase 234+
Phase 231 の成果により、Phase 230 の設計が正しいことが検証され、次のフェーズへの明確な道筋がついた。

View File

@ -337,8 +337,32 @@ Local Region (1000+):
- Pattern4 への統合完了: LoopBodyLocal 条件の昇格成功時に lowering を続行(以前は Fail-Fast
- Phase 223.5 実装内容:
- Pattern2 への統合完了: header/break 条件を分析し昇格を試みる。
- A-4digit_posテスト追加: cascading LoopBodyLocal パターンで Fail-Fast 動作を確認。
- error_messages.rs に Pattern2 用エラー関数追加: `format_error_pattern2_promotion_failed()` など。
- **ScopeManager / ExprLowererPhase 231 パイロット実装完了)**
- ファイル:
- `src/mir/join_ir/lowering/scope_manager.rs`
- `src/mir/join_ir/lowering/expr_lowerer.rs`
- 責務:
- **ScopeManager trait**: 変数参照を統一的に扱う traitConditionEnv / LoopBodyLocalEnv / CapturedEnv / CarrierInfo を統合)。
- **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。
- 箱化・モジュール化の原則に準拠ScopeManager は trait、ExprLowerer は再利用可能)。
- 設計原則:
- **Box-First**: ScopeManager は trait-based "box" で変数解決を抽象化。
- **Fail-Safe**: 未対応 AST ノードは明示的エラーで fallback 可能(実行時エラーにしない)。
- **Incremental Adoption**: Phase 231 は検証専用、Phase 232+ で実際の lowering 置き換え予定。
- 使用箇所:
- `pattern2_with_break.rs` の break 条件 lowering 前に pre-validation として実行。
- 将来は Pattern1/Pattern3/Pattern4 にも拡大予定Phase 232
- **DigitPosConditionNormalizerPhase 224-E 実装完了)**
- ファイル: `src/mir/join_ir/lowering/digitpos_condition_normalizer.rs`
- 責務:
- digit_pos 条件を正規化(`digit_pos < 0` → `!is_digit_pos`)。
- Pattern2 の break 条件 lowering 前に呼び出され、promoted variable の条件を bool 形式に変換。
- Phase 224 実装内容Core Implementation Complete ⚠️):
- **Two-tier promotion**: Step1 で A-3 Trim 試行 → 失敗なら Step2 で A-4 DigitPos 試行 → 両方失敗で Fail-Fast。
- **DigitPosPromoter 統合**: cascading indexOf パターンsubstring → indexOf → comparisonの昇格をサポート。
@ -381,6 +405,7 @@ Local Region (1000+):
- **単体テスト**: 5/5 PASShappy path, wrong operator/variable/constant, non-binary-op
- **E2E テスト**: `phase2235_p2_digit_pos_min.hako` で型エラー解消確認。
- **回帰テスト**: digitpos (11 tests), trim (32 tests) 全て PASS。
- **digit_pos 正規化ライン**: DigitPosPromoter + ConditionAlias + DigitPosConditionNormalizer で `digit_pos < 0` を bool キャリア `is_digit_pos` ベースの条件(`!is_digit_pos`)に直してから ConditionEnv / BoolExprLowerer へ渡す。
- 参考:
- 設計ドキュメント: `docs/development/current/main/phase224-digitpos-condition-normalizer.md`
- 実装サマリ: `docs/development/current/main/PHASE_224_SUMMARY.md`
@ -511,6 +536,7 @@ Local Region (1000+):
- ExitMeta から exit_bindings を構築Collector
- 変数再接続はヘッダ PHI の dst を使って `builder.variable_map` を更新Reconnector
- expr 用の PHI には一切触れないcarrier 専用ライン)。
- **ConditionOnly キャリア**: header PHI の entry は CarrierInitBoolConst(false) 等を起点にし、ExitLine では variable_map や ExprResult への書き戻しを行わずヘッダ PHI 経由に限定。
- **ExprResultResolverPhase 221-R 実装済み)**
- ファイル: `src/mir/builder/control_flow/joinir/merge/expr_result_resolver.rs`
@ -738,6 +764,43 @@ Phase 210221 で「数値ループif-sum」を実戦投入し、JoinIR イ
---
## 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」という役割分担に近づける。
- **Phase 230ExprLowerer / ScopeManager 設計フェーズ)**
- 目標: 条件式 / init 式 / carrier 更新式の lowering を将来ひとつの ExprLowerer + ScopeManager に統合できるよう、既存の散在する lowering/API/Env を設計レベルで整理する(このフェーズではコード変更なし)。
---
## 5. selfhost / .hako JoinIR Frontend との関係
JoinIR は Rust 側だけでなく、将来的に .hako selfhost コンパイラ側でも生成・解析される予定だよ:

View File

@ -128,6 +128,7 @@ ConditionPromotionResult::Promoted {
### E2E Test
**Test File**: `apps/tests/phase2235_p2_digit_pos_min.hako`
このテストは digit_pos 昇格と型整合性・SSA 安定性を確認するインフラ用途で、数値としての戻り値の意味論は今後の JsonParser 本体フェーズで定義する予定だよ。
**Success Criteria**:
- No type error ("unsupported compare Lt on Bool and Integer")

View File

@ -0,0 +1,164 @@
# Phase 230: ExprLowerer / ScopeManager Design
このドキュメントは、Phase 230 で検討する「ExprLowerer / ScopeManager」設計のメモだよ。
**コード変更は行わず、将来の統合先インターフェースだけを先に固める**ことが目的。
---
## 1. ExprLowerer の役割と API スケッチ
### 1.1 役割
- ExprLowerer は「AST の式ノード → JoinIR ValueId」の SSOT として振る舞う箱。
- 条件式 / init 式 / Update 式UpdateExpr に入る前の生 ASTなど、式まわりの lowering を一本化する。
- 変数解決やスコープ情報は ScopeManager に委譲し、ExprLowerer 自身は「式構造」を見ることに専念する。
### 1.2 API スケッチ
```rust
pub struct ExprLowerer<'env> {
/// 名前解決とスコープ情報を提供する窓口
scope: &'env dyn ScopeManager,
/// 型情報の参照(将来用。現時点では Option でもよい想定)
types: Option<&'env TypeContext>,
/// JoinIR 命令バッファ
instructions: &'env mut Vec<JoinInst>,
/// JoinIR ValueId アロケータ
alloc_value: &'env mut dyn FnMut() -> ValueId,
}
impl<'env> ExprLowerer<'env> {
/// 任意の式 AST を JoinIR ValueId に lowering する入口
pub fn lower_expr(
&mut self,
ast: &ASTNode,
ctx: ExprContext,
) -> Result<ValueId, ExprLoweringError> {
// ctx: Condition / Init / Update / Misc などの文脈ヒント
unimplemented!()
}
}
```
```rust
/// 式の文脈(どこから呼ばれているか)を表す軽量フラグ
pub enum ExprContext {
Condition, // ループ条件 / if 条件
InitBodyLocal, // body-local init
UpdateCarrier, // carrier update (UpdateExpr 相当)
Misc, // その他(将来用)
}
```
### 1.3 呼び出し元の想定
- 条件式:
- Pattern14 lowerer や `condition_to_joinir` から
- `lower_expr(ast, ExprContext::Condition)` として利用。
- init 式body-local:
- `LoopBodyLocalInitLowerer` 相当の責務を段階的に ExprLowerer に寄せる。
- `lower_expr(init_ast, ExprContext::InitBodyLocal)` を呼んで ValueId を受け取り、LoopBodyLocalEnv に名前を紐づけるだけの薄い箱にする。
- Update 式:
- いまは `LoopUpdateAnalyzer``UpdateExpr``CarrierUpdateEmitter` だが、
将来は UpdateExpr 生成の一部を ExprLowerer に委譲することも視野に入れる。
- 初期段階では `ExprContext::UpdateCarrier` だけ定義しておき、実際の統合は後続フェーズに回す。
### 1.4 既存箱からの委譲イメージ
- BoolExprLowerer:
- 現状ほぼ未使用の MIR 向け lowering だが、「条件式の構造を落とす」という責務は ExprLowerer と重なる。
- 将来は ExprLowerer の内部実装(もしくは Condition/MIR プロファイル)として吸収する候補。
- MethodCallLowerer:
- CoreMethodId メタデータと BoxCall emission ロジックはそのままユーティリティとして残す。
- ExprLowerer 側から `MethodCallLowerer::lower_*` を呼ぶ形で再利用。
- condition_to_joinir:
- 入口/オーケストレーターとして残しつつ、中身の AST → JoinIR 値の部分を ExprLowerer に差し替える方針。
---
## 2. ScopeManager の設計
### 2.1 目的
- 変数名 → ValueId の解決と、その変数がどのスコープに属しているかを一箇所で扱う。
- ConditionEnv / LoopBodyLocalEnv / CapturedEnv / CarrierInfo(promoted_loopbodylocals など) を覆う「ビュー」を提供する。
### 2.2 インターフェース案
```rust
/// 変数スコープの種類
pub enum VarScopeKind {
LoopParam, // ループ変数i, p など)
OuterLocal, // 関数ローカルだがループ外で定義されたもの
CapturedConst, // CapturedEnv に載っている実質定数
ConditionOnly, // 条件専用digits, len など)
BodyLocal, // ループ本体の localch, digit_pos など)
PromotedLoopBody, // 昇格済み LoopBodyLocalis_ws, is_digit_pos など)
CarrierLoopState, // キャリアsum, num_str など)
}
/// ScopeManager は ExprLowerer に対して「名前解決 + スコープ情報」を提供する。
pub trait ScopeManager {
/// 名前から JoinIR ValueId を取得(なければ None
fn lookup(&self, name: &str) -> Option<ValueId>;
/// 変数のスコープ種別を問い合わせる
fn scope_of(&self, name: &str) -> Option<VarScopeKind>;
}
```
### 2.3 既存構造との対応づけ
- ConditionEnv:
- loop_param + condition-only + body-only carrier を持っている。
- ScopeManager 実装の中で「LoopParam / ConditionOnly / CarrierLoopState」などに振り分ける役割。
- LoopBodyLocalEnv:
- `local ch`, `local digit_pos` などの body-local を保持。
- ScopeManager からは `VarScopeKind::BodyLocal` として見える。
- CapturedEnv:
- function_scope_capture.rs で検出された「関数スコープの実質定数」digits, base, limit 等)。
- ScopeManager 上では `VarScopeKind::CapturedConst` として扱う。
- CarrierInfo:
- carrier 名と join_id、ConditionOnly role、promoted_loopbodylocals 情報を持つ。
- ScopeManager 側からは、「PromotedLoopBody / CarrierLoopState」などの分類情報を取り出す。
ここではあくまでインターフェースレベルの設計に留めておき、
実際の `ScopeManagerImpl`ConditionEnv + LoopBodyLocalEnv + CapturedEnv + CarrierInfo を束ねる構造体)は後続フェーズで実装する想定だよ。
---
## 3. 既存箱とのマッピング表
Phase 230 の時点では「どの箱をどこに収めるか」をざっくり決めておくところまでにするよ。
| Box / モジュール名 | 現在の責務 | ExprLowerer との関係 | ScopeManager / TypeContext との関係 |
|------------------------------|----------------------------------------------------------|----------------------------------------------------|----------------------------------------------------------|
| BoolExprLowerer | AST → MIR boolean 式 loweringほぼ未使用 | 将来 `ExprLowerer` の内部ロジックとして統合候補 | 直接は関与しないMirBuilder 向けの歴史的遺産) |
| condition_to_joinir | 条件式 lowering のオーケストレーター | 入口モジュールとして残し、中身を ExprLowerer 呼びに | ScopeManager を使って ConditionEnv 系を内側に隠す |
| condition_lowerer | AST → JoinIR 値(条件用)のコアロジック | ExprLowerer に徐々に移管し、将来は内部実装に | 変数解決部分を ScopeManager に差し替える |
| LoopBodyLocalInitLowerer | body-local init 式の lowering + LoopBodyLocalEnv 更新 | lowering 部分は ExprLowerer に寄せ、将来は「宣言スキャン + env 更新」の薄い箱に | LoopBodyLocalEnv の管理は ScopeManager 実装が引き取る |
| MethodCallLowerer | MethodCall AST → BoxCallCoreMethodId メタ駆動) | ExprLowerer から utility として呼び出す | 引数の変数解決に ScopeManager を利用するよう将来変更 |
| CarrierUpdateEmitter | UpdateExpr構造化済み→ JoinIR 更新命令 | UpdateExpr 生成側が ExprLowerer と噛み合うよう設計する方向 | UpdateEnv を ScopeManager ベースの実装に置き換える候補 |
| ConditionEnv | 条件用の名前解決レイヤ | ScopeManager の内部実装の一部 | TypeContext と組み合わせて「型付き環境」にしていく余地 |
| LoopBodyLocalEnv | ループ本体 local 変数の JoinIR ValueId マップ | ScopeManager によって一段抽象化される | 将来的に型付き body-local として TypeContext と連携 |
| CapturedEnv | 関数スコープ実質定数の環境 | ScopeManager 実装が `CapturedConst` として吸収 | TypeContext から「不変 + 型」の情報を共有できると理想 |
---
## 4. Phase 230 のスコープと非スコープ
- スコープ(やること):
- 既存の式 lowering の散らばり方を inventory に落とし込む。
- ExprLowerer / ScopeManager / ExprContext / VarScopeKind のインターフェース案を決める。
- 既存箱がどこに入りそうかor 外に残りそうか)を表で整理する。
- 非スコープ(やらないこと):
- 実際のコードの統合・リファクタリング。
- condition_to_joinir / LoopBodyLocalInitLowerer などの実装変更。
- TypeContext 自体の設計・実装(ここでは「将来ここにくっつける」レベルの言及に留める)。
結論として Phase 230 は、JoinIR ラインにおける「式」と「スコープ」の SSOT を見据えた
**設計フェーズ(ドキュメントのみ)** として完了させるイメージだよ。

View File

@ -0,0 +1,117 @@
# Phase 230: Expr Lowering Inventory
このメモは、既存の式 lowering がどこに散らばっているかを棚卸しするためのインベントリだよ。
Phase 230 では「コードをいじらずに把握だけする」のが目的。
---
## 1. condition_to_joinir.rs / condition_lowerer.rs
- 役割:
- ループの条件式header 条件 / break 条件)を AST → JoinIR の Compare/BinOp/UnaryOp 列に落とす高レベル入口。
- `ConditionEnv` を使って「変数名 → JoinIR ValueId」の解決を行う。
- 主な機能:
- `lower_condition_to_joinir(ast, alloc_value, &ConditionEnv)`:
- 二項比較(`var < literal`, `var == var`)を JoinIR Compare に lowering。
- `ConditionPatternBox` による正規化後の単純条件を対象。
- `lower_value_expression(ast, alloc_value, &ConditionEnv, &mut instructions)`:
- 条件式の内部で出てくるサブ式(`i+1`, `s.length()`, `digits.indexOf(ch)` など)を JoinIR 値に潰す。
- `MethodCallLowerer` を経由してメソッド呼び出しを BoxCall に変換。
- 制約:
- JoinIR 専用MirBuilder には触らない)。
- 変数解決は ConditionEnv に限定LoopBodyLocalEnv や UpdateEnv には直接アクセスしない)。
- support 対象外の AST ノードは Fail-Fast`Result::Err`)で返す。
---
## 2. bool_expr_lowerer.rs
- 役割:
- 「MIR 向け」の boolean 式 loweringAST → MirBuilder / SSAを行う旧来の箱。
- OR チェーンや `&&`/`||`/`!` を MIR の Compare / BinOp / UnaryOp に展開する。
- 主な機能:
- `BoolExprLowerer::lower_condition(&ASTNode) -> Result<ValueId, String>`:
- BinaryOp比較演算子 + `&&`/`||`を再帰的に潰し、MirInstruction を emit。
- 変数・リテラル・メソッド呼び出しなどは MirBuilder の `build_expression` に委譲。
- 制約:
- MirBuilder 前提の API で、JoinIR condition_to_joinir とは別ライン。
- 現時点では「ほぼ未使用(テストもコメントアウト)」扱いの歴史的モジュール。
- condition_to_joinir 側と直接の接点はなく、将来 ExprLowerer に統合する際の候補。
---
## 3. loop_body_local_init.rsLoopBodyLocalInitLowerer
- 役割:
- ループ本体の `local` 宣言の初期化式を AST → JoinIR に落として `LoopBodyLocalEnv` に格納する。
- 「body-local 変数の定義側init」専用の lowering。
- 主な機能:
- `lower_inits_for_loop(body_ast, &mut LoopBodyLocalEnv)`:
- ループ本体 AST から `ASTNode::Local` をスキャンし、各変数の init 式を順番に処理。
- `lower_init_expr(expr, &LoopBodyLocalEnv) -> Result<ValueId, String>`:
- リテラル(整数/文字列)→ Const
- 変数参照 → ConditionEnv 経由で解決
- 二項演算(`+ - * /`)→ BinOp
- MethodCall`s.substring`, `digits.indexOf`)→ `emit_method_call_init` 経由で MethodCallLowerer に委譲
- 制約:
- 変数解決は ConditionEnv + 既存の LoopBodyLocalEnvcascadingのみ。
- サポート外のリテラル種別・演算子・複雑な式は Fail-Fast。
- lowering 対象は「init 式」だけで、更新式UpdateExprは別の箱CarrierUpdateEmitterが担当。
---
## 4. method_call_lowerer.rsMethodCallLowerer
- 役割:
- MethodCall AST ノードを CoreMethodId メタデータに基づいて JoinIR BoxCall に lowering する箱。
- 同じメソッド呼び出しでも「条件文から使うか」「init から使うか」でホワイトリストを分けている。
- 主な機能:
- `lower_for_condition(recv_val, method_name, args, alloc, &ConditionEnv, &mut instructions)`:
- `allowed_in_condition()` に通る CoreMethodId だけ許可(例: `length`, 一部の `indexOf`)。
- 引数は `condition_lowerer::lower_value_expression` で JoinIR 値に lowering。
- `lower_for_init(recv_val, method_name, args, alloc, &ConditionEnv, &LoopBodyLocalEnv, &mut instructions)`:
- `allowed_in_init()` に通るメソッド(`substring`, `indexOf` など)を body-local init 用に lowering。
- 引数の変数は LoopBodyLocalEnv → ConditionEnv の優先順で解決cascading local をサポート)。
- `lower_arg_with_cascading`:
- 引数用の小さなヘルパー。変数なら LoopBodyLocalEnv/ConditionEnv を見て、それ以外は condition_lowerer に委譲。
- 制約:
- CoreMethodId メタデータが前提(メソッド名や Box 名のハードコード禁止)。
- 文脈condition/initごとに別 API で呼び分ける必要がある。
- 型情報は暗黙的CoreMethodId 側に埋め込まれており、TypeContext のような統一ビューはまだない)。
---
## 5. carrier_update_emitter.rsCarrierUpdateEmitter
- 役割:
- LoopUpdateAnalyzer で分類された UpdateExpr`sum = sum + digit` など)を JoinIR 命令列に変換する。
- 「キャリア更新の右辺式」を安全なパターンだけ受理して lowering するホワイトリスト箱。
- 主な機能:
- `emit_carrier_update_with_env(carrier, &UpdateExpr, alloc, &UpdateEnv, &mut instructions)`:
- `UpdateRhs::Const` → Const + BinOp(Add/Mul)。
- `UpdateRhs::Variable` → UpdateEnv.resolve(name) 経由で条件変数・body-local を横断解決。
- `UpdateRhs::StringLiteral` → Const(String)。
- `UpdateRhs::NumberAccumulation` → base/digit 組み合わせを Mul + Add の2段構成で emit。
- `emit_carrier_update`(レガシー):
- ConditionEnv ベースの旧 APIbody-local 非対応)を後方互換のために残している。
- 制約:
- lowering 対象は「UpdateExpr に正規化済みの式」に限る(生 AST は扱わない)。
- `UpdateRhs::Other` や method call を含む複雑な更新は can_lower() 側で reject される前提。
- 型は整数/String の一部パターンに限定TypeContext への統合は未実施)。
---
## 6. 小まとめPhase 230 時点の散らばり方)
- 「条件式」と「init/body-local」と「UpdateExpr」が、それぞれ別の箱で AST / 中間表現を潰している:
- 条件式: `condition_to_joinir` + `condition_lowerer`+ 一部 BoolExprLowerer/MIR 側)
- body-local init: `LoopBodyLocalInitLowerer` + `MethodCallLowerer(lower_for_init)`
- carrier 更新: `CarrierUpdateEmitter` + `UpdateEnv`UpdateExpr ベース)
- 変数解決も 3 系統に分かれている:
- `ConditionEnv`loop param / captured / condition-only
- `LoopBodyLocalEnv`body 内 `local`
- `UpdateEnv`ConditionEnv + LoopBodyLocalEnv の合成ビュー)
- 将来の ExprLowerer/ScopeManager では、これらを
- 「式 lowering の SSOT」として ExprLowerer
- 「名前解決の SSOT」として ScopeManager
に段階統合していくのがターゲット、という整理になっているよ。