docs: Phase 190-impl-D complete - NumberAccumulation PHI wiring fixed

- Fixed ValueId collision between body-local and carrier params
- Added ExitLine contract verifier (debug assertions)
- Updated test files to use Main box
- E2E verified: atoi→12, parse_number→123

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-09 03:07:15 +09:00
parent f8d3fb08ba
commit 1af92d8aea
5 changed files with 94 additions and 12 deletions

View File

@ -145,8 +145,68 @@ impl ExitLineReconnector {
);
}
// Phase 190-impl-D-3: Contract verification (debug build only)
// Ensures all exit_bindings have corresponding entries in carrier_phis and variable_map
#[cfg(debug_assertions)]
Self::verify_exit_line_contract(boundary, carrier_phis, &builder.variable_map);
Ok(())
}
/// Phase 190-impl-D-3: Verify exit line contract (debug build only)
///
/// # Contract Requirements
///
/// 1. Every exit_binding must have a corresponding entry in carrier_phis
/// 2. Every exit_binding's carrier must exist in variable_map after reconnect
/// 3. The variable_map entry must point to the PHI dst (not the original host value)
///
/// # Panics
///
/// Panics if any contract violation is detected. This helps catch bugs where:
/// - PHI is missing for a carrier (Phase 190-impl-D root cause)
/// - variable_map update was skipped
/// - ValueId collision occurred
#[cfg(debug_assertions)]
fn verify_exit_line_contract(
boundary: &JoinInlineBoundary,
carrier_phis: &BTreeMap<String, ValueId>,
variable_map: &std::collections::HashMap<String, ValueId>,
) {
for binding in &boundary.exit_bindings {
// Contract 1: carrier_phis must contain this carrier
let phi_dst = carrier_phis.get(&binding.carrier_name);
if phi_dst.is_none() {
// Skip loop variable (it's handled separately in loop_header_phi)
// Only check carriers that have exit_bindings
eprintln!(
"[JoinIR/ExitLine/Contract] WARNING: Carrier '{}' has exit_binding but no PHI in carrier_phis",
binding.carrier_name
);
// Don't panic for now - loop variable might not be in carrier_phis
// Future: Distinguish loop_var from carriers in exit_bindings
}
// Contract 2: variable_map must contain this carrier after reconnect
let var_value = variable_map.get(&binding.carrier_name);
if var_value.is_none() {
panic!(
"[JoinIR/ExitLine/Contract] VIOLATION: Carrier '{}' missing from variable_map after reconnect",
binding.carrier_name
);
}
// Contract 3: variable_map entry should point to PHI dst (if PHI exists)
if let (Some(&phi), Some(&var)) = (phi_dst, var_value) {
if phi != var {
panic!(
"[JoinIR/ExitLine/Contract] VIOLATION: Carrier '{}' variable_map={:?} but PHI dst={:?} (mismatch!)",
binding.carrier_name, var, phi
);
}
}
}
}
}
#[cfg(test)]