From 4f3831c07b2e7dffe9a18b9e2295e88d6ddc8da1 Mon Sep 17 00:00:00 2001 From: nyash-codex Date: Mon, 17 Nov 2025 17:31:09 +0900 Subject: [PATCH] =?UTF-8?q?fix(builder):=20=E4=BF=AE=E6=AD=A3=E6=A1=88A?= =?UTF-8?q?=E5=AE=9F=E8=A3=85=20-=20emit=5Funified=5Fcall=E2=86=94emit=5Fb?= =?UTF-8?q?ox=5For=5Fplugin=5Fcall=E5=86=8D=E5=85=A5=E9=98=B2=E6=AD=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🎯 無限再帰の構造的防止(修正案A採用) ## 問題 Phase 2リファクタリング後、stack overflow発生: ``` emit_unified_call (emit.rs:15) ↓ emit_box_or_plugin_call (utils.rs:136) ↓ line 190 emit_unified_call (emit.rs:15) ← 無限ループ! ``` ## 修正案A: 再入防止ガード(採用理由) - B(Math機能削除): 対処療法で仕様削減 ❌ - C("birth"特別扱い): 局所的修正で他Boxに波及 ❌ - A(構造的再入防止): 根治的アプローチ ✅ ## 実装内容 ### 1. MirBuilder にフラグ追加 ```rust pub(super) in_unified_boxcall_fallback: bool ``` 役割: RouterPolicyでRoute::BoxCallと決めたフォールバック中マーク ### 2. emit_unified_call 側修正 (emit.rs) ```rust // Route::BoxCall のときだけ self.in_unified_boxcall_fallback = true; emit_box_or_plugin_call(...); self.in_unified_boxcall_fallback = false; ``` ### 3. emit_box_or_plugin_call 側修正 (utils.rs) ```rust if use_unified_env && matches!(route, Route::Unified) && !self.in_unified_boxcall_fallback // ← 追加 { // emit_unified_call(...) への再入を防止 } ``` ## 構造的改善 - RouterPolicyBox の決定を優先 - emit_unified_call → emit_box_or_plugin_call の一方向化 - 「上位の決定を尊重する」という明確なルール ## 残存課題 ⚠️ まだstack overflowが残存(別の再帰ルート存在の可能性) → 次のステップでstack trace解析が必要 ## テスト状況 - Test 1 (Direct VM): ✅ 成功 - Test 2 (Stage-B): ❌ stack overflow(別ルート調査中) - Test 3 (MIR verification): ✅ 成功 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/mir/builder.rs | 10 ++++++++++ src/mir/builder/calls/emit.rs | 11 ++++++++++- src/mir/builder/utils.rs | 9 ++++++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/mir/builder.rs b/src/mir/builder.rs index e8785e8d..4e53097d 100644 --- a/src/mir/builder.rs +++ b/src/mir/builder.rs @@ -180,6 +180,14 @@ pub struct MirBuilder { pub(super) local_ssa_map: HashMap<(BasicBlockId, ValueId, u8), ValueId>, /// BlockSchedule cache: deduplicate materialize copies per (bb, src) pub(super) schedule_mat_map: HashMap<(BasicBlockId, ValueId), ValueId>, + + /// Guard flag to prevent re-entering emit_unified_call from BoxCall fallback. + /// Used when RouterPolicyBox in emit_unified_call has already decided to + /// route a given Method call to BoxCall; emit_box_or_plugin_call must not + /// bounce back into the unified path for the same call, otherwise an + /// infinite recursion (emit_unified_call → emit_box_or_plugin_call → + /// emit_unified_call …) can occur when routing decisions disagree. + pub(super) in_unified_boxcall_fallback: bool, } impl MirBuilder { @@ -232,6 +240,8 @@ impl MirBuilder { local_ssa_map: HashMap::new(), schedule_mat_map: HashMap::new(), + + in_unified_boxcall_fallback: false, } } diff --git a/src/mir/builder/calls/emit.rs b/src/mir/builder/calls/emit.rs index b63b580c..f935a6fe 100644 --- a/src/mir/builder/calls/emit.rs +++ b/src/mir/builder/calls/emit.rs @@ -108,7 +108,16 @@ impl MirBuilder { eprintln!("[router-guard] {}.{} → BoxCall fallback (recv=%{})", box_name, method, r.0); } let effects = EffectMask::READ.add(Effect::ReadHeap); - return self.emit_box_or_plugin_call(dst, *r, method.clone(), None, args, effects); + // Prevent BoxCall helper from bouncing back into emit_unified_call + // for the same call. RouterPolicyBox has already decided on + // Route::BoxCall for this callee, so emit_box_or_plugin_call + // must not re-enter the unified path even if its own heuristics + // would otherwise choose Unified. + let prev_flag = self.in_unified_boxcall_fallback; + self.in_unified_boxcall_fallback = true; + let res = self.emit_box_or_plugin_call(dst, *r, method.clone(), None, args, effects); + self.in_unified_boxcall_fallback = prev_flag; + return res; } } diff --git a/src/mir/builder/utils.rs b/src/mir/builder/utils.rs index d11e4978..99abb223 100644 --- a/src/mir/builder/utils.rs +++ b/src/mir/builder/utils.rs @@ -181,7 +181,14 @@ impl super::MirBuilder { ); } } - if use_unified_env && matches!(route, crate::mir::builder::router::policy::Route::Unified) { + // Unified path from BoxCall helper is only allowed when we are not + // already in a BoxCall fallback originating from emit_unified_call. + // in_unified_boxcall_fallback is set by emit_unified_call's RouterPolicy + // guard when it has already decided that this call must be a BoxCall. + if use_unified_env + && matches!(route, crate::mir::builder::router::policy::Route::Unified) + && !self.in_unified_boxcall_fallback + { let target = super::builder_calls::CallTarget::Method { box_type, method: method.clone(),