//! 🎯 箱理論: Call構築専用モジュール //! //! 責務: ASTからCall構築のみ //! - build_function_call: 関数呼び出し構築 //! - build_method_call: メソッド呼び出し構築 //! - build_from_expression: from式構築 use super::super::{Effect, EffectMask, MirBuilder, MirInstruction, MirType, ValueId}; use super::special_handlers; use super::CallTarget; use crate::ast::{ASTNode, LiteralValue}; use crate::mir::TypeOpKind; 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 .scope_ctx .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.comp_ctx.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 { // 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, ) -> 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 ); } // 0. Dev-only: __mir__.log / __mir__.mark → MirInstruction::DebugLog へ直接 lowering if let ASTNode::Variable { name: obj_name, .. } = &object { if obj_name == "__mir__" { if let Some(result) = self.try_build_mir_debug_call(&method, &arguments)? { return Ok(result); } } } // 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.handle_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.type_ctx .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_ctx.variable_map.contains_key(obj_name); // Debug trace if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") { eprintln!( "[DEBUG] try_build_static_method_call: obj_name={}, method={}", obj_name, method ); eprintln!("[DEBUG] is_local_var={}", is_local_var); if is_local_var { eprintln!("[DEBUG] variable_map contains '{}' - treating as local variable, will use method call", obj_name); eprintln!( "[DEBUG] variable_map keys: {:?}", self.variable_ctx.variable_map.keys().collect::>() ); } else { eprintln!("[DEBUG] '{}' not in variable_map - treating as static box, will use global call", 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) } /// Dev-only: __mir__.log / __mir__.mark → MirInstruction::DebugLog への変換 /// /// 構文: /// __mir__.log("label", v1, v2, ...) /// __mir__.mark("label") /// /// - 第一引数は String リテラル想定(それ以外はこのハンドラをスキップして通常の解決に回す)。 /// - 戻り値は Void 定数の ValueId(式コンテキストでも型破綻しないようにするため)。 fn try_build_mir_debug_call( &mut self, method: &str, arguments: &[ASTNode], ) -> Result, String> { if method != "log" && method != "mark" { return Ok(None); } if arguments.is_empty() { return Err("__mir__.log/__mir__.mark requires at least a label argument".to_string()); } // 第一引数は String リテラルのみ対応(それ以外は通常経路にフォールバック) let label = match &arguments[0] { ASTNode::Literal { value: LiteralValue::String(s), .. } => s.clone(), _ => { // ラベルがリテラルでない場合はこのハンドラをスキップし、通常の static box 解決に任せる return Ok(None); } }; // 残りの引数を評価して ValueId を集める let mut values: Vec = Vec::new(); if method == "log" { for arg in &arguments[1..] { values.push(self.build_expression(arg.clone())?); } } // MIR に DebugLog 命令を 1 つ挿入(意味論は NYASH_MIR_DEBUG_LOG=1 のときにだけ効く) self.emit_instruction(MirInstruction::DebugLog { message: label, values, })?; // 式コンテキスト用に Void 定数を返す(呼び出し元では通常使われない) let void_id = crate::mir::builder::emission::constant::emit_void(self); Ok(Some(void_id)) } /// 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.comp_ctx.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.type_ctx.value_origin_newbox.get(&object_value) { eprintln!("[DEBUG/param-recv] origin: {}", origin); } if let Some(&mapped_id) = self.variable_ctx.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 ); } } } }