refactor(builder): Phase 3-A - UnifiedCallEmitterBox実装完了
箱理論の実践:統一Call発行ロジックを独立した箱に集約
- 単一責務:統一Call発行のみ(Legacy Callは別モジュール)
- 状態レス:MirBuilderを引数で受け取る設計
- ピュア関数的:入力CallTarget → 解決・発行 → MirCall命令
実装内容:
1. 新規ファイル作成
- src/mir/builder/calls/unified_emitter.rs (~250行)
- UnifiedCallEmitterBox構造体
- 4つの主要メソッド:
* emit_unified_call (公開API)
* emit_unified_call_impl (コア実装)
* emit_global_unified (Global関数呼び出し)
* emit_value_unified (第一級関数呼び出し)
2. emit.rs からロジック移動
- emit_unified_call → 委譲に変更(1行)
- emit_unified_call_impl → 削除(~150行削減)
- emit_global_unified → 削除(委譲に変更)
- emit_value_unified → 削除(委譲に変更)
- try_global_fallback_handlers → pub(super)に変更
- materialize_receiver_in_callee → pub(super)に変更
3. mod.rs更新
- unified_emitter モジュール追加
箱化効果(Phase 3-A単独):
- emit.rs: 467行 → 261行(-206行、44%削減!)
- unified_emitter.rs: 250行(新規、Unified専用箱)
- 読みやすさ大幅向上:統一Call発行ロジックが独立
- 責務分離明確化:Legacy/Unifiedの完全分離
Phase 3 進捗:
- Phase 3-A: UnifiedCallEmitterBox ✅ 完了(本コミット)
- Phase 3-B: EffectsAnalyzerBox ⏳ 次の目標
- Phase 3-C: CallMaterializerBox ⏳ 最終目標
ビルド・テスト:
- cargo build --release: ✅ 成功
- 既存機能互換性: ✅ 完全保持
This commit is contained in:
@ -10,183 +10,17 @@ use crate::mir::definitions::call_unified::Callee;
|
||||
use super::{CallTarget, call_unified};
|
||||
|
||||
impl MirBuilder {
|
||||
/// Unified call emission - replaces all emit_*_call methods
|
||||
/// ChatGPT5 Pro A++ design for complete call unification
|
||||
/// Unified call emission - delegates to UnifiedCallEmitterBox
|
||||
/// 箱理論: 統一Call発行ロジックを unified_emitter.rs に集約
|
||||
pub fn emit_unified_call(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
target: CallTarget,
|
||||
args: Vec<ValueId>,
|
||||
) -> Result<(), String> {
|
||||
// Debug: Check recursion depth
|
||||
const MAX_EMIT_DEPTH: usize = 100;
|
||||
self.recursion_depth += 1;
|
||||
if self.recursion_depth > MAX_EMIT_DEPTH {
|
||||
eprintln!("[FATAL] emit_unified_call recursion depth exceeded {}", MAX_EMIT_DEPTH);
|
||||
eprintln!("[FATAL] Current depth: {}", self.recursion_depth);
|
||||
eprintln!("[FATAL] Target: {:?}", target);
|
||||
return Err(format!("emit_unified_call recursion depth exceeded: {}", self.recursion_depth));
|
||||
}
|
||||
|
||||
// Check environment variable for unified call usage
|
||||
let result = if !call_unified::is_unified_call_enabled() {
|
||||
// Fall back to legacy implementation
|
||||
self.emit_legacy_call(dst, target, args)
|
||||
} else {
|
||||
self.emit_unified_call_impl(dst, target, args)
|
||||
};
|
||||
self.recursion_depth -= 1;
|
||||
result
|
||||
super::unified_emitter::UnifiedCallEmitterBox::emit_unified_call(self, dst, target, args)
|
||||
}
|
||||
|
||||
fn emit_unified_call_impl(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
target: CallTarget,
|
||||
args: Vec<ValueId>,
|
||||
) -> Result<(), String> {
|
||||
|
||||
// Emit resolve.try for method targets (dev-only; default OFF)
|
||||
let arity_for_try = args.len();
|
||||
if let CallTarget::Method { ref box_type, ref method, receiver } = target {
|
||||
let recv_cls = box_type.clone()
|
||||
.or_else(|| self.value_origin_newbox.get(&receiver).cloned())
|
||||
.unwrap_or_default();
|
||||
// Use indexed candidate lookup (tail → names)
|
||||
let candidates: Vec<String> = self.method_candidates(method, arity_for_try);
|
||||
let meta = serde_json::json!({
|
||||
"recv_cls": recv_cls,
|
||||
"method": method,
|
||||
"arity": arity_for_try,
|
||||
"candidates": candidates,
|
||||
});
|
||||
super::super::observe::resolve::emit_try(self, meta);
|
||||
}
|
||||
|
||||
// Centralized user-box rewrite for method targets (toString/stringify, equals/1, Known→unique)
|
||||
if let CallTarget::Method { ref box_type, ref method, receiver } = target {
|
||||
let class_name_opt = box_type.clone()
|
||||
.or_else(|| self.value_origin_newbox.get(&receiver).cloned())
|
||||
.or_else(|| self.value_types.get(&receiver).and_then(|t| if let super::super::MirType::Box(b) = t { Some(b.clone()) } else { None }));
|
||||
// Early str-like
|
||||
if let Some(res) = crate::mir::builder::rewrite::special::try_early_str_like_to_dst(
|
||||
self, dst, receiver, &class_name_opt, method, args.len(),
|
||||
) { res?; return Ok(()); }
|
||||
// equals/1
|
||||
if let Some(res) = crate::mir::builder::rewrite::special::try_special_equals_to_dst(
|
||||
self, dst, receiver, &class_name_opt, method, args.clone(),
|
||||
) { res?; return Ok(()); }
|
||||
// Known or unique
|
||||
if let Some(res) = crate::mir::builder::rewrite::known::try_known_or_unique_to_dst(
|
||||
self, dst, receiver, &class_name_opt, method, args.clone(),
|
||||
) { res?; return Ok(()); }
|
||||
}
|
||||
|
||||
// Convert CallTarget to Callee using CalleeResolverBox
|
||||
if let CallTarget::Global(ref _n) = target { /* dev trace removed */ }
|
||||
// Fallback: if Global target is unknown, try unique static-method mapping (name/arity)
|
||||
let resolver = super::resolver::CalleeResolverBox::new(
|
||||
&self.value_origin_newbox,
|
||||
&self.value_types,
|
||||
Some(&self.type_registry), // 🎯 TypeRegistry を渡す
|
||||
);
|
||||
let mut callee = match resolver.resolve(target.clone()) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
if let CallTarget::Global(ref name) = target {
|
||||
// Try fallback handlers
|
||||
if let Some(result) = self.try_global_fallback_handlers(dst, name, &args)? {
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
// Safety: ensure receiver is materialized even after callee conversion
|
||||
callee = self.materialize_receiver_in_callee(callee)?;
|
||||
|
||||
// Structural guard: prevent static compiler boxes from being called with runtime receivers
|
||||
// 箱理論: CalleeGuardBox による構造的分離
|
||||
let guard = super::guard::CalleeGuardBox::new(&self.value_types);
|
||||
callee = guard.apply_static_runtime_guard(callee)?;
|
||||
|
||||
// Emit resolve.choose for method callee (dev-only; default OFF)
|
||||
if let Callee::Method { box_name, method, certainty, .. } = &callee {
|
||||
let chosen = format!("{}.{}{}", box_name, method, format!("/{}", arity_for_try));
|
||||
let meta = serde_json::json!({
|
||||
"recv_cls": box_name,
|
||||
"method": method,
|
||||
"arity": arity_for_try,
|
||||
"chosen": chosen,
|
||||
"certainty": format!("{:?}", certainty),
|
||||
"reason": "unified",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(self, meta);
|
||||
}
|
||||
|
||||
// Validate call arguments
|
||||
// 箱理論: CalleeResolverBox で引数検証
|
||||
let resolver = super::resolver::CalleeResolverBox::new(
|
||||
&self.value_origin_newbox,
|
||||
&self.value_types,
|
||||
Some(&self.type_registry),
|
||||
);
|
||||
resolver.validate_args(&callee, &args)?;
|
||||
|
||||
// Stability guard: decide route via RouterPolicyBox (behavior-preserving rules)
|
||||
if let Callee::Method { box_name, method, receiver: Some(r), certainty, .. } = &callee {
|
||||
let route = crate::mir::builder::router::policy::choose_route(box_name, method, *certainty, arity_for_try);
|
||||
if let crate::mir::builder::router::policy::Route::BoxCall = route {
|
||||
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[router-guard] {}.{} → BoxCall fallback (recv=%{})", box_name, method, r.0);
|
||||
}
|
||||
let effects = EffectMask::READ.add(Effect::ReadHeap);
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize operands in current block (EmitGuardBox wrapper)
|
||||
let mut callee = callee;
|
||||
let mut args_local: Vec<ValueId> = args;
|
||||
crate::mir::builder::emit_guard::finalize_call_operands(self, &mut callee, &mut args_local);
|
||||
|
||||
// Create MirCall instruction using the new module (pure data composition)
|
||||
let mir_call = call_unified::create_mir_call(dst, callee.clone(), args_local.clone());
|
||||
|
||||
// Dev trace: show final callee/recv right before emission (guarded)
|
||||
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") || super::super::utils::builder_debug_enabled() {
|
||||
if let Callee::Method { method, receiver, box_name, .. } = &callee {
|
||||
if let Some(r) = receiver {
|
||||
eprintln!("[vm-call-final] bb={:?} method={} recv=%{} class={}",
|
||||
self.current_block, method, r.0, box_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For Phase 2: Convert to legacy Call instruction with new callee field (use finalized operands)
|
||||
let legacy_call = MirInstruction::Call {
|
||||
dst: mir_call.dst,
|
||||
func: ValueId::new(0), // Dummy value for legacy compatibility
|
||||
callee: Some(callee),
|
||||
args: args_local,
|
||||
effects: mir_call.effects,
|
||||
};
|
||||
|
||||
let res = self.emit_instruction(legacy_call);
|
||||
// Dev-only: verify block schedule invariants after emitting call
|
||||
crate::mir::builder::emit_guard::verify_after_call(self);
|
||||
res
|
||||
}
|
||||
|
||||
/// Legacy call fallback - preserves existing behavior
|
||||
pub fn emit_legacy_call(
|
||||
@ -237,10 +71,10 @@ impl MirBuilder {
|
||||
})
|
||||
},
|
||||
CallTarget::Global(name) => {
|
||||
self.emit_global_unified(dst, name, args)
|
||||
super::unified_emitter::UnifiedCallEmitterBox::emit_global_unified(self, dst, name, args)
|
||||
},
|
||||
CallTarget::Value(func_val) => {
|
||||
self.emit_value_unified(dst, func_val, args)
|
||||
super::unified_emitter::UnifiedCallEmitterBox::emit_value_unified(self, dst, func_val, args)
|
||||
},
|
||||
CallTarget::Closure { params, captures, me_capture } => {
|
||||
let dst = dst.ok_or("Closure creation must have destination")?;
|
||||
@ -305,7 +139,7 @@ impl MirBuilder {
|
||||
// ========================================
|
||||
|
||||
/// Try fallback handlers for global functions
|
||||
fn try_global_fallback_handlers(
|
||||
pub(super) fn try_global_fallback_handlers(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
name: &str,
|
||||
@ -374,8 +208,8 @@ impl MirBuilder {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Ensure receiver is materialized in Callee::Method
|
||||
fn materialize_receiver_in_callee(
|
||||
/// Ensure receiver is materialized in Callee::Method (pub for UnifiedCallEmitterBox)
|
||||
pub(super) fn materialize_receiver_in_callee(
|
||||
&mut self,
|
||||
callee: Callee,
|
||||
) -> Result<Callee, String> {
|
||||
@ -419,49 +253,9 @@ impl MirBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit global call with name constant
|
||||
fn emit_global_unified(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
name: String,
|
||||
args: Vec<ValueId>,
|
||||
) -> Result<(), String> {
|
||||
// Create a string constant for the function name via NameConstBox
|
||||
let name_const = crate::mir::builder::name_const::make_name_const_result(self, &name)?;
|
||||
// Allocate a destination if not provided so we can annotate it
|
||||
let actual_dst = if let Some(d) = dst { d } else { self.next_value_id() };
|
||||
let mut args = args;
|
||||
crate::mir::builder::ssa::local::finalize_args(self, &mut args);
|
||||
self.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(actual_dst),
|
||||
func: name_const,
|
||||
callee: Some(Callee::Global(name.clone())),
|
||||
args,
|
||||
effects: EffectMask::IO,
|
||||
})?;
|
||||
// Annotate from module signature (if present)
|
||||
self.annotate_call_result_from_func_name(actual_dst, name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Emit value call (first-class function)
|
||||
fn emit_value_unified(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
func_val: ValueId,
|
||||
args: Vec<ValueId>,
|
||||
) -> Result<(), String> {
|
||||
let mut args = args;
|
||||
crate::mir::builder::ssa::local::finalize_args(self, &mut args);
|
||||
self.emit_instruction(MirInstruction::Call {
|
||||
dst,
|
||||
func: func_val,
|
||||
callee: Some(Callee::Value(func_val)),
|
||||
args,
|
||||
effects: EffectMask::IO,
|
||||
})
|
||||
}
|
||||
|
||||
// ✅ 箱化完了: apply_static_runtime_guard → CalleeGuardBox::apply_static_runtime_guard
|
||||
// 構造ガードロジックは src/mir/builder/calls/guard.rs に移動済み
|
||||
// ✅ 箱化完了:
|
||||
// - emit_unified_call_impl → UnifiedCallEmitterBox::emit_unified_call_impl (unified_emitter.rs)
|
||||
// - emit_global_unified → UnifiedCallEmitterBox::emit_global_unified (unified_emitter.rs)
|
||||
// - emit_value_unified → UnifiedCallEmitterBox::emit_value_unified (unified_emitter.rs)
|
||||
// - apply_static_runtime_guard → CalleeGuardBox::apply_static_runtime_guard (guard.rs)
|
||||
}
|
||||
|
||||
@ -16,13 +16,14 @@ pub mod function_lowering;
|
||||
pub mod method_resolution;
|
||||
pub mod special_handlers;
|
||||
|
||||
// New refactored modules (Box Theory Phase 1 & 2 & 25.1d)
|
||||
// New refactored modules (Box Theory Phase 1 & 2 & 25.1d & Phase 3)
|
||||
pub mod lowering;
|
||||
pub mod utils;
|
||||
pub mod emit; // Phase 2: Call emission
|
||||
pub mod build; // Phase 2: Call building
|
||||
pub mod guard; // Phase 25.1d: Structural guard (static/runtime box separation)
|
||||
pub mod resolver; // Phase 25.1d: Callee resolution (CallTarget → Callee)
|
||||
pub mod emit; // Phase 2: Call emission
|
||||
pub mod build; // Phase 2: Call building
|
||||
pub mod guard; // Phase 25.1d: Structural guard (static/runtime box separation)
|
||||
pub mod resolver; // Phase 25.1d: Callee resolution (CallTarget → Callee)
|
||||
pub mod unified_emitter; // Phase 3-A: Unified call emitter (統一Call発行専用箱)
|
||||
|
||||
// Re-export public interfaces
|
||||
pub use call_target::CallTarget;
|
||||
|
||||
250
src/mir/builder/calls/unified_emitter.rs
Normal file
250
src/mir/builder/calls/unified_emitter.rs
Normal file
@ -0,0 +1,250 @@
|
||||
/*!
|
||||
* UnifiedCallEmitterBox - 統一Call発行専用箱
|
||||
*
|
||||
* 箱理論の実践:
|
||||
* - 箱にする: 統一Call発行ロジックを1箱に集約
|
||||
* - 境界を作る: Legacy/Unifiedの明確な分離
|
||||
* - 状態最小: MirBuilderを引数として受け取る(所有しない)
|
||||
*
|
||||
* 責務:
|
||||
* - emit_unified_call: 統一Call発行の公開API
|
||||
* - emit_unified_call_impl: コア実装(CallTarget → MirCall変換)
|
||||
* - emit_global_unified: Global関数呼び出し
|
||||
* - emit_value_unified: 第一級関数呼び出し
|
||||
*/
|
||||
|
||||
use crate::mir::builder::{MirBuilder, ValueId, MirInstruction, Effect, EffectMask};
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
use super::CallTarget;
|
||||
use super::call_unified;
|
||||
|
||||
/// 統一Call発行専用箱
|
||||
///
|
||||
/// 箱理論:
|
||||
/// - 単一責務: 統一Call発行のみ(Legacy Callは別モジュール)
|
||||
/// - 状態レス: MirBuilderを引数で受け取る設計
|
||||
/// - ピュア関数的: 入力CallTarget → 解決・発行 → MirCall命令
|
||||
pub struct UnifiedCallEmitterBox;
|
||||
|
||||
impl UnifiedCallEmitterBox {
|
||||
/// Unified call emission - replaces all emit_*_call methods
|
||||
/// ChatGPT5 Pro A++ design for complete call unification
|
||||
pub fn emit_unified_call(
|
||||
builder: &mut MirBuilder,
|
||||
dst: Option<ValueId>,
|
||||
target: CallTarget,
|
||||
args: Vec<ValueId>,
|
||||
) -> Result<(), String> {
|
||||
// Debug: Check recursion depth
|
||||
const MAX_EMIT_DEPTH: usize = 100;
|
||||
builder.recursion_depth += 1;
|
||||
if builder.recursion_depth > MAX_EMIT_DEPTH {
|
||||
eprintln!("[FATAL] emit_unified_call recursion depth exceeded {}", MAX_EMIT_DEPTH);
|
||||
eprintln!("[FATAL] Current depth: {}", builder.recursion_depth);
|
||||
eprintln!("[FATAL] Target: {:?}", target);
|
||||
return Err(format!("emit_unified_call recursion depth exceeded: {}", builder.recursion_depth));
|
||||
}
|
||||
|
||||
// Check environment variable for unified call usage
|
||||
let result = if !call_unified::is_unified_call_enabled() {
|
||||
// Fall back to legacy implementation
|
||||
builder.emit_legacy_call(dst, target, args)
|
||||
} else {
|
||||
Self::emit_unified_call_impl(builder, dst, target, args)
|
||||
};
|
||||
builder.recursion_depth -= 1;
|
||||
result
|
||||
}
|
||||
|
||||
fn emit_unified_call_impl(
|
||||
builder: &mut MirBuilder,
|
||||
dst: Option<ValueId>,
|
||||
target: CallTarget,
|
||||
args: Vec<ValueId>,
|
||||
) -> Result<(), String> {
|
||||
|
||||
// Emit resolve.try for method targets (dev-only; default OFF)
|
||||
let arity_for_try = args.len();
|
||||
if let CallTarget::Method { ref box_type, ref method, receiver } = target {
|
||||
let recv_cls = box_type.clone()
|
||||
.or_else(|| builder.value_origin_newbox.get(&receiver).cloned())
|
||||
.unwrap_or_default();
|
||||
// Use indexed candidate lookup (tail → names)
|
||||
let candidates: Vec<String> = builder.method_candidates(method, arity_for_try);
|
||||
let meta = serde_json::json!({
|
||||
"recv_cls": recv_cls,
|
||||
"method": method,
|
||||
"arity": arity_for_try,
|
||||
"candidates": candidates,
|
||||
});
|
||||
crate::mir::builder::observe::resolve::emit_try(builder, meta);
|
||||
}
|
||||
|
||||
// Centralized user-box rewrite for method targets (toString/stringify, equals/1, Known→unique)
|
||||
if let CallTarget::Method { ref box_type, ref method, receiver } = target {
|
||||
let class_name_opt = box_type.clone()
|
||||
.or_else(|| builder.value_origin_newbox.get(&receiver).cloned())
|
||||
.or_else(|| builder.value_types.get(&receiver).and_then(|t| if let crate::mir::MirType::Box(b) = t { Some(b.clone()) } else { None }));
|
||||
// Early str-like
|
||||
if let Some(res) = crate::mir::builder::rewrite::special::try_early_str_like_to_dst(
|
||||
builder, dst, receiver, &class_name_opt, method, args.len(),
|
||||
) { res?; return Ok(()); }
|
||||
// equals/1
|
||||
if let Some(res) = crate::mir::builder::rewrite::special::try_special_equals_to_dst(
|
||||
builder, dst, receiver, &class_name_opt, method, args.clone(),
|
||||
) { res?; return Ok(()); }
|
||||
// Known or unique
|
||||
if let Some(res) = crate::mir::builder::rewrite::known::try_known_or_unique_to_dst(
|
||||
builder, dst, receiver, &class_name_opt, method, args.clone(),
|
||||
) { res?; return Ok(()); }
|
||||
}
|
||||
|
||||
// Convert CallTarget to Callee using CalleeResolverBox
|
||||
if let CallTarget::Global(ref _n) = target { /* dev trace removed */ }
|
||||
// Fallback: if Global target is unknown, try unique static-method mapping (name/arity)
|
||||
let resolver = super::resolver::CalleeResolverBox::new(
|
||||
&builder.value_origin_newbox,
|
||||
&builder.value_types,
|
||||
Some(&builder.type_registry), // 🎯 TypeRegistry を渡す
|
||||
);
|
||||
let mut callee = match resolver.resolve(target.clone()) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
if let CallTarget::Global(ref name) = target {
|
||||
// Try fallback handlers
|
||||
if let Some(result) = builder.try_global_fallback_handlers(dst, name, &args)? {
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
// Safety: ensure receiver is materialized even after callee conversion
|
||||
callee = builder.materialize_receiver_in_callee(callee)?;
|
||||
|
||||
// Structural guard: prevent static compiler boxes from being called with runtime receivers
|
||||
// 箱理論: CalleeGuardBox による構造的分離
|
||||
let guard = super::guard::CalleeGuardBox::new(&builder.value_types);
|
||||
callee = guard.apply_static_runtime_guard(callee)?;
|
||||
|
||||
// Emit resolve.choose for method callee (dev-only; default OFF)
|
||||
if let Callee::Method { box_name, method, certainty, .. } = &callee {
|
||||
let chosen = format!("{}.{}{}", box_name, method, format!("/{}", arity_for_try));
|
||||
let meta = serde_json::json!({
|
||||
"recv_cls": box_name,
|
||||
"method": method,
|
||||
"arity": arity_for_try,
|
||||
"chosen": chosen,
|
||||
"certainty": format!("{:?}", certainty),
|
||||
"reason": "unified",
|
||||
});
|
||||
crate::mir::builder::observe::resolve::emit_choose(builder, meta);
|
||||
}
|
||||
|
||||
// Validate call arguments
|
||||
// 箱理論: CalleeResolverBox で引数検証
|
||||
let resolver = super::resolver::CalleeResolverBox::new(
|
||||
&builder.value_origin_newbox,
|
||||
&builder.value_types,
|
||||
Some(&builder.type_registry),
|
||||
);
|
||||
resolver.validate_args(&callee, &args)?;
|
||||
|
||||
// Stability guard: decide route via RouterPolicyBox (behavior-preserving rules)
|
||||
if let Callee::Method { box_name, method, receiver: Some(r), certainty, .. } = &callee {
|
||||
let route = crate::mir::builder::router::policy::choose_route(box_name, method, *certainty, arity_for_try);
|
||||
if let crate::mir::builder::router::policy::Route::BoxCall = route {
|
||||
if crate::mir::builder::utils::builder_debug_enabled() || std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[router-guard] {}.{} → BoxCall fallback (recv=%{})", box_name, method, r.0);
|
||||
}
|
||||
let effects = EffectMask::READ.add(Effect::ReadHeap);
|
||||
// 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 = builder.in_unified_boxcall_fallback;
|
||||
builder.in_unified_boxcall_fallback = true;
|
||||
let res = builder.emit_box_or_plugin_call(dst, *r, method.clone(), None, args, effects);
|
||||
builder.in_unified_boxcall_fallback = prev_flag;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize operands in current block (EmitGuardBox wrapper)
|
||||
let mut callee = callee;
|
||||
let mut args_local: Vec<ValueId> = args;
|
||||
crate::mir::builder::emit_guard::finalize_call_operands(builder, &mut callee, &mut args_local);
|
||||
|
||||
// Create MirCall instruction using the new module (pure data composition)
|
||||
let mir_call = call_unified::create_mir_call(dst, callee.clone(), args_local.clone());
|
||||
|
||||
// Dev trace: show final callee/recv right before emission (guarded)
|
||||
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") || crate::mir::builder::utils::builder_debug_enabled() {
|
||||
if let Callee::Method { method, receiver, box_name, .. } = &callee {
|
||||
if let Some(r) = receiver {
|
||||
eprintln!("[vm-call-final] bb={:?} method={} recv=%{} class={}",
|
||||
builder.current_block, method, r.0, box_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For Phase 2: Convert to legacy Call instruction with new callee field (use finalized operands)
|
||||
let legacy_call = MirInstruction::Call {
|
||||
dst: mir_call.dst,
|
||||
func: ValueId::new(0), // Dummy value for legacy compatibility
|
||||
callee: Some(callee),
|
||||
args: args_local,
|
||||
effects: mir_call.effects,
|
||||
};
|
||||
|
||||
let res = builder.emit_instruction(legacy_call);
|
||||
// Dev-only: verify block schedule invariants after emitting call
|
||||
crate::mir::builder::emit_guard::verify_after_call(builder);
|
||||
res
|
||||
}
|
||||
|
||||
/// Emit global call with name constant (public for legacy compatibility)
|
||||
pub fn emit_global_unified(
|
||||
builder: &mut MirBuilder,
|
||||
dst: Option<ValueId>,
|
||||
name: String,
|
||||
args: Vec<ValueId>,
|
||||
) -> Result<(), String> {
|
||||
// Create a string constant for the function name via NameConstBox
|
||||
let name_const = crate::mir::builder::name_const::make_name_const_result(builder, &name)?;
|
||||
// Allocate a destination if not provided so we can annotate it
|
||||
let actual_dst = if let Some(d) = dst { d } else { builder.next_value_id() };
|
||||
let mut args = args;
|
||||
crate::mir::builder::ssa::local::finalize_args(builder, &mut args);
|
||||
builder.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(actual_dst),
|
||||
func: name_const,
|
||||
callee: Some(Callee::Global(name.clone())),
|
||||
args,
|
||||
effects: EffectMask::IO,
|
||||
})?;
|
||||
// Annotate from module signature (if present)
|
||||
builder.annotate_call_result_from_func_name(actual_dst, name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Emit value call (first-class function, public for legacy compatibility)
|
||||
pub fn emit_value_unified(
|
||||
builder: &mut MirBuilder,
|
||||
dst: Option<ValueId>,
|
||||
func_val: ValueId,
|
||||
args: Vec<ValueId>,
|
||||
) -> Result<(), String> {
|
||||
let mut args = args;
|
||||
crate::mir::builder::ssa::local::finalize_args(builder, &mut args);
|
||||
builder.emit_instruction(MirInstruction::Call {
|
||||
dst,
|
||||
func: func_val,
|
||||
callee: Some(Callee::Value(func_val)),
|
||||
args,
|
||||
effects: EffectMask::IO,
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user