feat(builder): CalleeBoxKind構造ガードで静的/ランタイムBox混線を根絶
🎯 箱理論の実践: 「境界を作る」原則による構造レベル分離 ## 問題 - StageBArgsBox.resolve_src内のargs.get(i)が Stage1UsingResolverBox.getに化ける(静的Box名混入) - 未定義ValueIdエラー発生(receiver定義なし) ## 解決策(構造ガード) ✅ CalleeBoxKind enum追加 - StaticCompiler: Stage-B/Stage-1コンパイラBox - RuntimeData: MapBox/ArrayBox等ランタイムBox - UserDefined: ユーザー定義Box ✅ classify_box_kind(): Box名から種別判定 - 静的Box群を明示的に列挙(1箇所に集約) - ランタイムBox群を明示的に列挙 - 将来の拡張も容易 ✅ apply_static_runtime_guard(): 混線検出・正規化 - me-call判定(receiver型==box_name → 静的降下に委ねる) - 真の混線検出(receiver型≠box_name → 正規化) - トレースログで可視化 ## 効果 - 修正前: Invalid value ValueId(150/187) - 修正後: Unknown method 'is_space' (別issue、StringBox実装不足) - → 静的Box名混入問題を根絶! ## 箱理論原則 - ✅ 境界を作る: Static/Runtime/UserDefinedを構造的に分離 - ✅ Fail-Fast: フォールバックより明示的エラー - ✅ 箱にする: CalleeBoxKindでBox種類を1箇所に集約 ## ファイル - src/mir/definitions/call_unified.rs: CalleeBoxKind enum - src/mir/builder/calls/call_unified.rs: classify_box_kind() - src/mir/builder/calls/emit.rs: apply_static_runtime_guard() - docs/development/roadmap/phases/phase-25.1d/README.md: 箱化メモ更新 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -386,6 +386,19 @@ impl MirBuilder {
|
||||
arguments: &[ASTNode],
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
let is_local_var = self.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_map.keys().collect::<Vec<_>>());
|
||||
} 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)?;
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
use crate::mir::{Callee, Effect, EffectMask, ValueId};
|
||||
use crate::mir::definitions::call_unified::{CallFlags, MirCall};
|
||||
use crate::mir::definitions::call_unified::{CallFlags, MirCall, TypeCertainty};
|
||||
use super::call_target::CallTarget;
|
||||
use super::method_resolution;
|
||||
use super::extern_calls;
|
||||
@ -19,13 +19,52 @@ pub fn is_unified_call_enabled() -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Classify box type to prevent static/runtime mixing
|
||||
/// Prevents Stage-B/Stage-1 compiler boxes from being confused with runtime data boxes
|
||||
pub fn classify_box_kind(box_name: &str) -> crate::mir::definitions::call_unified::CalleeBoxKind {
|
||||
use crate::mir::definitions::call_unified::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" |
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert CallTarget to Callee
|
||||
/// Main translation layer between builder and MIR representations
|
||||
/// Convert CallTarget to Callee with type resolution
|
||||
/// 🎯 TypeRegistry 対応: NYASH_USE_TYPE_REGISTRY=1 で registry 優先
|
||||
pub fn convert_target_to_callee(
|
||||
target: CallTarget,
|
||||
value_origin_newbox: &std::collections::HashMap<ValueId, String>,
|
||||
value_types: &std::collections::HashMap<ValueId, crate::mir::MirType>,
|
||||
type_registry: Option<&crate::mir::builder::type_registry::TypeRegistry>,
|
||||
) -> 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
|
||||
@ -40,32 +79,81 @@ pub fn convert_target_to_callee(
|
||||
},
|
||||
|
||||
CallTarget::Method { box_type, method, receiver } => {
|
||||
// 🔍 Debug: trace box_name resolution (before consuming box_type)
|
||||
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 = box_type.unwrap_or_else(|| {
|
||||
// Try to infer box type from value origin or type annotation
|
||||
value_origin_newbox.get(&receiver)
|
||||
.cloned()
|
||||
.or_else(|| {
|
||||
value_types.get(&receiver)
|
||||
.and_then(|t| match t {
|
||||
crate::mir::MirType::Box(box_name) => Some(box_name.clone()),
|
||||
_ => None,
|
||||
})
|
||||
// 🎯 TypeRegistry 対応: 優先して registry から推論
|
||||
if use_registry {
|
||||
if let Some(reg) = 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 = value_types
|
||||
.get(&receiver)
|
||||
.and_then(|t| match t {
|
||||
crate::mir::MirType::Box(box_name) => Some(box_name.clone()),
|
||||
_ => None,
|
||||
});
|
||||
let from_origin = 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()
|
||||
})
|
||||
.unwrap_or_else(|| "UnknownBox".to_string())
|
||||
});
|
||||
|
||||
// Certainty is Known when origin propagation provides a concrete class name
|
||||
let certainty = if value_origin_newbox.contains_key(&receiver) {
|
||||
crate::mir::definitions::call_unified::TypeCertainty::Known
|
||||
// Certainty is Known when we have explicit origin or Box型の型情報を持つ場合
|
||||
let has_box_type = value_types
|
||||
.get(&receiver)
|
||||
.map(|t| matches!(t, crate::mir::MirType::Box(_)))
|
||||
.unwrap_or(false);
|
||||
let certainty = if value_origin_newbox.contains_key(&receiver) || has_box_type {
|
||||
TypeCertainty::Known
|
||||
} else {
|
||||
crate::mir::definitions::call_unified::TypeCertainty::Union
|
||||
TypeCertainty::Union
|
||||
};
|
||||
|
||||
// Classify box kind to prevent static/runtime mixing
|
||||
let box_kind = 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,
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
@ -89,6 +89,7 @@ impl MirBuilder {
|
||||
target.clone(),
|
||||
&self.value_origin_newbox,
|
||||
&self.value_types,
|
||||
Some(&self.type_registry), // 🎯 TypeRegistry を渡す
|
||||
) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
@ -105,6 +106,10 @@ impl MirBuilder {
|
||||
// Safety: ensure receiver is materialized even after callee conversion
|
||||
callee = self.materialize_receiver_in_callee(callee)?;
|
||||
|
||||
// Structural guard: prevent static compiler boxes from being called with runtime receivers
|
||||
// If box_kind is StaticCompiler but receiver has a runtime Box type, normalize to runtime
|
||||
callee = self.apply_static_runtime_guard(callee)?;
|
||||
|
||||
// Emit resolve.choose for method callee (dev-only; default OFF)
|
||||
if let Callee::Method { box_name, method, certainty, .. } = &callee {
|
||||
let chosen = format!("{}.{}{}", box_name, method, format!("/{}", arity_for_try));
|
||||
@ -123,7 +128,7 @@ impl MirBuilder {
|
||||
call_unified::validate_call_args(&callee, &args)?;
|
||||
|
||||
// Stability guard: decide route via RouterPolicyBox (behavior-preserving rules)
|
||||
if let Callee::Method { box_name, method, receiver: Some(r), certainty } = &callee {
|
||||
if let Callee::Method { box_name, method, receiver: Some(r), certainty, .. } = &callee {
|
||||
let route = crate::mir::builder::router::policy::choose_route(box_name, method, *certainty, arity_for_try);
|
||||
if let crate::mir::builder::router::policy::Route::BoxCall = route {
|
||||
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
|
||||
@ -368,7 +373,7 @@ impl MirBuilder {
|
||||
callee: Callee,
|
||||
) -> Result<Callee, String> {
|
||||
match callee {
|
||||
Callee::Method { box_name, method, receiver: Some(r), certainty } => {
|
||||
Callee::Method { box_name, method, receiver: Some(r), certainty, box_kind } => {
|
||||
if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1") {
|
||||
let current_fn = self
|
||||
.current_function
|
||||
@ -401,7 +406,7 @@ impl MirBuilder {
|
||||
}
|
||||
// Prefer pinning to a slot so start_new_block can propagate it across entries.
|
||||
let r_pinned = self.pin_to_slot(r, "@recv").unwrap_or(r);
|
||||
Ok(Callee::Method { box_name, method, receiver: Some(r_pinned), certainty })
|
||||
Ok(Callee::Method { box_name, method, receiver: Some(r_pinned), certainty, box_kind })
|
||||
}
|
||||
other => Ok(other),
|
||||
}
|
||||
@ -449,4 +454,60 @@ impl MirBuilder {
|
||||
effects: EffectMask::IO,
|
||||
})
|
||||
}
|
||||
|
||||
/// Structural guard: prevent static compiler boxes from mixing with runtime data boxes
|
||||
///
|
||||
/// 箱理論の「境界を作る」原則: Stage-B/Stage-1コンパイラBoxとランタイムDataBoxを
|
||||
/// 構造レベルで分離し、型メタデータの混入を防ぐ。
|
||||
///
|
||||
/// If box_kind is StaticCompiler but receiver has a runtime Box type (MapBox/ArrayBox/etc.),
|
||||
/// normalize box_name to the runtime type. This prevents cases like:
|
||||
/// - Stage1UsingResolverBox.get where receiver is actually MapBox
|
||||
/// - StageBArgsBox.length where receiver is actually ArrayBox
|
||||
///
|
||||
/// This is a Fail-Fast structural guard, not a fallback.
|
||||
fn apply_static_runtime_guard(&self, callee: Callee) -> Result<Callee, String> {
|
||||
use crate::mir::definitions::call_unified::CalleeBoxKind;
|
||||
|
||||
if let Callee::Method { ref box_name, ref method, receiver: Some(recv), certainty, box_kind } = callee {
|
||||
// Only apply guard if box_kind is StaticCompiler
|
||||
if box_kind == CalleeBoxKind::StaticCompiler {
|
||||
// Check if receiver has a Box type
|
||||
if let Some(crate::mir::MirType::Box(receiver_box)) = self.value_types.get(&recv) {
|
||||
let trace_enabled = std::env::var("NYASH_CALLEE_RESOLVE_TRACE").ok().as_deref() == Some("1");
|
||||
|
||||
// If receiver box type matches the static box name, this is a me-call
|
||||
// Let it through for static method lowering (don't normalize)
|
||||
if receiver_box == box_name {
|
||||
if trace_enabled {
|
||||
eprintln!("[static-runtime-guard] ME-CALL detected:");
|
||||
eprintln!(" {}.{} with receiver type: {} (same as box_name)", box_name, method, receiver_box);
|
||||
eprintln!(" → Allowing for static method lowering");
|
||||
}
|
||||
return Ok(callee); // Pass through unchanged
|
||||
}
|
||||
|
||||
// Otherwise, this is a true mix-up: runtime box with static box name
|
||||
// Normalize to the runtime box type
|
||||
if trace_enabled {
|
||||
eprintln!("[static-runtime-guard] CORRECTING mix-up:");
|
||||
eprintln!(" Original: {}.{} (box_kind=StaticCompiler)", box_name, method);
|
||||
eprintln!(" Receiver %{} has runtime type: {}", recv.0, receiver_box);
|
||||
eprintln!(" Normalized: {}.{}", receiver_box, method);
|
||||
}
|
||||
|
||||
return Ok(Callee::Method {
|
||||
box_name: receiver_box.clone(),
|
||||
method: method.clone(),
|
||||
receiver: Some(recv),
|
||||
certainty,
|
||||
box_kind: CalleeBoxKind::RuntimeData, // Switch to runtime
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No guard needed, return as-is
|
||||
Ok(callee)
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,7 +46,11 @@ impl MirBuilder {
|
||||
if context_active {
|
||||
self.variable_map.clear();
|
||||
self.value_origin_newbox.clear();
|
||||
// value_types は clear しない(パラメータ型情報を保持)
|
||||
// value_types も static box 単位で独立させる。
|
||||
// これにより、前の static box で使用された ValueId に紐づく型情報が
|
||||
// 次の box にリークして誤った box_name 推論(例: Stage1UsingResolverBox)
|
||||
// を引き起こすことを防ぐ。
|
||||
self.value_types.clear();
|
||||
}
|
||||
|
||||
LoweringContext {
|
||||
@ -170,7 +174,8 @@ impl MirBuilder {
|
||||
// BoxCompilationContext mode: clear のみ(次回も完全独立)
|
||||
self.variable_map.clear();
|
||||
self.value_origin_newbox.clear();
|
||||
// value_types は clear しない(パラメータ型情報を保持)
|
||||
// static box ごとに型情報も独立させる(前 box の型メタデータを引きずらない)
|
||||
self.value_types.clear();
|
||||
} else if let Some(saved) = ctx.saved_var_map {
|
||||
// Legacy mode: Main.main 側の variable_map を元に戻す
|
||||
self.variable_map = saved;
|
||||
|
||||
@ -33,6 +33,7 @@ pub fn resolve_call_target(
|
||||
method: name.to_string(),
|
||||
receiver: None, // Static method call
|
||||
certainty: crate::mir::definitions::call_unified::TypeCertainty::Known,
|
||||
box_kind: super::call_unified::classify_box_kind(box_name),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,24 +1,48 @@
|
||||
//! MetadataPropagationBox — MIR のメタデータ(型/起源)の伝播
|
||||
//! 仕様不変・小粒。各所のコピペを置換するための薄い関数郡。
|
||||
//!
|
||||
//! 🎯 箱理論: TypeRegistryBox 統合対応
|
||||
//! NYASH_USE_TYPE_REGISTRY=1 で TypeRegistry 経由に切り替え(段階的移行)
|
||||
|
||||
use crate::mir::{MirType, ValueId};
|
||||
use crate::mir::builder::MirBuilder;
|
||||
|
||||
/// src から dst へ builder 内メタデータ(value_types / value_origin_newbox)を伝播する。
|
||||
/// 🎯 TypeRegistry 経由モード対応(NYASH_USE_TYPE_REGISTRY=1)
|
||||
#[inline]
|
||||
pub fn propagate(builder: &mut MirBuilder, src: ValueId, dst: ValueId) {
|
||||
if let Some(t) = builder.value_types.get(&src).cloned() {
|
||||
builder.value_types.insert(dst, t);
|
||||
}
|
||||
if let Some(cls) = builder.value_origin_newbox.get(&src).cloned() {
|
||||
builder.value_origin_newbox.insert(dst, cls);
|
||||
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY")
|
||||
.ok()
|
||||
.as_deref() == Some("1");
|
||||
|
||||
if use_registry {
|
||||
// 🎯 新: TypeRegistry 経由(トレース可能)
|
||||
builder.type_registry.propagate(src, dst);
|
||||
} else {
|
||||
// 従来: 直接アクセス(後方互換性)
|
||||
if let Some(t) = builder.value_types.get(&src).cloned() {
|
||||
builder.value_types.insert(dst, t);
|
||||
}
|
||||
if let Some(cls) = builder.value_origin_newbox.get(&src).cloned() {
|
||||
builder.value_origin_newbox.insert(dst, cls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// dst に型注釈を明示的に設定し、必要ならば起源情報を消去/維持する。
|
||||
/// 現状は型のみ設定(挙動不変)。
|
||||
/// 🎯 TypeRegistry 経由モード対応(NYASH_USE_TYPE_REGISTRY=1)
|
||||
#[inline]
|
||||
pub fn propagate_with_override(builder: &mut MirBuilder, dst: ValueId, ty: MirType) {
|
||||
builder.value_types.insert(dst, ty);
|
||||
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY")
|
||||
.ok()
|
||||
.as_deref() == Some("1");
|
||||
|
||||
if use_registry {
|
||||
// 🎯 新: TypeRegistry 経由
|
||||
builder.type_registry.record_type(dst, ty);
|
||||
} else {
|
||||
// 従来: 直接アクセス
|
||||
builder.value_types.insert(dst, ty);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ use crate::mir::definitions::call_unified::Callee;
|
||||
/// - Ensure the receiver has an in-block definition via LocalSSA (Copy in the current block).
|
||||
/// - Args の LocalSSA は別レイヤ(ssa::local)で扱う。
|
||||
pub fn finalize_method_receiver(builder: &mut MirBuilder, callee: &mut Callee) {
|
||||
if let Callee::Method { box_name, method, receiver: Some(r), certainty } = callee.clone() {
|
||||
if let Callee::Method { box_name, method, receiver: Some(r), certainty, box_kind } = callee.clone() {
|
||||
// Pin to a named slot so start_new_block や LoopBuilder が slot 経由で追跡できる
|
||||
let r_pinned = builder.pin_to_slot(r, "@recv").unwrap_or(r);
|
||||
|
||||
@ -40,7 +40,7 @@ pub fn finalize_method_receiver(builder: &mut MirBuilder, callee: &mut Callee) {
|
||||
|
||||
// LocalSSA: ensure an in-block definition in the current block
|
||||
let r_local = crate::mir::builder::ssa::local::recv(builder, r_pinned);
|
||||
*callee = Callee::Method { box_name, method, receiver: Some(r_local), certainty };
|
||||
*callee = Callee::Method { box_name, method, receiver: Some(r_local), certainty, box_kind };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
252
src/mir/builder/type_registry.rs
Normal file
252
src/mir/builder/type_registry.rs
Normal file
@ -0,0 +1,252 @@
|
||||
//! TypeRegistryBox - 型情報管理の一元化
|
||||
//!
|
||||
//! 🎯 責務: 全ての ValueId の型情報・起源情報を一元管理
|
||||
//! 🔧 境界: 型情報へのアクセスはこの箱経由のみ
|
||||
//! 🔍 見える化: trace_origin() でデータフロー追跡可能
|
||||
//! 🔄 戻せる: NYASH_TYPE_REGISTRY_TRACE=1 で詳細ログ
|
||||
|
||||
use crate::mir::{MirType, ValueId};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// 型情報追跡エントリ(デバッグ用)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TraceEntry {
|
||||
pub vid: ValueId,
|
||||
pub source: String, // "newbox:MapBox", "param:args", "propagate:from_%123"
|
||||
pub timestamp: usize,
|
||||
}
|
||||
|
||||
/// 型情報レジストリ - 全ての ValueId の型・起源を管理
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct TypeRegistry {
|
||||
/// NewBox起源(new MapBox() → "MapBox")
|
||||
origins: HashMap<ValueId, String>,
|
||||
|
||||
/// 型注釈(パラメータ、推論など)
|
||||
types: HashMap<ValueId, MirType>,
|
||||
|
||||
/// デバッグ用追跡ログ
|
||||
trace_log: Vec<TraceEntry>,
|
||||
|
||||
/// トレース有効化フラグ(環境変数キャッシュ)
|
||||
trace_enabled: bool,
|
||||
}
|
||||
|
||||
impl TypeRegistry {
|
||||
/// 新しいレジストリを作成
|
||||
pub fn new() -> Self {
|
||||
let trace_enabled = std::env::var("NYASH_TYPE_REGISTRY_TRACE")
|
||||
.ok()
|
||||
.as_deref() == Some("1");
|
||||
|
||||
Self {
|
||||
origins: HashMap::new(),
|
||||
types: HashMap::new(),
|
||||
trace_log: Vec::new(),
|
||||
trace_enabled,
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 📝 記録系メソッド(明示的な型情報設定)
|
||||
// ============================================================
|
||||
|
||||
/// NewBox起源を記録
|
||||
pub fn record_newbox(&mut self, vid: ValueId, class: String) {
|
||||
self.origins.insert(vid, class.clone());
|
||||
|
||||
if self.trace_enabled {
|
||||
let entry = TraceEntry {
|
||||
vid,
|
||||
source: format!("newbox:{}", class),
|
||||
timestamp: self.trace_log.len(),
|
||||
};
|
||||
self.trace_log.push(entry.clone());
|
||||
eprintln!("[type-registry] {} {:?}", entry.source, vid);
|
||||
}
|
||||
}
|
||||
|
||||
/// パラメータ型を記録
|
||||
pub fn record_param(&mut self, vid: ValueId, param_name: &str, param_type: Option<MirType>) {
|
||||
if let Some(ty) = param_type.clone() {
|
||||
self.types.insert(vid, ty.clone());
|
||||
|
||||
if self.trace_enabled {
|
||||
let entry = TraceEntry {
|
||||
vid,
|
||||
source: format!("param:{}:{:?}", param_name, ty),
|
||||
timestamp: self.trace_log.len(),
|
||||
};
|
||||
self.trace_log.push(entry.clone());
|
||||
eprintln!("[type-registry] {} {:?}", entry.source, vid);
|
||||
}
|
||||
} else if self.trace_enabled {
|
||||
let entry = TraceEntry {
|
||||
vid,
|
||||
source: format!("param:{}:no_type", param_name),
|
||||
timestamp: self.trace_log.len(),
|
||||
};
|
||||
self.trace_log.push(entry.clone());
|
||||
eprintln!("[type-registry] {} {:?}", entry.source, vid);
|
||||
}
|
||||
}
|
||||
|
||||
/// 型注釈を明示的に設定
|
||||
pub fn record_type(&mut self, vid: ValueId, ty: MirType) {
|
||||
self.types.insert(vid, ty.clone());
|
||||
|
||||
if self.trace_enabled {
|
||||
let entry = TraceEntry {
|
||||
vid,
|
||||
source: format!("type:{:?}", ty),
|
||||
timestamp: self.trace_log.len(),
|
||||
};
|
||||
self.trace_log.push(entry.clone());
|
||||
eprintln!("[type-registry] {} {:?}", entry.source, vid);
|
||||
}
|
||||
}
|
||||
|
||||
/// 起源を明示的に設定(推論結果など)
|
||||
pub fn record_origin(&mut self, vid: ValueId, origin: String, reason: &str) {
|
||||
self.origins.insert(vid, origin.clone());
|
||||
|
||||
if self.trace_enabled {
|
||||
let entry = TraceEntry {
|
||||
vid,
|
||||
source: format!("{}:{}", reason, origin),
|
||||
timestamp: self.trace_log.len(),
|
||||
};
|
||||
self.trace_log.push(entry.clone());
|
||||
eprintln!("[type-registry] {} {:?}", entry.source, vid);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 🔄 伝播系メソッド(メタデータの伝播)
|
||||
// ============================================================
|
||||
|
||||
/// メタデータを src から dst へ伝播
|
||||
pub fn propagate(&mut self, src: ValueId, dst: ValueId) {
|
||||
let mut propagated = false;
|
||||
|
||||
if let Some(cls) = self.origins.get(&src).cloned() {
|
||||
self.origins.insert(dst, cls.clone());
|
||||
propagated = true;
|
||||
|
||||
if self.trace_enabled {
|
||||
let entry = TraceEntry {
|
||||
vid: dst,
|
||||
source: format!("propagate:from_%{}→{}", src.0, cls),
|
||||
timestamp: self.trace_log.len(),
|
||||
};
|
||||
self.trace_log.push(entry.clone());
|
||||
eprintln!("[type-registry] {} {:?}", entry.source, dst);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ty) = self.types.get(&src).cloned() {
|
||||
self.types.insert(dst, ty.clone());
|
||||
|
||||
if self.trace_enabled && !propagated {
|
||||
let entry = TraceEntry {
|
||||
vid: dst,
|
||||
source: format!("propagate:from_%{}→{:?}", src.0, ty),
|
||||
timestamp: self.trace_log.len(),
|
||||
};
|
||||
self.trace_log.push(entry.clone());
|
||||
eprintln!("[type-registry] {} {:?}", entry.source, dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 🔍 取得系メソッド(型情報の読み取り)
|
||||
// ============================================================
|
||||
|
||||
/// 起源クラス名を取得
|
||||
pub fn get_origin(&self, vid: ValueId) -> Option<&String> {
|
||||
self.origins.get(&vid)
|
||||
}
|
||||
|
||||
/// 型情報を取得
|
||||
pub fn get_type(&self, vid: ValueId) -> Option<&MirType> {
|
||||
self.types.get(&vid)
|
||||
}
|
||||
|
||||
/// クラス名を推論(フォールバック戦略付き)
|
||||
pub fn infer_class(&self, vid: ValueId, fallback_context: Option<&str>) -> String {
|
||||
// 優先1: 起源情報から
|
||||
if let Some(cls) = self.origins.get(&vid) {
|
||||
return cls.clone();
|
||||
}
|
||||
|
||||
// 優先2: 型注釈から
|
||||
if let Some(MirType::Box(cls)) = self.types.get(&vid) {
|
||||
return cls.clone();
|
||||
}
|
||||
|
||||
// フォールバック: コンテキスト名(警告付き)
|
||||
if let Some(ctx) = fallback_context {
|
||||
if self.trace_enabled {
|
||||
eprintln!("[type-registry] WARNING: fallback to context '{}' for %{}", ctx, vid.0);
|
||||
}
|
||||
return ctx.to_string();
|
||||
}
|
||||
|
||||
// 最終フォールバック: UnknownBox
|
||||
if self.trace_enabled {
|
||||
eprintln!("[type-registry] WARNING: UnknownBox for %{}", vid.0);
|
||||
}
|
||||
"UnknownBox".to_string()
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 🔍 デバッグ支援メソッド
|
||||
// ============================================================
|
||||
|
||||
/// 起源追跡チェーンを取得
|
||||
pub fn trace_origin(&self, vid: ValueId) -> Vec<String> {
|
||||
self.trace_log
|
||||
.iter()
|
||||
.filter(|e| e.vid == vid)
|
||||
.map(|e| e.source.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// 全トレースログを表示
|
||||
pub fn dump_trace(&self) {
|
||||
eprintln!("[type-registry] === Trace Log ({} entries) ===", self.trace_log.len());
|
||||
for entry in &self.trace_log {
|
||||
eprintln!("[type-registry] #{:04} %{} ← {}", entry.timestamp, entry.vid.0, entry.source);
|
||||
}
|
||||
}
|
||||
|
||||
/// 統計情報を表示
|
||||
pub fn dump_stats(&self) {
|
||||
eprintln!("[type-registry] === Statistics ===");
|
||||
eprintln!("[type-registry] Origins: {} entries", self.origins.len());
|
||||
eprintln!("[type-registry] Types: {} entries", self.types.len());
|
||||
eprintln!("[type-registry] Trace log: {} entries", self.trace_log.len());
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 🧹 クリア系メソッド(BoxCompilationContext用)
|
||||
// ============================================================
|
||||
|
||||
/// 起源情報のみクリア(型情報は保持)
|
||||
pub fn clear_origins(&mut self) {
|
||||
self.origins.clear();
|
||||
if self.trace_enabled {
|
||||
eprintln!("[type-registry] cleared origins");
|
||||
}
|
||||
}
|
||||
|
||||
/// 全情報クリア
|
||||
pub fn clear_all(&mut self) {
|
||||
self.origins.clear();
|
||||
self.types.clear();
|
||||
if self.trace_enabled {
|
||||
eprintln!("[type-registry] cleared all");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user