//! Method call handlers for MIR builder //! //! This module contains specialized handlers for different types of method calls, //! following the Single Responsibility Principle. use crate::ast::ASTNode; use crate::mir::builder::builder_calls::CallTarget; use crate::mir::builder::calls::function_lowering; use crate::mir::builder::{MirBuilder, ValueId}; use crate::mir::{MirInstruction, MirType, TypeOpKind}; /// Me-call 専用のポリシー箱。 /// /// - 責務: /// - me.method(...) を「インスタンス呼び出し」か「static メソッド呼び出し」か判定する。 /// - static box 文脈で実体のない receiver を生まないように、静的メソッド降下にフォールバックする。 struct MeCallPolicyBox; impl MeCallPolicyBox { fn resolve_me_call( builder: &mut MirBuilder, method: &str, arguments: &[ASTNode], ) -> Result, String> { // Instance box: prefer enclosing box method (lowered function) if存在 let enclosing_cls: Option = builder .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 mut arg_values = Vec::with_capacity(arguments.len()); for a in arguments { arg_values.push(builder.build_expression(a.clone())?); } let arity = arg_values.len(); let fname = function_lowering::generate_method_function_name(cls, method, arity); if let Some(ref module) = builder.current_module { if let Some(func) = module.functions.get(&fname) { // Decide whether this lowered function expects an implicit receiver. // Instance methods: params[0] is Box(box_name) // Static methods: params[0] is non-Box or params.is_empty() let params = &func.signature.params; let is_instance_method = !params.is_empty() && matches!(params[0], MirType::Box(_)); // Expected argument count from signature (including receiver for instance) let expected_params = params.len(); let provided_static = arg_values.len(); let provided_instance = arg_values.len() + 1; // Build call_args based on method kind let call_args: Vec = if is_instance_method { // Instance method: prepend 'me' receiver if expected_params != provided_instance { if std::env::var("NYASH_ME_CALL_ARITY_STRICT").ok().as_deref() == Some("1") { return Err(format!( "[me-call] arity mismatch (instance): {}: declared {} params, got {} args(+me)", fname, expected_params, provided_instance )); } else if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") { eprintln!( "[me-call] arity mismatch (instance): {}: declared {} params, got {} args(+me)", fname, expected_params, provided_instance ); } } let me_id = builder.build_me_expression()?; let mut v = Vec::with_capacity(provided_instance); v.push(me_id); v.extend(arg_values.into_iter()); v } else { // Static method: no receiver if expected_params != provided_static { if std::env::var("NYASH_ME_CALL_ARITY_STRICT").ok().as_deref() == Some("1") { return Err(format!( "[me-call] arity mismatch (static): {}: declared {} params, got {} args", fname, expected_params, provided_static )); } else if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") { eprintln!( "[me-call] arity mismatch (static): {}: declared {} params, got {} args", fname, expected_params, provided_static ); } } arg_values }; let dst = builder.next_value_id(); // Emit as unified global call to lowered function builder.emit_unified_call( Some(dst), CallTarget::Global(fname.clone()), call_args, )?; builder.annotate_call_result_from_func_name(dst, &fname); return Ok(Some(dst)); } } // Fallback: treat me.method(...) as a static method on the enclosing box. // - 旧挙動では me を Box 値として Method 呼び出ししようとしていたが、 // static box 文脈では実体インスタンスが存在しないため UndefinedValue を生みやすい。 // - ここでは receiver を持たない Global call に揃え、FuncScannerBox などの // static ヘルパー呼び出しを安全に扱う。 let static_dst = builder.handle_static_method_call(cls, method, arguments)?; return Ok(Some(static_dst)); } Ok(None) } } impl MirBuilder { /// Handle static method calls: BoxName.method(args) pub(super) fn handle_static_method_call( &mut self, box_name: &str, method: &str, arguments: &[ASTNode], ) -> Result { // Build argument values let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.build_expression(arg.clone())?); } // Compose lowered function name: BoxName.method/N let func_name = format!("{}.{}/{}", box_name, method, arg_values.len()); let dst = self.next_value_id(); if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") { eprintln!("[builder] static-call {}", func_name); } // Emit unified global call to the static-lowered function (module-local) self.emit_unified_call(Some(dst), CallTarget::Global(func_name), arg_values)?; Ok(dst) } /// Handle TypeOp method calls: value.is("Type") and value.as("Type") pub(super) fn handle_typeop_method( &mut self, object_value: ValueId, method: &str, type_name: &str, ) -> Result { let mir_ty = Self::parse_type_name_to_mir(type_name); let dst = self.next_value_id(); let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast }; self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty, })?; Ok(dst) } /// Check if this is a TypeOp method call #[allow(dead_code)] pub(super) fn is_typeop_method(method: &str, arguments: &[ASTNode]) -> Option { if (method == "is" || method == "as") && arguments.len() == 1 { Self::extract_string_literal(&arguments[0]) } else { None } } /// Handle me.method() calls within static box context pub(super) fn handle_me_method_call( &mut self, method: &str, arguments: &[ASTNode], ) -> Result, String> { MeCallPolicyBox::resolve_me_call(self, method, arguments) } /// Handle standard Box/Plugin method calls (fallback) pub(super) fn handle_standard_method_call( &mut self, object_value: ValueId, method: String, arguments: &[ASTNode], ) -> Result { // Build argument values let mut arg_values = Vec::new(); for arg in arguments { arg_values.push(self.build_expression(arg.clone())?); } // Receiver class hintは emit_unified_call 側で起源/型から判断する(重複回避) // 統一経路: emit_unified_call に委譲(RouterPolicy と rewrite::* で安定化) let dst = self.next_value_id(); self.emit_unified_call( Some(dst), CallTarget::Method { box_type: None, method, receiver: object_value, }, arg_values, )?; Ok(dst) } }