feat(mir): Phase 84-2 CopyTypePropagator for Copy chain type propagation
- Add CopyTypePropagator box (ChatGPT Pro design) for fixed-point Copy instruction type propagation - Integrate into lifecycle.rs before return type inference - Case D reduced from 12 to 9 (25% reduction) Implementation: - src/mir/phi_core/copy_type_propagator.rs: New box with fixed-point loop - src/mir/phi_core/mod.rs: Add module export - src/mir/builder/lifecycle.rs: Call propagator before return inference Test results: - Baseline: 494 passed, 33 failed (was 489/34) - Case D: 9 remaining (from 12) - Unit tests: 4/4 passed Remaining 9 Case D breakdown: - GroupA: Loop Edge Copy (7 cases) - PHI incoming needs Copy trace - GroupB: Multi-level PHI (2 cases) - Recursive PHI resolution needed Phase 84-3 will address GroupA with Edge Copy tracing in GenericTypeResolver. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -43,6 +43,8 @@ use crate::mir::join_ir::lowering::type_hint_policy::TypeHintPolicy;
|
||||
use crate::mir::join_ir::lowering::generic_type_resolver::GenericTypeResolver;
|
||||
// Phase 83: P3-D 既知メソッド戻り値型推論箱
|
||||
use crate::mir::join_ir::lowering::method_return_hint::MethodReturnHintBox;
|
||||
// Phase 84-2: Copy命令型伝播箱(ChatGPT Pro設計)
|
||||
use crate::mir::phi_core::copy_type_propagator::CopyTypePropagator;
|
||||
|
||||
// Phase 82: dev ガード用ヘルパー - Case 分類ロジック統一化
|
||||
//
|
||||
@ -303,6 +305,12 @@ impl super::MirBuilder {
|
||||
let mut module = self.current_module.take().unwrap();
|
||||
let mut function = self.current_function.take().unwrap();
|
||||
function.metadata.value_types = self.value_types.clone();
|
||||
// Phase 84-2: Copy命令型伝播(return型推論の前に実行)
|
||||
//
|
||||
// Loop exit や If merge の edge copy で発生する型欠如を解消する。
|
||||
// Copy チェーン: v1 → v2 → v3 で v1 の型が既知なら v2, v3 にも伝播。
|
||||
CopyTypePropagator::propagate(&function, &mut self.value_types);
|
||||
|
||||
// Phase 82-5: lifecycle.rs バグ修正 - terminator の Return のみをチェック
|
||||
// 問題: instructions を先に走査すると、中間値(const void 等)を誤って推論対象にしてしまう
|
||||
// 解決: terminator の Return のみをチェックし、実際の戻り値を正しく推論する
|
||||
|
||||
215
src/mir/phi_core/copy_type_propagator.rs
Normal file
215
src/mir/phi_core/copy_type_propagator.rs
Normal file
@ -0,0 +1,215 @@
|
||||
//! Phase 84-2: CopyTypePropagator — Copy命令型伝播箱(ChatGPT Pro設計)
|
||||
//!
|
||||
//! # 責務
|
||||
//!
|
||||
//! Copy命令の型伝播のみを担当する専用箱。
|
||||
//! 固定点ループで Copy チェーン全体に型を伝播させる。
|
||||
//!
|
||||
//! # 設計原則(箱理論)
|
||||
//!
|
||||
//! - **単一責務**: Copy命令の型伝播のみ
|
||||
//! - **固定点ループ**: 変化がなくなるまで反復
|
||||
//! - **副作用限定**: value_types のみ更新
|
||||
//!
|
||||
//! # アルゴリズム
|
||||
//!
|
||||
//! 1. 関数内の全 Copy 命令を走査
|
||||
//! 2. src の型が既知 & dst の型が未知 → dst に型を伝播
|
||||
//! 3. 変化がなくなるまで繰り返す(固定点)
|
||||
//!
|
||||
//! # 使用例
|
||||
//!
|
||||
//! ```ignore
|
||||
//! // lifecycle.rs から呼び出し
|
||||
//! CopyTypePropagator::propagate(&function, &mut value_types);
|
||||
//! ```
|
||||
|
||||
use crate::mir::{MirFunction, MirInstruction, MirType, ValueId};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// Phase 84-2: Copy命令型伝播箱
|
||||
///
|
||||
/// Copy チェーン: v1 → v2 → v3 で v1 の型が既知なら v2, v3 にも伝播。
|
||||
/// Loop exit や If merge の edge copy で発生する型欠如を解消する。
|
||||
pub struct CopyTypePropagator;
|
||||
|
||||
impl CopyTypePropagator {
|
||||
/// Copy命令の型を固定点ループで伝播
|
||||
///
|
||||
/// # 引数
|
||||
///
|
||||
/// - `function`: MIR 関数
|
||||
/// - `value_types`: 型マップ(更新される)
|
||||
///
|
||||
/// # 戻り値
|
||||
///
|
||||
/// 伝播された型の数
|
||||
pub fn propagate(function: &MirFunction, value_types: &mut BTreeMap<ValueId, MirType>) -> usize {
|
||||
let mut total_propagated = 0usize;
|
||||
|
||||
// 固定点ループ: 変化がなくなるまで反復
|
||||
loop {
|
||||
let propagated = Self::propagate_single_pass(function, value_types);
|
||||
if propagated == 0 {
|
||||
break;
|
||||
}
|
||||
total_propagated += propagated;
|
||||
|
||||
// 無限ループ防止(理論上は不要だが安全策)
|
||||
if total_propagated > 10000 {
|
||||
if std::env::var("NYASH_COPY_PROP_DEBUG").is_ok() {
|
||||
eprintln!("[copy_prop] warning: exceeded 10000 propagations, stopping");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if std::env::var("NYASH_COPY_PROP_DEBUG").is_ok() && total_propagated > 0 {
|
||||
eprintln!(
|
||||
"[copy_prop] {} total types propagated for function {}",
|
||||
total_propagated, function.signature.name
|
||||
);
|
||||
}
|
||||
|
||||
total_propagated
|
||||
}
|
||||
|
||||
/// 1パスの型伝播(内部用)
|
||||
fn propagate_single_pass(
|
||||
function: &MirFunction,
|
||||
value_types: &mut BTreeMap<ValueId, MirType>,
|
||||
) -> usize {
|
||||
let mut propagated = 0usize;
|
||||
|
||||
for (_bid, bb) in function.blocks.iter() {
|
||||
for inst in bb.instructions.iter() {
|
||||
if let MirInstruction::Copy { dst, src } = inst {
|
||||
// src の型が既知 & dst の型が未知 → 伝播
|
||||
if !value_types.contains_key(dst) {
|
||||
if let Some(src_type) = value_types.get(src).cloned() {
|
||||
value_types.insert(*dst, src_type.clone());
|
||||
propagated += 1;
|
||||
|
||||
if std::env::var("NYASH_COPY_PROP_TRACE").is_ok() {
|
||||
eprintln!(
|
||||
"[copy_prop] {:?} <- {:?} : {:?}",
|
||||
dst, src, src_type
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
propagated
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mir::{BasicBlock, BasicBlockId, MirFunction, FunctionSignature, EffectMask};
|
||||
|
||||
fn make_test_function() -> MirFunction {
|
||||
let sig = FunctionSignature {
|
||||
name: "test".to_string(),
|
||||
params: vec![],
|
||||
return_type: MirType::Void,
|
||||
effects: EffectMask::PURE,
|
||||
};
|
||||
MirFunction::new(sig, BasicBlockId::new(0))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_copy_propagation() {
|
||||
let mut f = make_test_function();
|
||||
let mut value_types = BTreeMap::new();
|
||||
|
||||
// Setup: v1 has type Integer, Copy v2 <- v1
|
||||
value_types.insert(ValueId(1), MirType::Integer);
|
||||
|
||||
let mut bb = BasicBlock::new(BasicBlockId::new(0));
|
||||
bb.instructions.push(MirInstruction::Copy {
|
||||
dst: ValueId(2),
|
||||
src: ValueId(1),
|
||||
});
|
||||
f.blocks.insert(BasicBlockId::new(0), bb);
|
||||
|
||||
// Propagate
|
||||
let count = CopyTypePropagator::propagate(&f, &mut value_types);
|
||||
|
||||
assert_eq!(count, 1);
|
||||
assert_eq!(value_types.get(&ValueId(2)), Some(&MirType::Integer));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chain_copy_propagation() {
|
||||
let mut f = make_test_function();
|
||||
let mut value_types = BTreeMap::new();
|
||||
|
||||
// Setup: v1 -> v2 -> v3 chain
|
||||
value_types.insert(ValueId(1), MirType::String);
|
||||
|
||||
let mut bb = BasicBlock::new(BasicBlockId::new(0));
|
||||
bb.instructions.push(MirInstruction::Copy {
|
||||
dst: ValueId(2),
|
||||
src: ValueId(1),
|
||||
});
|
||||
bb.instructions.push(MirInstruction::Copy {
|
||||
dst: ValueId(3),
|
||||
src: ValueId(2),
|
||||
});
|
||||
f.blocks.insert(BasicBlockId::new(0), bb);
|
||||
|
||||
// Propagate (needs 2 iterations for chain)
|
||||
let count = CopyTypePropagator::propagate(&f, &mut value_types);
|
||||
|
||||
assert_eq!(count, 2);
|
||||
assert_eq!(value_types.get(&ValueId(2)), Some(&MirType::String));
|
||||
assert_eq!(value_types.get(&ValueId(3)), Some(&MirType::String));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_propagation_when_dst_has_type() {
|
||||
let mut f = make_test_function();
|
||||
let mut value_types = BTreeMap::new();
|
||||
|
||||
// Setup: both v1 and v2 already have types
|
||||
value_types.insert(ValueId(1), MirType::Integer);
|
||||
value_types.insert(ValueId(2), MirType::Bool); // already typed
|
||||
|
||||
let mut bb = BasicBlock::new(BasicBlockId::new(0));
|
||||
bb.instructions.push(MirInstruction::Copy {
|
||||
dst: ValueId(2),
|
||||
src: ValueId(1),
|
||||
});
|
||||
f.blocks.insert(BasicBlockId::new(0), bb);
|
||||
|
||||
// Propagate - should not overwrite existing type
|
||||
let count = CopyTypePropagator::propagate(&f, &mut value_types);
|
||||
|
||||
assert_eq!(count, 0);
|
||||
assert_eq!(value_types.get(&ValueId(2)), Some(&MirType::Bool));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_propagation_when_src_unknown() {
|
||||
let mut f = make_test_function();
|
||||
let mut value_types = BTreeMap::new();
|
||||
|
||||
// Setup: v1 has no type, Copy v2 <- v1
|
||||
let mut bb = BasicBlock::new(BasicBlockId::new(0));
|
||||
bb.instructions.push(MirInstruction::Copy {
|
||||
dst: ValueId(2),
|
||||
src: ValueId(1),
|
||||
});
|
||||
f.blocks.insert(BasicBlockId::new(0), bb);
|
||||
|
||||
// Propagate - nothing to propagate
|
||||
let count = CopyTypePropagator::propagate(&f, &mut value_types);
|
||||
|
||||
assert_eq!(count, 0);
|
||||
assert_eq!(value_types.get(&ValueId(2)), None);
|
||||
}
|
||||
}
|
||||
@ -29,6 +29,9 @@ pub mod loopform_builder;
|
||||
// Phase 26-E: PHI SSOT Unification - PhiBuilderBox
|
||||
pub mod phi_builder_box;
|
||||
|
||||
// Phase 84-2: Copy命令型伝播箱(ChatGPT Pro設計)
|
||||
pub mod copy_type_propagator;
|
||||
|
||||
// Phase 35-5: if_body_local_merge 削除(PhiBuilderBoxに吸収済み)
|
||||
// Phase 35-5: phi_invariants 削除(JoinIR Verifierに移譲済み)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user