Files
hakorune/docs/development/current/main/phase230-expr-lowerer-design.md
nyash-codex d4f90976da refactor(joinir): Phase 244 - ConditionLoweringBox trait unification
Unify condition lowering logic across Pattern 2/4 with trait-based API.

New infrastructure:
- condition_lowering_box.rs: ConditionLoweringBox trait + ConditionContext (293 lines)
- ExprLowerer implements ConditionLoweringBox trait (+51 lines)

Pattern migrations:
- Pattern 2 (loop_with_break_minimal.rs): Use trait API
- Pattern 4 (loop_with_continue_minimal.rs): Use trait API

Benefits:
- Unified condition lowering interface
- Extensible for future lowering strategies
- Clean API boundary between patterns and lowering logic
- Zero code duplication

Test results: 911/911 PASS (+2 new tests)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-11 02:35:31 +09:00

10 KiB
Raw Blame History

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 スケッチ

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!()
    }
}
/// 式の文脈(どこから呼ばれているか)を表す軽量フラグ
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 式:
    • いまは LoopUpdateAnalyzerUpdateExprCarrierUpdateEmitter だが、 将来は 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 インターフェース案

/// 変数スコープの種類
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」などの分類情報を取り出す。

ここではあくまでインターフェースレベルの設計に留めておき、
実際の ScopeManagerImplConditionEnv + 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 を見据えた
設計フェーズ(ドキュメントのみ) として完了させるイメージだよ。

補足Phase 231 時点の実装状況メモ):

  • src/mir/join_ir/lowering/expr_lowerer.rs / scope_manager.rs には、Phase 231 のパイロット実装が入っている。
    • コンテキストは ExprContext::Condition のみサポート。
    • 実装は ScopeManager → ConditionEnv を構築してから、既存の lower_condition_to_joinir に委譲する薄いラッパー。
    • pattern2_with_break.rs では バリデーション専用(結果は捨てて、実際の lowering は従来経路)の呼び出しになっている。
  • 本ドキュメントは「最終的に目指す ExprLowerer/ScopeManager 像」の設計であり、Phase 231 の実装はあくまで
    その前段階のパイロットという位置づけだよ(実装が完全にこの API 図のとおりになっているわけではない点に注意)。 Status: Active
    Scope: Expr Lowerer 設計JoinIR/ExprLowerer ライン)