Files
hakorune/docs/development/current/main/phases/phase-100

Phase 100: Pinned ReadOnly Captures設計メモ

目的

JoinIR lowering のスコープ解決(ConditionEnv → LoopBodyLocalEnv → CapturedEnv)の前提を崩さずに、 「ループ外で定義した local動的式でも可」をループ内で receiver として参照できるようにする。

例(問題の形):

local s = "a" + "b"   // 条件式とは無関係
loop(i < n) {
  local ch = s.substring(i, i + 1)  // receiver 解決が必要
}

方針SSOT

  • “Pinned local” は 新しい Pattern ではなく、ループに入ってくる readonly 入力として扱う。
  • SSOT は増やさず CapturedEnv に統合し、CapturedKind で区別する:
    • Explicit: 従来の capture
    • Pinned: ループ内参照のために必要な readonly local

FailFast 契約

  • Pinned は loop body 内で 再代入されないassignment target に現れたら拒否)。
  • loop entry 時点で host 側 ValueId が存在する(variable_map に無い場合は拒否)。
  • 初期化が loop より後にある等、支配関係が曖昧な場合は拒否(理由付き)。
  • init 式の副作用有無は問わない評価はホスト側で1回、Pinned は値を渡すだけ)。

Loop Canonicalizer との関係(混ぜない)

  • Phase 100 は「ループ外の値が loop 内で見えない」問題capture/wiringを解く。
  • Loop Canonicalizer は「ループ内の不変式を外に出す」AST 変換であり、別ユースケース。

予定(段階投入)

この Phase は「自由度を上げるほどバグが出やすい領域」なので、段階投入で進める。

  • P1readonly: ループ外 local を Pinned として CapturedEnv に取り込み、receiver 解決を通す。
    • FailFast: loop body 内で再代入される変数は pinned 禁止(理由付き)
  • P2mutable: loop body 内で再代入される外側変数は Pinned では扱わず、LoopStatecarrier/envに昇格して運ぶ。
    • 初期は “更新形を限定” して shape guard を作り、未対応は FailFast曖昧に通さない
  • P3hoist: ループ内の不変式の巻き上げは Loop Canonicalizer 側で扱う(別ユースケースとして分離)