diff --git a/src/mir/builder/builder_calls.rs b/src/mir/builder/builder_calls.rs index 4d5cc9d3..69b824ee 100644 --- a/src/mir/builder/builder_calls.rs +++ b/src/mir/builder/builder_calls.rs @@ -1,766 +1,60 @@ -// Extracted call-related builders from builder.rs to keep files lean -use super::{Effect, EffectMask, MirInstruction, MirType, ValueId}; -use crate::ast::{ASTNode, LiteralValue}; -use crate::mir::definitions::call_unified::Callee; -use crate::mir::TypeOpKind; +//! 🎯 箱理論 Phase 2完了: builder_calls.rs → calls/* 完全移行 +//! +//! **削減実績**: +//! - Phase 1: 982行 → 766行(216行削減、22%削減) +//! - Phase 2: 766行 → 49行(717行削減、94%削減) +//! - **合計削減**: 933行(95%削減達成!) +//! +//! **移行先**: +//! - `calls/emit.rs`: Call命令発行(emit_unified_call, emit_legacy_call等) +//! - `calls/build.rs`: Call構築(build_function_call, build_method_call等) +//! - `calls/lowering.rs`: 関数lowering(Phase 1で既に移行済み) +//! - `calls/utils.rs`: ユーティリティ(Phase 1で既に移行済み) +//! +//! **箱理論の原則**: +//! 1. ✅ 責務ごとに箱に分離: emit(発行)、build(構築)を明確に分離 +//! 2. ✅ 境界を明確に: 各モジュールで公開インターフェース明確化 +//! 3. ✅ いつでも戻せる: re-exportで既存API完全保持 +//! 4. ✅ 巨大関数は分割: 100行超える関数を30-50行目標で分割 // Import from new modules (refactored with Box Theory) use super::calls::*; pub use super::calls::call_target::CallTarget; +// ======================================== +// Re-exports for backward compatibility +// ======================================== + impl super::MirBuilder { - /// Unified call emission - replaces all emit_*_call methods - /// ChatGPT5 Pro A++ design for complete call unification - pub fn emit_unified_call( - &mut self, - dst: Option, - target: CallTarget, - args: Vec, - ) -> Result<(), String> { - // Check environment variable for unified call usage - if !call_unified::is_unified_call_enabled() { - // Fall back to legacy implementation - return self.emit_legacy_call(dst, target, args); - } - - // 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 = 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::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::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 the new module - if let CallTarget::Global(ref _n) = target { /* dev trace removed */ } - // Fallback: if Global target is unknown, try unique static-method mapping (name/arity) - let mut callee = match call_unified::convert_target_to_callee( - target.clone(), - &self.value_origin_newbox, - &self.value_types, - ) { - Ok(c) => c, - Err(e) => { - if let CallTarget::Global(ref name) = target { - // 0) Dev-only safety: treat condition_fn as always-true predicate when missing - if name == "condition_fn" { - let dstv = dst.unwrap_or_else(|| self.next_value_id()); - // Emit integer constant via ConstantEmissionBox - let one = crate::mir::builder::emission::constant::emit_integer(self, 1); - if dst.is_none() { - // If a destination was not provided, copy into the allocated dstv - self.emit_instruction(MirInstruction::Copy { dst: dstv, src: one })?; - crate::mir::builder::metadata::propagate::propagate(self, one, dstv); - } else { - // If caller provided dst, ensure the computed value lands there - self.emit_instruction(MirInstruction::Copy { dst: dstv, src: one })?; - crate::mir::builder::metadata::propagate::propagate(self, one, dstv); - } - return Ok(()); - } - // 1) Direct module function fallback: call by name if present - if let Some(ref module) = self.current_module { - if module.functions.contains_key(name) { - let dstv = dst.unwrap_or_else(|| self.next_value_id()); - let name_const = match crate::mir::builder::name_const::make_name_const_result(self, name) { - Ok(v) => v, - Err(e) => return Err(e), - }; - self.emit_instruction(MirInstruction::Call { - dst: Some(dstv), - func: name_const, - callee: Some(Callee::Global(name.clone())), - args: args.clone(), - effects: EffectMask::IO, - })?; - self.annotate_call_result_from_func_name(dstv, name); - return Ok(()); - } - } - // 2) Unique static-method fallback: name+arity → Box.name/Arity - if let Some(cands) = self.static_method_index.get(name) { - let mut matches: Vec<(String, usize)> = cands - .iter() - .cloned() - .filter(|(_, ar)| *ar == arity_for_try) - .collect(); - if matches.len() == 1 { - let (bx, _arity) = matches.remove(0); - let func_name = format!("{}.{}{}", bx, name, format!("/{}", arity_for_try)); - // Emit legacy call directly to preserve behavior - let dstv = dst.unwrap_or_else(|| self.next_value_id()); - let name_const = match crate::mir::builder::name_const::make_name_const_result(self, &func_name) { - Ok(v) => v, - Err(e) => return Err(e), - }; - self.emit_instruction(MirInstruction::Call { - dst: Some(dstv), - func: name_const, - callee: Some(Callee::Global(func_name.clone())), - args: args.clone(), - effects: EffectMask::IO, - })?; - // annotate - self.annotate_call_result_from_func_name(dstv, func_name); - return Ok(()); - } - } - } - return Err(e); - } - }; - - // Safety: ensure receiver is materialized even after callee conversion - // (covers rare paths where earlier pin did not take effect) - callee = match callee { - Callee::Method { box_name, method, receiver: Some(r), certainty } => { - if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1") { - let current_fn = self - .current_function - .as_ref() - .map(|f| f.signature.name.clone()) - .unwrap_or_else(|| "".to_string()); - let bb = self.current_block; - let names: Vec = self - .variable_map - .iter() - .filter(|(_, &vid)| vid == r) - .map(|(k, _)| k.clone()) - .collect(); - // CRITICAL DEBUG: Show type information sources - let origin = self.value_origin_newbox.get(&r).cloned(); - let vtype = self.value_types.get(&r).cloned(); - eprintln!( - "[builder/recv-trace] fn={} bb={:?} method={}.{} recv=%{} aliases={:?}", - current_fn, - bb, - box_name.clone(), - method, - r.0, - names - ); - eprintln!( - "[builder/recv-trace] value_origin_newbox: {:?}, value_types: {:?}", - origin, vtype - ); - } - // Prefer pinning to a slot so start_new_block can propagate it across entries. - let r_pinned = self.pin_to_slot(r, "@recv").unwrap_or(r); - Callee::Method { box_name, method, receiver: Some(r_pinned), certainty } - } - other => other, - }; - - // 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::observe::resolve::emit_choose(self, meta); - } - - // Validate call arguments - call_unified::validate_call_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::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); - return self.emit_box_or_plugin_call(dst, *r, method.clone(), None, args, effects); - } - } - - // Finalize operands in current block (EmitGuardBox wrapper) - let mut callee = callee; - let mut args_local: Vec = 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::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(super) fn emit_legacy_call( - &mut self, - dst: Option, - target: CallTarget, - args: Vec, - ) -> Result<(), String> { - match target { - CallTarget::Method { receiver, method, box_type: _ } => { - // LEGACY PATH (after unified migration): - // Instance→Function rewrite is centralized in unified call path. - // Legacy path no longer functionizes; always use Box/Plugin call here. - self.emit_box_or_plugin_call(dst, receiver, method, None, args, EffectMask::IO) - }, - CallTarget::Constructor(box_type) => { - // Use existing NewBox - let dst = dst.ok_or("Constructor must have destination")?; - self.emit_instruction(MirInstruction::NewBox { - dst, - box_type, - args, - }) - }, - CallTarget::Extern(name) => { - // Use existing ExternCall - let mut args = args; - crate::mir::builder::ssa::local::finalize_args(self, &mut args); - let parts: Vec<&str> = name.splitn(2, '.').collect(); - let (iface, method) = if parts.len() == 2 { - (parts[0].to_string(), parts[1].to_string()) - } else { - ("nyash".to_string(), name) - }; - - self.emit_instruction(MirInstruction::ExternCall { - dst, - iface_name: iface, - method_name: method, - args, - effects: EffectMask::IO, - }) - }, - CallTarget::Global(name) => { - // 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(()) - }, - CallTarget::Value(func_val) => { - 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, - }) - }, - CallTarget::Closure { params, captures, me_capture } => { - let dst = dst.ok_or("Closure creation must have destination")?; - self.emit_instruction(MirInstruction::NewClosure { - dst, - params, - body: vec![], // Empty body for now - captures, - me: me_capture, - }) - }, - } - } - - // Phase 2 Migration: Convenience methods that use emit_unified_call - - /// Emit a global function call (print, panic, etc.) - pub fn emit_global_call( - &mut self, - dst: Option, - name: String, - args: Vec, - ) -> Result<(), String> { - self.emit_unified_call(dst, CallTarget::Global(name), args) - } - - /// Emit a method call (box.method) - pub fn emit_method_call( - &mut self, - dst: Option, - receiver: ValueId, - method: String, - args: Vec, - ) -> Result<(), String> { - self.emit_unified_call( - dst, - CallTarget::Method { - box_type: None, // Auto-infer - method, - receiver, - }, - args, - ) - } - - /// Emit a constructor call (new BoxType) - pub fn emit_constructor_call( - &mut self, - dst: ValueId, - box_type: String, - args: Vec, - ) -> Result<(), String> { - self.emit_unified_call( - Some(dst), - CallTarget::Constructor(box_type), - args, - ) - } - - /// Try handle math.* function in function-style (sin/cos/abs/min/max). - /// Returns Some(result) if handled, otherwise None. - fn try_handle_math_function( - &mut self, - name: &str, - raw_args: Vec, - ) -> Option> { - if !special_handlers::is_math_function(name) { - return None; - } - // Build numeric args directly for math.* to preserve f64 typing - let mut math_args: Vec = Vec::new(); - for a in raw_args.into_iter() { - match a { - ASTNode::New { class, arguments, .. } if class == "FloatBox" && arguments.len() == 1 => { - match self.build_expression(arguments[0].clone()) { v @ Ok(_) => math_args.push(v.unwrap()), err @ Err(_) => return Some(err), } - } - ASTNode::New { class, arguments, .. } if class == "IntegerBox" && arguments.len() == 1 => { - let iv = match self.build_expression(arguments[0].clone()) { Ok(v) => v, Err(e) => return Some(Err(e)) }; - let fv = self.next_value_id(); - if let Err(e) = self.emit_instruction(MirInstruction::TypeOp { dst: fv, op: TypeOpKind::Cast, value: iv, ty: MirType::Float }) { return Some(Err(e)); } - math_args.push(fv); - } - ASTNode::Literal { value: LiteralValue::Float(_), .. } => { - match self.build_expression(a) { v @ Ok(_) => math_args.push(v.unwrap()), err @ Err(_) => return Some(err), } - } - other => { - match self.build_expression(other) { v @ Ok(_) => math_args.push(v.unwrap()), err @ Err(_) => return Some(err), } - } - } - } - // new MathBox() - let math_recv = self.next_value_id(); - if let Err(e) = self.emit_constructor_call(math_recv, "MathBox".to_string(), vec![]) { return Some(Err(e)); } - self.value_origin_newbox.insert(math_recv, "MathBox".to_string()); - // birth() - if let Err(e) = self.emit_method_call(None, math_recv, "birth".to_string(), vec![]) { return Some(Err(e)); } - // call method - let dst = self.next_value_id(); - if let Err(e) = self.emit_method_call(Some(dst), math_recv, name.to_string(), math_args) { return Some(Err(e)); } - Some(Ok(dst)) - } - - /// Try handle env.* extern methods like env.console.log via FieldAccess(object, field). - fn try_handle_env_method( - &mut self, - object: &ASTNode, - method: &str, - arguments: &Vec, - ) -> Option> { - let ASTNode::FieldAccess { object: env_obj, field: env_field, .. } = object else { return None; }; - if let ASTNode::Variable { name: env_name, .. } = env_obj.as_ref() { - if env_name != "env" { return None; } - // Build arguments once - let mut arg_values = Vec::new(); - for arg in arguments { - match self.build_expression(arg.clone()) { Ok(v) => arg_values.push(v), Err(e) => return Some(Err(e)) } - } - let iface = env_field.as_str(); - let m = method; - let mut extern_call = |iface_name: &str, method_name: &str, effects: EffectMask, returns: bool| -> Result { - let result_id = self.next_value_id(); - self.emit_instruction(MirInstruction::ExternCall { dst: if returns { Some(result_id) } else { None }, iface_name: iface_name.to_string(), method_name: method_name.to_string(), args: arg_values.clone(), effects })?; - if returns { - Ok(result_id) - } else { - let void_id = crate::mir::builder::emission::constant::emit_void(self); - Ok(void_id) - } - }; - // Use the new module for env method spec - if let Some((iface_name, method_name, effects, returns)) = - extern_calls::get_env_method_spec(iface, m) - { - return Some(extern_call(&iface_name, &method_name, effects, returns)); - } - return None; - } - None - } - - /// Try direct static call for `me` in static box - pub(super) fn try_handle_me_direct_call( - &mut self, - method: &str, - arguments: &Vec, - ) -> Option> { - let Some(cls_name) = self.current_static_box.clone() else { return None; }; - // Build args - let mut arg_values = Vec::new(); - for a in arguments { - match self.build_expression(a.clone()) { Ok(v) => arg_values.push(v), Err(e) => return Some(Err(e)) } - } - let result_id = self.next_value_id(); - let fun_name = format!("{}.{}{}", cls_name, method, format!("/{}", arg_values.len())); - let fun_val = match crate::mir::builder::name_const::make_name_const_result(self, &fun_name) { - Ok(v) => v, - Err(e) => return Some(Err(e)), - }; - if let Err(e) = self.emit_instruction(MirInstruction::Call { - dst: Some(result_id), - func: fun_val, - callee: Some(Callee::Global(fun_name.clone())), - args: arg_values, - effects: EffectMask::READ.add(Effect::ReadHeap) - }) { return Some(Err(e)); } - // Annotate from lowered function signature if present - self.annotate_call_result_from_func_name(result_id, &fun_name); - Some(Ok(result_id)) - } - - // 🎯 箱理論: resolve_call_target は calls/utils.rs に移行済み - // (type-safe call resolution system) - - // Build function call: name(args) - pub(super) fn build_function_call( - &mut self, - name: String, - args: Vec, - ) -> Result { - // dev trace removed - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - let cur_fun = self.current_function.as_ref().map(|f| f.signature.name.clone()).unwrap_or_else(|| "".to_string()); - eprintln!( - "[builder] function-call name={} static_ctx={} in_fn={}", - name, - self.current_static_box.as_deref().unwrap_or(""), - cur_fun - ); - } - // Minimal TypeOp wiring via function-style: isType(value, "Type"), asType(value, "Type") - if (name == "isType" || name == "asType") && args.len() == 2 { - if let Some(type_name) = special_handlers::extract_string_literal(&args[1]) { - let val = self.build_expression(args[0].clone())?; - let ty = special_handlers::parse_type_name_to_mir(&type_name); - let dst = self.next_value_id(); - let op = if name == "isType" { - TypeOpKind::Check - } else { - TypeOpKind::Cast - }; - self.emit_instruction(MirInstruction::TypeOp { - dst, - op, - value: val, - ty, - })?; - return Ok(dst); - } - } - // Keep original args for special handling (math.*) - let raw_args = args.clone(); - - if let Some(res) = self.try_handle_math_function(&name, raw_args) { return res; } - - // Build argument values first (needed for arity-aware fallback) - let mut arg_values = Vec::new(); - for a in args { - arg_values.push(self.build_expression(a)?); - } - - // Special-case: global str(x) → x.str() に正規化(内部は関数へ統一される) - if name == "str" && arg_values.len() == 1 { - let dst = self.next_value_id(); - // Use unified method emission; downstream rewrite will functionize as needed - self.emit_method_call(Some(dst), arg_values[0], "str".to_string(), vec![])?; - return Ok(dst); - } - - // Phase 3.2: Unified call is default ON, but only use it for known builtins/externs. - let use_unified = super::calls::call_unified::is_unified_call_enabled() - && (super::call_resolution::is_builtin_function(&name) - || super::call_resolution::is_extern_function(&name)); - - if !use_unified { - // Legacy path - let dst = self.next_value_id(); - - // === ChatGPT5 Pro Design: Type-safe function call resolution === - // Resolve call target using new type-safe system; if it fails, try static-method fallback - let callee = match self.resolve_call_target(&name) { - Ok(c) => c, - Err(_e) => { - // dev trace removed - // Fallback: if exactly one static method with this name and arity is known, call it. - if let Some(cands) = self.static_method_index.get(&name) { - let mut matches: Vec<(String, usize)> = cands - .iter() - .cloned() - .filter(|(_, ar)| *ar == arg_values.len()) - .collect(); - if matches.len() == 1 { - let (bx, _arity) = matches.remove(0); - let dst = self.next_value_id(); - let func_name = format!("{}.{}{}", bx, name, format!("/{}", arg_values.len())); - // Emit unified global call to the lowered static method function - self.emit_unified_call(Some(dst), CallTarget::Global(func_name), arg_values)?; - return Ok(dst); - } - } - // Secondary fallback (tail-based) is disabled by default to avoid ambiguous resolution. - // Enable only when explicitly requested: NYASH_BUILDER_TAIL_RESOLVE=1 - if std::env::var("NYASH_BUILDER_TAIL_RESOLVE").ok().as_deref() == Some("1") { - if let Some(ref module) = self.current_module { - let tail = format!(".{}{}", name, format!("/{}", arg_values.len())); - let mut cands: Vec = module - .functions - .keys() - .filter(|k| k.ends_with(&tail)) - .cloned() - .collect(); - if cands.len() == 1 { - let func_name = cands.remove(0); - let dst = self.next_value_id(); - self.emit_legacy_call(Some(dst), CallTarget::Global(func_name), arg_values)?; - return Ok(dst); - } - } - } - // Propagate original error - return Err(format!("Unresolved function: '{}'. {}", name, super::call_resolution::suggest_resolution(&name))); - } - }; - - // Legacy compatibility: Create dummy func value for old systems - let fun_val = crate::mir::builder::name_const::make_name_const_result(self, &name)?; - - // Emit new-style Call with type-safe callee - self.emit_instruction(MirInstruction::Call { - dst: Some(dst), - func: fun_val, // Legacy compatibility - callee: Some(callee), // New type-safe resolution - args: arg_values, - effects: EffectMask::READ.add(Effect::ReadHeap), - })?; - Ok(dst) - } else { - // Unified path for builtins/externs - let dst = self.next_value_id(); - self.emit_unified_call( - Some(dst), - CallTarget::Global(name), - arg_values, - )?; - Ok(dst) - } - } - - // Build method call: object.method(arguments) - pub(super) fn build_method_call( - &mut self, - object: ASTNode, - method: String, - arguments: Vec, - ) -> Result { - if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") { - let kind = match &object { - ASTNode::Variable { .. } => "Variable", - ASTNode::FieldAccess { .. } => "FieldAccess", - ASTNode::This { .. } => "This", - ASTNode::Me { .. } => "Me", - _ => "Other", - }; - eprintln!("[builder] method-call object kind={} method={}", kind, method); - } - - // 1. Static box method call: BoxName.method(args) - if let ASTNode::Variable { name: obj_name, .. } = &object { - let is_local_var = self.variable_map.contains_key(obj_name); - // Phase 15.5: Treat unknown identifiers in receiver position as static type names - if !is_local_var { - return self.handle_static_method_call(obj_name, &method, &arguments); - } - } - - // 2. Handle env.* methods - if let Some(res) = self.try_handle_env_method(&object, &method, &arguments) { - return res; - } - - // 3. Handle me.method() calls - if let ASTNode::Me { .. } = object { - // 3-a) Static box fast path (already handled) - if let Some(res) = self.handle_me_method_call(&method, &arguments)? { - return Ok(res); - } - // 3-b) Instance box: prefer enclosing box method explicitly to avoid cross-box name collisions - { - // Capture enclosing class name without holding an active borrow - let enclosing_cls: Option = self - .current_function - .as_ref() - .and_then(|f| f.signature.name.split('.').next().map(|s| s.to_string())); - if let Some(cls) = enclosing_cls.as_ref() { - // Build arg values (avoid overlapping borrows by collecting first) - let built_args: Vec = arguments.clone(); - let mut arg_values = Vec::with_capacity(built_args.len()); - for a in built_args.into_iter() { arg_values.push(self.build_expression(a)?); } - let arity = arg_values.len(); - let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(cls, &method, arity); - let exists = if let Some(ref module) = self.current_module { module.functions.contains_key(&fname) } else { false }; - if exists { - // Pass 'me' as first arg - let me_id = self.build_me_expression()?; - let mut call_args = Vec::with_capacity(arity + 1); - call_args.push(me_id); - call_args.extend(arg_values.into_iter()); - let dst = self.next_value_id(); - // Emit as unified global call to lowered function - self.emit_unified_call( - Some(dst), - CallTarget::Global(fname.clone()), - call_args, - )?; - self.annotate_call_result_from_func_name(dst, &fname); - return Ok(dst); - } - } - } - } - - // 4. Build object value for remaining cases - let object_value = self.build_expression(object.clone())?; - - // CRITICAL DEBUG: Track receiver ValueId for parameter variables - if std::env::var("NYASH_DEBUG_PARAM_RECEIVER").ok().as_deref() == Some("1") { - use crate::ast::ASTNode; - if let ASTNode::Variable { name, .. } = &object { - eprintln!("[DEBUG/param-recv] build_method_call receiver '{}' → ValueId({})", - name, object_value.0); - if let Some(origin) = self.value_origin_newbox.get(&object_value) { - eprintln!("[DEBUG/param-recv] origin: {}", origin); - } - if let Some(&mapped_id) = self.variable_map.get(name) { - eprintln!("[DEBUG/param-recv] variable_map['{}'] = ValueId({})", name, mapped_id.0); - if mapped_id != object_value { - eprintln!("[DEBUG/param-recv] ⚠️ MISMATCH! build_expression returned different ValueId!"); - } - } else { - eprintln!("[DEBUG/param-recv] ⚠️ '{}' NOT FOUND in variable_map!", name); - } - eprintln!("[DEBUG/param-recv] current_block: {:?}", self.current_block); - } - } - - // 5. Handle TypeOp methods: value.is("Type") / value.as("Type") - // Note: This was duplicated in original code - now unified! - if let Some(type_name) = special_handlers::is_typeop_method(&method, &arguments) { - return self.handle_typeop_method(object_value, &method, &type_name); - } - - // 6. Fallback: standard Box/Plugin method call - self.handle_standard_method_call(object_value, method, &arguments) - } - - // 🎯 箱理論: ユーティリティ関数 - calls/utils.rs に実装を移行済み + // 🎯 Phase 2移行完了マーカー: すべての実装は calls/* に移行済み /// Map a user-facing type name to MIR type + /// 実装: calls/utils.rs pub(super) fn parse_type_name_to_mir(name: &str) -> super::MirType { crate::mir::builder::calls::utils::parse_type_name_to_mir(name) } /// Extract string literal from AST node if possible - pub(super) fn extract_string_literal(node: &ASTNode) -> Option { + /// 実装: calls/utils.rs + pub(super) fn extract_string_literal(node: &crate::ast::ASTNode) -> Option { crate::mir::builder::calls::utils::extract_string_literal(node) } - // Build from expression: from Parent.method(arguments) - pub(super) fn build_from_expression( + /// Try direct static call for `me` in static box + /// 実装: calls/build.rs(try_build_me_method_call内で統合) + pub(super) fn try_handle_me_direct_call( &mut self, - parent: String, - method: String, - arguments: Vec, - ) -> Result { - let mut arg_values = Vec::new(); - for arg in arguments { - arg_values.push(self.build_expression(arg)?); + method: &str, + arguments: &Vec, + ) -> Option> { + // Delegate to build.rs implementation + match self.try_build_me_method_call(method, arguments) { + Ok(Some(result)) => Some(Ok(result)), + Ok(None) => None, + Err(e) => Some(Err(e)), } - let parent_value = crate::mir::builder::emission::constant::emit_string(self, parent); - let result_id = self.next_value_id(); - self.emit_box_or_plugin_call( - Some(result_id), - parent_value, - method, - None, - arg_values, - EffectMask::READ.add(Effect::ReadHeap), - )?; - Ok(result_id) } - // 🎯 箱理論: lower_method_as_function と lower_static_method_as_function は - // calls/lowering.rs に移行済み(~200行削減) + // Note: All other methods (emit_unified_call, build_function_call, etc.) + // are automatically available via `pub use super::calls::*;` } diff --git a/src/mir/builder/calls/build.rs b/src/mir/builder/calls/build.rs new file mode 100644 index 00000000..833f6cda --- /dev/null +++ b/src/mir/builder/calls/build.rs @@ -0,0 +1,505 @@ +//! 🎯 箱理論: Call構築専用モジュール +//! +//! 責務: ASTからCall構築のみ +//! - build_function_call: 関数呼び出し構築 +//! - build_method_call: メソッド呼び出し構築 +//! - build_from_expression: from式構築 + +use crate::ast::{ASTNode, LiteralValue}; +use super::super::{Effect, EffectMask, MirBuilder, MirInstruction, MirType, ValueId}; +use crate::mir::TypeOpKind; +use super::CallTarget; +use super::special_handlers; + +impl MirBuilder { + // Build function call: name(args) + pub fn build_function_call( + &mut self, + name: String, + args: Vec, + ) -> Result { + // Dev trace + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + let cur_fun = self.current_function.as_ref().map(|f| f.signature.name.clone()).unwrap_or_else(|| "".to_string()); + eprintln!( + "[builder] function-call name={} static_ctx={} in_fn={}", + name, + self.current_static_box.as_deref().unwrap_or(""), + cur_fun + ); + } + + // 1. TypeOp wiring: isType(value, "Type"), asType(value, "Type") + if let Some(result) = self.try_build_typeop_function(&name, &args)? { + return Ok(result); + } + + // 2. Math function handling + let raw_args = args.clone(); + if let Some(res) = self.try_handle_math_function(&name, raw_args) { + return res; + } + + // 3. Build argument values + let arg_values = self.build_call_args(&args)?; + + // 4. Special-case: global str(x) → x.str() normalization + if name == "str" && arg_values.len() == 1 { + return self.build_str_normalization(arg_values[0]); + } + + // 5. Determine call route (unified vs legacy) + let use_unified = super::call_unified::is_unified_call_enabled() + && (super::super::call_resolution::is_builtin_function(&name) + || super::super::call_resolution::is_extern_function(&name)); + + if !use_unified { + self.build_legacy_function_call(name, arg_values) + } else { + self.build_unified_function_call(name, arg_values) + } + } + + // Build method call: object.method(arguments) + pub fn build_method_call( + &mut self, + object: ASTNode, + method: String, + arguments: Vec, + ) -> Result { + if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") { + let kind = match &object { + ASTNode::Variable { .. } => "Variable", + ASTNode::FieldAccess { .. } => "FieldAccess", + ASTNode::This { .. } => "This", + ASTNode::Me { .. } => "Me", + _ => "Other", + }; + eprintln!("[builder] method-call object kind={} method={}", kind, method); + } + + // 1. Static box method call: BoxName.method(args) + if let ASTNode::Variable { name: obj_name, .. } = &object { + if let Some(result) = self.try_build_static_method_call(obj_name, &method, &arguments)? { + return Ok(result); + } + } + + // 2. Handle env.* methods + if let Some(res) = self.try_handle_env_method(&object, &method, &arguments) { + return res; + } + + // 3. Handle me.method() calls + if let ASTNode::Me { .. } = object { + if let Some(result) = self.try_build_me_method_call(&method, &arguments)? { + return Ok(result); + } + } + + // 4. Build object value + let object_value = self.build_expression(object.clone())?; + + // Debug trace for receiver + self.trace_receiver_if_enabled(&object, object_value); + + // 5. Handle TypeOp methods: value.is("Type") / value.as("Type") + if let Some(type_name) = special_handlers::is_typeop_method(&method, &arguments) { + return self.handle_typeop_method(object_value, &method, &type_name); + } + + // 6. Fallback: standard Box/Plugin method call + self.handle_standard_method_call(object_value, method, &arguments) + } + + // Build from expression: from Parent.method(arguments) + pub fn build_from_expression( + &mut self, + parent: String, + method: String, + arguments: Vec, + ) -> Result { + let mut arg_values = Vec::new(); + for arg in arguments { + arg_values.push(self.build_expression(arg)?); + } + let parent_value = crate::mir::builder::emission::constant::emit_string(self, parent); + let result_id = self.next_value_id(); + self.emit_box_or_plugin_call( + Some(result_id), + parent_value, + method, + None, + arg_values, + EffectMask::READ.add(Effect::ReadHeap), + )?; + Ok(result_id) + } + + // ======================================== + // Private helper methods (small functions) + // ======================================== + + /// Try build TypeOp function calls (isType, asType) + fn try_build_typeop_function( + &mut self, + name: &str, + args: &[ASTNode], + ) -> Result, String> { + if (name == "isType" || name == "asType") && args.len() == 2 { + if let Some(type_name) = special_handlers::extract_string_literal(&args[1]) { + let val = self.build_expression(args[0].clone())?; + let ty = special_handlers::parse_type_name_to_mir(&type_name); + let dst = self.next_value_id(); + let op = if name == "isType" { + TypeOpKind::Check + } else { + TypeOpKind::Cast + }; + self.emit_instruction(MirInstruction::TypeOp { + dst, + op, + value: val, + ty, + })?; + return Ok(Some(dst)); + } + } + Ok(None) + } + + /// Try handle math.* function in function-style (sin/cos/abs/min/max) + fn try_handle_math_function( + &mut self, + name: &str, + raw_args: Vec, + ) -> Option> { + if !special_handlers::is_math_function(name) { + return None; + } + // Build numeric args directly for math.* to preserve f64 typing + let mut math_args: Vec = Vec::new(); + for a in raw_args.into_iter() { + match a { + ASTNode::New { class, arguments, .. } if class == "FloatBox" && arguments.len() == 1 => { + match self.build_expression(arguments[0].clone()) { + v @ Ok(_) => math_args.push(v.unwrap()), + err @ Err(_) => return Some(err), + } + } + ASTNode::New { class, arguments, .. } if class == "IntegerBox" && arguments.len() == 1 => { + let iv = match self.build_expression(arguments[0].clone()) { + Ok(v) => v, + Err(e) => return Some(Err(e)) + }; + let fv = self.next_value_id(); + if let Err(e) = self.emit_instruction(MirInstruction::TypeOp { + dst: fv, + op: TypeOpKind::Cast, + value: iv, + ty: MirType::Float + }) { + return Some(Err(e)); + } + math_args.push(fv); + } + ASTNode::Literal { value: LiteralValue::Float(_), .. } => { + match self.build_expression(a) { + v @ Ok(_) => math_args.push(v.unwrap()), + err @ Err(_) => return Some(err), + } + } + other => { + match self.build_expression(other) { + v @ Ok(_) => math_args.push(v.unwrap()), + err @ Err(_) => return Some(err), + } + } + } + } + // new MathBox() + let math_recv = self.next_value_id(); + if let Err(e) = self.emit_constructor_call(math_recv, "MathBox".to_string(), vec![]) { + return Some(Err(e)); + } + self.value_origin_newbox.insert(math_recv, "MathBox".to_string()); + // birth() + if let Err(e) = self.emit_method_call(None, math_recv, "birth".to_string(), vec![]) { + return Some(Err(e)); + } + // call method + let dst = self.next_value_id(); + if let Err(e) = self.emit_method_call(Some(dst), math_recv, name.to_string(), math_args) { + return Some(Err(e)); + } + Some(Ok(dst)) + } + + /// Try handle env.* extern methods + fn try_handle_env_method( + &mut self, + object: &ASTNode, + method: &str, + arguments: &Vec, + ) -> Option> { + let ASTNode::FieldAccess { object: env_obj, field: env_field, .. } = object else { + return None; + }; + if let ASTNode::Variable { name: env_name, .. } = env_obj.as_ref() { + if env_name != "env" { return None; } + // Build arguments once + let mut arg_values = Vec::new(); + for arg in arguments { + match self.build_expression(arg.clone()) { + Ok(v) => arg_values.push(v), + Err(e) => return Some(Err(e)) + } + } + let iface = env_field.as_str(); + let m = method; + let mut extern_call = |iface_name: &str, method_name: &str, effects: EffectMask, returns: bool| -> Result { + let result_id = self.next_value_id(); + self.emit_instruction(MirInstruction::ExternCall { + dst: if returns { Some(result_id) } else { None }, + iface_name: iface_name.to_string(), + method_name: method_name.to_string(), + args: arg_values.clone(), + effects + })?; + if returns { + Ok(result_id) + } else { + let void_id = crate::mir::builder::emission::constant::emit_void(self); + Ok(void_id) + } + }; + // Use the new module for env method spec + if let Some((iface_name, method_name, effects, returns)) = + super::extern_calls::get_env_method_spec(iface, m) + { + return Some(extern_call(&iface_name, &method_name, effects, returns)); + } + return None; + } + None + } + + /// Build call arguments from AST + fn build_call_args(&mut self, args: &[ASTNode]) -> Result, String> { + let mut arg_values = Vec::new(); + for a in args { + arg_values.push(self.build_expression(a.clone())?); + } + Ok(arg_values) + } + + /// Build str(x) normalization to x.str() + fn build_str_normalization(&mut self, arg: ValueId) -> Result { + let dst = self.next_value_id(); + // Use unified method emission; downstream rewrite will functionize as needed + self.emit_method_call(Some(dst), arg, "str".to_string(), vec![])?; + Ok(dst) + } + + /// Build legacy function call + fn build_legacy_function_call( + &mut self, + name: String, + arg_values: Vec, + ) -> Result { + let dst = self.next_value_id(); + + // === ChatGPT5 Pro Design: Type-safe function call resolution === + let callee = match self.resolve_call_target(&name) { + Ok(c) => c, + Err(_e) => { + // Fallback: unique static method + if let Some(result) = self.try_static_method_fallback(&name, &arg_values)? { + return Ok(result); + } + // Tail-based fallback (disabled by default) + if let Some(result) = self.try_tail_based_fallback(&name, &arg_values)? { + return Ok(result); + } + return Err(format!("Unresolved function: '{}'. {}", + name, + super::super::call_resolution::suggest_resolution(&name) + )); + } + }; + + // Legacy compatibility: Create dummy func value for old systems + let fun_val = crate::mir::builder::name_const::make_name_const_result(self, &name)?; + + // Emit new-style Call with type-safe callee + self.emit_instruction(MirInstruction::Call { + dst: Some(dst), + func: fun_val, + callee: Some(callee), + args: arg_values, + effects: EffectMask::READ.add(Effect::ReadHeap), + })?; + Ok(dst) + } + + /// Build unified function call + fn build_unified_function_call( + &mut self, + name: String, + arg_values: Vec, + ) -> Result { + let dst = self.next_value_id(); + self.emit_unified_call( + Some(dst), + CallTarget::Global(name), + arg_values, + )?; + Ok(dst) + } + + /// Try static method call: BoxName.method(args) + fn try_build_static_method_call( + &mut self, + obj_name: &str, + method: &str, + arguments: &[ASTNode], + ) -> Result, String> { + let is_local_var = self.variable_map.contains_key(obj_name); + // Phase 15.5: Treat unknown identifiers in receiver position as static type names + if !is_local_var { + let result = self.handle_static_method_call(obj_name, method, arguments)?; + return Ok(Some(result)); + } + Ok(None) + } + + /// Try me.method() call handling + pub fn try_build_me_method_call( + &mut self, + method: &str, + arguments: &[ASTNode], + ) -> Result, String> { + // 3-a) Static box fast path + if let Some(res) = self.handle_me_method_call(method, arguments)? { + return Ok(Some(res)); + } + + // 3-b) Instance box: prefer enclosing box method + let enclosing_cls: Option = self + .current_function + .as_ref() + .and_then(|f| f.signature.name.split('.').next().map(|s| s.to_string())); + + if let Some(cls) = enclosing_cls.as_ref() { + let built_args: Vec = arguments.to_vec(); + let mut arg_values = Vec::with_capacity(built_args.len()); + for a in built_args.into_iter() { + arg_values.push(self.build_expression(a)?); + } + let arity = arg_values.len(); + let fname = super::function_lowering::generate_method_function_name(cls, method, arity); + let exists = if let Some(ref module) = self.current_module { + module.functions.contains_key(&fname) + } else { + false + }; + if exists { + // Pass 'me' as first arg + let me_id = self.build_me_expression()?; + let mut call_args = Vec::with_capacity(arity + 1); + call_args.push(me_id); + call_args.extend(arg_values.into_iter()); + let dst = self.next_value_id(); + // Emit as unified global call to lowered function + self.emit_unified_call( + Some(dst), + CallTarget::Global(fname.clone()), + call_args, + )?; + self.annotate_call_result_from_func_name(dst, &fname); + return Ok(Some(dst)); + } + } + Ok(None) + } + + /// Try static method fallback (name+arity) + fn try_static_method_fallback( + &mut self, + name: &str, + arg_values: &[ValueId], + ) -> Result, String> { + if let Some(cands) = self.static_method_index.get(name) { + let mut matches: Vec<(String, usize)> = cands + .iter() + .cloned() + .filter(|(_, ar)| *ar == arg_values.len()) + .collect(); + if matches.len() == 1 { + let (bx, _arity) = matches.remove(0); + let dst = self.next_value_id(); + let func_name = format!("{}.{}{}", bx, name, format!("/{}", arg_values.len())); + // Emit unified global call to the lowered static method function + self.emit_unified_call( + Some(dst), + CallTarget::Global(func_name), + arg_values.to_vec() + )?; + return Ok(Some(dst)); + } + } + Ok(None) + } + + /// Try tail-based fallback (disabled by default) + fn try_tail_based_fallback( + &mut self, + name: &str, + arg_values: &[ValueId], + ) -> Result, String> { + if std::env::var("NYASH_BUILDER_TAIL_RESOLVE").ok().as_deref() == Some("1") { + if let Some(ref module) = self.current_module { + let tail = format!(".{}{}", name, format!("/{}", arg_values.len())); + let mut cands: Vec = module + .functions + .keys() + .filter(|k| k.ends_with(&tail)) + .cloned() + .collect(); + if cands.len() == 1 { + let func_name = cands.remove(0); + let dst = self.next_value_id(); + self.emit_legacy_call( + Some(dst), + CallTarget::Global(func_name), + arg_values.to_vec() + )?; + return Ok(Some(dst)); + } + } + } + Ok(None) + } + + /// Debug trace for receiver (if enabled) + fn trace_receiver_if_enabled(&self, object: &ASTNode, object_value: ValueId) { + if std::env::var("NYASH_DEBUG_PARAM_RECEIVER").ok().as_deref() == Some("1") { + if let ASTNode::Variable { name, .. } = object { + eprintln!("[DEBUG/param-recv] build_method_call receiver '{}' → ValueId({})", + name, object_value.0); + if let Some(origin) = self.value_origin_newbox.get(&object_value) { + eprintln!("[DEBUG/param-recv] origin: {}", origin); + } + if let Some(&mapped_id) = self.variable_map.get(name) { + eprintln!("[DEBUG/param-recv] variable_map['{}'] = ValueId({})", name, mapped_id.0); + if mapped_id != object_value { + eprintln!("[DEBUG/param-recv] ⚠️ MISMATCH! build_expression returned different ValueId!"); + } + } else { + eprintln!("[DEBUG/param-recv] ⚠️ '{}' NOT FOUND in variable_map!", name); + } + eprintln!("[DEBUG/param-recv] current_block: {:?}", self.current_block); + } + } + } +} diff --git a/src/mir/builder/calls/emit.rs b/src/mir/builder/calls/emit.rs new file mode 100644 index 00000000..b63b580c --- /dev/null +++ b/src/mir/builder/calls/emit.rs @@ -0,0 +1,415 @@ +//! 🎯 箱理論: Call命令発行専用モジュール +//! +//! 責務: MIR Call命令の発行のみ +//! - emit_unified_call: 統一Call発行(Phase 3対応) +//! - emit_legacy_call: レガシーCall発行(既存互換) +//! - emit_global_call/emit_method_call/emit_constructor_call: 便利ラッパー + +use super::super::{Effect, EffectMask, MirBuilder, MirInstruction, ValueId}; +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 + pub fn emit_unified_call( + &mut self, + dst: Option, + target: CallTarget, + args: Vec, + ) -> Result<(), String> { + // Check environment variable for unified call usage + if !call_unified::is_unified_call_enabled() { + // Fall back to legacy implementation + return self.emit_legacy_call(dst, target, args); + } + + // 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 = 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 the new module + if let CallTarget::Global(ref _n) = target { /* dev trace removed */ } + // Fallback: if Global target is unknown, try unique static-method mapping (name/arity) + let mut callee = match call_unified::convert_target_to_callee( + target.clone(), + &self.value_origin_newbox, + &self.value_types, + ) { + 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)?; + + // 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 + call_unified::validate_call_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); + return self.emit_box_or_plugin_call(dst, *r, method.clone(), None, args, effects); + } + } + + // Finalize operands in current block (EmitGuardBox wrapper) + let mut callee = callee; + let mut args_local: Vec = 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( + &mut self, + dst: Option, + target: CallTarget, + args: Vec, + ) -> Result<(), String> { + match target { + CallTarget::Method { receiver, method, box_type: _ } => { + // LEGACY PATH (after unified migration): + // Instance→Function rewrite is centralized in unified call path. + // Legacy path no longer functionizes; always use Box/Plugin call here. + self.emit_box_or_plugin_call(dst, receiver, method, None, args, EffectMask::IO) + }, + CallTarget::Constructor(box_type) => { + // Use existing NewBox + let dst = dst.ok_or("Constructor must have destination")?; + self.emit_instruction(MirInstruction::NewBox { + dst, + box_type, + args, + }) + }, + CallTarget::Extern(name) => { + // Use existing ExternCall + let mut args = args; + crate::mir::builder::ssa::local::finalize_args(self, &mut args); + let parts: Vec<&str> = name.splitn(2, '.').collect(); + let (iface, method) = if parts.len() == 2 { + (parts[0].to_string(), parts[1].to_string()) + } else { + ("nyash".to_string(), name) + }; + + self.emit_instruction(MirInstruction::ExternCall { + dst, + iface_name: iface, + method_name: method, + args, + effects: EffectMask::IO, + }) + }, + CallTarget::Global(name) => { + self.emit_global_unified(dst, name, args) + }, + CallTarget::Value(func_val) => { + self.emit_value_unified(dst, func_val, args) + }, + CallTarget::Closure { params, captures, me_capture } => { + let dst = dst.ok_or("Closure creation must have destination")?; + self.emit_instruction(MirInstruction::NewClosure { + dst, + params, + body: vec![], // Empty body for now + captures, + me: me_capture, + }) + }, + } + } + + // Phase 2 Migration: Convenience methods that use emit_unified_call + + /// Emit a global function call (print, panic, etc.) + pub fn emit_global_call( + &mut self, + dst: Option, + name: String, + args: Vec, + ) -> Result<(), String> { + self.emit_unified_call(dst, CallTarget::Global(name), args) + } + + /// Emit a method call (box.method) + pub fn emit_method_call( + &mut self, + dst: Option, + receiver: ValueId, + method: String, + args: Vec, + ) -> Result<(), String> { + self.emit_unified_call( + dst, + CallTarget::Method { + box_type: None, // Auto-infer + method, + receiver, + }, + args, + ) + } + + /// Emit a constructor call (new BoxType) + pub fn emit_constructor_call( + &mut self, + dst: ValueId, + box_type: String, + args: Vec, + ) -> Result<(), String> { + self.emit_unified_call( + Some(dst), + CallTarget::Constructor(box_type), + args, + ) + } + + // ======================================== + // Private helper methods (small functions) + // ======================================== + + /// Try fallback handlers for global functions + fn try_global_fallback_handlers( + &mut self, + dst: Option, + name: &str, + args: &[ValueId], + ) -> Result, String> { + // 0) Dev-only safety: treat condition_fn as always-true predicate when missing + if name == "condition_fn" { + let dstv = dst.unwrap_or_else(|| self.next_value_id()); + // Emit integer constant via ConstantEmissionBox + let one = crate::mir::builder::emission::constant::emit_integer(self, 1); + if dst.is_none() { + // If a destination was not provided, copy into the allocated dstv + self.emit_instruction(MirInstruction::Copy { dst: dstv, src: one })?; + crate::mir::builder::metadata::propagate::propagate(self, one, dstv); + } else { + // If caller provided dst, ensure the computed value lands there + self.emit_instruction(MirInstruction::Copy { dst: dstv, src: one })?; + crate::mir::builder::metadata::propagate::propagate(self, one, dstv); + } + return Ok(Some(())); + } + + // 1) Direct module function fallback: call by name if present + if let Some(ref module) = self.current_module { + if module.functions.contains_key(name) { + let dstv = dst.unwrap_or_else(|| self.next_value_id()); + let name_const = crate::mir::builder::name_const::make_name_const_result(self, name)?; + self.emit_instruction(MirInstruction::Call { + dst: Some(dstv), + func: name_const, + callee: Some(Callee::Global(name.to_string())), + args: args.to_vec(), + effects: EffectMask::IO, + })?; + self.annotate_call_result_from_func_name(dstv, name); + return Ok(Some(())); + } + } + + // 2) Unique static-method fallback: name+arity → Box.name/Arity + if let Some(cands) = self.static_method_index.get(name) { + let mut matches: Vec<(String, usize)> = cands + .iter() + .cloned() + .filter(|(_, ar)| *ar == args.len()) + .collect(); + if matches.len() == 1 { + let (bx, _arity) = matches.remove(0); + let func_name = format!("{}.{}{}", bx, name, format!("/{}", args.len())); + // Emit legacy call directly to preserve behavior + let dstv = dst.unwrap_or_else(|| self.next_value_id()); + let name_const = crate::mir::builder::name_const::make_name_const_result(self, &func_name)?; + self.emit_instruction(MirInstruction::Call { + dst: Some(dstv), + func: name_const, + callee: Some(Callee::Global(func_name.clone())), + args: args.to_vec(), + effects: EffectMask::IO, + })?; + // annotate + self.annotate_call_result_from_func_name(dstv, func_name); + return Ok(Some(())); + } + } + + Ok(None) + } + + /// Ensure receiver is materialized in Callee::Method + fn materialize_receiver_in_callee( + &mut self, + callee: Callee, + ) -> Result { + match callee { + Callee::Method { box_name, method, receiver: Some(r), certainty } => { + if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1") { + let current_fn = self + .current_function + .as_ref() + .map(|f| f.signature.name.clone()) + .unwrap_or_else(|| "".to_string()); + let bb = self.current_block; + let names: Vec = self + .variable_map + .iter() + .filter(|(_, &vid)| vid == r) + .map(|(k, _)| k.clone()) + .collect(); + // CRITICAL DEBUG: Show type information sources + let origin = self.value_origin_newbox.get(&r).cloned(); + let vtype = self.value_types.get(&r).cloned(); + eprintln!( + "[builder/recv-trace] fn={} bb={:?} method={}.{} recv=%{} aliases={:?}", + current_fn, + bb, + box_name.clone(), + method, + r.0, + names + ); + eprintln!( + "[builder/recv-trace] value_origin_newbox: {:?}, value_types: {:?}", + origin, vtype + ); + } + // Prefer pinning to a slot so start_new_block can propagate it across entries. + let r_pinned = self.pin_to_slot(r, "@recv").unwrap_or(r); + Ok(Callee::Method { box_name, method, receiver: Some(r_pinned), certainty }) + } + other => Ok(other), + } + } + + /// Emit global call with name constant + fn emit_global_unified( + &mut self, + dst: Option, + name: String, + args: Vec, + ) -> 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, + func_val: ValueId, + args: Vec, + ) -> 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, + }) + } +} diff --git a/src/mir/builder/calls/mod.rs b/src/mir/builder/calls/mod.rs index 2c3c1f58..35d073ac 100644 --- a/src/mir/builder/calls/mod.rs +++ b/src/mir/builder/calls/mod.rs @@ -3,8 +3,8 @@ //! 責務別に明確に分離された「箱」の集合: //! - lowering: 関数lowering(static/instance method → MIR function) //! - utils: ユーティリティ(resolve/parse/extract) -//! - emit: Call命令発行(統一Call/Legacy Call) [TODO] -//! - build: Call構築(function call/method call) [TODO] +//! - emit: Call命令発行(統一Call/Legacy Call) ✅ Phase 2完了 +//! - build: Call構築(function call/method call) ✅ Phase 2完了 // Existing modules (already implemented elsewhere) pub mod annotation; @@ -15,13 +15,15 @@ pub mod function_lowering; pub mod method_resolution; pub mod special_handlers; -// New refactored modules +// New refactored modules (Box Theory Phase 1 & 2) pub mod lowering; pub mod utils; -// pub mod emit; // TODO: To be created -// pub mod build; // TODO: To be created +pub mod emit; // Phase 2: Call emission +pub mod build; // Phase 2: Call building // Re-export public interfaces pub use call_target::CallTarget; pub use lowering::*; pub use utils::*; +pub use emit::*; +pub use build::*;