refactor(joinir): Phase 71-Pre - OwnershipPlanValidator Box (dev-only)
pattern3_with_if_phi.rs の check_ownership_plan_consistency() を独立した OwnershipPlanValidator Box に抽出。P2/P4 での再利用を可能にする。 Key changes: - plan_validator.rs: 新規作成 (~190行) - validate_relay_support(): Multi-hop relay Fail-Fast (Phase 70-A タグ) - validate_carrier_consistency(): Carrier set 整合性チェック (warn-only) - validate_condition_captures(): Condition captures チェック (warn-only) - validate_all(): All-in-one 検証 - pattern3_with_if_phi.rs: Validator box への委譲 - check_ownership_plan_consistency() → OwnershipPlanValidator::validate_all() Unit tests: 3 PASS - test_validate_relay_support_single_hop_ok - test_validate_relay_support_multihop_rejected - test_validate_all_with_consistent_data Tests: normalized_dev 50/50 PASS, lib 950/950 PASS 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -311,63 +311,13 @@ impl MirBuilder {
|
||||
/// 3. **Condition captures consistency**: plan captures vs condition bindings (warn-only)
|
||||
///
|
||||
/// Phase 70-A: Standardized error tag for runtime unsupported patterns.
|
||||
/// Phase 71-Pre: Delegated to OwnershipPlanValidator box.
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
fn check_ownership_plan_consistency(
|
||||
plan: &crate::mir::join_ir::ownership::OwnershipPlan,
|
||||
carrier_info: &crate::mir::join_ir::lowering::carrier_info::CarrierInfo,
|
||||
condition_bindings: &std::collections::BTreeSet<String>,
|
||||
) -> Result<(), String> {
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
// Check 1: Multi-hop relay is rejected (Fail-Fast)
|
||||
// Tag: [ownership/relay:runtime_unsupported] - standardized for Phase 70-A
|
||||
for relay in &plan.relay_writes {
|
||||
if relay.relay_path.len() > 1 {
|
||||
return Err(format!(
|
||||
"[ownership/relay:runtime_unsupported] Multihop relay not executable yet: var='{}', owner={:?}, relay_path={:?}",
|
||||
relay.name, relay.owner_scope, relay.relay_path
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Check 2: Carrier set consistency (warn-only, order SSOT deferred)
|
||||
let plan_carriers: BTreeSet<String> = plan
|
||||
.owned_vars
|
||||
.iter()
|
||||
.filter(|v| v.is_written)
|
||||
.map(|v| v.name.clone())
|
||||
.collect();
|
||||
|
||||
let existing_carriers: BTreeSet<String> = carrier_info
|
||||
.carriers
|
||||
.iter()
|
||||
.map(|c| c.name.clone())
|
||||
.collect();
|
||||
|
||||
if plan_carriers != existing_carriers {
|
||||
eprintln!("[phase64/ownership] Carrier set mismatch (warn-only, order SSOT deferred):");
|
||||
eprintln!(" OwnershipPlan carriers: {:?}", plan_carriers);
|
||||
eprintln!(" Existing carriers: {:?}", existing_carriers);
|
||||
// Don't fail - just warn (order SSOT not yet implemented)
|
||||
}
|
||||
|
||||
// Check 3: Condition captures consistency (warn-only)
|
||||
let plan_cond_captures: BTreeSet<String> = plan
|
||||
.condition_captures
|
||||
.iter()
|
||||
.map(|c| c.name.clone())
|
||||
.collect();
|
||||
|
||||
if !plan_cond_captures.is_subset(condition_bindings) {
|
||||
let extra: Vec<_> = plan_cond_captures
|
||||
.difference(condition_bindings)
|
||||
.collect();
|
||||
eprintln!(
|
||||
"[phase64/ownership] Extra condition captures in plan (warn-only): {:?}",
|
||||
extra
|
||||
);
|
||||
// Warn only - this might be expected in some cases
|
||||
}
|
||||
|
||||
Ok(())
|
||||
use crate::mir::join_ir::ownership::OwnershipPlanValidator;
|
||||
OwnershipPlanValidator::validate_all(plan, carrier_info, condition_bindings)
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
//! - Phase 57: Analyzer implemented (dev-only)
|
||||
//! - Phase 58: plan_to_lowering helper for P2 (analyzer-based testing only)
|
||||
//! - Phase 59: plan_to_lowering helper for P3 (if-sum patterns)
|
||||
//! - Phase 71-Pre: plan_validator box (reusable validation)
|
||||
|
||||
mod types;
|
||||
mod analyzer;
|
||||
@ -31,6 +32,8 @@ mod analyzer;
|
||||
mod plan_to_lowering;
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
mod ast_analyzer;
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
mod plan_validator;
|
||||
|
||||
pub use types::*;
|
||||
pub use analyzer::*;
|
||||
@ -38,3 +41,5 @@ pub use analyzer::*;
|
||||
pub use plan_to_lowering::*;
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
pub use ast_analyzer::*;
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
pub use plan_validator::*;
|
||||
|
||||
193
src/mir/join_ir/ownership/plan_validator.rs
Normal file
193
src/mir/join_ir/ownership/plan_validator.rs
Normal file
@ -0,0 +1,193 @@
|
||||
//! OwnershipPlan Validator Box
|
||||
//!
|
||||
//! Phase 71-Pre: Extracted from pattern3_with_if_phi.rs for reuse across patterns.
|
||||
//!
|
||||
//! # Responsibility
|
||||
//!
|
||||
//! Validates OwnershipPlan against CarrierInfo and condition bindings.
|
||||
//! This is **analysis-only** - no MIR generation or lowering.
|
||||
//!
|
||||
//! # Checks
|
||||
//!
|
||||
//! 1. **Relay support**: Multi-hop relay → Err with `[ownership/relay:runtime_unsupported]`
|
||||
//! 2. **Carrier consistency**: Plan carriers vs existing carriers (warn-only)
|
||||
//! 3. **Condition captures**: Plan captures vs condition bindings (warn-only)
|
||||
|
||||
use super::OwnershipPlan;
|
||||
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
/// Ownership Plan Validator
|
||||
///
|
||||
/// Provides reusable validation methods for OwnershipPlan consistency checks.
|
||||
/// Used by Pattern 2, 3, 4 lowering to ensure plan integrity before execution.
|
||||
pub struct OwnershipPlanValidator;
|
||||
|
||||
impl OwnershipPlanValidator {
|
||||
/// Validate relay support (Fail-Fast)
|
||||
///
|
||||
/// Returns Err if any relay has `relay_path.len() > 1` (multi-hop).
|
||||
/// Tag: `[ownership/relay:runtime_unsupported]`
|
||||
///
|
||||
/// # Phase 70-A
|
||||
///
|
||||
/// This is the standardized runtime guard. When Phase 70-B+ implements
|
||||
/// multi-hop execution, this check will be relaxed.
|
||||
pub fn validate_relay_support(plan: &OwnershipPlan) -> Result<(), String> {
|
||||
for relay in &plan.relay_writes {
|
||||
if relay.relay_path.len() > 1 {
|
||||
return Err(format!(
|
||||
"[ownership/relay:runtime_unsupported] Multihop relay not executable yet: var='{}', owner={:?}, relay_path={:?}",
|
||||
relay.name, relay.owner_scope, relay.relay_path
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate carrier set consistency (warn-only)
|
||||
///
|
||||
/// Compares plan's owned_vars (is_written=true) against existing CarrierInfo.
|
||||
/// Warns on mismatch but does not fail (order SSOT not yet implemented).
|
||||
pub fn validate_carrier_consistency(
|
||||
plan: &OwnershipPlan,
|
||||
carrier_info: &CarrierInfo,
|
||||
) -> Result<(), String> {
|
||||
let plan_carriers: BTreeSet<String> = plan
|
||||
.owned_vars
|
||||
.iter()
|
||||
.filter(|v| v.is_written)
|
||||
.map(|v| v.name.clone())
|
||||
.collect();
|
||||
|
||||
let existing_carriers: BTreeSet<String> = carrier_info
|
||||
.carriers
|
||||
.iter()
|
||||
.map(|c| c.name.clone())
|
||||
.collect();
|
||||
|
||||
if plan_carriers != existing_carriers {
|
||||
eprintln!("[ownership/validator] Carrier set mismatch (warn-only):");
|
||||
eprintln!(" OwnershipPlan carriers: {:?}", plan_carriers);
|
||||
eprintln!(" Existing carriers: {:?}", existing_carriers);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate condition captures consistency (warn-only)
|
||||
///
|
||||
/// Checks that plan's condition_captures are a subset of condition_bindings.
|
||||
/// Warns on extra captures but does not fail.
|
||||
pub fn validate_condition_captures(
|
||||
plan: &OwnershipPlan,
|
||||
condition_bindings: &BTreeSet<String>,
|
||||
) -> Result<(), String> {
|
||||
let plan_cond_captures: BTreeSet<String> = plan
|
||||
.condition_captures
|
||||
.iter()
|
||||
.map(|c| c.name.clone())
|
||||
.collect();
|
||||
|
||||
if !plan_cond_captures.is_subset(condition_bindings) {
|
||||
let extra: Vec<_> = plan_cond_captures.difference(condition_bindings).collect();
|
||||
eprintln!(
|
||||
"[ownership/validator] Extra condition captures in plan (warn-only): {:?}",
|
||||
extra
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate all checks (fail-fast on any Err)
|
||||
///
|
||||
/// Runs all validation checks in order:
|
||||
/// 1. validate_relay_support (Fail-Fast)
|
||||
/// 2. validate_carrier_consistency (warn-only)
|
||||
/// 3. validate_condition_captures (warn-only)
|
||||
pub fn validate_all(
|
||||
plan: &OwnershipPlan,
|
||||
carrier_info: &CarrierInfo,
|
||||
condition_bindings: &BTreeSet<String>,
|
||||
) -> Result<(), String> {
|
||||
Self::validate_relay_support(plan)?;
|
||||
Self::validate_carrier_consistency(plan, carrier_info)?;
|
||||
Self::validate_condition_captures(plan, condition_bindings)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole, CarrierVar};
|
||||
use crate::mir::join_ir::ownership::{CapturedVar, RelayVar, ScopeId, ScopeOwnedVar};
|
||||
use crate::mir::ValueId;
|
||||
|
||||
#[test]
|
||||
fn test_validate_relay_support_single_hop_ok() {
|
||||
let mut plan = OwnershipPlan::new(ScopeId(1));
|
||||
plan.relay_writes.push(RelayVar {
|
||||
name: "sum".to_string(),
|
||||
owner_scope: ScopeId(0),
|
||||
relay_path: vec![ScopeId(1)], // Single hop
|
||||
});
|
||||
|
||||
let result = OwnershipPlanValidator::validate_relay_support(&plan);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_relay_support_multihop_rejected() {
|
||||
let mut plan = OwnershipPlan::new(ScopeId(2));
|
||||
plan.relay_writes.push(RelayVar {
|
||||
name: "sum".to_string(),
|
||||
owner_scope: ScopeId(0),
|
||||
relay_path: vec![ScopeId(2), ScopeId(1)], // Multi-hop
|
||||
});
|
||||
|
||||
let result = OwnershipPlanValidator::validate_relay_support(&plan);
|
||||
assert!(result.is_err());
|
||||
let err = result.unwrap_err();
|
||||
assert!(
|
||||
err.contains("[ownership/relay:runtime_unsupported]"),
|
||||
"Error should contain standard tag: {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_all_with_consistent_data() {
|
||||
let mut plan = OwnershipPlan::new(ScopeId(1));
|
||||
plan.owned_vars.push(ScopeOwnedVar {
|
||||
name: "sum".to_string(),
|
||||
is_written: true,
|
||||
is_condition_only: false,
|
||||
});
|
||||
plan.condition_captures.push(CapturedVar {
|
||||
name: "limit".to_string(),
|
||||
owner_scope: ScopeId(0),
|
||||
});
|
||||
|
||||
let carrier_info = CarrierInfo {
|
||||
loop_var_name: "i".to_string(),
|
||||
loop_var_id: ValueId(0),
|
||||
carriers: vec![CarrierVar::with_role_and_init(
|
||||
"sum".to_string(),
|
||||
ValueId(1),
|
||||
CarrierRole::LoopState,
|
||||
CarrierInit::FromHost,
|
||||
)],
|
||||
trim_helper: None,
|
||||
promoted_loopbodylocals: vec![],
|
||||
};
|
||||
|
||||
let condition_bindings: BTreeSet<String> =
|
||||
["limit".to_string(), "i".to_string()].into_iter().collect();
|
||||
|
||||
let result =
|
||||
OwnershipPlanValidator::validate_all(&plan, &carrier_info, &condition_bindings);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user