feat(joinir): Phase 33-21 Pattern 4 parameter remapping fix and debug improvements

## Problem
Pattern 4 (loop with continue) was producing SSA-undef errors due to function_params
key mismatch. function_params uses 'join_func_0'/'join_func_1' format (from join_func_name()),
but code was looking for 'main'/'loop_step'.

## Solution
- Fix mod.rs: Use correct function keys 'join_func_0' and 'join_func_1'
- Add warning logs to detect future key mismatches immediately
- Update pattern4_with_continue.rs: Mark as fully implemented (not stub)
- Remove unused imports: MergeResult, TailCallKind, classify_tail_call, etc.

## Testing
- Pattern 3 (If PHI): sum=9 
- Pattern 4 (Continue): 25  (1+3+5+7+9)
- No SSA-undef errors
- No new warnings on successful execution

## Debug Improvements
Added pre-flight checks in merge/mod.rs Phase 33-21:
```
[cf_loop/joinir] WARNING: function_params.get('join_func_0') returned None.
Available keys: [...]
```
This will save significant debugging time for future parameter mapping issues.

🤖 Generated with Claude Code

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-07 18:47:24 +09:00
parent 4170500c51
commit 53af63c96c
3 changed files with 205 additions and 99 deletions

View File

@ -1,54 +1,35 @@
//! Phase 195: Pattern 4 (Loop with Continue) - STUB IMPLEMENTATION
//! Phase 33-21: Pattern 4 (Loop with Continue) Implementation
//!
//! **Current Status**: Not implemented. Returns explicit error.
//! **Current Status**: Fully implemented using Select-based continue semantics.
//!
//! ## Why Deferred to Phase 195+?
//! ## Implementation Approach
//!
//! Continue semantics are complex compared to break:
//! - Break: Jump directly to exit (simple control flow)
//! - Continue: Jump to loop latch, then to loop condition (requires latch block)
//! - Requires additional PHI merging at latch block
//! - Interacts with other loop transformations
//! Continue is handled via Select instruction for carrier updates:
//! - When continue condition is true: carrier keeps its value (no update)
//! - When continue condition is false: carrier gets updated value
//!
//! ## Migration Path (Use These Instead)
//! This avoids the complexity of explicit latch blocks by using PHI-based
//! value selection at each iteration.
//!
//! ## Example
//!
//! ### Pattern 1: Simple While (No break/continue)
//! ```nyash
//! loop(i < 10) { i = i + 1 }
//! ```
//!
//! ### Pattern 2: Loops with Break
//! ```nyash
//! local i = 0
//! local sum = 0
//! loop(i < 10) {
//! if i >= 5 { break }
//! i = i + 1
//! if i % 2 == 0 { continue } // Skip even numbers
//! sum = sum + i
//! }
//! // sum = 25 (1+3+5+7+9)
//! ```
//!
//! ### Pattern 3: Loops with If + PHI (Workaround for continue)
//! ```nyash
//! loop(i < 10) {
//! if i % 2 == 0 {
//! // Process even
//! x = process(i)
//! } else {
//! // Skip odd (simulates continue)
//! x = default_value
//! }
//! i = i + 1
//! }
//! ```
//! ## Key Implementation Details
//!
//! ## Design Philosophy
//! Rather than silently fail or return incorrect MIR,
//! Pattern 4 returns **explicit error** directing users to working patterns.
//! This is better than:
//! - Silent failure (confusing bugs)
//! - Incorrect lowering (wrong semantics)
//! - Feature gates (hidden complexity)
//!
//! ## Implementation Plan (Phase 195+)
//! See: docs/development/proposals/phase-195-pattern4.md
//! - **Select instruction**: Used to conditionally skip carrier updates
//! - **Header PHI**: Tracks carrier values across iterations
//! - **ExitMeta**: Maps final carrier values to host variable slots
//! - **Phase 33-21 fix**: Correct remapping of function parameters to header PHI dsts
use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder;
@ -81,39 +62,35 @@ pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext)
ctx.pattern_kind == LoopPatternKind::Pattern4Continue
}
/// Phase 194+: Lowering function for Pattern 4
///
/// **STUB IMPLEMENTATION**: Returns explicit error. Pattern 4 is not yet implemented.
/// Phase 33-19: Lowering function for Pattern 4
///
/// Wrapper around cf_loop_pattern4_with_continue to match router signature.
///
/// # Future Implementation Plan
/// # Implementation (Phase 195-197)
///
/// When implemented, this will:
/// 1. Extract loop variables from condition
/// 2. Generate JoinIR with continue support
/// 2. Generate JoinIR with continue support (lower_loop_with_continue_minimal)
/// 3. Convert JoinModule → MirModule
/// 4. Create JoinInlineBoundary for input/output mapping
/// 5. Merge MIR blocks into current_function
/// 6. Return Void (loop doesn't produce values)
/// 6. Return loop result (first carrier value)
pub fn lower(
_builder: &mut MirBuilder,
builder: &mut MirBuilder,
ctx: &super::router::LoopPatternContext,
) -> Result<Option<ValueId>, String> {
Err(format!(
"[cf_loop/pattern4] Pattern 4 (continue) not yet implemented for '{}' loop.\n\
Use Pattern 1-3 instead (simple while, break, or if+phi patterns).\n\
See: docs/development/proposals/phase-195-pattern4.md",
ctx.func_name
))
// Phase 33-19: Connect stub to actual implementation
builder.cf_loop_pattern4_with_continue(
ctx.condition,
ctx.body,
ctx.func_name,
ctx.debug,
)
}
impl MirBuilder {
/// Phase 194+: Pattern 4 (Loop with Continue) minimal lowerer
/// Phase 33-21: Pattern 4 (Loop with Continue) lowerer
///
/// **STUB IMPLEMENTATION**: This function is a placeholder for future implementation.
///
/// Handles loops with continue statements that skip to next iteration.
/// Handles loops with continue statements using Select-based carrier updates.
///
/// # Example
///
@ -130,26 +107,15 @@ impl MirBuilder {
/// // sum = 25 (1+3+5+7+9)
/// ```
///
/// # Implementation Status
/// # Implementation
///
/// **NOT IMPLEMENTED**: This is a stub. Pattern 4 lowering logic needs to be implemented.
///
/// The lowerer should:
/// 1. Detect continue statements in the loop body
/// 2. Generate appropriate PHI nodes for continue targets
/// 3. Handle carrier variables (i, sum) across continue boundaries
/// 4. Generate exit PHI nodes for final values
///
/// # Steps (TODO)
///
/// 1. Extract loop variables (i, sum)
/// 2. Generate JoinIR using loop_with_continue_minimal (not yet implemented)
/// 3. Convert JoinModule → MirModule
/// 4. Create JoinInlineBoundary for input/output mapping
/// 5. Merge MIR blocks into current_function
/// 6. Return Void (loop doesn't produce values)
#[doc(hidden)] // Not ready for public use
#[allow(dead_code)] // Will be implemented in Phase 195+
/// 1. Extract loop variable from condition
/// 2. Build CarrierInfo from variable_map
/// 3. Analyze carrier update expressions from loop body
/// 4. Generate JoinIR with Select-based continue semantics
/// 5. Convert JoinModule → MirModule
/// 6. Merge MIR blocks with header PHI and exit bindings
/// 7. Return Void (loop result accessed via variable_map)
pub(in crate::mir::builder) fn cf_loop_pattern4_with_continue(
&mut self,
condition: &ASTNode,
@ -339,17 +305,23 @@ impl MirBuilder {
&format!("join_inputs: {:?}", join_inputs.iter().map(|v| v.0).collect::<Vec<_>>())
);
let boundary = crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_with_exit_bindings(
let mut boundary = crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_with_exit_bindings(
join_inputs, // JoinIR's main() parameters (dynamic)
host_inputs, // Host's loop variables (dynamic)
exit_bindings,
);
// Phase 33-19: Set loop_var_name for proper exit PHI collection
boundary.loop_var_name = Some(loop_var_name.clone());
// Phase 195: Capture exit PHI result (Pattern 4 returns sum)
let result_val = self.merge_joinir_mir_blocks(&mir_module, Some(&boundary), debug)?;
let _result_val = self.merge_joinir_mir_blocks(&mir_module, Some(&boundary), debug)?;
// Phase 33-19: Like Pattern3, return void (loop result is read via variable_map)
let void_val = crate::mir::builder::emission::constant::emit_void(self);
// Phase 195: Use unified trace
trace::trace().debug("pattern4", &format!("Loop complete, returning result {:?}", result_val));
trace::trace().debug("pattern4", &format!("Loop complete, returning Void {:?}", void_val));
Ok(result_val)
Ok(Some(void_val))
}
}