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

@ -235,11 +235,16 @@
- AST 構造解析名前依存禁止、LHS 出現回数チェック
- Implementation phases 定義Phase 190-impl-A/B/C/D
- **次ステップ**: Phase 190-impl で実装(コード変更は別フェーズ)
- [ ] Phase 190-impl: NumberAccumulation 実装
- Phase 190-impl-A: Core Detection (LoopUpdateAnalyzer 拡張)
- Phase 190-impl-B: CarrierUpdateLowerer Extension (JoinIR emission)
- Phase 190-impl-C: Pattern2/4 Integration (can_lower 更新)
- Phase 190-impl-D: E2E Validation (JsonParserBox._atoi 動作確認)
- [x] **Phase 190-impl: NumberAccumulation 実装** ✅ (2025-12-09)
- Phase 190-impl-A: Core Detection (LoopUpdateAnalyzer 拡張)
- Phase 190-impl-B: CarrierUpdateLowerer Extension (JoinIR emission)
- Phase 190-impl-C: Pattern2/4 Integration (can_lower 更新)
- Phase 190-impl-D: E2E Validation + PHI 配線デバッグ ✅
- **バグ発見**: body-local と carrier の ValueId 衝突問題
- **修正**: `body_local_start_offset = env.len() + carrier_info.carriers.len()`
- **E2E 結果**: `phase190_atoi_impl.hako` → 12 ✅、`phase190_parse_number_impl.hako` → 123 ✅
- ExitLine contract Verifier 追加(`#[cfg(debug_assertions)]`
- **残課題**: body-local 変数 assignment は JoinIR 未対応Phase 186 line に残タスク)
- [ ] Phase 191: 複雑 NumberAccumulation 対応
- Complex addend 対応 (`v = v * 10 + f(x)`)
- Phase 189 キャリア検出ブロッカー解決

View File

@ -4,9 +4,16 @@
//
// Note: Phase 190-impl-D found that body-local variable support is incomplete.
// Using loop variable directly for now.
//
// Expected calculation:
// i=0: result = 0*10 + 0 = 0
// i=1: result = 0*10 + 1 = 1
// i=2: result = 1*10 + 2 = 12
// i=3: break (condition i >= 3)
// Expected result: 12
static box AtoiImpl {
method main() {
static box Main {
main() {
local result
result = 0
local i
@ -19,6 +26,7 @@ static box AtoiImpl {
result = result * 10 + i
i = i + 1
}
return result
print(result)
return 0
}
}

View File

@ -5,8 +5,8 @@
// Expected: i=1,2,3 → num = 0*10+1 = 1 → 1*10+2 = 12 → 12*10+3 = 123
// Result should be 123
static box ParseNumberImpl {
method main() {
static box Main {
main() {
local num
num = 0
local i
@ -19,6 +19,6 @@ static box ParseNumberImpl {
i = i + 1
}
print(num)
return num
return 0
}
}

View File

@ -970,4 +970,13 @@ Assignment {
## Revision History
- **2025-12-09**: Initial design (Section 1-12)
- **TBD**: Implementation review updates
- **2025-12-09**: Phase 190-impl 完了
- Phase 190-impl-A: LoopUpdateAnalyzer に NumberAccumulation 検出実装
- Phase 190-impl-B: CarrierUpdateLowerer で 2-instruction emission 実装
- Phase 190-impl-C: Pattern2 can_lower ホワイトリスト更新
- Phase 190-impl-D: E2E 検証成功 + PHI 配線修正
- **バグ発見**: body-local と carrier の ValueId 衝突問題
- **修正**: `body_local_start_offset = env.len() + carrier_info.carriers.len()` で安全な ValueId 空間分割
- **E2E 結果**: `phase190_atoi_impl.hako` → 12 ✅、`phase190_parse_number_impl.hako` → 123 ✅
- **制約**: body-local 変数 assignment は JoinIR 未対応Phase 186 残タスク)
- ExitLine contract Verifier 追加(`#[cfg(debug_assertions)]`

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)]