Files
hakorune/src/mir/builder/calls/effects_analyzer.rs

157 lines
5.7 KiB
Rust
Raw Normal View History

refactor(builder): Phase 3-B,C完了 - 読みやすさ革命達成! 箱理論の完全実践: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 - 既存機能互換性: ✅ 完全保持
2025-11-17 23:57:04 +09:00
/*!
* 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"));
}
}