feat(joinir): Phase 92 P0-2 - Skeleton to LoopPatternContext (Option A)
SSOT principle: Pass canonicalizer-derived ConditionalStep info to Pattern2 lowerer without re-detecting from AST. Changes: - router.rs: Add skeleton: Option<&'a LoopSkeleton> to LoopPatternContext - parity_checker.rs: Return (Result, Option<LoopSkeleton>) to reuse skeleton - routing.rs: Pass skeleton to context when joinir_dev_enabled() - pattern2_with_break.rs: Detect ConditionalStep in can_lower() Next: P0-3 implements actual JoinIR generation for ConditionalStep. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -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<LoopSkeleton>)
|
||||||
|
```
|
||||||
|
|
||||||
|
**変更理由**:
|
||||||
|
- 既に`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<crate::mir::loop_canonicalizer::LoopSkeleton>;
|
||||||
|
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のコメント)
|
||||||
@ -13,13 +13,16 @@ impl MirBuilder {
|
|||||||
/// pattern_kind. On mismatch:
|
/// pattern_kind. On mismatch:
|
||||||
/// - Debug mode (HAKO_JOINIR_DEBUG=1): Log warning
|
/// - Debug mode (HAKO_JOINIR_DEBUG=1): Log warning
|
||||||
/// - Strict mode (HAKO_JOINIR_STRICT=1 or NYASH_JOINIR_STRICT=1): Return error
|
/// - Strict mode (HAKO_JOINIR_STRICT=1 or NYASH_JOINIR_STRICT=1): Return error
|
||||||
|
///
|
||||||
|
/// Phase 92 P0-2: Now returns (Result<(), String>, Option<LoopSkeleton>)
|
||||||
|
/// The skeleton can be used by Pattern2 lowerer for ConditionalStep handling.
|
||||||
pub(super) fn verify_router_parity(
|
pub(super) fn verify_router_parity(
|
||||||
&self,
|
&self,
|
||||||
condition: &ASTNode,
|
condition: &ASTNode,
|
||||||
body: &[ASTNode],
|
body: &[ASTNode],
|
||||||
func_name: &str,
|
func_name: &str,
|
||||||
ctx: &super::patterns::LoopPatternContext,
|
ctx: &super::patterns::LoopPatternContext,
|
||||||
) -> Result<(), String> {
|
) -> (Result<(), String>, Option<crate::mir::loop_canonicalizer::LoopSkeleton>) {
|
||||||
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
|
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
|
||||||
|
|
||||||
// Reconstruct loop AST for canonicalizer
|
// Reconstruct loop AST for canonicalizer
|
||||||
@ -30,11 +33,16 @@ impl MirBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Run canonicalizer
|
// Run canonicalizer
|
||||||
let (_, decision) = canonicalize_loop_expr(&loop_ast)
|
let (skeleton, decision) = match canonicalize_loop_expr(&loop_ast) {
|
||||||
.map_err(|e| format!("[loop_canonicalizer/PARITY] Canonicalizer error: {}", e))?;
|
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
|
// 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;
|
let actual_pattern = ctx.pattern_kind;
|
||||||
|
|
||||||
if canonical_pattern != actual_pattern {
|
if canonical_pattern != actual_pattern {
|
||||||
@ -51,10 +59,11 @@ impl MirBuilder {
|
|||||||
|
|
||||||
if is_strict {
|
if is_strict {
|
||||||
// Strict mode: fail fast
|
// Strict mode: fail fast
|
||||||
return Err(msg);
|
Err(msg)
|
||||||
} else {
|
} else {
|
||||||
// Debug mode: log only
|
// Debug mode: log only
|
||||||
eprintln!("{}", msg);
|
eprintln!("{}", msg);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Patterns match - success!
|
// Patterns match - success!
|
||||||
@ -63,6 +72,7 @@ impl MirBuilder {
|
|||||||
canonical and actual agree on {:?}",
|
canonical and actual agree on {:?}",
|
||||||
func_name, canonical_pattern
|
func_name, canonical_pattern
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Canonicalizer failed (Fail-Fast)
|
// Canonicalizer failed (Fail-Fast)
|
||||||
@ -72,9 +82,11 @@ impl MirBuilder {
|
|||||||
func_name,
|
func_name,
|
||||||
decision.notes.join("; ")
|
decision.notes.join("; ")
|
||||||
);
|
);
|
||||||
}
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
Ok(())
|
// Phase 92 P0-2: Return both parity result and skeleton
|
||||||
|
(result, skeleton)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -647,10 +647,12 @@ fn collect_body_local_variables(
|
|||||||
/// Phase 192: Updated to structure-based detection
|
/// Phase 192: Updated to structure-based detection
|
||||||
/// Phase 178: Added string carrier rejection (unsupported by Pattern 2)
|
/// Phase 178: Added string carrier rejection (unsupported by Pattern 2)
|
||||||
/// Phase 187-2: No legacy fallback - rejection means error
|
/// Phase 187-2: No legacy fallback - rejection means error
|
||||||
|
/// Phase 92 P0-2: Added ConditionalStep detection from skeleton (Option A)
|
||||||
///
|
///
|
||||||
/// Pattern 2 matches:
|
/// Pattern 2 matches:
|
||||||
/// - Pattern kind is Pattern2Break (has break, no continue)
|
/// - Pattern kind is Pattern2Break (has break, no continue)
|
||||||
/// - No string/complex carrier updates (JoinIR doesn't support string concat)
|
/// - 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 {
|
pub(crate) fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
||||||
use super::common_init::CommonPatternInitializer;
|
use super::common_init::CommonPatternInitializer;
|
||||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
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
|
// Phase 188/Refactor: Use common carrier update validation
|
||||||
// Extracts loop variable for dummy carrier creation (not used but required by API)
|
// 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) {
|
let loop_var_name = match builder.extract_loop_variable_from_condition(ctx.condition) {
|
||||||
|
|||||||
@ -29,6 +29,9 @@ use crate::mir::loop_pattern_detection::{LoopFeatures, LoopPatternKind};
|
|||||||
/// (declared in mod.rs as pub module, import from parent)
|
/// (declared in mod.rs as pub module, import from parent)
|
||||||
use super::ast_feature_extractor as ast_features;
|
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
|
/// Context passed to pattern detect/lower functions
|
||||||
pub(crate) struct LoopPatternContext<'a> {
|
pub(crate) struct LoopPatternContext<'a> {
|
||||||
/// Loop condition AST node
|
/// Loop condition AST node
|
||||||
@ -61,6 +64,13 @@ pub(crate) struct LoopPatternContext<'a> {
|
|||||||
/// Phase 200-C: Optional function body AST for capture analysis
|
/// Phase 200-C: Optional function body AST for capture analysis
|
||||||
/// None if not available, Some(&[ASTNode]) if function body is accessible
|
/// None if not available, Some(&[ASTNode]) if function body is accessible
|
||||||
pub fn_body: Option<&'a [ASTNode]>,
|
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> {
|
impl<'a> LoopPatternContext<'a> {
|
||||||
@ -71,6 +81,7 @@ impl<'a> LoopPatternContext<'a> {
|
|||||||
/// Phase 193: Feature extraction delegated to ast_feature_extractor module
|
/// Phase 193: Feature extraction delegated to ast_feature_extractor module
|
||||||
/// Phase 131-11: Detects infinite loop condition
|
/// Phase 131-11: Detects infinite loop condition
|
||||||
/// Phase 137-6-S1: Use choose_pattern_kind() SSOT entry point
|
/// 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(
|
pub(crate) fn new(
|
||||||
condition: &'a ASTNode,
|
condition: &'a ASTNode,
|
||||||
body: &'a [ASTNode],
|
body: &'a [ASTNode],
|
||||||
@ -99,6 +110,7 @@ impl<'a> LoopPatternContext<'a> {
|
|||||||
features,
|
features,
|
||||||
pattern_kind,
|
pattern_kind,
|
||||||
fn_body: None, // Phase 200-C: Default to None
|
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.fn_body = Some(fn_body);
|
||||||
ctx
|
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
|
/// Phase 193: Feature extraction moved to ast_feature_extractor module
|
||||||
|
|||||||
@ -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(
|
trace::trace().routing(
|
||||||
"router",
|
"router",
|
||||||
func_name,
|
func_name,
|
||||||
@ -304,8 +304,19 @@ impl MirBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Phase 137-4: Router parity verification (after ctx is created)
|
// 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<crate::mir::loop_canonicalizer::LoopSkeleton>;
|
||||||
if crate::config::env::joinir_dev_enabled() {
|
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)? {
|
if let Some(result) = route_loop_pattern(self, &ctx)? {
|
||||||
|
|||||||
Reference in New Issue
Block a user