2025-12-06 02:05:19 +09:00
|
|
|
|
//! 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.
|
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>
2025-12-06 10:27:18 +09:00
|
|
|
|
//!
|
|
|
|
|
|
//! Phase 193-2: Enhanced builder methods for flexible construction
|
2025-12-08 22:19:41 +09:00
|
|
|
|
//!
|
|
|
|
|
|
//! # Phase 183-2: Primary CarrierInfo Construction
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! This module is the single source of truth for CarrierInfo initialization.
|
|
|
|
|
|
//! Both MIR and JoinIR contexts use `CarrierInfo::from_variable_map()` as the
|
|
|
|
|
|
//! primary construction method.
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! - MIR context: `common_init.rs` delegates to this module
|
|
|
|
|
|
//! - JoinIR context: Uses `from_variable_map()` directly
|
2025-12-13 05:35:14 +09:00
|
|
|
|
//!
|
|
|
|
|
|
//! # Phase 76: BindingId-Based Promotion Tracking
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! Replaces name-based promotion hacks (`"digit_pos"` → `"is_digit_pos"`) with
|
|
|
|
|
|
//! type-safe BindingId mapping. This eliminates fragile string matching while
|
|
|
|
|
|
//! maintaining backward compatibility through dual-path lookup.
|
2025-12-06 02:05:19 +09:00
|
|
|
|
|
|
|
|
|
|
use crate::mir::ValueId;
|
2025-12-10 13:59:23 +09:00
|
|
|
|
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
2025-12-06 02:05:19 +09:00
|
|
|
|
|
2025-12-13 05:35:14 +09:00
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
2025-12-13 16:20:33 +09:00
|
|
|
|
use crate::mir::BindingId; // Phase 76+78: BindingId for promoted carriers
|
2025-12-13 05:35:14 +09:00
|
|
|
|
|
2025-12-10 20:07:30 +09:00
|
|
|
|
/// Phase 227: CarrierRole - Distinguishes loop state carriers from condition-only carriers
|
|
|
|
|
|
///
|
|
|
|
|
|
/// When LoopBodyLocal variables are promoted to carriers, we need to know whether
|
|
|
|
|
|
/// they carry loop state (need exit PHI) or are only used in conditions (no exit PHI).
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Example
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ```ignore
|
|
|
|
|
|
/// // LoopState carrier: sum needs exit PHI (value persists after loop)
|
|
|
|
|
|
/// loop(i < n) {
|
|
|
|
|
|
/// sum = sum + i; // sum updated in loop body
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// print(sum); // sum used after loop
|
|
|
|
|
|
///
|
|
|
|
|
|
/// // ConditionOnly carrier: is_digit_pos does NOT need exit PHI
|
|
|
|
|
|
/// loop(p < s.length()) {
|
|
|
|
|
|
/// local digit_pos = digits.indexOf(s.substring(p, p+1));
|
|
|
|
|
|
/// if digit_pos < 0 { break; } // Only used in condition
|
|
|
|
|
|
/// num_str = num_str + ch;
|
|
|
|
|
|
/// p = p + 1;
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// // digit_pos not used after loop
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
|
pub enum CarrierRole {
|
|
|
|
|
|
/// Value needed after loop (sum, result, count, p, num_str)
|
|
|
|
|
|
/// - Participates in header PHI (loop iteration)
|
|
|
|
|
|
/// - Participates in exit PHI (final value after loop)
|
|
|
|
|
|
LoopState,
|
|
|
|
|
|
|
|
|
|
|
|
/// Only used for loop condition (is_digit_pos, is_whitespace)
|
|
|
|
|
|
/// - Participates in header PHI (loop iteration)
|
|
|
|
|
|
/// - Does NOT participate in exit PHI (not needed after loop)
|
|
|
|
|
|
ConditionOnly,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-10 21:10:28 +09:00
|
|
|
|
/// Phase 228: Initialization policy for carrier variables
|
|
|
|
|
|
///
|
|
|
|
|
|
/// When carriers participate in header PHI, they need an initial value.
|
|
|
|
|
|
/// Most carriers use their host_id value (FromHost), but promoted LoopBodyLocal
|
|
|
|
|
|
/// carriers need explicit bool initialization (BoolConst).
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Example
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ```ignore
|
|
|
|
|
|
/// // Regular carrier (sum): Use host_id value
|
|
|
|
|
|
/// CarrierVar { name: "sum", host_id: ValueId(10), init: FromHost, .. }
|
|
|
|
|
|
///
|
|
|
|
|
|
/// // ConditionOnly carrier (is_digit_pos): Initialize with false
|
|
|
|
|
|
/// CarrierVar { name: "is_digit_pos", host_id: ValueId(15), init: BoolConst(false), .. }
|
2025-12-11 16:46:53 +09:00
|
|
|
|
///
|
|
|
|
|
|
/// // Loop-local derived carrier (digit_value): Initialize with local zero (no host slot)
|
|
|
|
|
|
/// CarrierVar { name: "digit_value", host_id: ValueId(0), init: LoopLocalZero, .. }
|
2025-12-10 21:10:28 +09:00
|
|
|
|
/// ```
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
|
pub enum CarrierInit {
|
|
|
|
|
|
/// No explicit initialization (use host_id value)
|
|
|
|
|
|
FromHost,
|
|
|
|
|
|
/// Initialize with bool constant (for ConditionOnly carriers)
|
|
|
|
|
|
BoolConst(bool),
|
2025-12-11 16:46:53 +09:00
|
|
|
|
/// Initialize with loop-local zero (no host slot; used for derived carriers like digit_value)
|
|
|
|
|
|
LoopLocalZero,
|
2025-12-10 21:10:28 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-10 21:53:27 +09:00
|
|
|
|
// Phase 229: ConditionAlias removed - redundant with promoted_loopbodylocals
|
|
|
|
|
|
// The naming convention (old_name → "is_<old_name>" or "is_<old_name>_match")
|
|
|
|
|
|
// is sufficient to resolve promoted variables dynamically.
|
2025-12-10 18:45:04 +09:00
|
|
|
|
|
2025-12-06 02:05:19 +09:00
|
|
|
|
/// Information about a single carrier variable
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
|
pub struct CarrierVar {
|
2025-12-10 20:07:30 +09:00
|
|
|
|
/// Variable name (e.g., "sum", "printed", "is_digit_pos")
|
2025-12-06 02:05:19 +09:00
|
|
|
|
pub name: String,
|
2025-12-08 18:36:13 +09:00
|
|
|
|
/// Host ValueId for this variable (MIR側)
|
2025-12-06 02:05:19 +09:00
|
|
|
|
pub host_id: ValueId,
|
2025-12-08 18:36:13 +09:00
|
|
|
|
/// Phase 177-STRUCT: JoinIR側でこのキャリアを表すValueId
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ヘッダPHIのdstや、exitで使う値を記録する。
|
|
|
|
|
|
/// これにより、index ベースのマッチングを名前ベースに置き換えられる。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - `Some(vid)`: Header PHI生成後にセットされる
|
|
|
|
|
|
/// - `None`: まだPHI生成前、または該当なし
|
|
|
|
|
|
pub join_id: Option<ValueId>,
|
2025-12-10 20:07:30 +09:00
|
|
|
|
/// Phase 227: Role of this carrier (LoopState or ConditionOnly)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - `LoopState`: Value needed after loop (participates in exit PHI)
|
|
|
|
|
|
/// - `ConditionOnly`: Only used for loop condition (no exit PHI)
|
|
|
|
|
|
pub role: CarrierRole,
|
2025-12-10 21:10:28 +09:00
|
|
|
|
/// Phase 228: Initialization policy for header PHI
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - `FromHost`: Use host_id value (regular carriers)
|
|
|
|
|
|
/// - `BoolConst(false)`: Initialize with false (promoted LoopBodyLocal carriers)
|
|
|
|
|
|
pub init: CarrierInit,
|
2025-12-13 16:20:33 +09:00
|
|
|
|
/// Phase 78: BindingId for this carrier (dev-only)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// For promoted carriers (e.g., is_digit_pos), this is allocated separately
|
|
|
|
|
|
/// by CarrierBindingAssigner. For source-derived carriers, this comes from
|
|
|
|
|
|
/// builder.binding_map.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Enables type-safe lookup: BindingId → ValueId (join_id) in ConditionEnv.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Example
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ```ignore
|
|
|
|
|
|
/// // Source-derived carrier
|
|
|
|
|
|
/// CarrierVar {
|
|
|
|
|
|
/// name: "sum",
|
|
|
|
|
|
/// binding_id: Some(BindingId(5)), // from builder.binding_map["sum"]
|
|
|
|
|
|
/// ..
|
|
|
|
|
|
/// }
|
|
|
|
|
|
///
|
|
|
|
|
|
/// // Promoted carrier
|
|
|
|
|
|
/// CarrierVar {
|
|
|
|
|
|
/// name: "is_digit_pos",
|
|
|
|
|
|
/// binding_id: Some(BindingId(10)), // allocated by CarrierBindingAssigner
|
|
|
|
|
|
/// ..
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
pub binding_id: Option<BindingId>,
|
2025-12-10 20:07:30 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl CarrierVar {
|
|
|
|
|
|
/// Create a new CarrierVar with default LoopState role
|
|
|
|
|
|
///
|
|
|
|
|
|
/// This is the primary constructor for CarrierVar. Use this instead of
|
|
|
|
|
|
/// struct literal syntax to ensure role defaults to LoopState.
|
|
|
|
|
|
pub fn new(name: String, host_id: ValueId) -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
name,
|
|
|
|
|
|
host_id,
|
|
|
|
|
|
join_id: None,
|
|
|
|
|
|
role: CarrierRole::LoopState,
|
2025-12-10 21:10:28 +09:00
|
|
|
|
init: CarrierInit::FromHost, // Phase 228: Default to FromHost
|
2025-12-13 16:20:33 +09:00
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
binding_id: None, // Phase 78: No BindingId by default
|
2025-12-10 20:07:30 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Create a CarrierVar with explicit role
|
|
|
|
|
|
pub fn with_role(name: String, host_id: ValueId, role: CarrierRole) -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
name,
|
|
|
|
|
|
host_id,
|
|
|
|
|
|
join_id: None,
|
|
|
|
|
|
role,
|
2025-12-10 21:10:28 +09:00
|
|
|
|
init: CarrierInit::FromHost, // Phase 228: Default to FromHost
|
2025-12-13 16:20:33 +09:00
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
binding_id: None, // Phase 78: No BindingId by default
|
2025-12-10 21:10:28 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Phase 228: Create a CarrierVar with explicit role and init policy
|
|
|
|
|
|
pub fn with_role_and_init(
|
|
|
|
|
|
name: String,
|
|
|
|
|
|
host_id: ValueId,
|
|
|
|
|
|
role: CarrierRole,
|
|
|
|
|
|
init: CarrierInit,
|
|
|
|
|
|
) -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
name,
|
|
|
|
|
|
host_id,
|
|
|
|
|
|
join_id: None,
|
|
|
|
|
|
role,
|
|
|
|
|
|
init,
|
2025-12-13 16:20:33 +09:00
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
binding_id: None, // Phase 78: No BindingId by default
|
2025-12-10 20:07:30 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-06 02:05:19 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 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>,
|
2025-12-08 02:41:53 +09:00
|
|
|
|
/// Phase 171-C-5: Trim pattern helper (if this CarrierInfo was created from Trim promotion)
|
|
|
|
|
|
pub trim_helper: Option<crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper>,
|
2025-12-10 16:30:53 +09:00
|
|
|
|
/// Phase 224: Promoted LoopBodyLocal variables (e.g., "digit_pos" promoted to "is_digit_pos")
|
|
|
|
|
|
///
|
|
|
|
|
|
/// These variables were originally LoopBodyLocal but have been promoted to carriers
|
|
|
|
|
|
/// during condition promotion (e.g., DigitPosPromoter). The lowerer should skip
|
|
|
|
|
|
/// LoopBodyLocal checks for these variables.
|
2025-12-10 18:45:04 +09:00
|
|
|
|
///
|
2025-12-10 21:53:27 +09:00
|
|
|
|
/// Phase 229: Naming convention for promoted carriers:
|
|
|
|
|
|
/// - DigitPos pattern: "var" → "is_var" (e.g., "digit_pos" → "is_digit_pos")
|
|
|
|
|
|
/// - Trim pattern: "var" → "is_var_match" (e.g., "ch" → "is_ch_match")
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Condition variable resolution dynamically infers the carrier name from this list.
|
|
|
|
|
|
pub promoted_loopbodylocals: Vec<String>,
|
2025-12-13 05:35:14 +09:00
|
|
|
|
|
|
|
|
|
|
/// Phase 76: Type-safe promotion tracking (dev-only)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Maps original BindingId to promoted BindingId, eliminating name-based hacks.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Example
|
|
|
|
|
|
///
|
|
|
|
|
|
/// DigitPos promotion:
|
|
|
|
|
|
/// - Original: BindingId(5) for `"digit_pos"`
|
|
|
|
|
|
/// - Promoted: BindingId(10) for `"is_digit_pos"`
|
|
|
|
|
|
/// - Map entry: `promoted_bindings[BindingId(5)] = BindingId(10)`
|
|
|
|
|
|
///
|
|
|
|
|
|
/// This enables type-safe resolution:
|
|
|
|
|
|
/// ```ignore
|
|
|
|
|
|
/// if let Some(promoted_bid) = carrier_info.promoted_bindings.get(&original_bid) {
|
|
|
|
|
|
/// // Lookup promoted carrier by BindingId (no string matching!)
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Migration Strategy (Phase 76)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - **Dual Path**: BindingId lookup (NEW) OR name-based fallback (LEGACY)
|
|
|
|
|
|
/// - **Populated by**: DigitPosPromoter, TrimLoopHelper (Phase 76)
|
|
|
|
|
|
/// - **Used by**: ConditionEnv::resolve_var_with_binding (Phase 75+)
|
|
|
|
|
|
/// - **Phase 77**: Remove name-based fallback after full migration
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Design Notes
|
|
|
|
|
|
///
|
|
|
|
|
|
/// **Q: Why BindingId map instead of name map?**
|
|
|
|
|
|
/// - **Type Safety**: Compiler-checked binding identity (no typos)
|
|
|
|
|
|
/// - **Shadowing-Aware**: BindingId distinguishes inner/outer scope vars
|
|
|
|
|
|
/// - **No Name Collisions**: BindingId is unique even if names shadow
|
|
|
|
|
|
///
|
|
|
|
|
|
/// **Q: Why not remove `promoted_loopbodylocals` immediately?**
|
|
|
|
|
|
/// - **Legacy Compatibility**: Existing code uses name-based lookup
|
|
|
|
|
|
/// - **Gradual Migration**: Phase 76 adds BindingId, Phase 77 removes name-based
|
|
|
|
|
|
/// - **Fail-Safe**: Dual path ensures no regressions during transition
|
|
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
pub promoted_bindings: BTreeMap<BindingId, BindingId>,
|
2025-12-06 02:05:19 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
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>
2025-12-06 10:27:18 +09:00
|
|
|
|
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,
|
2025-12-10 13:59:23 +09:00
|
|
|
|
variable_map: &BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
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>
2025-12-06 10:27:18 +09:00
|
|
|
|
) -> Result<Self, String> {
|
|
|
|
|
|
// Find loop variable
|
2025-12-11 20:54:33 +09:00
|
|
|
|
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
|
|
|
|
|
|
)
|
|
|
|
|
|
})?;
|
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>
2025-12-06 10:27:18 +09:00
|
|
|
|
|
|
|
|
|
|
// 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,
|
2025-12-08 18:36:13 +09:00
|
|
|
|
join_id: None, // Phase 177-STRUCT-1: Set by header PHI generation
|
2025-12-10 20:07:30 +09:00
|
|
|
|
role: CarrierRole::LoopState, // Phase 227: Default to LoopState
|
2025-12-10 21:10:28 +09:00
|
|
|
|
init: CarrierInit::FromHost, // Phase 228: Default to FromHost
|
2025-12-13 16:20:33 +09:00
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
binding_id: None, // Phase 78: Set by CarrierBindingAssigner
|
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>
2025-12-06 10:27:18 +09:00
|
|
|
|
})
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
|
|
// Sort for determinism
|
|
|
|
|
|
carriers.sort_by(|a, b| a.name.cmp(&b.name));
|
|
|
|
|
|
|
|
|
|
|
|
Ok(CarrierInfo {
|
|
|
|
|
|
loop_var_name,
|
|
|
|
|
|
loop_var_id,
|
|
|
|
|
|
carriers,
|
2025-12-08 02:41:53 +09:00
|
|
|
|
trim_helper: None, // Phase 171-C-5: No Trim pattern by default
|
2025-12-10 16:30:53 +09:00
|
|
|
|
promoted_loopbodylocals: Vec::new(), // Phase 224: No promoted variables by default
|
2025-12-13 05:35:14 +09:00
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
promoted_bindings: BTreeMap::new(), // Phase 76: No promoted bindings by default
|
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>
2025-12-06 10:27:18 +09:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 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>,
|
2025-12-10 13:59:23 +09:00
|
|
|
|
variable_map: &BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
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>
2025-12-06 10:27:18 +09:00
|
|
|
|
) -> Result<Self, String> {
|
|
|
|
|
|
let mut carriers = Vec::new();
|
|
|
|
|
|
|
|
|
|
|
|
for name in carrier_names {
|
2025-12-11 20:54:33 +09:00
|
|
|
|
let host_id = variable_map
|
|
|
|
|
|
.get(&name)
|
|
|
|
|
|
.copied()
|
|
|
|
|
|
.ok_or_else(|| format!("Carrier variable '{}' not found in variable_map", name))?;
|
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>
2025-12-06 10:27:18 +09:00
|
|
|
|
|
|
|
|
|
|
carriers.push(CarrierVar {
|
|
|
|
|
|
name,
|
|
|
|
|
|
host_id,
|
2025-12-08 18:36:13 +09:00
|
|
|
|
join_id: None, // Phase 177-STRUCT-1: Set by header PHI generation
|
2025-12-10 20:07:30 +09:00
|
|
|
|
role: CarrierRole::LoopState, // Phase 227: Default to LoopState
|
2025-12-10 21:10:28 +09:00
|
|
|
|
init: CarrierInit::FromHost, // Phase 228: Default to FromHost
|
2025-12-13 16:20:33 +09:00
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
binding_id: None, // Phase 78: Set by CarrierBindingAssigner
|
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>
2025-12-06 10:27:18 +09:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Sort for determinism
|
|
|
|
|
|
carriers.sort_by(|a, b| a.name.cmp(&b.name));
|
|
|
|
|
|
|
|
|
|
|
|
Ok(CarrierInfo {
|
|
|
|
|
|
loop_var_name,
|
|
|
|
|
|
loop_var_id,
|
|
|
|
|
|
carriers,
|
2025-12-08 02:41:53 +09:00
|
|
|
|
trim_helper: None, // Phase 171-C-5: No Trim pattern by default
|
2025-12-10 16:30:53 +09:00
|
|
|
|
promoted_loopbodylocals: Vec::new(), // Phase 224: No promoted variables by default
|
2025-12-13 05:35:14 +09:00
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
promoted_bindings: BTreeMap::new(), // Phase 76: No promoted bindings by default
|
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>
2025-12-06 10:27:18 +09:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 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,
|
2025-12-08 02:41:53 +09:00
|
|
|
|
trim_helper: None, // Phase 171-C-5: No Trim pattern by default
|
2025-12-10 16:30:53 +09:00
|
|
|
|
promoted_loopbodylocals: Vec::new(), // Phase 224: No promoted variables by default
|
2025-12-13 05:35:14 +09:00
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
promoted_bindings: BTreeMap::new(), // Phase 76: No promoted bindings by default
|
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>
2025-12-06 10:27:18 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 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)
|
|
|
|
|
|
}
|
2025-12-08 02:41:53 +09:00
|
|
|
|
|
|
|
|
|
|
/// Phase 171-C-4: Merge carriers from another CarrierInfo
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Deduplicates by carrier name. If a carrier with the same name already exists,
|
|
|
|
|
|
/// it will not be added again.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Arguments
|
|
|
|
|
|
///
|
|
|
|
|
|
/// * `other` - Another CarrierInfo to merge from
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Example
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ```ignore
|
|
|
|
|
|
/// let mut carrier_info = CarrierInfo::from_variable_map("i", &variable_map)?;
|
|
|
|
|
|
/// let promoted_carrier = TrimPatternInfo::to_carrier_info();
|
|
|
|
|
|
/// carrier_info.merge_from(&promoted_carrier);
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
pub fn merge_from(&mut self, other: &CarrierInfo) {
|
|
|
|
|
|
for carrier in &other.carriers {
|
|
|
|
|
|
if !self.carriers.iter().any(|c| c.name == carrier.name) {
|
|
|
|
|
|
self.carriers.push(carrier.clone());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Maintain sorted order for determinism
|
|
|
|
|
|
self.carriers.sort_by(|a, b| a.name.cmp(&b.name));
|
|
|
|
|
|
|
|
|
|
|
|
// Phase 171-C-5: Also merge trim_helper if present
|
|
|
|
|
|
if other.trim_helper.is_some() {
|
|
|
|
|
|
self.trim_helper = other.trim_helper.clone();
|
|
|
|
|
|
}
|
2025-12-10 16:30:53 +09:00
|
|
|
|
|
|
|
|
|
|
// Phase 224: Merge promoted_loopbodylocals (deduplicate)
|
|
|
|
|
|
for promoted_var in &other.promoted_loopbodylocals {
|
|
|
|
|
|
if !self.promoted_loopbodylocals.contains(promoted_var) {
|
|
|
|
|
|
self.promoted_loopbodylocals.push(promoted_var.clone());
|
|
|
|
|
|
}
|
2025-12-10 18:45:04 +09:00
|
|
|
|
}
|
2025-12-13 05:35:14 +09:00
|
|
|
|
|
|
|
|
|
|
// Phase 76: Merge promoted_bindings (dev-only)
|
|
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
{
|
|
|
|
|
|
for (original, promoted) in &other.promoted_bindings {
|
|
|
|
|
|
self.promoted_bindings.insert(*original, *promoted);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-08 02:41:53 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Phase 171-C-5: Get Trim pattern helper
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Returns the TrimLoopHelper if this CarrierInfo was created from Trim promotion.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Returns
|
|
|
|
|
|
///
|
|
|
|
|
|
/// * `Some(&TrimLoopHelper)` - If this CarrierInfo contains Trim pattern information
|
|
|
|
|
|
/// * `None` - If this is a regular CarrierInfo (not from Trim promotion)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Example
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ```ignore
|
|
|
|
|
|
/// if let Some(helper) = carrier_info.trim_helper() {
|
|
|
|
|
|
/// eprintln!("Trim pattern detected: {}", helper.carrier_name);
|
|
|
|
|
|
/// eprintln!("Whitespace chars: {:?}", helper.whitespace_chars);
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// ```
|
2025-12-11 20:54:33 +09:00
|
|
|
|
pub fn trim_helper(
|
|
|
|
|
|
&self,
|
|
|
|
|
|
) -> Option<&crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper> {
|
2025-12-08 02:41:53 +09:00
|
|
|
|
self.trim_helper.as_ref()
|
|
|
|
|
|
}
|
2025-12-11 00:21:29 +09:00
|
|
|
|
|
|
|
|
|
|
/// Phase 229/231: Resolve promoted LoopBodyLocal name to carrier JoinIR ValueId
|
|
|
|
|
|
///
|
|
|
|
|
|
/// This helper centralizes the naming convention for promoted variables so that
|
|
|
|
|
|
/// ScopeManager 実装がそれぞれ命名規約を再実装しなくて済むようにするよ。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 命名規約:
|
|
|
|
|
|
/// - DigitPos パターン: `"var"` → `"is_var"`(例: "digit_pos" → "is_digit_pos")
|
|
|
|
|
|
/// - Trim パターン : `"var"` → `"is_var_match"`(例: "ch" → "is_ch_match")
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Arguments
|
|
|
|
|
|
///
|
|
|
|
|
|
/// * `original_name` - 元の LoopBodyLocal 名(例: "digit_pos")
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Returns
|
|
|
|
|
|
///
|
|
|
|
|
|
/// * `Some(ValueId)` - 対応する carrier の join_id が見つかった場合
|
|
|
|
|
|
/// * `None` - promoted_loopbodylocals に含まれない、または join_id 未設定の場合
|
2025-12-13 05:58:57 +09:00
|
|
|
|
///
|
|
|
|
|
|
/// # Phase 77: DEPRECATED
|
|
|
|
|
|
///
|
|
|
|
|
|
/// This method uses fragile naming conventions ("is_*", "is_*_match") and will
|
|
|
|
|
|
/// be removed in Phase 78+ when all call sites migrate to BindingId-based lookup.
|
|
|
|
|
|
/// Use `resolve_promoted_with_binding()` for type-safe BindingId lookup.
|
|
|
|
|
|
#[deprecated(
|
|
|
|
|
|
since = "phase77",
|
|
|
|
|
|
note = "Use resolve_promoted_with_binding() for type-safe BindingId lookup"
|
|
|
|
|
|
)]
|
2025-12-11 00:21:29 +09:00
|
|
|
|
pub fn resolve_promoted_join_id(&self, original_name: &str) -> Option<ValueId> {
|
2025-12-13 05:58:57 +09:00
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[phase77/legacy/carrier_info] WARNING: Using deprecated name-based promoted lookup for '{}'",
|
|
|
|
|
|
original_name
|
|
|
|
|
|
);
|
2025-12-11 20:54:33 +09:00
|
|
|
|
if !self
|
|
|
|
|
|
.promoted_loopbodylocals
|
|
|
|
|
|
.contains(&original_name.to_string())
|
|
|
|
|
|
{
|
2025-12-11 00:21:29 +09:00
|
|
|
|
return None;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let candidates = [
|
|
|
|
|
|
format!("is_{}", original_name), // DigitPos pattern
|
|
|
|
|
|
format!("is_{}_match", original_name), // Trim pattern
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
for carrier_name in &candidates {
|
|
|
|
|
|
// loop_var 自身が ConditionOnly carrier として扱われるケースは現状ほぼないが、
|
|
|
|
|
|
// 将来の拡張に備えて loop_var_name も一応チェックしておく。
|
|
|
|
|
|
if carrier_name == &self.loop_var_name {
|
|
|
|
|
|
if let Some(carrier) = self.carriers.iter().find(|c| c.name == self.loop_var_name) {
|
|
|
|
|
|
if let Some(join_id) = carrier.join_id {
|
|
|
|
|
|
return Some(join_id);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(carrier) = self.carriers.iter().find(|c| c.name == *carrier_name) {
|
|
|
|
|
|
if let Some(join_id) = carrier.join_id {
|
|
|
|
|
|
return Some(join_id);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
2025-12-13 05:35:14 +09:00
|
|
|
|
|
|
|
|
|
|
/// Phase 76: Type-safe promoted binding resolution (dev-only)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Resolves a promoted LoopBodyLocal binding via BindingId map, eliminating
|
|
|
|
|
|
/// name-based hacks (`format!("is_{}", name)`). Falls back to legacy name-based
|
|
|
|
|
|
/// lookup for backward compatibility during Phase 76-77 migration.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Arguments
|
|
|
|
|
|
///
|
|
|
|
|
|
/// * `original_binding` - Original LoopBodyLocal's BindingId (e.g., BindingId(5) for "digit_pos")
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Returns
|
|
|
|
|
|
///
|
|
|
|
|
|
/// * `Some(BindingId)` - Promoted carrier's BindingId (e.g., BindingId(10) for "is_digit_pos")
|
|
|
|
|
|
/// * `None` - No promotion mapping found
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Example
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ```ignore
|
|
|
|
|
|
/// // DigitPos promotion: BindingId(5) "digit_pos" → BindingId(10) "is_digit_pos"
|
|
|
|
|
|
/// let original_bid = BindingId(5);
|
|
|
|
|
|
/// if let Some(promoted_bid) = carrier_info.resolve_promoted_with_binding(original_bid) {
|
|
|
|
|
|
/// // Lookup carrier by promoted BindingId (type-safe!)
|
|
|
|
|
|
/// let promoted_value = condition_env.get_by_binding(promoted_bid);
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Migration Path (Phase 76-77)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - **Phase 76**: BindingId map populated by promoters, dual path (BindingId OR name)
|
|
|
|
|
|
/// - **Phase 77**: Remove name-based fallback, BindingId-only lookup
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Design Notes
|
|
|
|
|
|
///
|
|
|
|
|
|
/// **Why not merge with `resolve_promoted_join_id()`?**
|
|
|
|
|
|
/// - Different input type: BindingId vs String
|
|
|
|
|
|
/// - Different output: BindingId vs ValueId
|
|
|
|
|
|
/// - Different usage: ScopeManager (BindingId) vs legacy lowerers (name)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// **Why BTreeMap instead of HashMap?**
|
|
|
|
|
|
/// - Deterministic iteration (Phase 222.5-D consistency)
|
|
|
|
|
|
/// - Debug-friendly sorted output
|
|
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
pub fn resolve_promoted_with_binding(&self, original_binding: BindingId) -> Option<BindingId> {
|
|
|
|
|
|
self.promoted_bindings.get(&original_binding).copied()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Phase 76: Record a promoted binding (dev-only)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Helper method to populate the promoted_bindings map during promotion.
|
|
|
|
|
|
/// Called by wrapper functions that have access to both CarrierInfo and binding_map.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Arguments
|
|
|
|
|
|
///
|
|
|
|
|
|
/// * `original_binding` - Original LoopBodyLocal's BindingId
|
|
|
|
|
|
/// * `promoted_binding` - Promoted carrier's BindingId
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Example
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ```ignore
|
|
|
|
|
|
/// // After DigitPosPromoter creates CarrierInfo, record the binding mapping:
|
|
|
|
|
|
/// carrier_info.record_promoted_binding(
|
|
|
|
|
|
/// binding_map.get("digit_pos").copied().unwrap(), // BindingId(5)
|
|
|
|
|
|
/// binding_map.get("is_digit_pos").copied().unwrap() // BindingId(10)
|
|
|
|
|
|
/// );
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Phase 76 Note
|
|
|
|
|
|
///
|
|
|
|
|
|
/// This method is currently UNUSED because promoters (DigitPosPromoter, TrimLoopHelper)
|
|
|
|
|
|
/// don't have access to binding_map. Actual population happens in a future phase when
|
|
|
|
|
|
/// we integrate BindingId tracking into the promotion pipeline.
|
|
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
pub fn record_promoted_binding(&mut self, original_binding: BindingId, promoted_binding: BindingId) {
|
refactor(joinir): Phase 86 - Carrier init builder, debug migration, error tags
P1: Carrier Initialization Builder (HIGH) ✅
- New module: carrier_init_builder.rs (197 lines, 8 tests)
- Refactored loop_header_phi_builder.rs (-34 lines)
- Centralized CarrierInit value generation (SSOT)
- Eliminates scattered match patterns across header PHI, exit line
- Consistent debug output: [carrier_init_builder] format
- Net: -34 lines of duplicated logic
P2: Remaining DebugOutputBox Migration (QUICK) ✅
- Migrated carrier_info.rs::record_promoted_binding()
- Uses DebugOutputBox for JOINIR_DEBUG checks
- Maintains JOINIR_TEST_DEBUG override for test diagnostics
- Consistent log formatting: [context/category] message
- Net: +3 lines (SSOT migration)
P3: Error Message Centralization (LOW) ✅
- New module: error_tags.rs (136 lines, 5 tests)
- Migrated 3 error sites:
* ownership/relay:runtime_unsupported (plan_validator.rs)
* joinir/freeze (control_flow/mod.rs)
* (ExitLine errors were debug messages, not returns)
- Centralized error tag generation (freeze, exit_line_contract, ownership_relay_unsupported, etc.)
- Net: +133 lines (SSOT module + tests)
Total changes:
- New files: carrier_init_builder.rs (197), error_tags.rs (136)
- Modified: 6 files
- Production code: +162 lines (SSOT investment)
- Tests: 987/987 PASS (982→987, +5 new tests)
- Phase 81 ExitLine: 2/2 PASS
- Zero compilation errors/warnings
Benefits:
✅ Single Responsibility: Each helper has one concern
✅ Testability: 13 new unit tests (8 carrier init, 5 error tags)
✅ Consistency: Uniform debug/error formatting
✅ SSOT: Centralized CarrierInit and error tag generation
✅ Discoverability: Easy to find all error types
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-13 21:48:02 +09:00
|
|
|
|
use super::debug_output_box::DebugOutputBox;
|
|
|
|
|
|
|
|
|
|
|
|
// Phase 86: Use DebugOutputBox for consistent debug output
|
|
|
|
|
|
// Allow JOINIR_TEST_DEBUG override for test-specific diagnostics
|
|
|
|
|
|
let test_debug = std::env::var("JOINIR_TEST_DEBUG").is_ok();
|
|
|
|
|
|
let debug = DebugOutputBox::new("binding_pilot/promoted_bindings");
|
|
|
|
|
|
|
|
|
|
|
|
if debug.is_enabled() || test_debug {
|
2025-12-13 16:20:33 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[binding_pilot/promoted_bindings] {} → {}",
|
|
|
|
|
|
original_binding, promoted_binding
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-12-13 05:35:14 +09:00
|
|
|
|
self.promoted_bindings.insert(original_binding, promoted_binding);
|
|
|
|
|
|
}
|
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>
2025-12-06 10:27:18 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-06 02:05:19 +09:00
|
|
|
|
/// 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)>,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-07 05:07:28 +09:00
|
|
|
|
/// Phase 33-14: JoinFragmentMeta - Distinguishes expr result from carrier updates
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ## Purpose
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Separates two distinct use cases for JoinIR loops:
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 1. **Expr Result Pattern** (joinir_min_loop.hako):
|
|
|
|
|
|
/// ```nyash
|
|
|
|
|
|
/// local result = loop(...) { ... } // Loop used as expression
|
|
|
|
|
|
/// return result
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
/// Here, the k_exit return value is the "expr result" that should go to exit_phi_inputs.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 2. **Carrier Update Pattern** (trim pattern):
|
|
|
|
|
|
/// ```nyash
|
|
|
|
|
|
/// loop(...) { start = start + 1 } // Loop used for side effects
|
|
|
|
|
|
/// print(start) // Use carrier after loop
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
/// Here, there's no "expr result" - only carrier variable updates.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ## SSA Correctness
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Previously, exit_phi_inputs mixed expr results with carrier updates, causing:
|
|
|
|
|
|
/// - PHI inputs that referenced undefined remapped values
|
|
|
|
|
|
/// - SSA-undef errors in VM execution
|
|
|
|
|
|
///
|
|
|
|
|
|
/// With JoinFragmentMeta:
|
|
|
|
|
|
/// - `expr_result`: Only goes to exit_phi_inputs (generates PHI for expr value)
|
|
|
|
|
|
/// - `exit_meta`: Only goes to carrier_inputs (updates variable_map via carrier PHIs)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ## Example: Pattern 2 (joinir_min_loop.hako)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ```rust
|
|
|
|
|
|
/// JoinFragmentMeta {
|
|
|
|
|
|
/// expr_result: Some(i_exit), // k_exit returns i as expr value
|
|
|
|
|
|
/// exit_meta: ExitMeta::single("i".to_string(), i_exit), // Also a carrier
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ## Example: Pattern 3 (trim pattern)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ```rust
|
|
|
|
|
|
/// JoinFragmentMeta {
|
|
|
|
|
|
/// expr_result: None, // Loop doesn't return a value
|
|
|
|
|
|
/// exit_meta: ExitMeta::multiple(vec![
|
|
|
|
|
|
/// ("start".to_string(), start_exit),
|
|
|
|
|
|
/// ("end".to_string(), end_exit),
|
|
|
|
|
|
/// ]),
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
|
pub struct JoinFragmentMeta {
|
|
|
|
|
|
/// Expression result ValueId from k_exit (JoinIR-local)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - `Some(vid)`: Loop is used as expression, k_exit's return value → exit_phi_inputs
|
|
|
|
|
|
/// - `None`: Loop is used for side effects only, no PHI for expr value
|
|
|
|
|
|
pub expr_result: Option<ValueId>,
|
|
|
|
|
|
|
|
|
|
|
|
/// Carrier variable exit bindings (existing ExitMeta)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Maps carrier names to their JoinIR-local exit values.
|
|
|
|
|
|
/// These go to carrier_inputs for carrier PHI generation.
|
|
|
|
|
|
pub exit_meta: ExitMeta,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl JoinFragmentMeta {
|
|
|
|
|
|
/// Create JoinFragmentMeta for expression result pattern
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Use when the loop returns a value (like `return loop(...)`).
|
|
|
|
|
|
pub fn with_expr_result(expr_result: ValueId, exit_meta: ExitMeta) -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
expr_result: Some(expr_result),
|
|
|
|
|
|
exit_meta,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Create JoinFragmentMeta for carrier-only pattern
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Use when the loop only updates carriers (like trim pattern).
|
|
|
|
|
|
pub fn carrier_only(exit_meta: ExitMeta) -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
expr_result: None,
|
|
|
|
|
|
exit_meta,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Create empty JoinFragmentMeta (no expr result, no carriers)
|
|
|
|
|
|
pub fn empty() -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
expr_result: None,
|
|
|
|
|
|
exit_meta: ExitMeta::empty(),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Check if this fragment has an expression result
|
|
|
|
|
|
pub fn has_expr_result(&self) -> bool {
|
|
|
|
|
|
self.expr_result.is_some()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Phase 33-14: Backward compatibility - convert to ExitMeta
|
|
|
|
|
|
///
|
|
|
|
|
|
/// During migration, some code may still expect ExitMeta.
|
|
|
|
|
|
/// This extracts just the carrier bindings.
|
|
|
|
|
|
#[deprecated(since = "33-14", note = "Use exit_meta directly for carrier access")]
|
|
|
|
|
|
pub fn to_exit_meta(&self) -> ExitMeta {
|
|
|
|
|
|
self.exit_meta.clone()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-06 02:05:19 +09:00
|
|
|
|
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 }
|
|
|
|
|
|
}
|
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>
2025-12-06 10:27:18 +09:00
|
|
|
|
|
|
|
|
|
|
/// 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
|
|
|
|
|
|
}
|
2025-12-06 02:05:19 +09:00
|
|
|
|
}
|
2025-12-08 02:41:53 +09:00
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod tests {
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
|
|
// Helper: Create a CarrierVar for testing
|
|
|
|
|
|
fn test_carrier(name: &str, id: u32) -> CarrierVar {
|
|
|
|
|
|
CarrierVar {
|
|
|
|
|
|
name: name.to_string(),
|
|
|
|
|
|
host_id: ValueId(id),
|
2025-12-11 20:54:33 +09:00
|
|
|
|
join_id: None, // Phase 177-STRUCT-1
|
2025-12-10 20:07:30 +09:00
|
|
|
|
role: CarrierRole::LoopState, // Phase 227: Default to LoopState
|
2025-12-11 20:54:33 +09:00
|
|
|
|
init: CarrierInit::FromHost, // Phase 228: Default to FromHost
|
2025-12-13 16:20:33 +09:00
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
binding_id: None, // Phase 78: No BindingId by default
|
2025-12-08 02:41:53 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Helper: Create a CarrierInfo for testing
|
|
|
|
|
|
fn test_carrier_info(loop_var: &str, loop_id: u32, carriers: Vec<CarrierVar>) -> CarrierInfo {
|
2025-12-11 20:54:33 +09:00
|
|
|
|
CarrierInfo::with_carriers(loop_var.to_string(), ValueId(loop_id), carriers)
|
2025-12-08 02:41:53 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_merge_from_empty() {
|
|
|
|
|
|
// Merge empty CarrierInfo should not change anything
|
2025-12-11 20:54:33 +09:00
|
|
|
|
let mut carrier_info = test_carrier_info("i", 5, vec![test_carrier("sum", 10)]);
|
2025-12-08 02:41:53 +09:00
|
|
|
|
|
|
|
|
|
|
let other = test_carrier_info("j", 20, vec![]);
|
|
|
|
|
|
|
|
|
|
|
|
carrier_info.merge_from(&other);
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(carrier_info.carrier_count(), 1);
|
|
|
|
|
|
assert_eq!(carrier_info.carriers[0].name, "sum");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_merge_from_new_carrier() {
|
|
|
|
|
|
// Merge a new carrier that doesn't exist yet
|
2025-12-11 20:54:33 +09:00
|
|
|
|
let mut carrier_info = test_carrier_info("i", 5, vec![test_carrier("sum", 10)]);
|
2025-12-08 02:41:53 +09:00
|
|
|
|
|
2025-12-11 20:54:33 +09:00
|
|
|
|
let other = test_carrier_info("j", 20, vec![test_carrier("count", 15)]);
|
2025-12-08 02:41:53 +09:00
|
|
|
|
|
|
|
|
|
|
carrier_info.merge_from(&other);
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(carrier_info.carrier_count(), 2);
|
|
|
|
|
|
// Should be sorted by name
|
|
|
|
|
|
assert_eq!(carrier_info.carriers[0].name, "count"); // 'c' < 's'
|
|
|
|
|
|
assert_eq!(carrier_info.carriers[1].name, "sum");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_merge_from_duplicate_carrier() {
|
|
|
|
|
|
// Merge a carrier with the same name should NOT duplicate
|
2025-12-11 20:54:33 +09:00
|
|
|
|
let mut carrier_info = test_carrier_info("i", 5, vec![test_carrier("sum", 10)]);
|
2025-12-08 02:41:53 +09:00
|
|
|
|
|
|
|
|
|
|
let other = test_carrier_info(
|
|
|
|
|
|
"j",
|
|
|
|
|
|
20,
|
|
|
|
|
|
vec![test_carrier("sum", 999)], // Same name, different ID
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
carrier_info.merge_from(&other);
|
|
|
|
|
|
|
|
|
|
|
|
// Should still have only 1 carrier (no duplication)
|
|
|
|
|
|
assert_eq!(carrier_info.carrier_count(), 1);
|
|
|
|
|
|
assert_eq!(carrier_info.carriers[0].name, "sum");
|
|
|
|
|
|
// Original ID should be preserved
|
|
|
|
|
|
assert_eq!(carrier_info.carriers[0].host_id, ValueId(10));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_merge_from_multiple_carriers() {
|
|
|
|
|
|
// Merge multiple carriers
|
2025-12-11 20:54:33 +09:00
|
|
|
|
let mut carrier_info = test_carrier_info("i", 5, vec![test_carrier("sum", 10)]);
|
2025-12-08 02:41:53 +09:00
|
|
|
|
|
|
|
|
|
|
let other = test_carrier_info(
|
|
|
|
|
|
"j",
|
|
|
|
|
|
20,
|
2025-12-11 20:54:33 +09:00
|
|
|
|
vec![test_carrier("count", 15), test_carrier("product", 18)],
|
2025-12-08 02:41:53 +09:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
carrier_info.merge_from(&other);
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(carrier_info.carrier_count(), 3);
|
|
|
|
|
|
// Should be sorted by name
|
|
|
|
|
|
assert_eq!(carrier_info.carriers[0].name, "count");
|
|
|
|
|
|
assert_eq!(carrier_info.carriers[1].name, "product");
|
|
|
|
|
|
assert_eq!(carrier_info.carriers[2].name, "sum");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_merge_from_preserves_determinism() {
|
|
|
|
|
|
// Test that merge maintains sorted order
|
|
|
|
|
|
let mut carrier_info = test_carrier_info(
|
|
|
|
|
|
"i",
|
|
|
|
|
|
5,
|
2025-12-11 20:54:33 +09:00
|
|
|
|
vec![test_carrier("zebra", 30), test_carrier("alpha", 10)],
|
2025-12-08 02:41:53 +09:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
let other = test_carrier_info(
|
|
|
|
|
|
"j",
|
|
|
|
|
|
20,
|
2025-12-11 20:54:33 +09:00
|
|
|
|
vec![test_carrier("beta", 15), test_carrier("gamma", 18)],
|
2025-12-08 02:41:53 +09:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
carrier_info.merge_from(&other);
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(carrier_info.carrier_count(), 4);
|
|
|
|
|
|
// Should be sorted alphabetically
|
|
|
|
|
|
assert_eq!(carrier_info.carriers[0].name, "alpha");
|
|
|
|
|
|
assert_eq!(carrier_info.carriers[1].name, "beta");
|
|
|
|
|
|
assert_eq!(carrier_info.carriers[2].name, "gamma");
|
|
|
|
|
|
assert_eq!(carrier_info.carriers[3].name, "zebra");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_merge_from_with_trim_helper() {
|
|
|
|
|
|
// Test that trim_helper is merged
|
|
|
|
|
|
use crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper;
|
|
|
|
|
|
|
|
|
|
|
|
let mut carrier_info = test_carrier_info("i", 5, vec![]);
|
|
|
|
|
|
|
|
|
|
|
|
let mut other = test_carrier_info("j", 20, vec![]);
|
|
|
|
|
|
other.trim_helper = Some(TrimLoopHelper {
|
|
|
|
|
|
original_var: "ch".to_string(),
|
|
|
|
|
|
carrier_name: "is_whitespace".to_string(),
|
|
|
|
|
|
whitespace_chars: vec![" ".to_string(), "\t".to_string()],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
carrier_info.merge_from(&other);
|
|
|
|
|
|
|
|
|
|
|
|
// trim_helper should be copied
|
|
|
|
|
|
assert!(carrier_info.trim_helper.is_some());
|
|
|
|
|
|
let helper = carrier_info.trim_helper.as_ref().unwrap();
|
|
|
|
|
|
assert_eq!(helper.original_var, "ch");
|
|
|
|
|
|
assert_eq!(helper.carrier_name, "is_whitespace");
|
|
|
|
|
|
assert_eq!(helper.whitespace_count(), 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_trim_helper_accessor() {
|
|
|
|
|
|
// Test the trim_helper() accessor method
|
|
|
|
|
|
use crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper;
|
|
|
|
|
|
|
|
|
|
|
|
let mut carrier_info = test_carrier_info("i", 5, vec![]);
|
|
|
|
|
|
|
|
|
|
|
|
// Initially None
|
|
|
|
|
|
assert!(carrier_info.trim_helper().is_none());
|
|
|
|
|
|
|
|
|
|
|
|
// Add trim_helper
|
|
|
|
|
|
carrier_info.trim_helper = Some(TrimLoopHelper {
|
|
|
|
|
|
original_var: "ch".to_string(),
|
|
|
|
|
|
carrier_name: "is_whitespace".to_string(),
|
|
|
|
|
|
whitespace_chars: vec![" ".to_string()],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Now Some
|
|
|
|
|
|
assert!(carrier_info.trim_helper().is_some());
|
|
|
|
|
|
let helper = carrier_info.trim_helper().unwrap();
|
|
|
|
|
|
assert_eq!(helper.original_var, "ch");
|
|
|
|
|
|
}
|
2025-12-13 05:35:14 +09:00
|
|
|
|
|
|
|
|
|
|
// ========== Phase 76: promoted_bindings tests ==========
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
fn test_promoted_bindings_record_and_resolve() {
|
|
|
|
|
|
use crate::mir::BindingId;
|
|
|
|
|
|
|
|
|
|
|
|
let mut carrier_info = test_carrier_info("i", 5, vec![]);
|
|
|
|
|
|
|
|
|
|
|
|
// Record a promotion: BindingId(5) → BindingId(10)
|
|
|
|
|
|
carrier_info.record_promoted_binding(BindingId(5), BindingId(10));
|
|
|
|
|
|
|
|
|
|
|
|
// Resolve should find the mapping
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
|
carrier_info.resolve_promoted_with_binding(BindingId(5)),
|
|
|
|
|
|
Some(BindingId(10))
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// Unknown BindingId should return None
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
|
carrier_info.resolve_promoted_with_binding(BindingId(99)),
|
|
|
|
|
|
None
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
fn test_promoted_bindings_multiple_mappings() {
|
|
|
|
|
|
use crate::mir::BindingId;
|
|
|
|
|
|
|
|
|
|
|
|
let mut carrier_info = test_carrier_info("i", 5, vec![]);
|
|
|
|
|
|
|
|
|
|
|
|
// Record multiple promotions (e.g., DigitPos + Trim in same loop)
|
|
|
|
|
|
carrier_info.record_promoted_binding(BindingId(5), BindingId(10)); // digit_pos → is_digit_pos
|
|
|
|
|
|
carrier_info.record_promoted_binding(BindingId(6), BindingId(11)); // ch → is_ch_match
|
|
|
|
|
|
|
|
|
|
|
|
// Both should resolve independently
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
|
carrier_info.resolve_promoted_with_binding(BindingId(5)),
|
|
|
|
|
|
Some(BindingId(10))
|
|
|
|
|
|
);
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
|
carrier_info.resolve_promoted_with_binding(BindingId(6)),
|
|
|
|
|
|
Some(BindingId(11))
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
fn test_promoted_bindings_merge() {
|
|
|
|
|
|
use crate::mir::BindingId;
|
|
|
|
|
|
|
|
|
|
|
|
let mut carrier_info1 = test_carrier_info("i", 5, vec![test_carrier("sum", 10)]);
|
|
|
|
|
|
carrier_info1.record_promoted_binding(BindingId(1), BindingId(2));
|
|
|
|
|
|
|
|
|
|
|
|
let mut carrier_info2 = test_carrier_info("j", 20, vec![test_carrier("count", 15)]);
|
|
|
|
|
|
carrier_info2.record_promoted_binding(BindingId(3), BindingId(4));
|
|
|
|
|
|
|
|
|
|
|
|
// Merge carrier_info2 into carrier_info1
|
|
|
|
|
|
carrier_info1.merge_from(&carrier_info2);
|
|
|
|
|
|
|
|
|
|
|
|
// Both promoted_bindings should be present
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
|
carrier_info1.resolve_promoted_with_binding(BindingId(1)),
|
|
|
|
|
|
Some(BindingId(2))
|
|
|
|
|
|
);
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
|
carrier_info1.resolve_promoted_with_binding(BindingId(3)),
|
|
|
|
|
|
Some(BindingId(4))
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
fn test_promoted_bindings_default_empty() {
|
|
|
|
|
|
use crate::mir::BindingId;
|
|
|
|
|
|
|
|
|
|
|
|
// Newly created CarrierInfo should have empty promoted_bindings
|
|
|
|
|
|
let carrier_info = test_carrier_info("i", 5, vec![test_carrier("sum", 10)]);
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
|
carrier_info.resolve_promoted_with_binding(BindingId(0)),
|
|
|
|
|
|
None
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
#[cfg(feature = "normalized_dev")]
|
|
|
|
|
|
fn test_promoted_bindings_overwrite() {
|
|
|
|
|
|
use crate::mir::BindingId;
|
|
|
|
|
|
|
|
|
|
|
|
let mut carrier_info = test_carrier_info("i", 5, vec![]);
|
|
|
|
|
|
|
|
|
|
|
|
// Record initial mapping
|
|
|
|
|
|
carrier_info.record_promoted_binding(BindingId(5), BindingId(10));
|
|
|
|
|
|
|
|
|
|
|
|
// Overwrite with new mapping (should replace)
|
|
|
|
|
|
carrier_info.record_promoted_binding(BindingId(5), BindingId(20));
|
|
|
|
|
|
|
|
|
|
|
|
// Should return the new value
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
|
carrier_info.resolve_promoted_with_binding(BindingId(5)),
|
|
|
|
|
|
Some(BindingId(20))
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-12-08 02:41:53 +09:00
|
|
|
|
}
|