feat(joinir): Phase 171 complete - Trim pattern LoopBodyLocal promotion

Phase 171-C-4/5 + impl-Trim: Full Trim pattern validation infrastructure

## CarrierInfo 統合 (C-4)
- CarrierInfo::merge_from(): Deduplicated carrier merging
- TrimPatternInfo::to_carrier_info(): Conversion helper
- Pattern2/4: Promoted carrier merge integration
- 7 unit tests for merge logic

## TrimLoopHelper 設計 (C-5)
- TrimLoopHelper struct: Trim-specific validation box
- carrier_type(), initial_value(), whitespace helpers
- CarrierInfo::trim_helper() accessor
- 5 unit tests

## Validation-Only Integration (impl-Trim)
- TrimLoopHelper::is_safe_trim(), is_trim_like(), has_valid_structure()
- Pattern2/4: Trim exception route with safety validation
- body_locals extraction from loop body AST
- LoopBodyCarrierPromoter: ASTNode::Local handler extension
- 4 unit tests for safety validation

## Architecture
- Box Theory: TrimLoopHelper is "validation only" (no JoinIR generation)
- Fail-Fast: Non-Trim LoopBodyLocal immediately rejected
- Whitelist approach: Only Trim pattern bypasses LoopBodyLocal restriction

Tests: 16 new unit tests, all passing
E2E: test_trim_main_pattern.hako validation successful

Next: Phase 172 - Actual JoinIR lowering for Trim pattern

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-08 02:41:53 +09:00
parent a1f3d913f9
commit 14c84fc583
9 changed files with 939 additions and 12 deletions

View File

@ -110,6 +110,7 @@ impl CommonPatternInitializer {
loop_var_name: loop_var_name.clone(),
loop_var_id,
carriers,
trim_helper: None, // Phase 171-C-5: No Trim pattern by default
};
Ok((loop_var_name, loop_var_id, carrier_info))

View File

@ -53,8 +53,9 @@ impl MirBuilder {
trace::trace().debug("pattern2", "Calling Pattern 2 minimal lowerer");
// Phase 33-22: Use CommonPatternInitializer for loop variable extraction
// Phase 171-C-4: carrier_info is now mutable for promotion merging
use super::common_init::CommonPatternInitializer;
let (loop_var_name, loop_var_id, _carrier_info) =
let (loop_var_name, loop_var_id, mut carrier_info) =
CommonPatternInitializer::initialize_pattern(
self,
condition,
@ -122,8 +123,19 @@ impl MirBuilder {
}
// Create a minimal LoopScopeShape (Phase 188: hardcoded for joinir_min_loop.hako)
// Pattern 2 lowerer ignores the scope anyway, so this is just a placeholder
// Phase 171-impl-Trim: Extract body_locals from loop body AST for proper variable classification
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
let mut body_locals = BTreeSet::new();
// Extract local variable declarations from loop body
for stmt in _body {
if let ASTNode::Local { variables, .. } = stmt {
for var_name in variables {
body_locals.insert(var_name.clone());
}
}
}
let scope = LoopScopeShape {
header: BasicBlockId(0),
body: BasicBlockId(0),
@ -131,7 +143,7 @@ impl MirBuilder {
exit: BasicBlockId(0),
pinned: BTreeSet::new(),
carriers: BTreeSet::new(),
body_locals: BTreeSet::new(),
body_locals, // Phase 171-impl-Trim: Now populated from AST
exit_live: BTreeSet::new(),
progress_carrier: None,
variable_definitions: BTreeMap::new(),
@ -175,7 +187,15 @@ impl MirBuilder {
Some(&scope),
);
eprintln!("[pattern2/check] Analyzing condition scope: {} variables", cond_scope.vars.len());
for var in &cond_scope.vars {
eprintln!("[pattern2/check] '{}': {:?}", var.name, var.scope);
}
eprintln!("[pattern2/check] has_loop_body_local() = {}", cond_scope.has_loop_body_local());
if cond_scope.has_loop_body_local() {
eprintln!("[pattern2/promotion] LoopBodyLocal detected in condition scope");
// Phase 171-C-3: Try promotion
let request = PromotionRequest {
scope: &scope,
@ -190,8 +210,46 @@ impl MirBuilder {
"[pattern2/promoter] LoopBodyLocal '{}' promoted to carrier '{}'",
trim_info.var_name, trim_info.carrier_name
);
// Phase 171-C-3: Detection only - CarrierInfo merge is future work
// For now, we just log successful detection and continue normal flow
// Phase 171-C-4: Convert to CarrierInfo and merge
let promoted_carrier = trim_info.to_carrier_info();
carrier_info.merge_from(&promoted_carrier);
eprintln!(
"[pattern2/promoter] Phase 171-C-4: Merged carrier '{}' into CarrierInfo (total carriers: {})",
trim_info.carrier_name,
carrier_info.carrier_count()
);
// Phase 171-impl-Trim: Check if this is a safe Trim pattern
if let Some(helper) = carrier_info.trim_helper() {
if helper.is_safe_trim() {
eprintln!("[pattern2/trim] Safe Trim pattern detected, bypassing LoopBodyLocal restriction");
eprintln!("[pattern2/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/pattern2] ✅ Trim pattern validation successful! \
Carrier '{}' ready for Phase 172 implementation. \
(Pattern detection: PASS, Safety check: PASS, JoinIR lowering: TODO)",
helper.carrier_name
));
} else {
return Err(format!(
"[cf_loop/pattern2] Trim pattern detected but not safe: carrier='{}', whitespace_count={}",
helper.carrier_name,
helper.whitespace_count()
));
}
} else {
return Err(format!(
"[cf_loop/pattern2] Promoted but no TrimLoopHelper attached (carrier: '{}')",
trim_info.carrier_name
));
}
}
PromotionResult::CannotPromote { reason, vars } => {
// Phase 171-C-3: Fail-Fast on promotion failure

View File

@ -158,6 +158,7 @@ impl MirBuilder {
// Phase 33-19: Build CarrierInfo with ONLY updated variables as carriers
// This prevents constant variables (like M, args) from being treated as carriers
// Phase 171-C-4: carrier_info is now mutable for promotion merging
let mut carriers = Vec::new();
for temp_carrier in &temp_carriers {
if carrier_updates.contains_key(&temp_carrier.name) {
@ -165,10 +166,11 @@ impl MirBuilder {
}
}
let carrier_info = CarrierInfo {
let mut carrier_info = CarrierInfo {
loop_var_name: loop_var_name.clone(),
loop_var_id,
carriers: carriers.clone(),
trim_helper: None, // Phase 171-C-5: No Trim pattern by default
};
trace::trace().debug(
@ -195,8 +197,19 @@ impl MirBuilder {
trace::trace().varmap("pattern4_start", &self.variable_map);
// Create a minimal LoopScopeShape (Phase 195: hardcoded for loop_continue_pattern4.hako)
// Pattern 4 lowerer ignores the scope anyway, so this is just a placeholder
// Phase 171-impl-Trim: Extract body_locals from loop body AST for proper variable classification
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
let mut body_locals = BTreeSet::new();
// Extract local variable declarations from loop body
for stmt in body_to_analyze {
if let ASTNode::Local { variables, .. } = stmt {
for var_name in variables {
body_locals.insert(var_name.clone());
}
}
}
let scope = LoopScopeShape {
header: BasicBlockId(0),
body: BasicBlockId(0),
@ -204,7 +217,7 @@ impl MirBuilder {
exit: BasicBlockId(0),
pinned: BTreeSet::new(),
carriers: BTreeSet::new(),
body_locals: BTreeSet::new(),
body_locals, // Phase 171-impl-Trim: Now populated from AST
exit_live: BTreeSet::new(),
progress_carrier: None,
variable_definitions: BTreeMap::new(),
@ -241,8 +254,46 @@ impl MirBuilder {
"[pattern4/promoter] LoopBodyLocal '{}' promoted to carrier '{}'",
trim_info.var_name, trim_info.carrier_name
);
// Phase 171-C-3: Detection only - CarrierInfo merge is future work
// For now, we just log successful detection and continue normal flow
// Phase 171-C-4: Convert to CarrierInfo and merge
let promoted_carrier = trim_info.to_carrier_info();
carrier_info.merge_from(&promoted_carrier);
eprintln!(
"[pattern4/promoter] Phase 171-C-4: Merged carrier '{}' into CarrierInfo (total carriers: {})",
trim_info.carrier_name,
carrier_info.carrier_count()
);
// Phase 171-impl-Trim: 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
));
} else {
return Err(format!(
"[cf_loop/pattern4] Trim pattern detected but not safe: carrier='{}', whitespace_count={}",
helper.carrier_name,
helper.whitespace_count()
));
}
} else {
return Err(format!(
"[cf_loop/pattern4] Promoted but no TrimLoopHelper attached (carrier: '{}')",
trim_info.carrier_name
));
}
}
PromotionResult::CannotPromote { reason, vars } => {
// Phase 171-C-3: Fail-Fast on promotion failure