## Task A: Loop Invariants Architecture (Option A - Boundary Extension) Introduced explicit loop_invariants concept for variables that are: - Used in loop body but never modified (e.g., substring needle in index_of) - Need header PHI (all iterations use same value) - Do NOT need exit PHI (not a LoopState) Implementation: - Added `loop_invariants: Vec<(String, ValueId)>` field to JoinInlineBoundary - Added `with_loop_invariants()` method to JoinInlineBoundaryBuilder - Modified Pattern 6 to use loop_invariants instead of ConditionOnly misuse * s (haystack) and ch (needle) now properly classified * exit_bindings simplified to only LoopState carriers - Extended LoopHeaderPhiBuilder to generate invariant PHIs * Entry PHI: from host * Latch PHI: self-reference (same value every iteration) - Updated instruction_rewriter to handle invariant latch incoming Files Modified: - src/mir/join_ir/lowering/inline_boundary.rs (structure + builder) - src/mir/join_ir/lowering/inline_boundary_builder.rs (builder method) - src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs - src/mir/builder/control_flow/joinir/merge/loop_header_phi_builder.rs - src/mir/builder/control_flow/joinir/merge/mod.rs - src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs ## Task B: Code Quality - var() Helper Unification Created common module to eliminate var() duplication: - New module: src/mir/builder/control_flow/joinir/patterns/common/ - Centralized var() helper in ast_helpers.rs - Updated Pattern 6 and Pattern 2 to use common var() - Test code (5 occurrences) deferred to Phase 256+ per 80/20 rule Result: Eliminated 2 duplicate var() functions, 5 test occurrences remain Files Created: - src/mir/builder/control_flow/joinir/patterns/common/ast_helpers.rs - src/mir/builder/control_flow/joinir/patterns/common/mod.rs Files Modified: - src/mir/builder/control_flow/joinir/patterns/mod.rs - src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs - src/mir/builder/control_flow/joinir/patterns/policies/balanced_depth_scan_policy.rs ## Task C: Documentation - PostLoopEarlyReturnPlan Usage Examples Enhanced post_loop_early_return_plan.rs with: - Architecture explanation (exit PHI usage prevents DCE) - Pattern 2 example (Less: balanced_depth_scan) - Pattern 6 example (NotEqual: index_of) - Builder defer decision: when 4+ patterns emerge, add builder Files Modified: - src/mir/builder/control_flow/joinir/patterns/policies/post_loop_early_return_plan.rs ## Task D: Phase 256 Preparation Analyzed next failure from smoke tests: - `StringUtils.split/2` with variable-step loop - Not constant step (i += len or similar) - Three implementation options identified Created Phase 256 README with: - Minimal reproduction code - Root cause analysis - Implementation options with trade-offs - Clear next steps Files Created: - docs/development/current/main/phases/phase-256/README.md ## Verification Results ✅ All tests passing: - pattern254_p0_index_of_vm.sh: PASS - No regression in Pattern 1-5 - Smoke test progresses past json_lint_vm to next failure ✅ Architecture achievements: - Semantic clarity: loop_invariants vs exit_bindings properly separated - ConditionOnly misuse eliminated - PHI generation correct for all carrier types - Code quality: var() duplication reduced - Next phase clearly defined ## Summary Phase 255 P2 achieves root-cause fix for multi-param loop support: - Boundary concept expanded (loop_invariants field) - Pattern 6 architecture corrected (no ConditionOnly misuse) - Code quality improved (var() centralized) - Next pattern (variable-step) is now the challenge ✨ Box-first principles maintained: clean separation, explicit roles, minimal coupling 🧠 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
JoinIR Pattern Policies - ルーティング箱の責務
概要
このディレクトリには、Pattern認識とルーティング(policy決定)を行う「箱」が格納されています。
Policy箱の責務
ルーティング決定
- 入力: LoopSkeleton、break条件、carrier情報
- 出力: 適用可能なPattern(Pattern2, Pattern3, etc.)とLoweringResult
- 判断基準: パターンマッチング条件(Trim, ConditionalStep, Escape, etc.)
Lowering Result生成
- Pattern固有の情報(ConditionOnlyRecipe, ConditionalStepInfo, etc.)を生成
- CarrierInfo拡張(promoted variables, trim_helper, etc.)
- ConditionBinding設定
Policy箱の候補(将来の整理対象)
trim_loop_lowering.rs (Phase 180/92/93)
現在の場所: patterns/trim_loop_lowering.rs
責務: Trimパターン認識とConditionOnlyルーティング
判断基準:
- LoopBodyLocal変数がTrim条件(whitespace check)
- break条件がConditionOnly(毎イテレーション再計算)
出力:
TrimLoweringResult- condition, carrier_info, condition_only_recipe
将来的な整理:
- Trimパターン判断ロジックをpolicies/へ移動検討
- 現在はpatterns/直下に配置(Phase 180統合済み)
p5b_escape_derived_policy.rs (Phase 94)
現在の場所: patterns/policies/p5b_escape_derived_policy.rs
責務: P5b escapeパターン認識とBodyLocalDerivedルーティング
判断基準:
- body-local変数の再代入(例:
ch = s.substring(...)) - escape skip条件(例:
if ch == "\\" { i = i + 2 }) - loop変数の条件付き更新
出力:
P5bEscapeDerivedDecision::UseDerived(BodyLocalDerivedRecipe)- escape recipeP5bEscapeDerivedDecision::Reject(String)- 検出失敗理由P5bEscapeDerivedDecision::None- 該当なし
将来的な整理:
- policies/配下でpolicy箱として統一済み(Phase 96)
loop_true_read_digits_policy.rs (Phase 104/105)
現在の場所: patterns/policies/loop_true_read_digits_policy.rs
責務: loop(true) + break-only digits(read_digits_from family)の認識とルーティング
判断基準:
loop(true)である- read-digits(loop(true)) detector に一致する
ReadDigitsBreakConditionBoxが eos 条件 + digit 条件を抽出できる
出力:
PolicyDecision::Use(LoopTrueReadDigitsPolicyResult)-break_when_true := (ch == \"\") || !(digit_cond)とchallow-listPolicyDecision::Reject(String)- 検出失敗理由(Fail-Fast)PolicyDecision::None- 該当なし
body_local_policy.rs
現在の場所: patterns/body_local_policy.rs
責務: Body-local変数の昇格判断
判断基準:
- body-local変数がloop carrier候補か
- 昇格可能なパターンか(Trim, Escape, etc.)
将来的な整理:
- policies/へ移動してpolicy箱として統一
設計原則
単一判断の原則
- 各policy箱は1つのパターン判断のみ
- 複数パターンの判断は別のpolicy箱に委譲
非破壊的判断
- 入力を変更しない
- 判断結果をResultで返す(
Ok(Some(result))/Ok(None)/Err(msg))
Fail-Fast
- パターンマッチング失敗は即座に
Ok(None)を返す - エラーは明示的に
Err(msg) - Reject理由は
error_tags::freeze()でタグ付与
Decision型の統一
PolicyDecision<T>(Use / Reject / None)をSSOTにする- 例:
P5bEscapeDerivedDecision = PolicyDecision<BodyLocalDerivedRecipe>,TrimPolicyResult
使用パターン
Pattern2ルーティング(現在のパターン)
// Step 1: P5b escapeパターン判断
match classify_p5b_escape_derived(body, loop_var_name) {
P5bEscapeDerivedDecision::UseDerived(recipe) => {
// P5b escape適用
return lower_with_p5b_escape(recipe, ...);
}
P5bEscapeDerivedDecision::Reject(reason) => {
return Err(reason);
}
P5bEscapeDerivedDecision::None => {
// 次のパターンへ
}
}
// Step 2: Trimパターン判断
if let Some(trim_result) = TrimLoopLowerer::try_lower_trim_pattern(...)? {
// Trimパターン適用
return Ok(trim_result);
}
// Step 3: デフォルトパターン
Ok(default_pattern2_lowering(...))
将来の統一パターン(policies/移動後)
use patterns::policies::{P5bEscapePolicy, TrimPolicy, ConditionalStepPolicy};
// Policy boxの統一インターフェース
trait PatternPolicy {
type Decision;
fn classify(&self, context: &PatternContext) -> Self::Decision;
}
// ルーティングパイプライン
let decision = P5bEscapePolicy.classify(&ctx)
.or_else(|| TrimPolicy.classify(&ctx))
.or_else(|| ConditionalStepPolicy.classify(&ctx))
.unwrap_or(DefaultDecision);
デバッグ
ログprefixの統一
[policy/p5b-escape]- P5b escape判断[policy/trim]- Trim判断[policy/conditional-step]- ConditionalStep判断
環境変数
HAKO_JOINIR_DEBUG=1- JoinIR全般のデバッグログjoinir_dev_enabled()- 既存の制御機構
デバッグ出力例
if joinir_dev_enabled() {
eprintln!("[policy/p5b-escape] Detected escape pattern: {:?}", info);
}
命名規則
ファイル命名
<パターン名>_policy.rs- パターン判断policy箱- 例:
trim_policy.rs,escape_policy.rs,conditional_step_policy.rs
型命名
<パターン名>Decision- 判断結果型<パターン名>Policy- policy箱の構造体(将来)
列挙型パターン
pub enum Decision {
None, // 該当なし
Use(Recipe), // 適用可能
Reject(String), // 検出失敗
}
将来の拡張
Phase 95以降の候補
trim_policy.rs- Trimパターン判断をpolicies/へ移動escape_policy.rs- P5b escapeを統一インターフェースにarray_loop_policy.rs- 配列ループパターン判断map_loop_policy.rs- Mapループパターン判断conditional_step_policy.rs- ConditionalStep独立箱化
段階的な移行計画
Phase 1: ディレクトリ準備(今回)
- policies/ディレクトリ作成 ✅
- README.md作成 ✅
- mod.rs作成 ✅
Phase 2: 既存policy箱の移動(将来)
p5b_escape_derived_policy.rs→policies/escape_policy.rsbody_local_policy.rs→policies/body_local_policy.rs- trim関連ロジック抽出 →
policies/trim_policy.rs
Phase 3: インターフェース統一(将来)
PatternPolicytrait定義- Decision型の統一
- ルーティングパイプラインの実装
拡張時の注意
- README.mdにpolicy箱の責務を追加
- Decision型を統一パターンに従う
- 既存のpolicy箱との一貫性を保つ
- Fail-Fastタグを明記
参考資料
関連ドキュメント
- JoinIR アーキテクチャ概要
- JoinIR 設計マップ
- Pattern Pipeline - パターン判断の統合処理
- Escape Pattern Recognizer - escape検出ロジック
Phase Log
- Phase 92 Log - ConditionalStep
- Phase 93 Log - ConditionOnly
- Phase 94 Log - BodyLocalDerived
設計哲学(Box Theory)
箱理論の適用
- 箱にする: Policy判断を独立した箱に分離
- 境界を作る: Decisionを統一インターフェースに
- 戻せる: 段階的移行で既存コードを壊さない
- 見える化: ログprefixで判断過程を可視化
Fail-Fast原則の徹底
- フォールバック処理は原則禁止
- Reject理由を明示的に返す
- エラータグで根本原因を追跡可能に
単一責任の箱
- 1つのpolicy箱 = 1つのパターン判断
- 複数パターンの組み合わせは上位層で制御
- policy箱同士は独立・疎結合