Implementation: - Add make_pattern2_scope_manager() helper for DRY - Header conditions use ExprLowerer for supported patterns - Legacy fallback for unsupported patterns - Fail-Fast on supported patterns that fail Tests: - 4 new tests (all pass) - test_expr_lowerer_supports_simple_header_condition_i_less_literal - test_expr_lowerer_supports_header_condition_var_less_var - test_expr_lowerer_header_condition_generates_expected_instructions - test_pattern2_header_condition_via_exprlowerer Also: Archive old phase documentation (34k lines removed) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
3.2 KiB
3.2 KiB
Phase 170‑D: LoopConditionScopeBox 設計メモ
日付: 2025‑12‑07
状態: 設計完了(実装は今後の Phase で)
背景
Pattern2/Pattern4(Loop with Break / Loop with Continue)は、もともと
- ループパラメータ(例:
i) - ループ外のローカル・引数(例:
start,end,len)
のみを条件式に使う前提で設計されていた。
しかし JsonParserBox / trim などでは、
local ch = …
if (ch != " ") { break }
のように「ループ本体ローカル」を条件に使うパターンが現れ、この範囲を超えると
- バグが出たり(ValueId 伝播ミス)
- たまたま動いたり
という 曖昧な状態 になっていた。
これを箱理論的に整理するために、条件で使われる変数の「スコープ」を明示的に分類する LoopConditionScopeBox を導入する。
LoopConditionScopeBox の責務
責務は 1 つだけ:
「条件式に登場する変数が、どのスコープで定義されたか」を教える
型イメージ
pub enum CondVarScope {
LoopParam, // ループパラメータ (i)
OuterLocal, // ループ外のローカル/引数 (start, end, len)
LoopBodyLocal, // ループ本体で定義された変数 (ch)
}
pub struct CondVarInfo {
pub name: String,
pub scope: CondVarScope,
}
pub struct LoopConditionScope {
pub vars: Vec<CondVarInfo>,
}
主なメソッド例:
impl LoopConditionScope {
pub fn has_loop_body_local(&self) -> bool { … }
pub fn all_in(&self, allowed: &[CondVarScope]) -> bool { … }
pub fn vars_in(&self, scope: CondVarScope) -> impl Iterator<Item = &CondVarInfo> { … }
}
入力 / 出力
入力:
- ループヘッダ条件 AST
- break/continue 条件 AST(Pattern2/4)
- LoopScopeShape(どの変数がどのスコープで定義されたか)
出力:
LoopConditionScope(CondVarInfo の集合)
Pattern2/Pattern4 との関係
Pattern2/4 は LoopConditionScopeBox の結果だけを見て「対応可否」を決める:
let cond_scope = LoopConditionScopeBox::analyze(&loop_ast, &loop_scope);
// 対応範囲:LoopParam + OuterLocal のみ
if !cond_scope.all_in(&[CondVarScope::LoopParam, CondVarScope::OuterLocal]) {
return Err(JoinIrError::UnsupportedPattern { … });
}
これにより、
- いままで暗黙だった「対応範囲」が 設計として明示される
- LoopBodyLocal を条件に含む trim/JsonParser 系ループは
- 現状は
[joinir/freeze] UnsupportedPatternにする - 将来 Pattern5+ で扱いたくなったときに、LoopConditionScopeBox の結果を使って設計できる
- 現状は
将来の拡張
LoopBodyLocal を含む条件式を扱いたくなった場合は:
- LoopConditionScopeBox の結果から
vars_in(LoopBodyLocal)を取り出し、- その変数を carrier に昇格させる
- もしくは LoopHeader に「状態保持用」の追加パラメータを生やす
- それを新しい Pattern5 として設計すれば、既存 Pattern2/4 の仕様を崩さずに拡張できる。
このドキュメントは設計メモのみであり、実装は別フェーズ(Phase 170‑D‑impl など)で行う。 Status: Historical