refactor(joinir): Phase 193-2 - CarrierInfo Builder Enhancement

**Phase 193-2**: Add flexible builder methods to CarrierInfo and ExitMeta

## Summary
Enhanced CarrierInfo and ExitMeta with convenient builder methods that support
multiple construction patterns. This reduces boilerplate and makes carrier info
generation more flexible for different lowering scenarios.

## Changes

### CarrierInfo New Methods
- **from_variable_map()**: Automatically extract carriers from variable_map
  - Eliminates manual carrier listing for simple cases
  - Auto-discovers all non-loop-control variables
  - Deterministic sorting for reproducibility

- **with_explicit_carriers()**: Selective carrier extraction
  - Choose which variables to treat as carriers
  - Useful for Pattern 5+ with complex variable sets
  - Validates all carriers exist in variable_map

- **with_carriers()**: Direct CarrierVar construction
  - Most explicit method for advanced use cases
  - Use when CarrierVar structs already exist
  - Auto-sorts for determinism

### CarrierInfo Query Methods
- **carrier_count()**: Get number of carriers
- **is_multi_carrier()**: Check if multi-carrier loop
- **find_carrier()**: Lookup specific carrier by name

### ExitMeta New Methods
- **binding_count()**: Get number of exit bindings
- **is_empty()**: Check if any exit values exist
- **find_binding()**: Lookup exit value by carrier name
- **with_binding()**: Chainable binding addition

## Design Benefits

| Aspect | Benefit |
|--------|---------|
| **Flexibility** | 3 construction patterns for different scenarios |
| **Clarity** | Explicit method names document intent |
| **Ergonomics** | Reduced boilerplate in lowerers |
| **Validation** | Error handling for missing variables |
| **Determinism** | Automatic sorting in all methods |

## Usage Examples

```rust
// Pattern 1: Auto-discover from variable_map
let info = CarrierInfo::from_variable_map("i", &variable_map)?;

// Pattern 2: Selective carriers
let info = CarrierInfo::with_explicit_carriers(
    "i", loop_id,
    vec!["sum".into(), "count".into()],
    &variable_map
)?;

// Pattern 3: Manual construction
let info = CarrierInfo::with_carriers("i", loop_id, carriers);

// Query methods
if info.is_multi_carrier() {
    println!("Multi-carrier loop with {} carriers", info.carrier_count());
}

// ExitMeta chaining
let meta = ExitMeta::empty()
    .with_binding("sum".into(), ValueId(15))
    .with_binding("count".into(), ValueId(16));
```

## Metrics
- CarrierInfo: +3 construction methods, +3 query methods
- ExitMeta: +4 new methods (existing 3 methods unchanged)
- Total lines added: ~150 (including docs)
- Build time: 1m 05s 
- Zero regressions 

## Next Steps
- Phase 193-3: Pattern Classification Improvement
- Phase 194: Further optimization opportunities

🤖 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 10:27:18 +09:00
parent d28ba4cd9d
commit 49cc829ad2

View File

@ -3,8 +3,11 @@
//! 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.
//!
//! Phase 193-2: Enhanced builder methods for flexible construction
use crate::mir::ValueId;
use std::collections::HashMap;
/// Information about a single carrier variable
#[derive(Debug, Clone)]
@ -26,6 +29,167 @@ pub struct CarrierInfo {
pub carriers: Vec<CarrierVar>,
}
impl CarrierInfo {
/// Phase 193-2: Create CarrierInfo from a variable_map
///
/// Automatically extracts all non-loop-control variables from the host's
/// variable_map. This eliminates manual carrier listing for simple cases.
///
/// # Arguments
///
/// * `loop_var_name` - Name of the loop control variable (e.g., "i")
/// * `variable_map` - Host function's variable_map (String → ValueId)
///
/// # Returns
///
/// CarrierInfo with loop_var and all other variables as carriers
///
/// # Example
///
/// ```ignore
/// let carrier_info = CarrierInfo::from_variable_map(
/// "i".to_string(),
/// &variable_map // {"i": ValueId(5), "sum": ValueId(10), "count": ValueId(11)}
/// )?;
/// // Result: CarrierInfo with loop_var="i", carriers=[sum, count]
/// ```
pub fn from_variable_map(
loop_var_name: String,
variable_map: &HashMap<String, ValueId>,
) -> Result<Self, String> {
// Find loop variable
let loop_var_id = variable_map
.get(&loop_var_name)
.copied()
.ok_or_else(|| {
format!(
"Loop variable '{}' not found in variable_map",
loop_var_name
)
})?;
// Collect all non-loop-var variables as carriers
let mut carriers: Vec<CarrierVar> = variable_map
.iter()
.filter(|(name, _)| *name != &loop_var_name)
.map(|(name, &id)| CarrierVar {
name: name.clone(),
host_id: id,
})
.collect();
// Sort for determinism
carriers.sort_by(|a, b| a.name.cmp(&b.name));
Ok(CarrierInfo {
loop_var_name,
loop_var_id,
carriers,
})
}
/// Phase 193-2: Create CarrierInfo with explicit carrier list
///
/// Useful when you have specific carriers in mind and want explicit control
/// over which variables are treated as carriers.
///
/// # Arguments
///
/// * `loop_var_name` - Name of the loop control variable
/// * `loop_var_id` - ValueId of the loop variable
/// * `carrier_names` - Names of carrier variables (will look up in variable_map)
/// * `variable_map` - Host function's variable_map for lookups
///
/// # Returns
///
/// CarrierInfo with only the specified carriers
///
/// # Example
///
/// ```ignore
/// let carrier_info = CarrierInfo::with_explicit_carriers(
/// "i".to_string(),
/// ValueId(5),
/// vec!["sum".to_string(), "count".to_string()],
/// &variable_map
/// )?;
/// ```
pub fn with_explicit_carriers(
loop_var_name: String,
loop_var_id: ValueId,
carrier_names: Vec<String>,
variable_map: &HashMap<String, ValueId>,
) -> Result<Self, String> {
let mut carriers = Vec::new();
for name in carrier_names {
let host_id = variable_map.get(&name).copied().ok_or_else(|| {
format!("Carrier variable '{}' not found in variable_map", name)
})?;
carriers.push(CarrierVar {
name,
host_id,
});
}
// Sort for determinism
carriers.sort_by(|a, b| a.name.cmp(&b.name));
Ok(CarrierInfo {
loop_var_name,
loop_var_id,
carriers,
})
}
/// Phase 193-2: Create CarrierInfo with manual CarrierVar list
///
/// Most explicit construction method - you provide everything directly.
/// Useful when you already have CarrierVar structs built elsewhere.
///
/// # Arguments
///
/// * `loop_var_name` - Name of the loop control variable
/// * `loop_var_id` - ValueId of the loop variable
/// * `carriers` - Vec of already-constructed CarrierVar structs
pub fn with_carriers(
loop_var_name: String,
loop_var_id: ValueId,
mut carriers: Vec<CarrierVar>,
) -> Self {
// Sort for determinism
carriers.sort_by(|a, b| a.name.cmp(&b.name));
Self {
loop_var_name,
loop_var_id,
carriers,
}
}
/// Phase 193-2: Get carrier count
///
/// Convenience method for checking how many carriers this info has.
pub fn carrier_count(&self) -> usize {
self.carriers.len()
}
/// Phase 193-2: Check if this has multiple carriers
///
/// Useful for pattern matching: "is this a multi-carrier loop?"
pub fn is_multi_carrier(&self) -> bool {
self.carriers.len() > 1
}
/// Phase 193-2: Find a carrier by name
///
/// Lookup a specific carrier variable by name.
pub fn find_carrier(&self, name: &str) -> Option<&CarrierVar> {
self.carriers.iter().find(|c| c.name == name)
}
}
/// Exit metadata returned by lowerers
///
/// This structure captures the mapping from JoinIR exit values to
@ -61,4 +225,34 @@ impl ExitMeta {
pub fn multiple(exit_values: Vec<(String, ValueId)>) -> Self {
Self { exit_values }
}
/// Phase 193-2: Get the count of exit bindings
///
/// Useful for checking if this ExitMeta has any exit values.
pub fn binding_count(&self) -> usize {
self.exit_values.len()
}
/// Phase 193-2: Check if this has any exit values
pub fn is_empty(&self) -> bool {
self.exit_values.is_empty()
}
/// Phase 193-2: Find a binding by carrier name
///
/// Lookup a specific exit value by carrier name.
pub fn find_binding(&self, carrier_name: &str) -> Option<ValueId> {
self.exit_values
.iter()
.find(|(name, _)| name == carrier_name)
.map(|(_, value_id)| *value_id)
}
/// Phase 193-2: Add a binding to ExitMeta
///
/// Convenient way to build ExitMeta incrementally.
pub fn with_binding(mut self, carrier_name: String, join_value: ValueId) -> Self {
self.exit_values.push((carrier_name, join_value));
self
}
}