diff --git a/src/mir/builder/calls/emit.rs b/src/mir/builder/calls/emit.rs index cb8bbd9a..f1526928 100644 --- a/src/mir/builder/calls/emit.rs +++ b/src/mir/builder/calls/emit.rs @@ -107,8 +107,9 @@ impl MirBuilder { 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)?; + // 箱理論: CalleeGuardBox による構造的分離 + let guard = super::guard::CalleeGuardBox::new(&self.value_types); + callee = guard.apply_static_runtime_guard(callee)?; // Emit resolve.choose for method callee (dev-only; default OFF) if let Callee::Method { box_name, method, certainty, .. } = &callee { @@ -455,59 +456,6 @@ impl MirBuilder { }) } - /// 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 { - 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) - } + // ✅ 箱化完了: apply_static_runtime_guard → CalleeGuardBox::apply_static_runtime_guard + // 構造ガードロジックは src/mir/builder/calls/guard.rs に移動済み } diff --git a/src/mir/builder/calls/guard.rs b/src/mir/builder/calls/guard.rs new file mode 100644 index 00000000..956e684f --- /dev/null +++ b/src/mir/builder/calls/guard.rs @@ -0,0 +1,195 @@ +/*! + * CalleeGuardBox - 構造ガード専用箱 + * + * 箱理論の実践: + * - 箱にする: 構造ガード機能を1箱に集約 + * - 境界を作る: 静的Box/ランタイムBoxの混線を構造的に防ぐ + * - Fail-Fast: フォールバックより明示的エラー + * + * 責務: + * - Callee::Methodの静的Box/ランタイムBox混線検出・正規化 + * - me-call判定(receiver型==box_name) + * - receiver実体化の保証 + */ + +use crate::mir::{Callee, MirType, ValueId}; +use crate::mir::definitions::call_unified::CalleeBoxKind; +use std::collections::HashMap; + +/// 構造ガード専用箱 +/// +/// 箱理論: +/// - 単一責務: Calleeの構造検証・正規化のみ +/// - 状態最小: value_typesのみ保持(型情報参照用) +/// - ピュア関数的: 入力Callee → 検証・変換 → 出力Callee +pub struct CalleeGuardBox<'a> { + /// 型情報マップ(ValueId → MirType) + value_types: &'a HashMap, +} + +impl<'a> CalleeGuardBox<'a> { + /// 新しいCalleeGuardBoxを作成 + pub fn new(value_types: &'a HashMap) -> Self { + CalleeGuardBox { value_types } + } + + /// 静的Box/ランタイムBox混線を検出・正規化 + /// + /// 箱理論の「境界を作る」原則: + /// - Stage-B/Stage-1コンパイラBoxとランタイムDataBoxを構造的に分離 + /// + /// ロジック: + /// 1. box_kind==StaticCompiler かつ receiver型==同一Box名 + /// → me-call判定、静的メソッド降下に委ねる(そのまま通す) + /// 2. box_kind==StaticCompiler かつ receiver型==異なるランタイムBox + /// → 正規化(MapBox/ArrayBoxなど実際のruntime型に修正) + /// 3. それ以外 → そのまま通す + /// + /// 実例: + /// - StageBArgsBox.resolve_src内のargs.get(i)がStage1UsingResolverBox.getに + /// 化けるのを防ぐ(args型はMapBox/ArrayBox → 正規化) + pub fn apply_static_runtime_guard(&self, callee: Callee) -> Result { + 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(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) + } + + /// receiver型の検証(ヘルパー) + /// + /// 指定されたreceiverがBox型を持っているか確認 + pub fn has_box_type(&self, receiver: ValueId) -> bool { + matches!(self.value_types.get(&receiver), Some(MirType::Box(_))) + } + + /// receiver型の取得(ヘルパー) + /// + /// 指定されたreceiverのBox型名を返す(存在しない場合はNone) + pub fn get_box_type(&self, receiver: ValueId) -> Option<&String> { + match self.value_types.get(&receiver) { + Some(MirType::Box(box_name)) => Some(box_name), + _ => None, + } + } + + /// me-call判定 + /// + /// box_name と receiver型が一致するか判定 + /// (静的メソッド呼び出しの検出用) + pub fn is_me_call(&self, box_name: &str, receiver: ValueId) -> bool { + match self.get_box_type(receiver) { + Some(recv_box) => recv_box == box_name, + None => false, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_me_call_detection() { + let mut value_types = HashMap::new(); + value_types.insert(ValueId::new(10), MirType::Box("StageBArgsBox".to_string())); + + let guard = CalleeGuardBox::new(&value_types); + + // Same box name → me-call + assert!(guard.is_me_call("StageBArgsBox", ValueId::new(10))); + + // Different box name → not me-call + assert!(!guard.is_me_call("Stage1UsingResolverBox", ValueId::new(10))); + + // No type info → not me-call + assert!(!guard.is_me_call("StageBArgsBox", ValueId::new(999))); + } + + #[test] + fn test_static_runtime_guard_me_call() { + use crate::mir::definitions::call_unified::TypeCertainty; + + let mut value_types = HashMap::new(); + value_types.insert(ValueId::new(10), MirType::Box("StageBArgsBox".to_string())); + + let guard = CalleeGuardBox::new(&value_types); + + let callee = Callee::Method { + box_name: "StageBArgsBox".to_string(), + method: "process".to_string(), + receiver: Some(ValueId::new(10)), + certainty: TypeCertainty::Known, + box_kind: CalleeBoxKind::StaticCompiler, + }; + + // me-call → should pass through unchanged + let result = guard.apply_static_runtime_guard(callee.clone()).unwrap(); + assert_eq!(result, callee); + } + + #[test] + fn test_static_runtime_guard_normalization() { + use crate::mir::definitions::call_unified::TypeCertainty; + + let mut value_types = HashMap::new(); + value_types.insert(ValueId::new(10), MirType::Box("MapBox".to_string())); + + let guard = CalleeGuardBox::new(&value_types); + + let callee = Callee::Method { + box_name: "Stage1UsingResolverBox".to_string(), + method: "get".to_string(), + receiver: Some(ValueId::new(10)), + certainty: TypeCertainty::Known, + box_kind: CalleeBoxKind::StaticCompiler, + }; + + // Mix-up → should normalize to MapBox + let result = guard.apply_static_runtime_guard(callee).unwrap(); + match result { + Callee::Method { box_name, box_kind, .. } => { + assert_eq!(box_name, "MapBox"); + assert_eq!(box_kind, CalleeBoxKind::RuntimeData); + } + _ => panic!("Expected Method callee"), + } + } +} diff --git a/src/mir/builder/calls/mod.rs b/src/mir/builder/calls/mod.rs index 35d073ac..1d961f86 100644 --- a/src/mir/builder/calls/mod.rs +++ b/src/mir/builder/calls/mod.rs @@ -5,6 +5,7 @@ //! - utils: ユーティリティ(resolve/parse/extract) //! - emit: Call命令発行(統一Call/Legacy Call) ✅ Phase 2完了 //! - build: Call構築(function call/method call) ✅ Phase 2完了 +//! - guard: 構造ガード(静的Box/ランタイムBox混線防止) ✅ Phase 25.1d完了 // Existing modules (already implemented elsewhere) pub mod annotation; @@ -15,11 +16,12 @@ pub mod function_lowering; pub mod method_resolution; pub mod special_handlers; -// New refactored modules (Box Theory Phase 1 & 2) +// New refactored modules (Box Theory Phase 1 & 2 & 25.1d) pub mod lowering; pub mod utils; pub mod emit; // Phase 2: Call emission pub mod build; // Phase 2: Call building +pub mod guard; // Phase 25.1d: Structural guard (static/runtime box separation) // Re-export public interfaces pub use call_target::CallTarget;