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:
@ -1066,3 +1066,178 @@ fn test_phase54_structural_axis_discrimination_p3() {
|
||||
assert!(has_selfhost_p3, "selfhost P3 should be detected as selfhost (with name guard): {:?}", shapes);
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 58: Compare ownership-based P2 lowering with existing path
|
||||
///
|
||||
/// This test demonstrates that OwnershipAnalyzer can analyze P2 fixtures
|
||||
/// and plan_to_p2_inputs can convert the result to reasonable CarrierVar structures.
|
||||
///
|
||||
/// Note: This is analysis-only testing. We're NOT modifying the actual lowering path yet.
|
||||
/// Full integration will come in Phase 60+ after validating the analysis is correct.
|
||||
#[test]
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
fn test_phase58_ownership_p2_comparison() {
|
||||
use nyash_rust::mir::join_ir::ownership::{plan_to_p2_inputs, OwnershipAnalyzer};
|
||||
use serde_json::json;
|
||||
|
||||
// Create a simple P2 fixture JSON (loop with i and sum)
|
||||
let json = json!({
|
||||
"functions": [{
|
||||
"name": "main",
|
||||
"params": [],
|
||||
"body": {
|
||||
"kind": "Block",
|
||||
"statements": [
|
||||
{"kind": "Local", "name": "sum", "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": 3}
|
||||
},
|
||||
"body": {
|
||||
"kind": "Block",
|
||||
"statements": [
|
||||
{
|
||||
"kind": "If",
|
||||
"condition": {
|
||||
"kind": "BinaryOp",
|
||||
"op": "Ge",
|
||||
"lhs": {"kind": "Var", "name": "i"},
|
||||
"rhs": {"kind": "Const", "value": 2}
|
||||
},
|
||||
"then": {"kind": "Break"}
|
||||
},
|
||||
{
|
||||
"kind": "Assign",
|
||||
"target": "sum",
|
||||
"value": {
|
||||
"kind": "BinaryOp",
|
||||
"op": "Add",
|
||||
"lhs": {"kind": "Var", "name": "sum"},
|
||||
"rhs": {"kind": "Var", "name": "i"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Assign",
|
||||
"target": "i",
|
||||
"value": {
|
||||
"kind": "BinaryOp",
|
||||
"op": "Add",
|
||||
"lhs": {"kind": "Var", "name": "i"},
|
||||
"rhs": {"kind": "Const", "value": 1}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
// Run ownership analyzer
|
||||
let mut analyzer = OwnershipAnalyzer::new();
|
||||
let plans = analyzer.analyze_json(&json).expect("analysis should succeed");
|
||||
|
||||
// Find loop plan (the one that has relay writes to function-owned sum/i)
|
||||
let loop_plan = plans
|
||||
.iter()
|
||||
.find(|p| !p.relay_writes.is_empty())
|
||||
.expect("should have a loop plan with relay writes");
|
||||
|
||||
eprintln!(
|
||||
"[phase58/test] Loop plan: relay_writes={:?}",
|
||||
loop_plan.relay_writes
|
||||
);
|
||||
|
||||
// Phase 58 Fail-Fast: relay_writes should be rejected
|
||||
let result = plan_to_p2_inputs(loop_plan, "i");
|
||||
assert!(
|
||||
result.is_err(),
|
||||
"Phase 58: relay_writes should be rejected (not supported yet)"
|
||||
);
|
||||
let err = result.unwrap_err();
|
||||
assert!(
|
||||
err.contains("relay_writes not yet supported"),
|
||||
"Error should mention relay limitation, got: {}",
|
||||
err
|
||||
);
|
||||
|
||||
eprintln!("[phase58/test] Phase 58 Fail-Fast verified: relay_writes correctly rejected");
|
||||
|
||||
// Also test the case where variables ARE owned by loop (future scenario)
|
||||
// This would work once we support loop-local carriers
|
||||
let loop_local_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": "Assign",
|
||||
"target": "sum",
|
||||
"value": {
|
||||
"kind": "BinaryOp",
|
||||
"op": "Add",
|
||||
"lhs": {"kind": "Var", "name": "sum"},
|
||||
"rhs": {"kind": "Var", "name": "i"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Assign",
|
||||
"target": "i",
|
||||
"value": {
|
||||
"kind": "BinaryOp",
|
||||
"op": "Add",
|
||||
"lhs": {"kind": "Var", "name": "i"},
|
||||
"rhs": {"kind": "Const", "value": 1}
|
||||
}
|
||||
},
|
||||
{"kind": "Break"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
let mut analyzer2 = OwnershipAnalyzer::new();
|
||||
let plans2 = analyzer2
|
||||
.analyze_json(&loop_local_json)
|
||||
.expect("loop-local analysis should succeed");
|
||||
|
||||
// Find loop plan (variables owned by loop, not function)
|
||||
let loop_plan2 = plans2
|
||||
.iter()
|
||||
.find(|p| !p.owned_vars.is_empty())
|
||||
.expect("should have a loop plan with owned vars");
|
||||
|
||||
eprintln!(
|
||||
"[phase58/test] Loop-local plan: owned_vars={:?}",
|
||||
loop_plan2
|
||||
.owned_vars
|
||||
.iter()
|
||||
.map(|v| &v.name)
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
// Convert to P2 inputs (should succeed - no relay)
|
||||
let inputs = plan_to_p2_inputs(loop_plan2, "i").expect("should convert successfully");
|
||||
|
||||
eprintln!("[phase58/test] P2 inputs: {:?}", inputs);
|
||||
|
||||
// Verify: i is skipped (loop var), sum becomes carrier
|
||||
assert_eq!(inputs.carriers.len(), 1, "Should have 1 carrier (sum)");
|
||||
assert_eq!(inputs.carriers[0].name, "sum");
|
||||
|
||||
eprintln!("[phase58/test] Phase 58 conversion verified: sum correctly extracted as carrier");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user