refactor(joinir): Phase 170-A Step 1.5 - LoopFeatures-based detection API

Clarify design principle: CaseALoweringShape does NOT look at function names.
Input must be LoopFeatures/LoopPatternKind only (structure-based detection).

Changes:
- Add detect_from_features(LoopFeatures, carrier_count, has_progress_carrier)
- Deprecate detect(LoopScopeShape) with backward-compat wrapper
- Case-A now rejects loops with `has_continue` (only break is allowed)
- Document Phase 170-B future work (loop body AST analysis)

Design Principle:
> "CaseALoweringShape は 関数名を見ない。LoopFeatures/LoopPatternKind だけを入力にする"

This ensures generic routing works for ANY structurally matching loop.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-07 13:19:28 +09:00
parent 4b30edd6f4
commit 82857c515b

View File

@ -82,32 +82,48 @@ pub enum CaseALoweringShape {
}
impl CaseALoweringShape {
/// Detect lowering shape from LoopScopeShape
/// Detect lowering shape from LoopFeatures
///
/// Phase 170-A: Heuristic-based shape detection
/// # Phase 170-A Design Principle
///
/// Returns NotCaseA if structural requirements not met.
/// Returns one of the above if structural analysis suggests a pattern.
/// **CaseALoweringShape does NOT look at function names.**
/// Input: LoopFeatures / LoopPatternKind only (structure-based detection).
///
/// This is the core architectural invariant that enables generic routing.
///
/// # Heuristics
/// - Single carrier (1) → likely StringExamination or ArrayAccumulation
/// - Multiple carriers (2+) → likely IterationWithAccumulation
/// - Pinned count → disambiguation hint
/// - has_break/has_continue → affects Case-A eligibility
///
/// Note: More sophisticated pattern matching (analyzing loop body structure)
/// is deferred to Phase 170-B.
pub fn detect(scope: &super::shape::LoopScopeShape) -> Self {
use super::shape::LoopScopeShape;
/// # Phase 170-B Future Work
/// - Analyze loop body AST for more precise classification
/// - Distinguish StringExamination vs ArrayAccumulation
///
/// # Arguments
/// * `features` - LoopFeatures (structure-based, name-agnostic)
/// * `carrier_count` - Number of carrier variables from LoopScopeShape
/// * `has_progress_carrier` - Whether progress carrier exists
pub fn detect_from_features(
features: &crate::mir::loop_pattern_detection::LoopFeatures,
carrier_count: usize,
has_progress_carrier: bool,
) -> Self {
// Case-A requirement: must have a progress carrier
if !has_progress_carrier {
return CaseALoweringShape::NotCaseA;
}
// Check if structurally Case-A
if scope.progress_carrier.is_none() {
// Case-A requirement: no complex control flow (continue)
// Note: break is allowed (StringExamination patterns use break)
if features.has_continue {
return CaseALoweringShape::NotCaseA;
}
// Phase 170-A: Simple heuristic based on carrier count
match scope.carriers.len() {
match carrier_count {
0 => {
// This shouldn't happen if progress_carrier is Some, but be safe
// This shouldn't happen if has_progress_carrier is true, but be safe
CaseALoweringShape::NotCaseA
}
1 => {
@ -124,6 +140,33 @@ impl CaseALoweringShape {
}
}
/// Legacy wrapper: Detect from LoopScopeShape (deprecated, use detect_from_features)
///
/// Phase 170-A: Kept for backward compatibility during transition.
#[deprecated(
since = "Phase 170-A",
note = "Use detect_from_features() with LoopFeatures instead"
)]
pub fn detect(scope: &super::shape::LoopScopeShape) -> Self {
// Construct minimal LoopFeatures from LoopScopeShape
// Note: This loses some information (has_break, has_continue not available)
let has_progress_carrier = scope.progress_carrier.is_some();
let carrier_count = scope.carriers.len();
// Create stub features (Phase 170-B will use real LoopFeatures)
let stub_features = crate::mir::loop_pattern_detection::LoopFeatures {
has_break: false, // Unknown from LoopScopeShape alone
has_continue: false, // Unknown from LoopScopeShape alone
has_if: false,
has_if_else_phi: false,
carrier_count,
break_count: 0,
continue_count: 0,
};
Self::detect_from_features(&stub_features, carrier_count, has_progress_carrier)
}
/// Is this a recognized lowering shape?
pub fn is_recognized(&self) -> bool {
!matches!(self, CaseALoweringShape::NotCaseA | CaseALoweringShape::Generic)