箱理論の完全実践:Call系処理を9個の専用箱で完全分離 - Phase 3-B: EffectsAnalyzerBox(エフェクト解析専用) - Phase 3-C: CallMaterializerBox(Call前処理専用) 実装内容: 【Phase 3-B: EffectsAnalyzerBox】 1. 新規ファイル作成 - src/mir/builder/calls/effects_analyzer.rs (~155行) - compute_call_effects: Calleeから副作用マスクを計算 - is_pure_method: Pureメソッド判定 - 5つのユニットテスト ✅ 2. call_unified.rs整理 - compute_call_effects → 委譲に変更 - is_pure_method → 削除 - ~50行削減 【Phase 3-C: CallMaterializerBox】 1. 新規ファイル作成 - src/mir/builder/calls/materializer.rs (~151行) - try_global_fallback_handlers: Global関数フォールバック - materialize_receiver_in_callee: Receiver実体化 - Call前処理全般を集約 2. emit.rs整理 - 2つの大きな関数を委譲に変更 - ~115行削減 3. unified_emitter.rs更新 - CallMaterializerBox経由に変更 箱化効果(Phase 3全体): 【劇的な削減】 - emit.rs: 467行 → 164行(-303行、65%削減!) - call_unified.rs: 144行 → 98行(-46行、32%削減!) 【新規箱(責務明確・読みやすい)】 - unified_emitter.rs: 250行(統一Call発行専用) - effects_analyzer.rs: 155行(エフェクト解析専用) - materializer.rs: 151行(Call前処理専用) 【読みやすさ革命】 - ✅ 500行超えファイル根絶(最大489行まで) - ✅ 責務分離完璧(各ファイルが単一責務) - ✅ 9個の専用箱で管理(guard/resolver/emitter/effects/materializer) - ✅ テスト容易性劇的向上(独立した箱で簡単テスト) Phase 3 最終状態: - Phase 3-A: UnifiedCallEmitterBox ✅ - Phase 3-B: EffectsAnalyzerBox ✅ - Phase 3-C: CallMaterializerBox ✅ - 読みやすさ革命 ✅ 完全達成! ビルド・テスト: - cargo build --release: ✅ 成功 - effects_analyzer tests (5): ✅ all passed - 既存機能互換性: ✅ 完全保持
157 lines
5.7 KiB
Rust
157 lines
5.7 KiB
Rust
/*!
|
||
* EffectsAnalyzerBox - エフェクト解析専用箱
|
||
*
|
||
* 箱理論の実践:
|
||
* - 箱にする: エフェクト解析ロジックを1箱に集約
|
||
* - 境界を作る: Pure/IO/Alloc/Control等の効果分類を一元管理
|
||
* - 状態レス: すべて静的解析(実行時状態不要)
|
||
*
|
||
* 責務:
|
||
* - compute_call_effects: Calleeから副作用マスクを計算
|
||
* - is_pure_method: メソッドがPure(副作用なし)か判定
|
||
* - 既知の関数・メソッドのエフェクト知識を集約
|
||
*/
|
||
|
||
use crate::mir::definitions::call_unified::Callee;
|
||
use crate::mir::builder::{Effect, EffectMask};
|
||
use super::extern_calls;
|
||
|
||
/// エフェクト解析専用箱
|
||
///
|
||
/// 箱理論:
|
||
/// - 単一責務: Calleeのエフェクト解析のみ
|
||
/// - 状態レス: すべて静的関数(Callee情報のみで判定)
|
||
/// - 知識集約: 既知の関数・メソッドのエフェクト知識を一元管理
|
||
pub struct EffectsAnalyzerBox;
|
||
|
||
impl EffectsAnalyzerBox {
|
||
/// Compute effects for a call based on its callee
|
||
///
|
||
/// エフェクト分類:
|
||
/// - PURE: 副作用なし(純粋計算)
|
||
/// - READ: ヒープ読み取りのみ
|
||
/// - IO: 入出力あり
|
||
/// - Alloc: メモリ確保
|
||
/// - Control: 制御フロー変更(panic/exit)
|
||
/// - WriteHeap: ヒープ書き込み
|
||
pub fn compute_call_effects(callee: &Callee) -> EffectMask {
|
||
match callee {
|
||
Callee::Global(name) => {
|
||
match name.as_str() {
|
||
"print" | "error" => EffectMask::IO,
|
||
"panic" | "exit" => EffectMask::IO.add(Effect::Control),
|
||
"gc_collect" => EffectMask::IO.add(Effect::Alloc),
|
||
_ => EffectMask::IO,
|
||
}
|
||
},
|
||
|
||
Callee::Method { method, box_name, .. } => {
|
||
match method.as_str() {
|
||
"birth" => EffectMask::PURE.add(Effect::Alloc),
|
||
"get" | "length" | "size" => EffectMask::READ,
|
||
"set" | "push" | "pop" => EffectMask::READ.add(Effect::WriteHeap),
|
||
_ => {
|
||
// Check if it's a known pure method
|
||
if Self::is_pure_method(box_name, method) {
|
||
EffectMask::PURE
|
||
} else {
|
||
EffectMask::READ
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
Callee::Constructor { .. } => EffectMask::PURE.add(Effect::Alloc),
|
||
|
||
Callee::Closure { .. } => EffectMask::PURE.add(Effect::Alloc),
|
||
|
||
Callee::Extern(name) => {
|
||
let (iface, method) = extern_calls::parse_extern_name(name);
|
||
extern_calls::compute_extern_effects(&iface, &method)
|
||
},
|
||
|
||
Callee::Value(_) => EffectMask::IO, // Conservative for dynamic calls
|
||
}
|
||
}
|
||
|
||
/// Check if a method is known to be pure (no side effects)
|
||
///
|
||
/// Pure メソッドの条件:
|
||
/// - 副作用なし(ヒープ変更なし、I/Oなし)
|
||
/// - 同じ入力に対して同じ出力を返す
|
||
/// - プログラムの状態を変更しない
|
||
///
|
||
/// 既知のPureメソッド:
|
||
/// - StringBox: upper, lower, trim, length
|
||
/// - IntegerBox: abs, toString
|
||
/// - FloatBox: round, floor, ceil
|
||
/// - BoolBox: not
|
||
pub fn is_pure_method(box_name: &str, method: &str) -> bool {
|
||
match (box_name, method) {
|
||
("StringBox", m) => matches!(m, "upper" | "lower" | "trim" | "length"),
|
||
("IntegerBox", m) => matches!(m, "abs" | "toString"),
|
||
("FloatBox", m) => matches!(m, "round" | "floor" | "ceil"),
|
||
("BoolBox", "not") => true,
|
||
_ => false,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use crate::mir::ValueId;
|
||
use crate::mir::definitions::call_unified::{CalleeBoxKind, TypeCertainty};
|
||
|
||
#[test]
|
||
fn test_compute_effects_global() {
|
||
let callee = Callee::Global("print".to_string());
|
||
let effects = EffectsAnalyzerBox::compute_call_effects(&callee);
|
||
assert_eq!(effects, EffectMask::IO);
|
||
}
|
||
|
||
#[test]
|
||
fn test_compute_effects_method_pure() {
|
||
let callee = Callee::Method {
|
||
box_name: "StringBox".to_string(),
|
||
method: "upper".to_string(),
|
||
receiver: Some(ValueId::new(1)),
|
||
certainty: TypeCertainty::Known,
|
||
box_kind: CalleeBoxKind::RuntimeData,
|
||
};
|
||
let effects = EffectsAnalyzerBox::compute_call_effects(&callee);
|
||
assert_eq!(effects, EffectMask::PURE);
|
||
}
|
||
|
||
#[test]
|
||
fn test_compute_effects_method_read() {
|
||
let callee = Callee::Method {
|
||
box_name: "ArrayBox".to_string(),
|
||
method: "get".to_string(),
|
||
receiver: Some(ValueId::new(1)),
|
||
certainty: TypeCertainty::Known,
|
||
box_kind: CalleeBoxKind::RuntimeData,
|
||
};
|
||
let effects = EffectsAnalyzerBox::compute_call_effects(&callee);
|
||
assert_eq!(effects, EffectMask::READ);
|
||
}
|
||
|
||
#[test]
|
||
fn test_compute_effects_constructor() {
|
||
let callee = Callee::Constructor {
|
||
box_type: "StringBox".to_string(),
|
||
};
|
||
let effects = EffectsAnalyzerBox::compute_call_effects(&callee);
|
||
// Constructor should have PURE + Alloc
|
||
assert_eq!(effects, EffectMask::PURE.add(crate::mir::builder::Effect::Alloc));
|
||
}
|
||
|
||
#[test]
|
||
fn test_is_pure_method() {
|
||
assert!(EffectsAnalyzerBox::is_pure_method("StringBox", "upper"));
|
||
assert!(EffectsAnalyzerBox::is_pure_method("IntegerBox", "abs"));
|
||
assert!(EffectsAnalyzerBox::is_pure_method("BoolBox", "not"));
|
||
assert!(!EffectsAnalyzerBox::is_pure_method("ArrayBox", "push"));
|
||
}
|
||
}
|