refactor(pattern2): introduce PromoteDecision enum to eliminate Option wrapping ambiguity

Phase 263 P0.1: PromoteDecision API hardening

目的: Result<Option<PromoteStepResult>, String> の揺れを型で固定し、迷子をゼロに

Changes:
- promote_step_box.rs:
  - PromoteDecision enum を導入(Promoted/NotApplicable/Freeze)
  - PromoteStepBox::run() の戻り値を Result<PromoteDecision, String> に統一
  - promote_and_prepare_carriers() が inputs の所有権を受け取り、PromoteDecision を直接構築
  - Reject 分岐を型安全に二分化(文字列マッチング維持、型で意図を明確化)
- pattern2_lowering_orchestrator.rs:
  - orchestrator 側の分岐を1箇所に固定(match PromoteDecision {...})
  - NotApplicable → Ok(None) で後続経路へ
  - Freeze → Err で Fail-Fast

受け入れ:
- quick smoke: 45/46 PASS (悪化なし)
- NotApplicable は必ず Ok(None) で Pattern2全体を抜ける
- Freeze は必ず Err(fail-fast)

Next: ファイル構造リファクタリング(pattern2/api/ フォルダ化)を別フェーズで検討
This commit is contained in:
2025-12-21 10:54:46 +09:00
parent e3dd1bbecb
commit abdb860e7e
2 changed files with 42 additions and 21 deletions

View File

@ -62,15 +62,22 @@ impl Pattern2LoweringOrchestrator {
let facts = GatherFactsStepBox::gather(builder, condition, body, fn_body, &ctx, verbose)?;
let inputs = ApplyPolicyStepBox::apply(condition, body, facts)?;
let promoted = PromoteStepBox::run(builder, condition, body, inputs, debug, verbose)?;
let mut inputs = match promoted {
Some(result) => result.inputs,
None => {
// Phase 263 P0: Pattern2 cannot handle this loop (e.g., reassigned LoopBodyLocal)
// Phase 263 P0.1: PromoteDecision で分岐を1箇所に固定型安全
use super::pattern2_steps::promote_step_box::PromoteDecision;
let mut inputs = match PromoteStepBox::run(builder, condition, body, inputs, debug, verbose)? {
PromoteDecision::Promoted(result) => {
result.inputs
}
PromoteDecision::NotApplicable => {
// Pattern2 cannot handle this loop (e.g., reassigned LoopBodyLocal)
// Return Ok(None) to allow router to try next path (legacy binding)
super::super::trace::trace().debug("pattern2", "Pattern2 aborted (promotion failed), allowing fallback");
super::super::trace::trace().debug("pattern2", "Pattern2 aborted (not applicable), allowing fallback");
return Ok(None);
}
PromoteDecision::Freeze(reason) => {
// Pattern2 should handle this but implementation is missing → Fail-Fast
return Err(reason);
}
};
// Phase 256.5: Wire current_static_box_name from builder context or function name

View File

@ -16,6 +16,22 @@ pub(crate) struct PromoteStepResult {
pub inputs: Pattern2Inputs,
}
/// Phase 263 P0.1: Promotion decision for Pattern2 LoopBodyLocal handling
///
/// Eliminates Option<_> wrapping ambiguity by making decision explicit.
pub(crate) enum PromoteDecision {
/// Promotion succeeded - Pattern2 can proceed
Promoted(PromoteStepResult),
/// Pattern2 not applicable (e.g., reassigned LoopBodyLocal, no promotable pattern)
/// → Router should try next path (legacy binding, etc.)
NotApplicable,
/// Pattern2 should handle this but implementation is missing
/// → Fail-Fast with error message
Freeze(String),
}
pub(crate) struct PromoteStepBox;
impl PromoteStepBox {
@ -23,24 +39,22 @@ impl PromoteStepBox {
builder: &mut MirBuilder,
condition: &ASTNode,
body: &[ASTNode],
mut inputs: Pattern2Inputs,
inputs: Pattern2Inputs,
debug: bool,
verbose: bool,
) -> Result<Option<PromoteStepResult>, String> {
match Self::promote_and_prepare_carriers(builder, condition, body, &mut inputs, debug, verbose)? {
Some(()) => Ok(Some(PromoteStepResult { inputs })),
None => Ok(None), // Pattern2 cannot handle this loop
}
) -> Result<PromoteDecision, String> {
Self::promote_and_prepare_carriers(builder, condition, body, inputs, debug, verbose)
}
pub(in crate::mir::builder) fn promote_and_prepare_carriers(
builder: &mut MirBuilder,
condition: &ASTNode,
body: &[ASTNode],
inputs: &mut Pattern2Inputs,
inputs: Pattern2Inputs,
debug: bool,
verbose: bool,
) -> Result<Option<()>, String> {
) -> Result<PromoteDecision, String> {
let mut inputs = inputs;
use crate::mir::join_ir::lowering::digitpos_condition_normalizer::DigitPosConditionNormalizer;
use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScopeBox;
@ -121,11 +135,11 @@ impl PromoteStepBox {
inputs.read_only_body_local_slot = Some(slot);
}
PolicyDecision::Reject(reason) => {
// Phase 263 P0 + Step 2.5: Reject を二分化
// Phase 263 P0.1: Reject を PromoteDecision で二分化(型安全)
if reason.contains("not_readonly")
|| reason.contains("No promotable pattern detected")
{
// 対象外: Pattern2 で処理できない形 → Ok(None) で後続経路へ
// 対象外: Pattern2 で処理できない形 → NotApplicable で後続経路へ
#[cfg(debug_assertions)]
{
eprintln!(
@ -133,13 +147,13 @@ impl PromoteStepBox {
cond_body_local_vars, reason
);
}
return Ok(None); // Pattern2 全体を中止
return Ok(PromoteDecision::NotApplicable);
} else {
// 対象だが未対応freeze級: 実装バグ or 将来実装予定 → Err で Fail-Fast
return Err(format!(
// 対象だが未対応freeze級: 実装バグ or 将来実装予定 → Freeze で Fail-Fast
return Ok(PromoteDecision::Freeze(format!(
"[pattern2/promote_step] Pattern2 未対応エラーLoopBodyLocal {:?}: {}",
cond_body_local_vars, reason
));
)));
}
}
PolicyDecision::None => {}
@ -192,7 +206,7 @@ impl PromoteStepBox {
}
}
Ok(Some(()))
Ok(PromoteDecision::Promoted(PromoteStepResult { inputs }))
}
}