feat(joinir): Phase 223-3 - LoopBodyCondPromoter implementation

Implements LoopBodyLocal condition promotion for Pattern4/continue patterns.
Previously, loops with LoopBodyLocal in conditions would Fail-Fast.
Now, safe Trim/skip_whitespace patterns (Category A-3) are promoted and lowering continues.

## Implementation

- **LoopBodyCondPromoter Box** (loop_body_cond_promoter.rs)
  - extract_continue_condition(): Extract if-condition from continue branches
  - try_promote_for_condition(): Thin wrapper delegating to LoopBodyCarrierPromoter
  - ConditionPromotionRequest/Result API for Pattern2/4 integration

- **Pattern4 Integration** (pattern4_with_continue.rs)
  - Analyze both header condition + continue condition
  - On promotion success: merge carrier_info → continue lowering
  - On promotion failure: Fail-Fast with clear error message

- **Unit Tests** (5 tests, all PASS)
  - test_cond_promoter_skip_whitespace_pattern
  - test_cond_promoter_break_condition
  - test_cond_promoter_non_substring_pattern
  - test_cond_promoter_no_scope_shape
  - test_cond_promoter_no_body_locals

- **E2E Test** (phase223_p4_skip_whitespace_min.hako)
  - Category A-3 pattern: local ch = s.substring(...); if ch == " " { continue }
  - Verifies promotion succeeds and Pattern4 lowering proceeds

## Documentation

- joinir-architecture-overview.md: Updated LoopBodyCondPromoter section (Phase 223-3 完了)
- CURRENT_TASK.md: Added Phase 223-3 completion summary
- PHASE_223_SUMMARY.md: Phase overview and status tracking
- phase223-loopbodylocal-condition-*.md: Design docs and inventory

## Achievement

- **Before**: LoopBodyLocal in condition → Fail-Fast
- **Now**: Trim/skip_whitespace patterns promoted → Pattern4 lowering continues
- **Remaining**: Phase 172+ JoinIR Trim lowering for complete RC correctness

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-10 15:00:20 +09:00
parent 9dbf053781
commit 89a769198a
9 changed files with 1962 additions and 40 deletions

View File

@ -209,64 +209,74 @@ impl MirBuilder {
// Phase 195: Use unified trace
trace::trace().varmap("pattern4_start", &self.variable_map);
// Phase 171-C-3: LoopBodyCarrierPromoter integration
// Check if LoopConditionScopeBox detects LoopBodyLocal variables, and attempt promotion
// Phase 223-3: LoopBodyCondPromoter integration
// Check for LoopBodyLocal in continue condition and attempt promotion
{
use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScopeBox;
use crate::mir::loop_pattern_detection::loop_body_carrier_promoter::{
LoopBodyCarrierPromoter, PromotionRequest, PromotionResult,
use crate::mir::loop_pattern_detection::loop_body_cond_promoter::{
LoopBodyCondPromoter, ConditionPromotionRequest, ConditionPromotionResult,
};
// Phase 223-3: Extract continue condition from body (if present)
// This handles skip_whitespace pattern: if ch == " " || ... { continue }
let continue_cond = LoopBodyCondPromoter::extract_continue_condition(body_to_analyze);
// Analyze both header condition and continue condition for LoopBodyLocal
let conditions_to_analyze: Vec<&ASTNode> = if let Some(cont_cond) = continue_cond {
vec![condition, cont_cond]
} else {
vec![condition]
};
// First check: Does the condition reference LoopBodyLocal variables?
let cond_scope = LoopConditionScopeBox::analyze(
&loop_var_name,
&[condition],
&conditions_to_analyze,
Some(&scope),
);
if cond_scope.has_loop_body_local() {
// Phase 171-C-3: Try promotion
// Pattern 4 has no break condition
let request = PromotionRequest {
scope: &scope,
// Phase 223-3: Try promotion using LoopBodyCondPromoter
let promotion_req = ConditionPromotionRequest {
loop_param_name: &loop_var_name,
cond_scope: &cond_scope,
break_cond: None, // Pattern 4 doesn't have break
scope_shape: Some(&scope),
break_cond: None, // Pattern 4 has no break
continue_cond,
loop_body: body_to_analyze,
};
match LoopBodyCarrierPromoter::try_promote(&request) {
PromotionResult::Promoted { trim_info } => {
match LoopBodyCondPromoter::try_promote_for_condition(promotion_req) {
ConditionPromotionResult::Promoted {
carrier_info: promoted_carrier,
promoted_var,
carrier_name,
} => {
eprintln!(
"[pattern4/promoter] LoopBodyLocal '{}' promoted to carrier '{}'",
trim_info.var_name, trim_info.carrier_name
"[pattern4/cond_promoter] LoopBodyLocal '{}' promoted to carrier '{}'",
promoted_var, carrier_name
);
// Phase 171-C-4: Convert to CarrierInfo and merge
let promoted_carrier = trim_info.to_carrier_info();
// Phase 223-3: Merge promoted carrier into existing CarrierInfo
carrier_info.merge_from(&promoted_carrier);
eprintln!(
"[pattern4/promoter] Phase 171-C-4: Merged carrier '{}' into CarrierInfo (total carriers: {})",
trim_info.carrier_name,
"[pattern4/cond_promoter] Merged carrier '{}' into CarrierInfo (total carriers: {})",
carrier_name,
carrier_info.carrier_count()
);
// Phase 171-impl-Trim: Check if this is a safe Trim pattern
// Phase 223-3: Check if this is a safe Trim pattern
if let Some(helper) = carrier_info.trim_helper() {
if helper.is_safe_trim() {
eprintln!("[pattern4/trim] Safe Trim pattern detected, bypassing LoopBodyLocal restriction");
eprintln!("[pattern4/trim] Carrier: '{}', original var: '{}', whitespace chars: {:?}",
helper.carrier_name, helper.original_var, helper.whitespace_chars);
// Phase 171-impl-Trim: Validation successful!
// Phase 172+ will implement the actual JoinIR generation for Trim patterns
// For now, return an informative message that the pattern is recognized but not yet lowered
return Err(format!(
"[cf_loop/pattern4] ✅ Trim pattern validation successful! \
Carrier '{}' ready for Phase 172 implementation. \
(Pattern detection: PASS, Safety check: PASS, JoinIR lowering: TODO)",
helper.carrier_name
));
eprintln!(
"[pattern4/cond_promoter] Safe Trim/skip_whitespace pattern detected"
);
eprintln!(
"[pattern4/cond_promoter] Carrier: '{}', original var: '{}', whitespace chars: {:?}",
helper.carrier_name, helper.original_var, helper.whitespace_chars
);
// Phase 223-3: Continue with Pattern4 lowering
// (fall through to normal lowering path)
} else {
return Err(format!(
"[cf_loop/pattern4] Trim pattern detected but not safe: carrier='{}', whitespace_count={}",
@ -274,15 +284,12 @@ impl MirBuilder {
helper.whitespace_count()
));
}
} else {
return Err(format!(
"[cf_loop/pattern4] Promoted but no TrimLoopHelper attached (carrier: '{}')",
trim_info.carrier_name
));
}
// Phase 223-3: Continue with normal Pattern4 lowering
// The carrier has been promoted and merged, lowering can proceed
}
PromotionResult::CannotPromote { reason, vars } => {
// Phase 171-C-3: Fail-Fast on promotion failure
ConditionPromotionResult::CannotPromote { reason, vars } => {
// Phase 223-3: Fail-Fast on promotion failure
return Err(format!(
"[cf_loop/pattern4] Cannot promote LoopBodyLocal variables {:?}: {}",
vars, reason