Phase 25.1 完了成果: - ✅ LoopForm v2 テスト・ドキュメント・コメント完備 - 4ケース(A/B/C/D)完全テストカバレッジ - 最小再現ケース作成(SSAバグ調査用) - SSOT文書作成(loopform_ssot.md) - 全ソースに [LoopForm] コメントタグ追加 - ✅ Stage-1 CLI デバッグ環境構築 - stage1_cli.hako 実装 - stage1_bridge.rs ブリッジ実装 - デバッグツール作成(stage1_debug.sh/stage1_minimal.sh) - アーキテクチャ改善提案文書 - ✅ 環境変数削減計画策定 - 25変数の完全調査・分類 - 6段階削減ロードマップ(25→5、80%削減) - 即時削除可能変数特定(NYASH_CONFIG/NYASH_DEBUG) Phase 26-D からの累積変更: - PHI実装改善(ExitPhiBuilder/HeaderPhiBuilder等) - MIRビルダーリファクタリング - 型伝播・最適化パス改善 - その他約300ファイルの累積変更 🎯 技術的成果: - SSAバグ根本原因特定(条件分岐内loop変数変更) - Region+next_iパターン適用完了(UsingCollectorBox等) - LoopFormパターン文書化・テスト化完了 - セルフホスティング基盤強化 Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: ChatGPT <noreply@openai.com> Co-Authored-By: Task Assistant <task@anthropic.com>
382 lines
14 KiB
Rust
382 lines
14 KiB
Rust
/*!
|
||
* 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::HashMap;
|
||
|
||
/// Callee解決専用箱
|
||
///
|
||
/// 箱理論:
|
||
/// - 単一責務: CallTarget → Callee の型安全な解決のみ
|
||
/// - 状態保持: 型情報参照を保持して効率化
|
||
/// - ピュア解決器: 入力CallTarget → 解決・検証 → 出力Callee
|
||
pub struct CalleeResolverBox<'a> {
|
||
/// 変数のnewbox起源マップ(ValueId → Box名)
|
||
value_origin_newbox: &'a HashMap<ValueId, String>,
|
||
/// 型情報マップ(ValueId → MirType)
|
||
value_types: &'a HashMap<ValueId, MirType>,
|
||
/// 型レジストリ(オプショナル)
|
||
type_registry: Option<&'a TypeRegistry>,
|
||
}
|
||
|
||
impl<'a> CalleeResolverBox<'a> {
|
||
/// 新しいCalleeResolverBoxを作成
|
||
pub fn new(
|
||
value_origin_newbox: &'a HashMap<ValueId, String>,
|
||
value_types: &'a HashMap<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. 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<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;
|
||
}
|
||
}
|
||
|
||
// 従来: 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"),
|
||
}
|
||
}
|
||
}
|