feat(joinir): Phase 66-68 GenericTypeResolver + JoinIR First Chapter Wrap

Phase 66: P3-C ジェネリック型推論箱化
- generic_type_resolver.rs 新設 (180行)
  - is_generic_method(): ArrayBox.get/pop/first/last, MapBox.get 判定
  - resolve_from_phi(): PHI解析によるジェネリック型推論
- TypeHintPolicy::is_p3c_target() 追加
  - P1/P2/P3-A/P3-B 以外を P3-C 候補として判定

Phase 67: P3-C 実利用への一歩
- phase67_generic_type_resolver.rs テスト追加 (3テスト)
- lifecycle.rs に P3-C 経路フック追加
  - GenericTypeResolver を P3-C 対象関数で優先使用
- A/B テストで旧経路との一致確認 (11 tests PASS)

Phase 68: JoinIR First Chapter Wrap (ドキュメント整理)
- 68-1: phase-30 README.md に Section 9 追加 (JoinIR 第1章完了サマリー)
- 68-2: README.md に JoinIR status セクション追加
- 68-3: CURRENT_TASK.md スリム化 (351→132行, 62%削減)
- 68-4: PHI_BOX_INVENTORY.md に Phase 66-67 完了セクション追加

Phase 69-1: Trio 棚卸し
- phase69-1-trio-inventory.md 作成
- Trio 使用箇所の完全棚卸し完了 (削減見込み 457-707行)

🐱 JoinIR 第1章完了!4つの柱確立:
- Structure (LoopForm)
- Scope (LoopScopeShape)
- JoinIR (Select/IfMerge/Loop)
- Type Hints (P1-P3-C)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-30 08:54:18 +09:00
parent aef9374b5a
commit 58c5d8c9bc
9 changed files with 694 additions and 300 deletions

View File

@ -0,0 +1,204 @@
// Phase 66: P3-C ジェネリック型推論箱
//
// ArrayBox.get, MapBox.get などのジェネリック型を持つメソッドの
// 戻り値型を推論する。
//
// # 責務
//
// - P3-C 対象メソッドの判定
// - コンテナ使用文脈からの型推論PHI 解析フォールバック)
// - 将来の型変数システム導入の土台
//
// # 設計原則(箱理論)
//
// - **単一責務**: P3-C ジェネリック型推論のみ
// - **質問箱**: is_generic_method() / resolve_generic_type() で判定
// - **if_phi.rs 依存削減**: infer_type_from_phi を内部に取り込み
use crate::mir::{MirFunction, MirInstruction, MirType, ValueId};
use std::collections::BTreeMap;
/// Phase 66: ジェネリック型推論箱
///
/// # 対象パターンP3-C
///
/// - `ArrayBox.get(index)` → 配列要素の型T
/// - `ArrayBox.pop()` → 配列要素の型T
/// - `MapBox.get(key)` → マップ値の型V
///
/// # Phase 65 との関係
///
/// Phase 65 で P1/P2/P3-A/P3-B は JoinIR 型ヒント経路に移行完了。
/// この箱は **P3-C 専用** として設計し、TypeHintPolicy と連携する。
///
/// # 将来拡張Phase 67+
///
/// - 型変数システム(`T`, `V` の明示的な型パラメータ)
/// - コンテナ生成時の型推論(`new ArrayBox<StringBox>()`
pub struct GenericTypeResolver;
impl GenericTypeResolver {
/// P3-C 対象メソッドかどうかを判定
///
/// # 引数
/// - `receiver_type`: 受け手の型
/// - `method_name`: メソッド名
///
/// # 戻り値
/// - `true`: ジェネリック型推論が必要なメソッド
/// - `false`: P3-A/P3-B で処理可能、または未対応
pub fn is_generic_method(receiver_type: &MirType, method_name: &str) -> bool {
match receiver_type {
MirType::Box(box_name) => match box_name.as_str() {
"ArrayBox" => matches!(method_name, "get" | "pop" | "first" | "last"),
"MapBox" => matches!(method_name, "get"),
_ => false,
},
_ => false,
}
}
/// P3-C 対象関数かどうかを判定TypeHintPolicy 連携用)
///
/// # 用途
///
/// TypeHintPolicy.is_target() で P1/P2/P3-A/P3-B に該当しない場合、
/// この関数で P3-C 候補かどうかを判定する。
///
/// # 現在の実装
///
/// ArrayBox/MapBox を使用する関数を P3-C 候補として判定。
/// 関数名ベースのフィルタリングは行わない(汎用判定)。
pub fn is_p3c_candidate(func_name: &str) -> bool {
// Phase 66: 現在は全関数を P3-C 候補とみなす
// 将来的に関数内の ArrayBox.get/MapBox.get 使用を解析して判定
!func_name.is_empty() // 常に trueP1/P2/P3-A/B 以外は全て P3-C 候補)
}
/// PHI 解析によるジェネリック型推論
///
/// # 責務
///
/// P3-C メソッドArrayBox.get, MapBox.get など)の戻り値型を
/// PHI 命令の incoming 値から推論する。
///
/// # アルゴリズム
///
/// 1. ret_val を定義する PHI 命令を探索
/// 2. PHI の incoming 値の型を types マップから取得
/// 3. 全ての incoming 値が同じ型なら、その型を返す
///
/// # Phase 65 との違い
///
/// - if_phi.rs::infer_type_from_phi() と同等のロジック
/// - P3-C 専用として明示的に責務を限定
/// - 将来的に型変数システムで置き換え予定
pub fn resolve_from_phi(
function: &MirFunction,
ret_val: ValueId,
types: &BTreeMap<ValueId, MirType>,
) -> Option<MirType> {
for (_bid, bb) in function.blocks.iter() {
for inst in bb.instructions.iter() {
if let MirInstruction::Phi { dst, inputs, .. } = inst {
if *dst == ret_val {
let mut it = inputs.iter().filter_map(|(_, v)| types.get(v));
if let Some(first) = it.next() {
if it.all(|mt| mt == first) {
return Some(first.clone());
}
}
}
}
}
}
None
}
/// PHI の type_hint から型を抽出(将来拡張用)
///
/// # Phase 66 現在
///
/// P3-C メソッドは JoinIR で type_hint を設定しないため、
/// この関数は現在使用されない。
///
/// # Phase 67+ 拡張
///
/// 型変数システム導入後、ArrayBox<T>.get() の T を
/// type_hint として伝播する際に使用。
#[allow(dead_code)]
pub fn extract_type_hint(function: &MirFunction, ret_val: ValueId) -> Option<MirType> {
function
.blocks
.values()
.flat_map(|bb| &bb.instructions)
.find_map(|inst| {
if let MirInstruction::Phi { dst, type_hint, .. } = inst {
if *dst == ret_val {
return type_hint.clone();
}
}
None
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_generic_method_arraybox() {
let array_type = MirType::Box("ArrayBox".to_string());
// P3-C 対象
assert!(GenericTypeResolver::is_generic_method(&array_type, "get"));
assert!(GenericTypeResolver::is_generic_method(&array_type, "pop"));
assert!(GenericTypeResolver::is_generic_method(
&array_type,
"first"
));
assert!(GenericTypeResolver::is_generic_method(&array_type, "last"));
// P3-A/P3-B 対象(非 P3-C
assert!(!GenericTypeResolver::is_generic_method(&array_type, "size"));
assert!(!GenericTypeResolver::is_generic_method(&array_type, "push"));
}
#[test]
fn test_is_generic_method_mapbox() {
let map_type = MirType::Box("MapBox".to_string());
// P3-C 対象
assert!(GenericTypeResolver::is_generic_method(&map_type, "get"));
// P3-A/P3-B 対象(非 P3-C
assert!(!GenericTypeResolver::is_generic_method(&map_type, "size"));
assert!(!GenericTypeResolver::is_generic_method(&map_type, "has"));
}
#[test]
fn test_is_generic_method_stringbox() {
// StringBox は P3-A で処理済み
assert!(!GenericTypeResolver::is_generic_method(
&MirType::String,
"substring"
));
assert!(!GenericTypeResolver::is_generic_method(
&MirType::String,
"length"
));
}
#[test]
fn test_is_p3c_candidate() {
// 全関数が P3-C 候補P1/P2/P3-A/B 以外)
assert!(GenericTypeResolver::is_p3c_candidate("Main.main/0"));
assert!(GenericTypeResolver::is_p3c_candidate(
"FuncScanner.parse/1"
));
// 空文字列は false
assert!(!GenericTypeResolver::is_p3c_candidate(""));
}
}

View File

@ -35,6 +35,7 @@ pub mod stageb_body;
pub mod stageb_funcscanner;
pub mod type_hint_policy; // Phase 65.5: 型ヒントポリシー箱化
pub mod type_inference; // Phase 65-2-A
pub mod generic_type_resolver; // Phase 66: P3-C ジェネリック型推論箱
pub mod value_id_ranges;
// Re-export public lowering functions

View File

@ -88,8 +88,23 @@ impl TypeHintPolicy {
func_name.starts_with("NewBoxTest.")
}
// Phase 66+ で P3-Cジェネリック型推論)追加予定
// fn is_p3c_target(func_name: &str) -> bool { ... }
/// P3-C: ジェネリック型推論対象判定Phase 66
///
/// # 対象
/// - P1/P2/P3-A/P3-B 以外のすべての関数
/// - ArrayBox.get, MapBox.get などを使用する可能性がある関数
///
/// # Phase 66 設計
/// - GenericTypeResolver と連携して P3-C 型推論を実行
/// - is_target() が false の場合のフォールバック経路
pub fn is_p3c_target(func_name: &str) -> bool {
// P1/P2/P3-A/P3-B に該当しない場合は P3-C 候補
!Self::is_p1_target(func_name)
&& !Self::is_p2_target(func_name)
&& !Self::is_p3a_target(func_name)
&& !Self::is_p3b_target(func_name)
&& !func_name.is_empty()
}
/// PHI 命令から型ヒントを抽出
///
@ -104,10 +119,7 @@ impl TypeHintPolicy {
/// # Phase
/// - Phase 63-6 で導入
/// - Phase 65.5 で箱化・関数型スタイルに改善
pub fn extract_phi_type_hint(
function: &MirFunction,
ret_val: ValueId,
) -> Option<MirType> {
pub fn extract_phi_type_hint(function: &MirFunction, ret_val: ValueId) -> Option<MirType> {
function
.blocks
.values()
@ -157,7 +169,9 @@ mod tests {
fn test_is_p3a_target() {
// P3-A: read_quoted 系関数
assert!(TypeHintPolicy::is_p3a_target("read_quoted_from/1"));
assert!(TypeHintPolicy::is_p3a_target("FuncScannerBox.read_quoted/1"));
assert!(TypeHintPolicy::is_p3a_target(
"FuncScannerBox.read_quoted/1"
));
// P3-A 以外
assert!(!TypeHintPolicy::is_p3a_target("IfSelectTest.simple/0"));
@ -203,4 +217,36 @@ mod tests {
// どちらかに該当すれば is_target() は true
assert!(TypeHintPolicy::is_target("read_quoted_from/1"));
}
/// Phase 66: P3-C パターン判定テスト
#[test]
fn test_is_p3c_target() {
// P3-C: P1/P2/P3-A/P3-B 以外
assert!(TypeHintPolicy::is_p3c_target("Main.main/0"));
assert!(TypeHintPolicy::is_p3c_target("SomeBox.some_method/3"));
assert!(TypeHintPolicy::is_p3c_target("ArrayProcessor.process/1"));
// P1/P2/P3-A/P3-B は P3-C ではない
assert!(!TypeHintPolicy::is_p3c_target("IfSelectTest.simple/0")); // P1
assert!(!TypeHintPolicy::is_p3c_target("IfMergeTest.simple/0")); // P2
assert!(!TypeHintPolicy::is_p3c_target("read_quoted_from/1")); // P3-A
assert!(!TypeHintPolicy::is_p3c_target("NewBoxTest.array/0")); // P3-B
// 空文字列は false
assert!(!TypeHintPolicy::is_p3c_target(""));
}
/// Phase 66: is_target と is_p3c_target の排他性確認
#[test]
fn test_is_target_and_p3c_mutually_exclusive() {
// is_target() が true なら is_p3c_target() は false
let p1_func = "IfSelectTest.simple/0";
assert!(TypeHintPolicy::is_target(p1_func));
assert!(!TypeHintPolicy::is_p3c_target(p1_func));
// is_target() が false なら is_p3c_target() は true
let general_func = "Main.main/0";
assert!(!TypeHintPolicy::is_target(general_func));
assert!(TypeHintPolicy::is_p3c_target(general_func));
}
}