Files
hakorune/src/mir/builder/method_call_handlers.rs

223 lines
9.1 KiB
Rust
Raw Normal View History

//! 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)
}
}