diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index b7bb7e78..271cc660 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -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 キャリア検出ブロッカー解決 diff --git a/apps/tests/phase190_atoi_impl.hako b/apps/tests/phase190_atoi_impl.hako index 853493fa..198c1609 100644 --- a/apps/tests/phase190_atoi_impl.hako +++ b/apps/tests/phase190_atoi_impl.hako @@ -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 } } diff --git a/apps/tests/phase190_parse_number_impl.hako b/apps/tests/phase190_parse_number_impl.hako index 279882ba..1b95c1d0 100644 --- a/apps/tests/phase190_parse_number_impl.hako +++ b/apps/tests/phase190_parse_number_impl.hako @@ -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 } } diff --git a/docs/development/current/main/phase190-number-update-design.md b/docs/development/current/main/phase190-number-update-design.md index e5a379bd..afc721e0 100644 --- a/docs/development/current/main/phase190-number-update-design.md +++ b/docs/development/current/main/phase190-number-update-design.md @@ -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)]`) diff --git a/src/mir/builder/control_flow/joinir/merge/exit_line/reconnector.rs b/src/mir/builder/control_flow/joinir/merge/exit_line/reconnector.rs index 6297b0cf..4bda76af 100644 --- a/src/mir/builder/control_flow/joinir/merge/exit_line/reconnector.rs +++ b/src/mir/builder/control_flow/joinir/merge/exit_line/reconnector.rs @@ -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, + variable_map: &std::collections::HashMap, + ) { + 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)]