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)
|
/// 3. **Condition captures consistency**: plan captures vs condition bindings (warn-only)
|
||||||
///
|
///
|
||||||
/// Phase 70-A: Standardized error tag for runtime unsupported patterns.
|
/// Phase 70-A: Standardized error tag for runtime unsupported patterns.
|
||||||
|
/// Phase 71-Pre: Delegated to OwnershipPlanValidator box.
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
fn check_ownership_plan_consistency(
|
fn check_ownership_plan_consistency(
|
||||||
plan: &crate::mir::join_ir::ownership::OwnershipPlan,
|
plan: &crate::mir::join_ir::ownership::OwnershipPlan,
|
||||||
carrier_info: &crate::mir::join_ir::lowering::carrier_info::CarrierInfo,
|
carrier_info: &crate::mir::join_ir::lowering::carrier_info::CarrierInfo,
|
||||||
condition_bindings: &std::collections::BTreeSet<String>,
|
condition_bindings: &std::collections::BTreeSet<String>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
use std::collections::BTreeSet;
|
use crate::mir::join_ir::ownership::OwnershipPlanValidator;
|
||||||
|
OwnershipPlanValidator::validate_all(plan, carrier_info, condition_bindings)
|
||||||
// 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(())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@
|
|||||||
//! - Phase 57: Analyzer implemented (dev-only)
|
//! - Phase 57: Analyzer implemented (dev-only)
|
||||||
//! - Phase 58: plan_to_lowering helper for P2 (analyzer-based testing 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 59: plan_to_lowering helper for P3 (if-sum patterns)
|
||||||
|
//! - Phase 71-Pre: plan_validator box (reusable validation)
|
||||||
|
|
||||||
mod types;
|
mod types;
|
||||||
mod analyzer;
|
mod analyzer;
|
||||||
@ -31,6 +32,8 @@ mod analyzer;
|
|||||||
mod plan_to_lowering;
|
mod plan_to_lowering;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
mod ast_analyzer;
|
mod ast_analyzer;
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
mod plan_validator;
|
||||||
|
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
pub use analyzer::*;
|
pub use analyzer::*;
|
||||||
@ -38,3 +41,5 @@ pub use analyzer::*;
|
|||||||
pub use plan_to_lowering::*;
|
pub use plan_to_lowering::*;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub use ast_analyzer::*;
|
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