//! JoinIR routing logic for loop lowering
use super::trace;
use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;
/// Pattern 選択の SSOT 入口
///
/// 既存の分散した選択ロジックをここに集約する。
/// 将来的には Canonicalizer decision に委譲する。
///
/// Phase 137-6-S1: 現時点では既存の router ロジック(LoopFeatures ベース)を使用
/// Phase 137-6-S2: dev-only で canonicalizer decision を提案として受け取る
pub(in crate::mir::builder) fn choose_pattern_kind(
condition: &ASTNode,
body: &[ASTNode],
) -> crate::mir::loop_pattern_detection::LoopPatternKind {
use crate::mir::builder::control_flow::joinir::patterns::ast_feature_extractor as ast_features;
use crate::mir::builder::control_flow::joinir::patterns::policies::balanced_depth_scan_policy_box::BalancedDepthScanPolicyBox;
use crate::mir::builder::control_flow::joinir::patterns::policies::PolicyDecision;
use crate::mir::loop_pattern_detection;
// Phase 107: Route balanced depth-scan (return-in-loop) to Pattern2 via policy.
//
// This keeps Pattern routing structural: no by-name dispatch, no silent fallback.
match BalancedDepthScanPolicyBox::decide(condition, body) {
PolicyDecision::Use(_) => {
return loop_pattern_detection::LoopPatternKind::Pattern2Break;
}
PolicyDecision::Reject(_reason) => {
// In strict mode, treat "close-but-unsupported" as a fail-fast
// Pattern2 route so the policy can surface the precise contract violation.
if crate::config::env::joinir_dev::strict_enabled() {
return loop_pattern_detection::LoopPatternKind::Pattern2Break;
}
}
PolicyDecision::None => {}
}
// Phase 193: Use AST Feature Extractor Box for break/continue detection
let has_continue = ast_features::detect_continue_in_body(body);
let has_break = ast_features::detect_break_in_body(body);
let has_return = ast_features::detect_return_in_body(body);
// Phase 110: StepTree parity check (structure-only SSOT).
//
// This is dev-only; strict mode turns mismatch into a fail-fast.
if crate::config::env::joinir_dev_enabled() {
use crate::ast::Span;
use crate::mir::control_tree::StepTreeBuilderBox;
let loop_ast = ASTNode::Loop {
condition: Box::new(condition.clone()),
body: body.to_vec(),
span: Span::unknown(),
};
let tree = StepTreeBuilderBox::build_from_ast(&loop_ast);
if tree.features.has_break != has_break
|| tree.features.has_continue != has_continue
|| tree.features.has_return != has_return
{
let msg = format!(
"[choose_pattern_kind/STEPTREE_PARITY] step_tree(break={}, cont={}, ret={}) != extractor(break={}, cont={}, ret={})",
tree.features.has_break,
tree.features.has_continue,
tree.features.has_return,
has_break,
has_continue,
has_return
);
if crate::config::env::joinir_dev::strict_enabled() {
panic!("{}", msg);
} else {
trace::trace().dev("choose_pattern_kind/step_tree_parity", &msg);
}
}
}
// Phase 193: Extract features using modularized extractor
let features = ast_features::extract_features(condition, body, has_continue, has_break);
// Phase 192: Classify pattern based on features (既存の router 結果)
let router_choice = loop_pattern_detection::classify(&features);
// Phase 137-6-S2: dev-only で Canonicalizer の提案を取得
if crate::config::env::joinir_dev_enabled() {
use crate::ast::Span;
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
let loop_ast = ASTNode::Loop {
condition: Box::new(condition.clone()),
body: body.to_vec(),
span: Span::unknown(),
};
if let Ok((_skeleton, decision)) = canonicalize_loop_expr(&loop_ast) {
if let Some(canonical_choice) = decision.chosen {
// parity check
if canonical_choice != router_choice {
let msg = format!(
"[choose_pattern_kind/PARITY] router={:?}, canonicalizer={:?}",
router_choice, canonical_choice
);
if crate::config::env::joinir_dev::strict_enabled() {
// strict mode: 不一致は Fail-Fast
panic!("{}", msg);
} else {
// debug mode: ログのみ
trace::trace().dev("choose_pattern_kind/parity", &msg);
}
} else {
// Patterns match - success!
trace::trace().dev(
"choose_pattern_kind/parity",
&format!(
"[choose_pattern_kind/PARITY] OK: canonical and actual agree on {:?}",
canonical_choice
),
);
}
// TODO (Phase 137-6-S3): ここで canonical_choice を返す
// 現時点では router_choice を維持(既定挙動不変)
//
// 有効化条件(将来実装):
// 1. joinir_dev_enabled() && 新フラグ(例: canonicalizer_preferred())
// 2. または joinir_dev_enabled() をそのまま使用
//
// 注意: 有効化時は全 Pattern の parity が green であること
//
// 有効化後のコード例:
// ```rust
// if crate::config::env::canonicalizer_preferred() {
// return canonical_choice;
// }
// ```
}
}
}
router_choice
}
impl MirBuilder {
/// Phase 49: Try JoinIR Frontend for mainline integration
///
/// Returns `Ok(Some(value))` if the loop is successfully lowered via JoinIR,
/// `Ok(None)` if no JoinIR pattern matched (unsupported loop structure).
/// Phase 187-2: Legacy LoopBuilder removed - all loops must use JoinIR.
///
/// # Phase 49-4: Multi-target support
///
/// Targets are enabled via separate dev flags:
/// - `HAKO_JOINIR_PRINT_TOKENS_MAIN=1`: JsonTokenizer.print_tokens/0
/// - `HAKO_JOINIR_ARRAY_FILTER_MAIN=1`: ArrayExtBox.filter/2
///
/// Note: Arity in function names does NOT include implicit `me` receiver.
/// - Instance method `print_tokens()` → `/0` (no explicit params)
/// - Static method `filter(arr, pred)` → `/2` (two params)
pub(in crate::mir::builder) fn try_cf_loop_joinir(
&mut self,
condition: &ASTNode,
body: &[ASTNode],
) -> Result