/*! * CalleeResolverBox - Callee解決専用箱 * * 箱理論の実践: * - 箱にする: Callee解決ロジックを1箱に集約 * - 境界を作る: 型情報を保持し、効率的な解決を実現 * - 状態最小: 型情報参照のみ(変更なし) * * 責務: * - CallTarget → Callee への解決 * - Box種別分類(StaticCompiler/RuntimeData/UserDefined) * - Call引数検証 */ use crate::mir::{Callee, MirType, ValueId}; use crate::mir::builder::CallTarget; use crate::mir::definitions::call_unified::{CalleeBoxKind, TypeCertainty}; use crate::mir::builder::type_registry::TypeRegistry; use super::method_resolution; use std::collections::HashMap; /// Callee解決専用箱 /// /// 箱理論: /// - 単一責務: CallTarget → Callee の型安全な解決のみ /// - 状態保持: 型情報参照を保持して効率化 /// - ピュア解決器: 入力CallTarget → 解決・検証 → 出力Callee pub struct CalleeResolverBox<'a> { /// 変数のnewbox起源マップ(ValueId → Box名) value_origin_newbox: &'a HashMap, /// 型情報マップ(ValueId → MirType) value_types: &'a HashMap, /// 型レジストリ(オプショナル) type_registry: Option<&'a TypeRegistry>, } impl<'a> CalleeResolverBox<'a> { /// 新しいCalleeResolverBoxを作成 pub fn new( value_origin_newbox: &'a HashMap, value_types: &'a HashMap, type_registry: Option<&'a TypeRegistry>, ) -> Self { CalleeResolverBox { value_origin_newbox, value_types, type_registry, } } /// CallTarget → Callee への型安全な解決 /// /// 箱理論の「境界を作る」原則: /// - Global/Method/Constructor/Extern/Value/Closureを明確に分類 /// - 型情報を活用してbox_nameとbox_kindを決定 /// /// 🎯 TypeRegistry対応: NYASH_USE_TYPE_REGISTRY=1 で registry 優先 pub fn resolve(&self, target: CallTarget) -> Result { let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY") .ok() .as_deref() == Some("1"); match target { CallTarget::Global(name) => { // Prefer explicit categories; otherwise treat as module-global function if method_resolution::is_builtin_function(&name) { Ok(Callee::Global(name)) } else if method_resolution::is_extern_function(&name) { Ok(Callee::Extern(name)) } else { // Module-local or static lowered function (e.g., "Box.method/N") Ok(Callee::Global(name)) } }, CallTarget::Method { box_type, method, receiver } => { // 🔍 Debug: trace box_name resolution let trace_enabled = std::env::var("NYASH_CALLEE_RESOLVE_TRACE") .ok() .as_deref() == Some("1"); if trace_enabled { eprintln!("[callee-resolve] receiver=%{} method={}", receiver.0, method); eprintln!("[callee-resolve] explicit box_type: {:?}", box_type); eprintln!("[callee-resolve] use_registry: {}", use_registry); } let inferred_box_type = self.infer_box_type(receiver, box_type, trace_enabled, use_registry); // Certainty is Known when we have explicit origin or Box型の型情報を持つ場合 let has_box_type = self.value_types .get(&receiver) .map(|t| matches!(t, MirType::Box(_))) .unwrap_or(false); let certainty = if self.value_origin_newbox.contains_key(&receiver) || has_box_type { TypeCertainty::Known } else { TypeCertainty::Union }; // Classify box kind to prevent static/runtime mixing let box_kind = self.classify_box_kind(&inferred_box_type); if trace_enabled { eprintln!("[callee-resolve] inferred_box_name: {}", inferred_box_type); eprintln!("[callee-resolve] box_kind: {:?}", box_kind); } Ok(Callee::Method { box_name: inferred_box_type, method, receiver: Some(receiver), certainty, box_kind, }) }, CallTarget::Constructor(box_type) => { Ok(Callee::Constructor { box_type }) }, CallTarget::Extern(name) => { Ok(Callee::Extern(name)) }, CallTarget::Value(func_val) => { Ok(Callee::Value(func_val)) }, CallTarget::Closure { params, captures, me_capture } => { Ok(Callee::Closure { params, captures, me_capture }) }, } } /// Box種別の分類 /// /// 箱理論の「箱にする」原則: /// - 静的コンパイラBox群を明示的に列挙(1箇所に集約) /// - ランタイムDataBox群を明示的に列挙 /// - ユーザー定義Boxをデフォルト扱い pub fn classify_box_kind(&self, box_name: &str) -> CalleeBoxKind { // Static compiler boxes (Stage-B, Stage-1, parsers, resolvers) // These should ONLY appear in static method lowering, never in runtime method dispatch match box_name { // Stage-B compiler boxes "StageBArgsBox" | "StageBBodyExtractorBox" | "StageBDriverBox" | // Stage-1 using/namespace resolver boxes "Stage1UsingResolverBox" | "BundleResolver" | // Parser boxes "ParserBox" | "ParserStmtBox" | "ParserExprBox" | "ParserControlBox" | "ParserLiteralBox" | "ParserTokenBox" | // Scanner/builder boxes "FuncScannerBox" | "MirBuilderBox" | // LoopSSA / Exit PHI analyzers (Stage-1/Stage-B) "BreakFinderBox" | "PhiInjectorBox" | "LoopSSA" | // Other compiler-internal boxes "JsonFragBox" => CalleeBoxKind::StaticCompiler, // Runtime data boxes (built-in types that handle actual runtime values) "MapBox" | "ArrayBox" | "StringBox" | "IntegerBox" | "BoolBox" | "FloatBox" | "NullBox" | "VoidBox" | "UnknownBox" | "FileBox" | "ConsoleBox" | "PathBox" => CalleeBoxKind::RuntimeData, // Everything else is user-defined _ => CalleeBoxKind::UserDefined, } } /// Call引数の検証 /// /// 既知の関数/メソッドについてarity等を検証 pub fn validate_args(&self, callee: &Callee, args: &[ValueId]) -> Result<(), String> { match callee { Callee::Global(name) => { // Check known global functions match name.as_str() { "print" | "error" | "panic" => { if args.is_empty() { return Err(format!("{} requires at least one argument", name)); } } "exit" => { if args.len() != 1 { return Err("exit requires exactly one argument (exit code)".to_string()); } } _ => {} // Unknown functions pass through } }, Callee::Method { box_name, method, .. } => { // Validate known methods match (box_name.as_str(), method.as_str()) { ("ArrayBox", "get") | ("ArrayBox", "set") => { if args.is_empty() { return Err(format!("ArrayBox.{} requires an index", method)); } } _ => {} // Unknown methods pass through } }, _ => {} // Other callee types don't have validation yet } Ok(()) } /// Box型の推論(内部ヘルパー) /// /// 優先順位: /// 1. TypeRegistry(NYASH_USE_TYPE_REGISTRY=1の場合) /// 2. value_types(MirType::Box) /// 3. value_origin_newbox(起源情報) /// 4. "UnknownBox"(フォールバック) fn infer_box_type( &self, receiver: ValueId, explicit_box_type: Option, trace_enabled: bool, use_registry: bool, ) -> String { explicit_box_type.unwrap_or_else(|| { // 🎯 TypeRegistry 対応: 優先して registry から推論 if use_registry { if let Some(reg) = self.type_registry { let inferred = reg.infer_class(receiver, None); if trace_enabled { eprintln!("[callee-resolve] from_registry: {}", inferred); // トレースチェーン表示 let chain = reg.trace_origin(receiver); if !chain.is_empty() { eprintln!("[callee-resolve] trace_chain: {:?}", chain); } } return inferred; } } // 従来: HashMap から推論(型情報を優先し、origin は補助とする) let from_type = self.value_types .get(&receiver) .and_then(|t| match t { MirType::Box(box_name) => Some(box_name.clone()), _ => None, }); let from_origin = self.value_origin_newbox.get(&receiver).cloned(); if trace_enabled { eprintln!("[callee-resolve] from_type: {:?}", from_type); eprintln!("[callee-resolve] from_origin: {:?}", from_origin); } // 型情報(MirType)がある場合はそれを優先し、無い場合のみ origin にフォールバックする。 from_type .or(from_origin) .unwrap_or_else(|| { if trace_enabled { eprintln!("[callee-resolve] FALLBACK: UnknownBox"); } "UnknownBox".to_string() }) }) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_classify_static_compiler_boxes() { let value_origin = HashMap::new(); let value_types = HashMap::new(); let resolver = CalleeResolverBox::new(&value_origin, &value_types, None); // Stage-B boxes assert_eq!(resolver.classify_box_kind("StageBArgsBox"), CalleeBoxKind::StaticCompiler); assert_eq!(resolver.classify_box_kind("StageBDriverBox"), CalleeBoxKind::StaticCompiler); // Stage-1 boxes assert_eq!(resolver.classify_box_kind("Stage1UsingResolverBox"), CalleeBoxKind::StaticCompiler); // Parser boxes assert_eq!(resolver.classify_box_kind("ParserBox"), CalleeBoxKind::StaticCompiler); } #[test] fn test_classify_runtime_data_boxes() { let value_origin = HashMap::new(); let value_types = HashMap::new(); let resolver = CalleeResolverBox::new(&value_origin, &value_types, None); assert_eq!(resolver.classify_box_kind("MapBox"), CalleeBoxKind::RuntimeData); assert_eq!(resolver.classify_box_kind("ArrayBox"), CalleeBoxKind::RuntimeData); assert_eq!(resolver.classify_box_kind("StringBox"), CalleeBoxKind::RuntimeData); assert_eq!(resolver.classify_box_kind("UnknownBox"), CalleeBoxKind::RuntimeData); } #[test] fn test_classify_user_defined_boxes() { let value_origin = HashMap::new(); let value_types = HashMap::new(); let resolver = CalleeResolverBox::new(&value_origin, &value_types, None); assert_eq!(resolver.classify_box_kind("MyCustomBox"), CalleeBoxKind::UserDefined); assert_eq!(resolver.classify_box_kind("PersonBox"), CalleeBoxKind::UserDefined); } #[test] fn test_resolve_global() { let value_origin = HashMap::new(); let value_types = HashMap::new(); let resolver = CalleeResolverBox::new(&value_origin, &value_types, None); let target = CallTarget::Global("print".to_string()); let result = resolver.resolve(target).unwrap(); match result { Callee::Global(name) => assert_eq!(name, "print"), _ => panic!("Expected Global callee"), } } #[test] fn test_resolve_constructor() { let value_origin = HashMap::new(); let value_types = HashMap::new(); let resolver = CalleeResolverBox::new(&value_origin, &value_types, None); let target = CallTarget::Constructor("StringBox".to_string()); let result = resolver.resolve(target).unwrap(); match result { Callee::Constructor { box_type } => assert_eq!(box_type, "StringBox"), _ => panic!("Expected Constructor callee"), } } }