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:
nyash-codex
2025-12-02 19:37:01 +09:00
parent 40dfbc68a8
commit 4ef5eec162
10 changed files with 1667 additions and 33 deletions

View File

@ -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 のみをチェックし、実際の戻り値を正しく推論する

View 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);
}
}

View File

@ -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に移譲済み