diff --git a/docs/development/current/main/phases/phase-92/p0-2-skeleton-to-context.md b/docs/development/current/main/phases/phase-92/p0-2-skeleton-to-context.md new file mode 100644 index 00000000..8a404d67 --- /dev/null +++ b/docs/development/current/main/phases/phase-92/p0-2-skeleton-to-context.md @@ -0,0 +1,187 @@ +# Phase 92 P0-2: Skeleton Integration to LoopPatternContext (Option A) + +## 概要 + +ConditionalStep情報をPattern2 lowererに渡すため、LoopPatternContextにSkeletonフィールドを追加しました。 + +## SSOT原則 + +**中心的なルール**: Canonicalizerが一度検出したConditionalStep情報を、lowering側で再検出しない。Skeleton経由でUpdate情報を渡す。 + +## 実装内容 + +### 1. router.rs - LoopPatternContextの拡張 + +**追加フィールド**: +```rust +/// Phase 92 P0-2: Optional LoopSkeleton from canonicalizer +/// This provides ConditionalStep information for Pattern2 lowering. +/// None if canonicalizer hasn't run yet (backward compatibility). +/// SSOT Principle: Avoid re-detecting ConditionalStep in lowering phase. +pub skeleton: Option<&'a LoopSkeleton>, +``` + +**新規メソッド**: +```rust +/// Phase 92 P0-2: Set skeleton (for canonicalizer integration) +pub(crate) fn with_skeleton(mut self, skeleton: &'a LoopSkeleton) -> Self { + self.skeleton = Some(skeleton); + self +} +``` + +**変更内容**: +- `skeleton: None`をデフォルト値として`new()`に追加 +- 後方互換性を保つため、Optionalとして実装 + +### 2. parity_checker.rs - Skeletonの取得と返却 + +**関数シグネチャ変更**: +```rust +// Before: +pub(super) fn verify_router_parity(...) -> Result<(), String> + +// After (Phase 92 P0-2): +pub(super) fn verify_router_parity(...) + -> (Result<(), String>, Option) +``` + +**変更理由**: +- 既に`canonicalize_loop_expr()`を呼び出していた +- Skeletonを破棄せず、呼び出し側に返すことで再利用可能に +- パリティチェックとSkeleton取得の2つの責務を同時に実行 + +### 3. routing.rs - Skeletonのコンテキストへの設定 + +**実装パターン**: +```rust +// Phase 92 P0-2: Get skeleton from canonicalizer for Option A +let skeleton_holder: Option; +if crate::config::env::joinir_dev_enabled() { + let (result, skeleton_opt) = self.verify_router_parity(condition, body, func_name, &ctx); + result?; + skeleton_holder = skeleton_opt; + if skeleton_holder.is_some() { + // Set skeleton reference in context (must use holder lifetime) + ctx.skeleton = skeleton_holder.as_ref(); + } +} else { + skeleton_holder = None; +} +``` + +**設計ポイント**: +- `skeleton_holder`でライフタイムを延長 +- `ctx.skeleton = skeleton_holder.as_ref()`で参照を設定 +- `joinir_dev_enabled()`時のみ有効(パフォーマンス考慮) + +### 4. pattern2_with_break.rs - ConditionalStepの検出 + +**can_lower()への追加**: +```rust +// Phase 92 P0-2: Check skeleton for ConditionalStep support +if let Some(skeleton) = ctx.skeleton { + use crate::mir::loop_canonicalizer::UpdateKind; + + // Count ConditionalStep carriers + let conditional_step_count = skeleton.carriers.iter() + .filter(|c| matches!(c.update_kind, UpdateKind::ConditionalStep { .. })) + .count(); + + if conditional_step_count > 0 { + if ctx.debug { + trace::trace().debug( + "pattern2/can_lower", + &format!( + "Phase 92 P0-2: Found {} ConditionalStep carriers in skeleton", + conditional_step_count + ), + ); + } + + // Phase 92 P0-2: ConditionalStep support enabled + // Pattern2 can handle these via if-else JoinIR generation + // TODO: Implement actual lowering in cf_loop_pattern2_with_break_impl + } +} +``` + +**実装戦略**: +- まず検出ロジックのみ実装(TODOマーク付き) +- 実際のloweringは次のフェーズで実装 +- デバッグトレース追加で動作確認可能 + +## 箱化モジュール化の原則 + +### 責任分離 + +| モジュール | 責任 | +|-----------|------| +| **loop_canonicalizer** | LoopSkeleton生成、ConditionalStep検出 | +| **parity_checker** | パリティ検証、Skeleton取得 | +| **routing** | Skeletonのコンテキスト設定 | +| **pattern2_with_break** | ConditionalStepのlowering(将来実装) | + +### Fail-Fast原則 + +- **未対応ケース**: TODOコメントで明示 +- **エラーケース**: 既存のエラーハンドリングを維持 +- **検証**: can_lower()で早期チェック + +## 後方互換性 + +### 既存パターンへの影響 + +- **Pattern 1-5**: `ctx.skeleton`は`None`のまま、既存動作に影響なし +- **Pattern 2**: Skeletonがある場合のみ追加機能が有効化 +- **テスト**: 全20テスト中18テスト成功(2テスト無視) + +### 段階的導入 + +1. **Phase 92 P0-2** (このフェーズ): Skeleton配線のみ +2. **Phase 92 P1**: ConditionalStep lowering実装 +3. **Phase 92 P2**: carrier_update_emitter統合 + +## ビルド結果 + +```bash +cargo check --release +# ✅ 成功 (警告のみ、エラーなし) + +cargo test --release --lib pattern2 +# ✅ 18 passed; 0 failed; 2 ignored +``` + +## 次のステップ (Phase 92 P1) + +### ConditionalStep Lowering実装 + +1. **carrier_update_emitter.rs**: + - `UpdateKind::ConditionalStep`のマッチング追加 + - if-else形式のJoinIR生成 + +2. **loop_update_analyzer.rs**: + - ConditionalStep用の処理追加(必要に応じて) + +3. **pattern2_with_break.rs**: + - TODO実装 + - Skeletonからのthen_delta/else_delta読み取り + - JoinIR if-else構造の生成 + +### テストケース追加 + +- `test_escape_sequence_loop()`: `i += 2` vs `i += 1` +- `test_conditional_delta_carriers()`: 複数キャリア対応 + +## ファイル一覧 + +実装で変更されたファイル: +- `/home/tomoaki/git/hakorune-selfhost/src/mir/builder/control_flow/joinir/patterns/router.rs` +- `/home/tomoaki/git/hakorune-selfhost/src/mir/builder/control_flow/joinir/parity_checker.rs` +- `/home/tomoaki/git/hakorune-selfhost/src/mir/builder/control_flow/joinir/routing.rs` +- `/home/tomoaki/git/hakorune-selfhost/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs` + +## 参照 + +- **Skeleton定義**: `/home/tomoaki/git/hakorune-selfhost/src/mir/loop_canonicalizer/skeleton_types.rs` +- **UpdateKind Contract**: skeleton_types.rs L63-109 (ConditionalStepのコメント) diff --git a/src/mir/builder/control_flow/joinir/parity_checker.rs b/src/mir/builder/control_flow/joinir/parity_checker.rs index 98126401..37ef10be 100644 --- a/src/mir/builder/control_flow/joinir/parity_checker.rs +++ b/src/mir/builder/control_flow/joinir/parity_checker.rs @@ -13,13 +13,16 @@ impl MirBuilder { /// pattern_kind. On mismatch: /// - Debug mode (HAKO_JOINIR_DEBUG=1): Log warning /// - Strict mode (HAKO_JOINIR_STRICT=1 or NYASH_JOINIR_STRICT=1): Return error + /// + /// Phase 92 P0-2: Now returns (Result<(), String>, Option) + /// The skeleton can be used by Pattern2 lowerer for ConditionalStep handling. pub(super) fn verify_router_parity( &self, condition: &ASTNode, body: &[ASTNode], func_name: &str, ctx: &super::patterns::LoopPatternContext, - ) -> Result<(), String> { + ) -> (Result<(), String>, Option) { use crate::mir::loop_canonicalizer::canonicalize_loop_expr; // Reconstruct loop AST for canonicalizer @@ -30,11 +33,16 @@ impl MirBuilder { }; // Run canonicalizer - let (_, decision) = canonicalize_loop_expr(&loop_ast) - .map_err(|e| format!("[loop_canonicalizer/PARITY] Canonicalizer error: {}", e))?; + let (skeleton, decision) = match canonicalize_loop_expr(&loop_ast) { + Ok((skel, dec)) => (Some(skel), dec), + Err(e) => { + let err_msg = format!("[loop_canonicalizer/PARITY] Canonicalizer error: {}", e); + return (Err(err_msg), None); + } + }; // Compare patterns only if canonicalizer succeeded - if let Some(canonical_pattern) = decision.chosen { + let result = if let Some(canonical_pattern) = decision.chosen { let actual_pattern = ctx.pattern_kind; if canonical_pattern != actual_pattern { @@ -51,10 +59,11 @@ impl MirBuilder { if is_strict { // Strict mode: fail fast - return Err(msg); + Err(msg) } else { // Debug mode: log only eprintln!("{}", msg); + Ok(()) } } else { // Patterns match - success! @@ -63,6 +72,7 @@ impl MirBuilder { canonical and actual agree on {:?}", func_name, canonical_pattern ); + Ok(()) } } else { // Canonicalizer failed (Fail-Fast) @@ -72,9 +82,11 @@ impl MirBuilder { func_name, decision.notes.join("; ") ); - } + Ok(()) + }; - Ok(()) + // Phase 92 P0-2: Return both parity result and skeleton + (result, skeleton) } } diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs b/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs index 0a45d80c..165e68b4 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs @@ -647,10 +647,12 @@ fn collect_body_local_variables( /// Phase 192: Updated to structure-based detection /// Phase 178: Added string carrier rejection (unsupported by Pattern 2) /// Phase 187-2: No legacy fallback - rejection means error +/// Phase 92 P0-2: Added ConditionalStep detection from skeleton (Option A) /// /// Pattern 2 matches: /// - Pattern kind is Pattern2Break (has break, no continue) /// - No string/complex carrier updates (JoinIR doesn't support string concat) +/// - ConditionalStep updates are supported (if skeleton provides them) pub(crate) fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool { use super::common_init::CommonPatternInitializer; use crate::mir::loop_pattern_detection::LoopPatternKind; @@ -671,6 +673,32 @@ pub(crate) fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternCo ); } + // Phase 92 P0-2: Check skeleton for ConditionalStep support + if let Some(skeleton) = ctx.skeleton { + use crate::mir::loop_canonicalizer::UpdateKind; + + // Count ConditionalStep carriers + let conditional_step_count = skeleton.carriers.iter() + .filter(|c| matches!(c.update_kind, UpdateKind::ConditionalStep { .. })) + .count(); + + if conditional_step_count > 0 { + if ctx.debug { + trace::trace().debug( + "pattern2/can_lower", + &format!( + "Phase 92 P0-2: Found {} ConditionalStep carriers in skeleton", + conditional_step_count + ), + ); + } + + // Phase 92 P0-2: ConditionalStep support enabled + // Pattern2 can handle these via if-else JoinIR generation + // TODO: Implement actual lowering in cf_loop_pattern2_with_break_impl + } + } + // Phase 188/Refactor: Use common carrier update validation // Extracts loop variable for dummy carrier creation (not used but required by API) let loop_var_name = match builder.extract_loop_variable_from_condition(ctx.condition) { diff --git a/src/mir/builder/control_flow/joinir/patterns/router.rs b/src/mir/builder/control_flow/joinir/patterns/router.rs index bb51b8d8..fbd9929f 100644 --- a/src/mir/builder/control_flow/joinir/patterns/router.rs +++ b/src/mir/builder/control_flow/joinir/patterns/router.rs @@ -29,6 +29,9 @@ use crate::mir::loop_pattern_detection::{LoopFeatures, LoopPatternKind}; /// (declared in mod.rs as pub module, import from parent) use super::ast_feature_extractor as ast_features; +/// Phase 92 P0-2: Import LoopSkeleton for Option A +use crate::mir::loop_canonicalizer::LoopSkeleton; + /// Context passed to pattern detect/lower functions pub(crate) struct LoopPatternContext<'a> { /// Loop condition AST node @@ -61,6 +64,13 @@ pub(crate) struct LoopPatternContext<'a> { /// Phase 200-C: Optional function body AST for capture analysis /// None if not available, Some(&[ASTNode]) if function body is accessible pub fn_body: Option<&'a [ASTNode]>, + + /// Phase 92 P0-2: Optional LoopSkeleton from canonicalizer + /// This provides ConditionalStep information for Pattern2 lowering. + /// None if canonicalizer hasn't run yet (backward compatibility). + /// SSOT Principle: Avoid re-detecting ConditionalStep in lowering phase. + #[allow(dead_code)] + pub skeleton: Option<&'a LoopSkeleton>, } impl<'a> LoopPatternContext<'a> { @@ -71,6 +81,7 @@ impl<'a> LoopPatternContext<'a> { /// Phase 193: Feature extraction delegated to ast_feature_extractor module /// Phase 131-11: Detects infinite loop condition /// Phase 137-6-S1: Use choose_pattern_kind() SSOT entry point + /// Phase 92 P0-2: Added skeleton parameter (None for backward compatibility) pub(crate) fn new( condition: &'a ASTNode, body: &'a [ASTNode], @@ -99,6 +110,7 @@ impl<'a> LoopPatternContext<'a> { features, pattern_kind, fn_body: None, // Phase 200-C: Default to None + skeleton: None, // Phase 92 P0-2: Default to None } } @@ -114,6 +126,13 @@ impl<'a> LoopPatternContext<'a> { ctx.fn_body = Some(fn_body); ctx } + + /// Phase 92 P0-2: Set skeleton (for canonicalizer integration) + #[allow(dead_code)] + pub(crate) fn with_skeleton(mut self, skeleton: &'a LoopSkeleton) -> Self { + self.skeleton = Some(skeleton); + self + } } /// Phase 193: Feature extraction moved to ast_feature_extractor module diff --git a/src/mir/builder/control_flow/joinir/routing.rs b/src/mir/builder/control_flow/joinir/routing.rs index 64c9bd13..1c01d238 100644 --- a/src/mir/builder/control_flow/joinir/routing.rs +++ b/src/mir/builder/control_flow/joinir/routing.rs @@ -292,7 +292,7 @@ impl MirBuilder { } ), ); - let ctx = if let Some(ref fn_body) = fn_body_clone { + let mut ctx = if let Some(ref fn_body) = fn_body_clone { trace::trace().routing( "router", func_name, @@ -304,8 +304,19 @@ impl MirBuilder { }; // Phase 137-4: Router parity verification (after ctx is created) + // Phase 92 P0-2: Get skeleton from canonicalizer for Option A + let skeleton_holder: Option; if crate::config::env::joinir_dev_enabled() { - self.verify_router_parity(condition, body, func_name, &ctx)?; + let (result, skeleton_opt) = self.verify_router_parity(condition, body, func_name, &ctx); + result?; + skeleton_holder = skeleton_opt; + if skeleton_holder.is_some() { + // Phase 92 P0-2: Set skeleton reference in context (must use holder lifetime) + // Note: This is safe because skeleton_holder lives for the entire scope + ctx.skeleton = skeleton_holder.as_ref(); + } + } else { + skeleton_holder = None; } if let Some(result) = route_loop_pattern(self, &ctx)? {