Files
hakorune/src/mir/builder/method_call_handlers.rs
2025-11-24 14:17:02 +09:00

223 lines
9.1 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.

//! 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<Option<ValueId>, String> {
// Instance box: prefer enclosing box method (lowered function) if存在
let enclosing_cls: Option<String> = 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<ValueId> = 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<ValueId, String> {
// 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<ValueId, String> {
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<String> {
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<Option<ValueId>, 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<ValueId, String> {
// 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)
}
}