Files
hakorune/src/mir/builder/calls/build.rs

522 lines
20 KiB
Rust

//! 🎯 箱理論: 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<ASTNode>,
) -> Result<ValueId, String> {
// 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(|| "<none>".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<ASTNode>,
) -> Result<ValueId, String> {
// Debug: Check recursion depth
const MAX_METHOD_DEPTH: usize = 100;
self.recursion_depth += 1;
if self.recursion_depth > MAX_METHOD_DEPTH {
eprintln!("[FATAL] build_method_call recursion depth exceeded {}", MAX_METHOD_DEPTH);
eprintln!("[FATAL] Current depth: {}", self.recursion_depth);
eprintln!("[FATAL] Method: {}", method);
return Err(format!("build_method_call recursion depth exceeded: {}", self.recursion_depth));
}
let result = self.build_method_call_impl(object, method, arguments);
self.recursion_depth -= 1;
result
}
fn build_method_call_impl(
&mut self,
object: ASTNode,
method: String,
arguments: Vec<ASTNode>,
) -> Result<ValueId, String> {
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<ASTNode>,
) -> Result<ValueId, String> {
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<Option<ValueId>, 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<ASTNode>,
) -> Option<Result<ValueId, String>> {
if !special_handlers::is_math_function(name) {
return None;
}
// Build numeric args directly for math.* to preserve f64 typing
let mut math_args: Vec<ValueId> = 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<ASTNode>,
) -> Option<Result<ValueId, String>> {
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<ValueId, String> {
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<Vec<ValueId>, 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<ValueId, String> {
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<ValueId>,
) -> Result<ValueId, String> {
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<ValueId>,
) -> Result<ValueId, String> {
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<Option<ValueId>, 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<Option<ValueId>, String> {
// Instance box: prefer enclosing box method
let enclosing_cls: Option<String> = 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<ASTNode> = 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<Option<ValueId>, 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<Option<ValueId>, 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<String> = 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);
}
}
}
}