feat(joinir): Phase 58 - Ownership P2 plumbing (dev-only)
OwnershipPlan → P2 lowering inputs 変換の dev 経路を追加。 Key changes: - New plan_to_lowering.rs (~210 lines): - P2LoweringInputs struct (carriers/captures/condition_captures) - plan_to_p2_inputs() converter - Fail-Fast on relay_writes (Phase 58 scope limitation) Conversion rules: - owned_vars where is_written=true → carriers - Loop variable skipped (pinned, handled separately) - is_condition_only=true → CarrierRole::ConditionOnly - Read-only captures preserved Entry point strategy: - Analysis-only testing (no lowering path modification yet) - Proves OwnershipAnalyzer can analyze P2 fixtures - Full integration deferred to Phase 60+ Tests: 946/946 PASS (7 ownership tests) - test_simple_p2_conversion - test_relay_rejected (Fail-Fast verification) - test_phase58_ownership_p2_comparison All code under #[cfg(feature = "normalized_dev")] - zero impact on canonical path. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -19,12 +19,17 @@
|
||||
//! 3. **Relay Propagation**: writes to ancestor-owned → relay up
|
||||
//! 4. **Capture Read-Only**: captures have no PHI at this scope
|
||||
//!
|
||||
//! # Phase 57 Status
|
||||
//! # Phase 58 Status
|
||||
//!
|
||||
//! Analyzer implemented (dev-only, not connected to lowering yet).
|
||||
//! - Phase 57: Analyzer implemented (dev-only)
|
||||
//! - Phase 58: plan_to_lowering helper for P2 (analyzer-based testing only)
|
||||
|
||||
mod types;
|
||||
mod analyzer;
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
mod plan_to_lowering;
|
||||
|
||||
pub use types::*;
|
||||
pub use analyzer::*;
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
pub use plan_to_lowering::*;
|
||||
|
||||
199
src/mir/join_ir/ownership/plan_to_lowering.rs
Normal file
199
src/mir/join_ir/ownership/plan_to_lowering.rs
Normal file
@ -0,0 +1,199 @@
|
||||
//! Convert OwnershipPlan to P2 lowering inputs.
|
||||
//!
|
||||
//! Phase 58: dev-only, P2 only.
|
||||
|
||||
use super::OwnershipPlan;
|
||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole, CarrierVar};
|
||||
|
||||
/// Result of converting OwnershipPlan for P2 lowering
|
||||
#[derive(Debug)]
|
||||
pub struct P2LoweringInputs {
|
||||
/// Carriers derived from owned_vars (is_written=true)
|
||||
pub carriers: Vec<CarrierVar>,
|
||||
/// Captured variables (read-only)
|
||||
pub captures: Vec<String>,
|
||||
/// Condition captures
|
||||
pub condition_captures: Vec<String>,
|
||||
}
|
||||
|
||||
/// Convert OwnershipPlan to P2 lowering inputs.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns Err if relay_writes is non-empty (Phase 58 scope limitation).
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
pub fn plan_to_p2_inputs(
|
||||
plan: &OwnershipPlan,
|
||||
loop_var: &str,
|
||||
) -> Result<P2LoweringInputs, String> {
|
||||
// Fail-Fast: relay_writes not supported in Phase 58
|
||||
if !plan.relay_writes.is_empty() {
|
||||
return Err(format!(
|
||||
"Phase 58 limitation: relay_writes not yet supported. Found: {:?}",
|
||||
plan.relay_writes.iter().map(|r| &r.name).collect::<Vec<_>>()
|
||||
));
|
||||
}
|
||||
|
||||
let mut carriers = Vec::new();
|
||||
|
||||
for var in &plan.owned_vars {
|
||||
// Skip loop variable (pinned, handled separately)
|
||||
if var.name == loop_var {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only written vars become carriers
|
||||
if !var.is_written {
|
||||
continue;
|
||||
}
|
||||
|
||||
let role = if var.is_condition_only {
|
||||
CarrierRole::ConditionOnly
|
||||
} else {
|
||||
CarrierRole::LoopState
|
||||
};
|
||||
|
||||
carriers.push(CarrierVar {
|
||||
name: var.name.clone(),
|
||||
role,
|
||||
init: CarrierInit::FromHost, // Default (Phase 228)
|
||||
host_id: crate::mir::ValueId(0), // Placeholder - not used in dev analysis
|
||||
join_id: None,
|
||||
});
|
||||
}
|
||||
|
||||
let captures: Vec<String> = plan.captures.iter().map(|c| c.name.clone()).collect();
|
||||
|
||||
let condition_captures: Vec<String> = plan
|
||||
.condition_captures
|
||||
.iter()
|
||||
.map(|c| c.name.clone())
|
||||
.collect();
|
||||
|
||||
Ok(P2LoweringInputs {
|
||||
carriers,
|
||||
captures,
|
||||
condition_captures,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mir::join_ir::ownership::{RelayVar, ScopeId, ScopeOwnedVar};
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
fn test_simple_p2_conversion() {
|
||||
let mut plan = OwnershipPlan::new(ScopeId(1));
|
||||
plan.owned_vars.push(ScopeOwnedVar {
|
||||
name: "i".to_string(), // loop var
|
||||
is_written: true,
|
||||
is_condition_only: false,
|
||||
});
|
||||
plan.owned_vars.push(ScopeOwnedVar {
|
||||
name: "sum".to_string(), // carrier
|
||||
is_written: true,
|
||||
is_condition_only: false,
|
||||
});
|
||||
|
||||
let inputs = plan_to_p2_inputs(&plan, "i").unwrap();
|
||||
|
||||
// i is skipped (loop var), sum becomes carrier
|
||||
assert_eq!(inputs.carriers.len(), 1);
|
||||
assert_eq!(inputs.carriers[0].name, "sum");
|
||||
assert_eq!(inputs.carriers[0].role, CarrierRole::LoopState);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
fn test_condition_only_carrier() {
|
||||
let mut plan = OwnershipPlan::new(ScopeId(1));
|
||||
plan.owned_vars.push(ScopeOwnedVar {
|
||||
name: "i".to_string(), // loop var
|
||||
is_written: true,
|
||||
is_condition_only: false,
|
||||
});
|
||||
plan.owned_vars.push(ScopeOwnedVar {
|
||||
name: "is_digit_pos".to_string(), // condition-only carrier
|
||||
is_written: true,
|
||||
is_condition_only: true,
|
||||
});
|
||||
|
||||
let inputs = plan_to_p2_inputs(&plan, "i").unwrap();
|
||||
|
||||
// i is skipped, is_digit_pos becomes ConditionOnly carrier
|
||||
assert_eq!(inputs.carriers.len(), 1);
|
||||
assert_eq!(inputs.carriers[0].name, "is_digit_pos");
|
||||
assert_eq!(inputs.carriers[0].role, CarrierRole::ConditionOnly);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
fn test_relay_rejected() {
|
||||
let mut plan = OwnershipPlan::new(ScopeId(1));
|
||||
plan.relay_writes.push(RelayVar {
|
||||
name: "outer_var".to_string(),
|
||||
owner_scope: ScopeId(0),
|
||||
relay_path: vec![],
|
||||
});
|
||||
|
||||
let result = plan_to_p2_inputs(&plan, "i");
|
||||
assert!(result.is_err());
|
||||
assert!(result
|
||||
.unwrap_err()
|
||||
.contains("relay_writes not yet supported"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
fn test_read_only_vars_not_carriers() {
|
||||
let mut plan = OwnershipPlan::new(ScopeId(1));
|
||||
plan.owned_vars.push(ScopeOwnedVar {
|
||||
name: "i".to_string(), // loop var
|
||||
is_written: true,
|
||||
is_condition_only: false,
|
||||
});
|
||||
plan.owned_vars.push(ScopeOwnedVar {
|
||||
name: "limit".to_string(), // read-only owned
|
||||
is_written: false,
|
||||
is_condition_only: false,
|
||||
});
|
||||
|
||||
let inputs = plan_to_p2_inputs(&plan, "i").unwrap();
|
||||
|
||||
// Only i is written, and it's skipped (loop var), so no carriers
|
||||
assert_eq!(inputs.carriers.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
fn test_captures_and_condition_captures() {
|
||||
use crate::mir::join_ir::ownership::CapturedVar;
|
||||
|
||||
let mut plan = OwnershipPlan::new(ScopeId(1));
|
||||
plan.owned_vars.push(ScopeOwnedVar {
|
||||
name: "i".to_string(), // loop var
|
||||
is_written: true,
|
||||
is_condition_only: false,
|
||||
});
|
||||
|
||||
// Captured variable (read-only)
|
||||
plan.captures.push(CapturedVar {
|
||||
name: "limit".to_string(),
|
||||
owner_scope: ScopeId(0),
|
||||
});
|
||||
|
||||
// Condition capture
|
||||
plan.condition_captures.push(CapturedVar {
|
||||
name: "limit".to_string(),
|
||||
owner_scope: ScopeId(0),
|
||||
});
|
||||
|
||||
let inputs = plan_to_p2_inputs(&plan, "i").unwrap();
|
||||
|
||||
assert_eq!(inputs.captures.len(), 1);
|
||||
assert_eq!(inputs.captures[0], "limit");
|
||||
assert_eq!(inputs.condition_captures.len(), 1);
|
||||
assert_eq!(inputs.condition_captures[0], "limit");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user