feat(joinir): Phase 59 - Ownership P3 plumbing (dev-only)
P3 (if-sum) パターン用の OwnershipPlan → lowering inputs 変換を追加。 Key changes: - plan_to_lowering.rs (+153 lines): - P3LoweringInputs struct (same structure as P2) - plan_to_p3_inputs() converter - 4 unit tests (multi-carrier, 5+, condition-only, relay-rejected) P3 specific features: - Multi-carrier support (sum, count, 5+ carriers) - Same Fail-Fast on relay_writes (Phase 59 scope) - Same CarrierRole discrimination (LoopState vs ConditionOnly) Integration tests: - test_phase59_ownership_p3_relay_failfast: Verifies relay detection - test_phase59_ownership_p3_loop_local_success: Verifies loop-local success Design: Perfect parallelism with P2 - Same struct layout (carriers, captures, condition_captures) - Same conversion logic (skip loop var, filter written vars) - Same error handling (Fail-Fast on relay) Tests: 946/946 PASS (16 ownership tests) All code under #[cfg(feature = "normalized_dev")] - zero impact on canonical. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -1241,3 +1241,174 @@ fn test_phase58_ownership_p2_comparison() {
|
||||
|
||||
eprintln!("[phase58/test] Phase 58 conversion verified: sum correctly extracted as carrier");
|
||||
}
|
||||
|
||||
/// Phase 59: P3 with outer-owned carriers (relay case) should fail-fast
|
||||
#[test]
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
fn test_phase59_ownership_p3_relay_failfast() {
|
||||
use nyash_rust::mir::join_ir::ownership::{plan_to_p3_inputs, OwnershipAnalyzer};
|
||||
use serde_json::json;
|
||||
|
||||
// P3 where sum/count are defined OUTSIDE the loop -> relay
|
||||
let json = json!({
|
||||
"functions": [{
|
||||
"name": "main",
|
||||
"params": [],
|
||||
"body": {
|
||||
"kind": "Block",
|
||||
"statements": [
|
||||
{"kind": "Local", "name": "sum", "init": {"kind": "Const", "value": 0}},
|
||||
{"kind": "Local", "name": "count", "init": {"kind": "Const", "value": 0}},
|
||||
{"kind": "Local", "name": "i", "init": {"kind": "Const", "value": 0}},
|
||||
{
|
||||
"kind": "Loop",
|
||||
"condition": {
|
||||
"kind": "BinaryOp", "op": "Lt",
|
||||
"lhs": {"kind": "Var", "name": "i"},
|
||||
"rhs": {"kind": "Const", "value": 10}
|
||||
},
|
||||
"body": {
|
||||
"kind": "Block",
|
||||
"statements": [
|
||||
{
|
||||
"kind": "If",
|
||||
"condition": {
|
||||
"kind": "BinaryOp", "op": "Gt",
|
||||
"lhs": {"kind": "Var", "name": "i"},
|
||||
"rhs": {"kind": "Const", "value": 0}
|
||||
},
|
||||
"then": {
|
||||
"kind": "Block",
|
||||
"statements": [
|
||||
{"kind": "Assign", "target": "sum", "value": {
|
||||
"kind": "BinaryOp", "op": "Add",
|
||||
"lhs": {"kind": "Var", "name": "sum"},
|
||||
"rhs": {"kind": "Var", "name": "i"}
|
||||
}},
|
||||
{"kind": "Assign", "target": "count", "value": {
|
||||
"kind": "BinaryOp", "op": "Add",
|
||||
"lhs": {"kind": "Var", "name": "count"},
|
||||
"rhs": {"kind": "Const", "value": 1}
|
||||
}}
|
||||
]
|
||||
}
|
||||
},
|
||||
{"kind": "Assign", "target": "i", "value": {
|
||||
"kind": "BinaryOp", "op": "Add",
|
||||
"lhs": {"kind": "Var", "name": "i"},
|
||||
"rhs": {"kind": "Const", "value": 1}
|
||||
}}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
let mut analyzer = OwnershipAnalyzer::new();
|
||||
let plans = analyzer.analyze_json(&json).expect("analysis should succeed");
|
||||
|
||||
// Find loop plan
|
||||
let loop_plan = plans
|
||||
.iter()
|
||||
.find(|p| !p.relay_writes.is_empty())
|
||||
.expect("loop should have relay_writes for sum/count");
|
||||
|
||||
// Verify relay_writes contains sum and count
|
||||
assert!(loop_plan.relay_writes.iter().any(|r| r.name == "sum"));
|
||||
assert!(loop_plan.relay_writes.iter().any(|r| r.name == "count"));
|
||||
|
||||
// plan_to_p3_inputs should fail
|
||||
let result = plan_to_p3_inputs(loop_plan, "i");
|
||||
assert!(result.is_err(), "Should fail-fast on relay_writes");
|
||||
assert!(
|
||||
result.unwrap_err().contains("relay_writes not yet supported for P3"),
|
||||
"Error should mention P3 relay limitation"
|
||||
);
|
||||
|
||||
eprintln!("[phase59/test] P3 relay fail-fast verified");
|
||||
}
|
||||
|
||||
/// Phase 59: P3 with loop-local carriers should succeed
|
||||
#[test]
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
fn test_phase59_ownership_p3_loop_local_success() {
|
||||
use nyash_rust::mir::join_ir::ownership::{plan_to_p3_inputs, OwnershipAnalyzer};
|
||||
use serde_json::json;
|
||||
|
||||
// P3 where sum/count are defined INSIDE the loop -> no relay
|
||||
let json = json!({
|
||||
"functions": [{
|
||||
"name": "main",
|
||||
"params": [],
|
||||
"body": {
|
||||
"kind": "Loop",
|
||||
"condition": {"kind": "Const", "value": true},
|
||||
"body": {
|
||||
"kind": "Block",
|
||||
"statements": [
|
||||
{"kind": "Local", "name": "i", "init": {"kind": "Const", "value": 0}},
|
||||
{"kind": "Local", "name": "sum", "init": {"kind": "Const", "value": 0}},
|
||||
{"kind": "Local", "name": "count", "init": {"kind": "Const", "value": 0}},
|
||||
{
|
||||
"kind": "If",
|
||||
"condition": {
|
||||
"kind": "BinaryOp", "op": "Gt",
|
||||
"lhs": {"kind": "Var", "name": "i"},
|
||||
"rhs": {"kind": "Const", "value": 0}
|
||||
},
|
||||
"then": {
|
||||
"kind": "Block",
|
||||
"statements": [
|
||||
{"kind": "Assign", "target": "sum", "value": {
|
||||
"kind": "BinaryOp", "op": "Add",
|
||||
"lhs": {"kind": "Var", "name": "sum"},
|
||||
"rhs": {"kind": "Var", "name": "i"}
|
||||
}},
|
||||
{"kind": "Assign", "target": "count", "value": {
|
||||
"kind": "BinaryOp", "op": "Add",
|
||||
"lhs": {"kind": "Var", "name": "count"},
|
||||
"rhs": {"kind": "Const", "value": 1}
|
||||
}}
|
||||
]
|
||||
}
|
||||
},
|
||||
{"kind": "Break"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
let mut analyzer = OwnershipAnalyzer::new();
|
||||
let plans = analyzer.analyze_json(&json).expect("analysis should succeed");
|
||||
|
||||
// Find loop plan with owned vars
|
||||
let loop_plan = plans
|
||||
.iter()
|
||||
.find(|p| p.owned_vars.iter().any(|v| v.name == "sum"))
|
||||
.expect("loop should own sum");
|
||||
|
||||
// No relay
|
||||
assert!(
|
||||
loop_plan.relay_writes.is_empty(),
|
||||
"No relay for loop-local vars"
|
||||
);
|
||||
|
||||
// plan_to_p3_inputs should succeed
|
||||
let inputs = plan_to_p3_inputs(loop_plan, "i").expect("Should succeed");
|
||||
|
||||
eprintln!("[phase59/test] P3 inputs: {:?}", inputs);
|
||||
|
||||
// sum and count should be carriers
|
||||
assert!(inputs.carriers.iter().any(|c| c.name == "sum"));
|
||||
assert!(inputs.carriers.iter().any(|c| c.name == "count"));
|
||||
assert_eq!(
|
||||
inputs.carriers.len(),
|
||||
2,
|
||||
"Should have 2 carriers (sum and count)"
|
||||
);
|
||||
|
||||
eprintln!("[phase59/test] P3 loop-local conversion verified: sum and count correctly extracted as carriers");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user