Files
hakorune/src/mir/builder/calls/resolver.rs
nyash-codex 7812c3d4c1 feat(phi): Phase 25.1 - BTreeMap移行 (21ファイル、80%決定性達成)
## 修正内容

### Core MIR/PHI (5ファイル)
- builder.rs: variable_map, value_types, value_origin_newbox
- context.rs: 3つのマップ
- loop_builder.rs: 3箇所
- loop_snapshot_manager.rs: snapshot マップ
- loop_snapshot_merge.rs: 2箇所

### MIR関連 (4ファイル)
- function.rs: FunctionMetadata.value_types
- resolver.rs: CalleeResolverBox
- guard.rs: CalleeGuardBox
- loop_common.rs: apply_increment_before_continue

### JSON Bridge (5ファイル)
- json_v0_bridge/lowering.rs
- json_v0_bridge/lowering/expr.rs
- json_v0_bridge/lowering/if_else.rs
- json_v0_bridge/lowering/merge.rs
- json_v0_bridge/lowering/try_catch.rs
- json_v0_bridge/mod.rs

### Printer & Providers (4ファイル)
- printer.rs, printer_helpers.rs
- host_providers/mir_builder.rs
- backend/mir_interpreter/handlers/extern_provider.rs

### Tests (3ファイル)
- phi_core/conservative.rs
- tests/json_program_loop.rs
- tests/mir_stage1_using_resolver_verify.rs (2テスト有効化)

## テスト結果
- mir_stage1_using_resolver_resolve_with_modules_map_verifies: 80%成功率
- 完全な決定性は未達成 (HashMap 86箇所、HashSet 63箇所が残存)

🐱 Generated with Claude Code
2025-11-22 05:33:40 +09:00

382 lines
14 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.

/*!
* CalleeResolverBox - Callee解決専用箱
*
* 箱理論の実践:
* - 箱にする: Callee解決ロジックを1箱に集約
* - 境界を作る: 型情報を保持し、効率的な解決を実現
* - 状態最小: 型情報参照のみ(変更なし)
*
* 責務:
* - CallTarget → Callee への解決
* - Box種別分類StaticCompiler/RuntimeData/UserDefined
* - Call引数検証
*/
use super::method_resolution;
use crate::mir::builder::type_registry::TypeRegistry;
use crate::mir::builder::CallTarget;
use crate::mir::definitions::call_unified::{CalleeBoxKind, TypeCertainty};
use crate::mir::{Callee, MirType, ValueId};
use std::collections::BTreeMap;
/// Callee解決専用箱
///
/// 箱理論:
/// - 単一責務: CallTarget → Callee の型安全な解決のみ
/// - 状態保持: 型情報参照を保持して効率化
/// - ピュア解決器: 入力CallTarget → 解決・検証 → 出力Callee
pub struct CalleeResolverBox<'a> {
/// 変数のnewbox起源マップValueId → Box名
value_origin_newbox: &'a BTreeMap<ValueId, String>,
/// 型情報マップValueId → MirType
value_types: &'a BTreeMap<ValueId, MirType>,
/// 型レジストリ(オプショナル)
type_registry: Option<&'a TypeRegistry>,
}
impl<'a> CalleeResolverBox<'a> {
/// 新しいCalleeResolverBoxを作成
pub fn new(
value_origin_newbox: &'a BTreeMap<ValueId, String>,
value_types: &'a BTreeMap<ValueId, MirType>,
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<Callee, String> {
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. TypeRegistryNYASH_USE_TYPE_REGISTRY=1の場合
/// 2. value_typesMirType::Box
/// 3. value_origin_newbox起源情報
/// 4. "UnknownBox"(フォールバック)
fn infer_box_type(
&self,
receiver: ValueId,
explicit_box_type: Option<String>,
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;
}
}
// 従来: BTreeMap から推論型情報を優先し、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 = BTreeMap::new();
let value_types = BTreeMap::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 = BTreeMap::new();
let value_types = BTreeMap::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 = BTreeMap::new();
let value_types = BTreeMap::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 = BTreeMap::new();
let value_types = BTreeMap::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 = BTreeMap::new();
let value_types = BTreeMap::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"),
}
}
}