Phase 61: structural if-sum+break lowering (dev-only)

This commit is contained in:
nyash-codex
2025-12-12 22:15:41 +09:00
parent 6aba138950
commit acb6720d9b
13 changed files with 1140 additions and 39 deletions

View File

@ -638,7 +638,7 @@ fn normalized_selfhost_if_sum_p3_vm_bridge_direct_matches_structured() {
let _ctx = normalized_dev_test_ctx();
let structured = build_selfhost_if_sum_p3_structured_for_normalized_dev();
let entry = structured.entry.expect("structured entry required");
let cases = [0, 1, 3, 4];
let cases = [0, 1, 3, 4, 5];
for n in cases {
let input = [JoinValue::Int(n)];
@ -646,6 +646,7 @@ fn normalized_selfhost_if_sum_p3_vm_bridge_direct_matches_structured() {
let dev = run_joinir_vm_bridge(&structured, entry, &input, true);
assert_eq!(base, dev, "vm bridge mismatch for selfhost_if_sum_p3 n={}", n);
assert_eq!(dev, JoinValue::Int(expected_selfhost_if_sum_p3(n)));
}
}
@ -654,7 +655,7 @@ fn normalized_selfhost_if_sum_p3_ext_vm_bridge_direct_matches_structured() {
let _ctx = normalized_dev_test_ctx();
let structured = build_selfhost_if_sum_p3_ext_structured_for_normalized_dev();
let entry = structured.entry.expect("structured entry required");
let cases = [0, 1, 3, 4];
let cases = [0, 1, 3, 4, 5];
for n in cases {
let input = [JoinValue::Int(n)];
@ -662,9 +663,30 @@ fn normalized_selfhost_if_sum_p3_ext_vm_bridge_direct_matches_structured() {
let dev = run_joinir_vm_bridge(&structured, entry, &input, true);
assert_eq!(base, dev, "vm bridge mismatch for selfhost_if_sum_p3_ext n={}", n);
assert_eq!(dev, JoinValue::Int(expected_selfhost_if_sum_p3_ext(n)));
}
}
fn expected_selfhost_if_sum_p3(n: i64) -> i64 {
if n <= 1 {
return 0;
}
let sum = (n - 1) * n / 2;
let count = n - 1;
sum + count
}
fn expected_selfhost_if_sum_p3_ext(n: i64) -> i64 {
if n <= 0 {
return 0;
}
// i=0: sum += 1
// i=1..n-1: sum += i, count += 1
let sum = 1 + (n - 1) * n / 2;
let count = n - 1;
sum + count
}
/// Phase 53: selfhost args-parse P2 (practical variation with string carrier)
#[test]
fn normalized_selfhost_args_parse_p2_vm_bridge_direct_matches_structured() {
@ -1067,17 +1089,17 @@ fn test_phase54_structural_axis_discrimination_p3() {
}
}
/// Phase 58: Compare ownership-based P2 lowering with existing path
/// Phase 60: Ownership relay threading helpers for P2 (analysis + contract)
///
/// 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.
/// plan_to_p2_inputs remains Fail-Fast on relay_writes (legacy contract),
/// while plan_to_p2_inputs_with_relay accepts single-hop relay and promotes them
/// to carriers (dev-only).
#[test]
#[cfg(feature = "normalized_dev")]
fn test_phase58_ownership_p2_comparison() {
use nyash_rust::mir::join_ir::ownership::{plan_to_p2_inputs, OwnershipAnalyzer};
fn test_phase60_ownership_p2_with_relay_conversion() {
use nyash_rust::mir::join_ir::ownership::{
plan_to_p2_inputs, plan_to_p2_inputs_with_relay, OwnershipAnalyzer,
};
use serde_json::json;
// Create a simple P2 fixture JSON (loop with i and sum)
@ -1154,11 +1176,11 @@ fn test_phase58_ownership_p2_comparison() {
loop_plan.relay_writes
);
// Phase 58 Fail-Fast: relay_writes should be rejected
// Legacy 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)"
"Legacy contract: relay_writes should be rejected"
);
let err = result.unwrap_err();
assert!(
@ -1167,7 +1189,22 @@ fn test_phase58_ownership_p2_comparison() {
err
);
eprintln!("[phase58/test] Phase 58 Fail-Fast verified: relay_writes correctly rejected");
// Phase 60 dev-only: with_relay should accept and include relay vars as carriers
let inputs_with_relay =
plan_to_p2_inputs_with_relay(loop_plan, "i").expect("with_relay should accept");
assert!(
inputs_with_relay.carriers.iter().any(|c| c.name == "sum"),
"relay carrier sum should be promoted"
);
eprintln!(
"[phase60/test] with_relay carriers={:?}",
inputs_with_relay
.carriers
.iter()
.map(|c| &c.name)
.collect::<Vec<_>>()
);
// Also test the case where variables ARE owned by loop (future scenario)
// This would work once we support loop-local carriers
@ -1239,7 +1276,41 @@ fn test_phase58_ownership_p2_comparison() {
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");
eprintln!("[phase60/test] Loop-local conversion verified");
}
/// Phase 60: P2 dev-only ownership relay route matches legacy Break lowering.
#[test]
#[cfg(feature = "normalized_dev")]
fn test_phase60_break_lowering_ownership_matches_legacy() {
use nyash_rust::mir::join_ir::frontend::ast_lowerer::lower_break_legacy_for_comparison;
use nyash_rust::mir::join_ir::frontend::ast_lowerer::AstToJoinIrLowerer;
let _ctx = normalized_dev_test_ctx();
let program_json: serde_json::Value = serde_json::from_str(include_str!(
"../docs/private/roadmap2/phases/phase-34-joinir-frontend/fixtures/loop_frontend_break.program.json"
))
.expect("fixture json");
let mut lowerer_new = AstToJoinIrLowerer::new();
let structured_new = lowerer_new.lower_program_json(&program_json);
let mut lowerer_old = AstToJoinIrLowerer::new();
let structured_old =
lower_break_legacy_for_comparison(&mut lowerer_old, &program_json);
let entry_new = structured_new.entry.expect("new entry");
let entry_old = structured_old.entry.expect("old entry");
let input = vec![JoinValue::Int(5)];
let out_old = run_joinir_vm_bridge(&structured_old, entry_old, &input, false);
let out_new = run_joinir_vm_bridge(&structured_new, entry_new, &input, false);
assert_eq!(
out_old, out_new,
"ownership relay dev route must match legacy output"
);
}
/// Phase 59: P3 with outer-owned carriers (relay case) should fail-fast
@ -1412,3 +1483,40 @@ fn test_phase59_ownership_p3_loop_local_success() {
eprintln!("[phase59/test] P3 loop-local conversion verified: sum and count correctly extracted as carriers");
}
/// Phase 60: Program(JSON v0) fixture (selfhost_if_sum_p3) should produce relay_writes and convert with single-hop relay.
#[test]
#[cfg(feature = "normalized_dev")]
fn test_phase60_ownership_p3_program_json_fixture_with_relay() {
use nyash_rust::mir::join_ir::ownership::{plan_to_p3_inputs_with_relay, OwnershipAnalyzer};
let program_json: serde_json::Value = serde_json::from_str(include_str!(
"../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_if_sum_p3.program.json"
))
.expect("fixture json");
let mut analyzer = OwnershipAnalyzer::new();
let plans = analyzer
.analyze_json(&program_json)
.expect("Program(JSON v0) analysis should succeed");
let loop_plan = plans
.iter()
.find(|p| !p.relay_writes.is_empty())
.expect("expected a loop plan with relay_writes");
// i/sum/count are defined outside the loop but updated in the loop body -> relay_writes
assert!(loop_plan.relay_writes.iter().any(|r| r.name == "i"));
assert!(loop_plan.relay_writes.iter().any(|r| r.name == "sum"));
assert!(loop_plan.relay_writes.iter().any(|r| r.name == "count"));
let inputs = plan_to_p3_inputs_with_relay(loop_plan, "i").expect("with_relay should succeed");
let mut carriers: Vec<&str> = inputs.carriers.iter().map(|c| c.name.as_str()).collect();
carriers.sort();
assert_eq!(carriers, vec!["count", "sum"]);
// n is read-only in loop condition -> capture + condition_capture
assert!(inputs.captures.iter().any(|n| n == "n"));
assert!(inputs.condition_captures.iter().any(|n| n == "n"));
}