refactor(joinir): Pattern 4 modularization with CarrierInfo/ExitMeta

Removes hardcoded "sum" and ValueId(15) from Pattern 4 lowerer by
introducing CarrierInfo and ExitMeta structures.

Changes:
- New carrier_info.rs: CarrierInfo, CarrierVar, ExitMeta structs
- loop_with_continue_minimal.rs: Returns (JoinModule, ExitMeta)
- pattern4_with_continue.rs: Dynamic binding generation from metadata

Design approach: "Thin meta on existing boxes" (ChatGPT proposal)
- CarrierInfo: Built from variable_map, not AST re-analysis
- ExitMeta: Carrier name + JoinIR ValueId pairs from lowerer
- LoopExitBinding: Auto-generated from CarrierInfo + ExitMeta

Test: loop_continue_pattern4.hako outputs 25 (unchanged)

🤖 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-06 02:05:19 +09:00
parent 318dceebfe
commit 60bd5487e6
4 changed files with 146 additions and 23 deletions

View File

@ -0,0 +1,64 @@
//! Carrier variable metadata for JoinIR loop lowering
//!
//! This module defines metadata structures for tracking carrier variables
//! in loop lowering. This enables dynamic generation of exit bindings
//! without hardcoded variable names or ValueIds.
use crate::mir::ValueId;
/// Information about a single carrier variable
#[derive(Debug, Clone)]
pub struct CarrierVar {
/// Variable name (e.g., "sum", "printed")
pub name: String,
/// Host ValueId for this variable
pub host_id: ValueId,
}
/// Complete carrier information for a loop
#[derive(Debug, Clone)]
pub struct CarrierInfo {
/// Loop control variable name (e.g., "i")
pub loop_var_name: String,
/// Loop control variable ValueId in host
pub loop_var_id: ValueId,
/// Additional carrier variables (e.g., sum, printed)
pub carriers: Vec<CarrierVar>,
}
/// Exit metadata returned by lowerers
///
/// This structure captures the mapping from JoinIR exit values to
/// carrier variable names, enabling dynamic binding generation.
#[derive(Debug, Clone)]
pub struct ExitMeta {
/// Exit value bindings: (carrier_name, join_exit_value_id)
///
/// Example for Pattern 4:
/// ```
/// vec![("sum".to_string(), ValueId(15))]
/// ```
/// where ValueId(15) is the k_exit parameter in JoinIR-local space.
pub exit_values: Vec<(String, ValueId)>,
}
impl ExitMeta {
/// Create new ExitMeta with no exit values
pub fn empty() -> Self {
Self {
exit_values: vec![],
}
}
/// Create ExitMeta with a single exit value
pub fn single(carrier_name: String, join_value: ValueId) -> Self {
Self {
exit_values: vec![(carrier_name, join_value)],
}
}
/// Create ExitMeta with multiple exit values
pub fn multiple(exit_values: Vec<(String, ValueId)>) -> Self {
Self { exit_values }
}
}

View File

@ -68,6 +68,7 @@
//!
//! Following the "80/20 rule" from CLAUDE.md - get it working first, generalize later.
use crate::mir::join_ir::lowering::carrier_info::ExitMeta;
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
use crate::mir::join_ir::{
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
@ -101,7 +102,7 @@ use crate::mir::ValueId;
///
/// # Returns
///
/// * `Some(JoinModule)` - Successfully lowered to JoinIR
/// * `Some((JoinModule, ExitMeta))` - Successfully lowered to JoinIR with exit metadata
/// * `None` - Pattern not matched (fallback to other lowerers)
///
/// # Boundary Contract
@ -109,8 +110,9 @@ use crate::mir::ValueId;
/// This function returns a JoinModule with:
/// - **Input slots**: ValueId(0) = i_init, ValueId(1) = sum_init
/// - **Output slot**: k_exit returns the final sum value
/// - **Exit metadata**: ExitMeta containing ("sum", ValueId(15)) binding
/// - **Caller responsibility**: Create JoinInlineBoundary to map ValueIds
pub fn lower_loop_with_continue_minimal(_scope: LoopScopeShape) -> Option<JoinModule> {
pub fn lower_loop_with_continue_minimal(_scope: LoopScopeShape) -> Option<(JoinModule, ExitMeta)> {
// Phase 195: Use local ValueId allocator (sequential from 0)
// JoinIR has NO knowledge of host ValueIds - boundary handled separately
let mut value_counter = 0u32;
@ -345,5 +347,11 @@ pub fn lower_loop_with_continue_minimal(_scope: LoopScopeShape) -> Option<JoinMo
eprintln!("[joinir/pattern4] Continue: Jump(loop_step) to skip iteration");
eprintln!("[joinir/pattern4] Carriers: i, sum");
Some(join_module)
// Phase 196: Return ExitMeta with carrier bindings
// k_exit parameter sum_exit (ValueId(15)) should be bound to host's "sum" variable
let exit_meta = ExitMeta::single("sum".to_string(), sum_exit);
eprintln!("[joinir/pattern4] ExitMeta: sum → ValueId({})", sum_exit.0);
Some((join_module, exit_meta))
}

View File

@ -15,6 +15,7 @@
//! - `if_select.rs`: Phase 33 If/Else → Select lowering
//! - `if_dry_runner.rs`: Phase 33-10 If lowering dry-run スキャナー(箱化版)
pub mod carrier_info; // Phase 196: Carrier metadata for loop lowering
pub mod common;
pub mod exit_args_resolver;
pub mod funcscanner_append_defs;