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:
@ -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))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user