Files
hakorune/src/mir/builder/calls/unified_emitter.rs
nyash-codex 3a82633924 refactor(funcscanner): Region+next_i パターン統一 & SSA テスト追加
**FuncScanner .hako 側改善**:
- scan_all_boxes を Region + next_i 形式に統一(continue 多発による SSA/PHI 複雑さ削減)
- インデント修正(タブ→スペース統一)
- デバッグ print 削除

**SSA テスト追加**:
- lang/src/compiler/tests/funcscanner_scan_methods_min.hako
- src/tests/mir_funcscanner_ssa.rs (scan_methods & fib_min SSA デバッグテスト)

**Phase 25.3 ドキュメント**:
- docs/development/roadmap/phases/phase-25.3-funcscanner/ 追加

**関連**: Phase 25.3 FuncScanner 箱化準備作業

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 06:38:43 +09:00

259 lines
12 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*!
* 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 (via CallMaterializerBox)
if let Some(result) = super::materializer::CallMaterializerBox::try_global_fallback_handlers(builder, dst, name, &args)? {
return Ok(result);
}
}
return Err(e);
}
};
// Safety: ensure receiver is materialized even after callee conversion (via CallMaterializerBox)
callee = super::materializer::CallMaterializerBox::materialize_receiver_in_callee(builder, 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);
// 📦 Hotfix 7: Include receiver in args for Callee::Method
// VM's exec_function_inner expects receiver as the first parameter (ValueId(0))
// but finalize_call_operands keeps receiver in Callee, not in args.
// We must add it to args_local here so VM can bind it correctly.
if let Callee::Method { receiver: Some(recv), .. } = &callee {
args_local.insert(0, *recv);
}
// 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::INVALID, // Dummy value for legacy compatibility (not a real SSA use)
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,
})
}
}