feat(mir): Phase 30 F-1 LoopScopeShape SSOT preparation

LoopScopeShape を変数分類の唯一の情報源 (SSOT) にするための準備:

F-1.1 LoopVarClassBox:
- 4分類(Pinned/Carrier/BodyLocalExit/BodyLocalInternal)の仕様を
  loop_scope_shape.rs にドキュメント化(PHI生成ルール表付き)
- LoopScopeShape.classify() / classify_all() メソッド追加
- LoopVarClassBox.classify_with_scope() 委譲メソッド追加
- 旧APIにPhase 30 TODOコメント追加

F-1.2 LoopExitLivenessBox:
- exit_live フィールドにSSOT説明ドキュメント追加
- get_exit_live_from_scope() 委譲メソッド追加

F-1.3 LocalScopeInspectorBox:
- Phase 30移行中のTODOコメント追加
- is_available_in_all_with_scope() 委譲メソッド追加

未使用フィールド削除:
- CaseAContext::scope フィールド削除
- LoopBypassFlags::exit フィールド削除
- PhiBuilderBox::loop_context / LoopPhiContext 削除

テスト結果: 37テスト全てPASS
- loop_scope_shape: 12 PASS (+2 new)
- loop_var_classifier: 11 PASS
- loop_exit_liveness: 3 PASS
- local_scope_inspector: 11 PASS

🤖 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-25 15:14:54 +09:00
parent e93727dd37
commit de5d5fae52
6 changed files with 576 additions and 33 deletions

View File

@ -44,8 +44,52 @@ use crate::mir::{BasicBlockId, MirFunction, MirQuery, ValueId};
/// ループ変数スコープの統合ビュー
///
/// # Fields
/// # Phase 30: 変数分類の唯一の仕様ソース (SSOT)
///
/// ## 4分類の定義 (from LoopVarClassBox)
///
/// ### 1. Pinnedループ外パラメータ
/// - **定義**: ループ開始前に定義され、ループ内で**変更されない**変数
/// - **needs_header_phi**: ✅ trueループ再入時に値を維持
/// - **needs_exit_phi**: ✅ trueループ後も値が必要
/// - **例**: `loop(i < limit) { ... }` の `limit`
///
/// ### 2. Carrierループ更新変数
/// - **定義**: 各イテレーションで**更新される**変数
/// - **needs_header_phi**: ✅ true前回値と新値をマージ
/// - **needs_exit_phi**: ✅ true最終値が必要
/// - **例**: `loop(i < 10) { i = i + 1 }` の `i`
///
/// ### 3. BodyLocalExit全exit経路で定義
/// - **定義**: ループ内で定義され、**全ての exit predecessor** で利用可能
/// - **needs_header_phi**: ❌ falseループ外には存在しない
/// - **needs_exit_phi**: ✅ true全exitで値が定義済み
/// - **例**: `loop { local x = compute(); if x > 10 { break } }`
///
/// ### 4. BodyLocalInternal一部exit経路のみ
/// - **定義**: ループ内で定義され、**一部の exit predecessor** でのみ利用可能
/// - **needs_header_phi**: ❌ false
/// - **needs_exit_phi**: ❌ falseOption C: PHI pred mismatch 防止!)
/// - **例**: skip_whitespace の `ch`一部break経路でのみ定義
///
/// ## PHI生成ルール早見表
///
/// | 分類 | header_phi | exit_phi |
/// |-------------------|------------|----------|
/// | Pinned | ✅ | ✅ |
/// | Carrier | ✅ | ✅ |
/// | BodyLocalExit | ❌ | ✅ |
/// | BodyLocalInternal | ❌ | ❌ |
///
/// # Fields (Phase 30 Final Form)
///
/// ## Block IDs (from LoopForm)
/// - `header`: ループヘッダブロック(条件チェック)
/// - `body`: ループボディブロック(メイン処理)
/// - `latch`: ループラッチブロックheader へ戻る)
/// - `exit`: ループ終了ブロック(ループ後の継続)
///
/// ## Variable Classification
/// - `pinned`: ループ外から来て変わらない変数header/exit PHI 両方必要)
/// - `carriers`: 各イテレーションで更新される変数header/exit PHI 両方必要)
/// - `body_locals`: ループ内だけで完結する変数JoinIR では参照しない)
@ -53,6 +97,22 @@ use crate::mir::{BasicBlockId, MirFunction, MirQuery, ValueId};
/// - `progress_carrier`: ループを前に進める変数(将来の Verifier 用)
#[derive(Debug, Clone)]
pub(crate) struct LoopScopeShape {
// === Block IDs (Phase 30: from LoopForm) ===
/// Loop header block (condition check)
pub header: BasicBlockId,
/// Loop body block (main processing)
pub body: BasicBlockId,
/// Loop latch block (back-edge to header)
pub latch: BasicBlockId,
/// Loop exit block (continuation after loop)
pub exit: BasicBlockId,
// === Variable Classification ===
/// Loop-crossing parameters (always need header/exit PHI)
pub pinned: BTreeSet<String>,
@ -65,6 +125,27 @@ pub(crate) struct LoopScopeShape {
/// Variables live at exit (needs_exit_phi() == true)
/// = pinned carriers BodyLocalExit
///
/// # Phase 30: exit_live の唯一の情報源 (SSOT)
///
/// このフィールドはループ終了後に使用される変数の唯一の正式ソース。
/// LoopExitLivenessBox が返していた情報はここに統合される。
///
/// ## 計算方法
///
/// 1. LoopVarClassBox の分類結果から:
/// - Pinned → exit_live に追加
/// - Carrier → exit_live に追加
/// - BodyLocalExit → exit_live に追加
/// - BodyLocalInternal → exit_live には**追加しない**Option C
///
/// 2. LoopExitLivenessBox.compute_live_at_exit() の結果をマージ
/// Phase 1: 空集合、Phase 2+: MIRスキャン結果
///
/// ## 将来計画
///
/// Phase 30完了後、LoopExitLivenessBox は削除され、
/// このフィールドが唯一の live_at_exit 情報源になる。
pub exit_live: BTreeSet<String>,
/// Progress carrier for loop termination check (future Verifier use)
@ -73,31 +154,37 @@ pub(crate) struct LoopScopeShape {
}
impl LoopScopeShape {
/// Create LoopScopeShape from existing boxes (Phase 29: thin wrapper)
/// Create LoopScopeShape from existing boxes (Phase 30: unified interface)
///
/// # Arguments
///
/// - `loop_form`: LoopForm containing block structure
/// - `intake`: LoopFormIntake containing classified variable info
/// - `var_classes`: LoopVarClassBox for classification
/// - `exit_live_box`: LoopExitLivenessBox for exit liveness
/// - `query`: MirQuery for liveness computation
/// - `exit_block`: Exit block ID
///
/// # Returns
///
/// Some(LoopScopeShape) if successful, None if critical data is missing.
///
/// # Phase 29 Behavior
/// # Phase 30 Design
///
/// Currently just reads from existing boxes without modifying behavior.
/// Future Phase 30 will absorb the boxes entirely.
/// This is the primary entry point for creating LoopScopeShape.
/// Block IDs come from loop_form, variable classification from existing boxes.
/// Future phases will internalize the box logic entirely.
pub(crate) fn from_existing_boxes(
loop_form: &LoopForm,
intake: &LoopFormIntake,
var_classes: &LoopVarClassBox,
exit_live_box: &LoopExitLivenessBox,
query: &impl MirQuery,
exit_block: BasicBlockId,
) -> Option<Self> {
// Extract block IDs from LoopForm
let header = loop_form.header;
let body = loop_form.body;
let latch = loop_form.latch;
let exit = loop_form.exit;
// Extract pinned and carriers from intake (already classified)
let pinned: BTreeSet<String> = intake.pinned_ordered.iter().cloned().collect();
let carriers: BTreeSet<String> = intake.carrier_ordered.iter().cloned().collect();
@ -105,7 +192,7 @@ impl LoopScopeShape {
// Build LocalScopeInspectorBox for body_local classification
let mut inspector =
crate::mir::phi_core::local_scope_inspector::LocalScopeInspectorBox::new();
inspector.record_snapshot(exit_block, &intake.header_snapshot);
inspector.record_snapshot(exit, &intake.header_snapshot);
for (bb, snap) in &intake.exit_snapshots {
inspector.record_snapshot(*bb, snap);
}
@ -145,7 +232,7 @@ impl LoopScopeShape {
// Compute exit_live from LoopExitLivenessBox (Phase 1: usually empty)
let exit_live_from_box = exit_live_box.compute_live_at_exit(
query,
exit_block,
exit, // Use exit from LoopForm
&intake.header_snapshot,
&intake.exit_snapshots,
);
@ -159,6 +246,12 @@ impl LoopScopeShape {
let progress_carrier = carriers.iter().next().cloned();
Some(Self {
// Block IDs from LoopForm
header,
body,
latch,
exit,
// Variable classification
pinned,
carriers,
body_locals,
@ -198,6 +291,55 @@ impl LoopScopeShape {
pub fn exit_phi_vars(&self) -> Vec<String> {
self.exit_live.iter().cloned().collect()
}
/// Phase 30 F-1.1: 変数を4分類に分類する
///
/// LoopVarClassBox.classify() と同じロジックを LoopScopeShape の内部状態から導出。
/// 将来は LoopVarClassBox がこのメソッドに委譲するようになる。
///
/// # Returns
///
/// - Pinned: pinned に含まれる
/// - Carrier: carriers に含まれる
/// - BodyLocalExit: body_locals かつ exit_live に含まれる
/// - BodyLocalInternal: body_locals に含まれるが exit_live には含まれない
///
/// # Note
///
/// 既知の変数でない場合は BodyLocalInternal を返す(保守的)
pub fn classify(&self, var_name: &str) -> LoopVarClass {
// Priority 1: Check if it's a pinned variable
if self.pinned.contains(var_name) {
return LoopVarClass::Pinned;
}
// Priority 2: Check if it's a carrier variable
if self.carriers.contains(var_name) {
return LoopVarClass::Carrier;
}
// Priority 3: Check body_local classification
if self.body_locals.contains(var_name) {
if self.exit_live.contains(var_name) {
return LoopVarClass::BodyLocalExit;
} else {
return LoopVarClass::BodyLocalInternal;
}
}
// Unknown variable: conservative fallback (no PHI)
LoopVarClass::BodyLocalInternal
}
/// Phase 30 F-1.1: 複数変数を一括分類
///
/// LoopVarClassBox.classify_all() の代替
pub fn classify_all(&self, var_names: &[String]) -> Vec<(String, LoopVarClass)> {
var_names
.iter()
.map(|name| (name.clone(), self.classify(name)))
.collect()
}
}
// ============================================================================
@ -223,8 +365,7 @@ impl LoopScopeShape {
/// ```
#[derive(Debug, Clone)]
pub(crate) struct CaseAContext {
/// LoopScopeShape変数スコープ情報
pub scope: LoopScopeShape,
// Phase 30: scope フィールド削除ordered_pinned/carriers/exit_args に情報コピー済みで重複
/// 順序付き pinned 変数名
pub ordered_pinned: Vec<String>,
@ -285,13 +426,13 @@ impl CaseAContext {
// 2) MIR から pinned/carrier/exit 情報を抽出
let intake = intake_loop_form(loop_form, var_classes, query, mir_func)?;
// 3) LoopScopeShape を構築
// 3) LoopScopeShape を構築 (Phase 30: includes block IDs)
let scope = LoopScopeShape::from_existing_boxes(
loop_form,
&intake,
var_classes,
exit_live,
query,
loop_form.exit,
)?;
let ordered_pinned = scope.pinned_ordered();
@ -323,7 +464,72 @@ impl CaseAContext {
let exit_args = resolve_exit_args(&scope.exit_live, &name_to_loop_id, &ordered_carriers)?;
Some(Self {
scope,
ordered_pinned,
ordered_carriers,
name_to_loop_id,
pinned_ids,
carrier_ids,
exit_args,
})
}
/// Phase 30: LoopScopeShape を直接受け取る新コンストラクタ
///
/// # Arguments
///
/// - `scope`: LoopScopeShape変数スコープ情報
/// - `log_tag`: ログ出力用タグ(例: "skip_ws", "trim"
/// - `loop_step_id_fn`: offset から ValueId を生成する関数
///
/// # Returns
///
/// Some(CaseAContext) if successful, None if validation fails.
pub(crate) fn from_scope<F>(
scope: LoopScopeShape,
log_tag: &str,
loop_step_id_fn: F,
) -> Option<Self>
where
F: Fn(u32) -> ValueId,
{
// LoopForm validation using scope's block IDs
if scope.header == scope.exit {
eprintln!(
"[joinir/generic_case_a/{}] loop_form malformed (header == exit), fallback",
log_tag
);
return None;
}
let ordered_pinned = scope.pinned_ordered();
let ordered_carriers = scope.carriers_ordered();
// 変数名 → ValueId マッピングを構築
let mut name_to_loop_id: BTreeMap<String, ValueId> = BTreeMap::new();
let mut offset: u32 = 0;
for name in &ordered_pinned {
name_to_loop_id.insert(name.clone(), loop_step_id_fn(offset));
offset += 1;
}
for name in &ordered_carriers {
name_to_loop_id.insert(name.clone(), loop_step_id_fn(offset));
offset += 1;
}
// pinned_ids / carrier_ids を構築
let pinned_ids: Vec<ValueId> = ordered_pinned
.iter()
.filter_map(|k| name_to_loop_id.get(k).copied())
.collect();
let carrier_ids: Vec<ValueId> = ordered_carriers
.iter()
.filter_map(|k| name_to_loop_id.get(k).copied())
.collect();
// exit_args を解決
let exit_args = resolve_exit_args(&scope.exit_live, &name_to_loop_id, &ordered_carriers)?;
Some(Self {
ordered_pinned,
ordered_carriers,
name_to_loop_id,
@ -365,6 +571,18 @@ mod tests {
use crate::mir::ValueId;
use std::collections::BTreeMap;
fn make_dummy_loop_form() -> LoopForm {
LoopForm {
preheader: BasicBlockId::new(1),
header: BasicBlockId::new(2),
body: BasicBlockId::new(3),
latch: BasicBlockId::new(4),
exit: BasicBlockId::new(100),
continue_targets: vec![],
break_targets: vec![],
}
}
fn make_dummy_intake() -> LoopFormIntake {
let mut header_snapshot = BTreeMap::new();
header_snapshot.insert("s".to_string(), ValueId(10));
@ -398,22 +616,29 @@ mod tests {
#[test]
fn test_from_existing_boxes_basic() {
let loop_form = make_dummy_loop_form();
let intake = make_dummy_intake();
let var_classes = LoopVarClassBox::new();
let exit_live_box = LoopExitLivenessBox::new();
let query = EmptyQuery;
let scope = LoopScopeShape::from_existing_boxes(
&loop_form,
&intake,
&var_classes,
&exit_live_box,
&query,
BasicBlockId::new(100),
);
assert!(scope.is_some());
let scope = scope.unwrap();
// Block IDs should match loop_form
assert_eq!(scope.header, BasicBlockId::new(2));
assert_eq!(scope.body, BasicBlockId::new(3));
assert_eq!(scope.latch, BasicBlockId::new(4));
assert_eq!(scope.exit, BasicBlockId::new(100));
// pinned should be {s, n}
assert!(scope.pinned.contains("s"));
assert!(scope.pinned.contains("n"));
@ -429,17 +654,18 @@ mod tests {
#[test]
fn test_needs_header_phi() {
let loop_form = make_dummy_loop_form();
let intake = make_dummy_intake();
let var_classes = LoopVarClassBox::new();
let exit_live_box = LoopExitLivenessBox::new();
let query = EmptyQuery;
let scope = LoopScopeShape::from_existing_boxes(
&loop_form,
&intake,
&var_classes,
&exit_live_box,
&query,
BasicBlockId::new(100),
)
.unwrap();
@ -451,17 +677,18 @@ mod tests {
#[test]
fn test_needs_exit_phi() {
let loop_form = make_dummy_loop_form();
let intake = make_dummy_intake();
let var_classes = LoopVarClassBox::new();
let exit_live_box = LoopExitLivenessBox::new();
let query = EmptyQuery;
let scope = LoopScopeShape::from_existing_boxes(
&loop_form,
&intake,
&var_classes,
&exit_live_box,
&query,
BasicBlockId::new(100),
)
.unwrap();
@ -473,17 +700,18 @@ mod tests {
#[test]
fn test_ordered_accessors() {
let loop_form = make_dummy_loop_form();
let intake = make_dummy_intake();
let var_classes = LoopVarClassBox::new();
let exit_live_box = LoopExitLivenessBox::new();
let query = EmptyQuery;
let scope = LoopScopeShape::from_existing_boxes(
&loop_form,
&intake,
&var_classes,
&exit_live_box,
&query,
BasicBlockId::new(100),
)
.unwrap();
@ -496,4 +724,216 @@ mod tests {
assert_eq!(carriers.len(), 1);
assert!(carriers.contains(&"i".to_string()));
}
// Phase 30: 追加テスト4本
/// CaseAContext::from_scope で header == exit のとき None を返すテスト
#[test]
fn test_from_scope_validation_header_eq_exit() {
use crate::mir::join_ir::lowering::value_id_ranges::skip_ws as vid;
// header == exit の不正な LoopScopeShape を作成
let scope = LoopScopeShape {
header: BasicBlockId::new(10),
body: BasicBlockId::new(11),
latch: BasicBlockId::new(12),
exit: BasicBlockId::new(10), // header と同じ!
pinned: vec!["s".to_string()].into_iter().collect(),
carriers: vec!["i".to_string()].into_iter().collect(),
body_locals: std::collections::BTreeSet::new(),
exit_live: vec!["i".to_string()].into_iter().collect(),
progress_carrier: Some("i".to_string()),
};
// from_scope は None を返すべき
let ctx = CaseAContext::from_scope(scope, "test", |offset| vid::loop_step(offset));
assert!(ctx.is_none(), "from_scope should return None when header == exit");
}
/// block IDs が LoopForm から正しく伝播されるテスト
#[test]
fn test_block_ids_preserved() {
let loop_form = LoopForm {
preheader: BasicBlockId::new(100),
header: BasicBlockId::new(200),
body: BasicBlockId::new(300),
latch: BasicBlockId::new(400),
exit: BasicBlockId::new(500),
continue_targets: vec![],
break_targets: vec![],
};
let intake = make_dummy_intake();
let var_classes = LoopVarClassBox::new();
let exit_live_box = LoopExitLivenessBox::new();
let query = EmptyQuery;
let scope = LoopScopeShape::from_existing_boxes(
&loop_form,
&intake,
&var_classes,
&exit_live_box,
&query,
)
.unwrap();
// 正確に LoopForm の値が伝播されている
assert_eq!(scope.header, BasicBlockId::new(200));
assert_eq!(scope.body, BasicBlockId::new(300));
assert_eq!(scope.latch, BasicBlockId::new(400));
assert_eq!(scope.exit, BasicBlockId::new(500));
}
/// BTreeSet による順序決定性の確認テスト
#[test]
fn test_deterministic_order() {
// 異なる挿入順で同じ要素を持つセット
let mut set1: std::collections::BTreeSet<String> = std::collections::BTreeSet::new();
set1.insert("z".to_string());
set1.insert("a".to_string());
set1.insert("m".to_string());
let mut set2: std::collections::BTreeSet<String> = std::collections::BTreeSet::new();
set2.insert("m".to_string());
set2.insert("z".to_string());
set2.insert("a".to_string());
// BTreeSet はソート順でイテレート
let vec1: Vec<_> = set1.iter().cloned().collect();
let vec2: Vec<_> = set2.iter().cloned().collect();
assert_eq!(vec1, vec2);
assert_eq!(vec1, vec!["a".to_string(), "m".to_string(), "z".to_string()]);
}
/// needs_header_phi と needs_exit_phi の一貫性テスト
#[test]
fn test_needs_phi_consistency() {
let loop_form = make_dummy_loop_form();
let intake = make_dummy_intake();
let var_classes = LoopVarClassBox::new();
let exit_live_box = LoopExitLivenessBox::new();
let query = EmptyQuery;
let scope = LoopScopeShape::from_existing_boxes(
&loop_form,
&intake,
&var_classes,
&exit_live_box,
&query,
)
.unwrap();
// pinned 変数: header_phi必要、exit_phi必要
for var in &scope.pinned {
assert!(
scope.needs_header_phi(var),
"pinned var {} should need header phi",
var
);
assert!(
scope.needs_exit_phi(var),
"pinned var {} should need exit phi",
var
);
}
// carrier 変数: header_phi必要、exit_phi必要
for var in &scope.carriers {
assert!(
scope.needs_header_phi(var),
"carrier var {} should need header phi",
var
);
assert!(
scope.needs_exit_phi(var),
"carrier var {} should need exit phi",
var
);
}
// body_local 変数: header_phi不要
for var in &scope.body_locals {
assert!(
!scope.needs_header_phi(var),
"body_local var {} should NOT need header phi",
var
);
}
}
/// Phase 30 F-1.1: classify() メソッドのテスト
#[test]
fn test_classify_method() {
// 手動で LoopScopeShape を構築body_locals も含む)
let scope = LoopScopeShape {
header: BasicBlockId::new(2),
body: BasicBlockId::new(3),
latch: BasicBlockId::new(4),
exit: BasicBlockId::new(100),
pinned: vec!["s".to_string(), "n".to_string()].into_iter().collect(),
carriers: vec!["i".to_string()].into_iter().collect(),
body_locals: vec!["x".to_string(), "ch".to_string()].into_iter().collect(),
exit_live: vec![
"s".to_string(),
"n".to_string(),
"i".to_string(),
"x".to_string(), // BodyLocalExit
]
.into_iter()
.collect(),
progress_carrier: Some("i".to_string()),
};
// Pinned classification
assert_eq!(scope.classify("s"), LoopVarClass::Pinned);
assert_eq!(scope.classify("n"), LoopVarClass::Pinned);
// Carrier classification
assert_eq!(scope.classify("i"), LoopVarClass::Carrier);
// BodyLocalExit classification (in body_locals AND exit_live)
assert_eq!(scope.classify("x"), LoopVarClass::BodyLocalExit);
// BodyLocalInternal classification (in body_locals but NOT in exit_live)
assert_eq!(scope.classify("ch"), LoopVarClass::BodyLocalInternal);
// Unknown variable → BodyLocalInternal (conservative)
assert_eq!(scope.classify("unknown"), LoopVarClass::BodyLocalInternal);
}
/// Phase 30 F-1.1: classify() と needs_*_phi() の一貫性
#[test]
fn test_classify_phi_consistency() {
let scope = LoopScopeShape {
header: BasicBlockId::new(2),
body: BasicBlockId::new(3),
latch: BasicBlockId::new(4),
exit: BasicBlockId::new(100),
pinned: vec!["s".to_string()].into_iter().collect(),
carriers: vec!["i".to_string()].into_iter().collect(),
body_locals: vec!["x".to_string(), "ch".to_string()].into_iter().collect(),
exit_live: vec!["s".to_string(), "i".to_string(), "x".to_string()]
.into_iter()
.collect(),
progress_carrier: Some("i".to_string()),
};
// classify() と needs_header_phi() / needs_exit_phi() が一致することを確認
for var in ["s", "i", "x", "ch", "unknown"] {
let class = scope.classify(var);
assert_eq!(
class.needs_header_phi(),
scope.needs_header_phi(var),
"classify and needs_header_phi mismatch for {}",
var
);
assert_eq!(
class.needs_exit_phi(),
scope.needs_exit_phi(var),
"classify and needs_exit_phi mismatch for {}",
var
);
}
}
}

View File

@ -51,8 +51,7 @@ pub(crate) fn is_joinir_header_bypass_target(fn_name: &str) -> bool {
pub(crate) struct LoopBypassFlags {
/// Header φ バイパスが有効か
pub header: bool,
/// Exit φ バイパスが有効かPhase 27.6-2, 現在未使用
pub exit: bool,
// Phase 30: exit フィールド削除(完全未使用、将来 JoinIR で代替予定
}
/// Phase 27.4-C Refactor: JoinIR Loop φ バイパスフラグを取得
@ -80,8 +79,7 @@ pub(crate) fn get_loop_bypass_flags(fn_name: &str) -> LoopBypassFlags {
LoopBypassFlags {
header: joinir_exp && header_exp && is_joinir_header_bypass_target(fn_name),
// Phase 27.6-2: Exit φ バイパスは将来的に追加予定
exit: false,
// Phase 30: exit フィールド削除済み
}
}

View File

@ -1,5 +1,14 @@
/// LocalScopeInspectorBox - Variable definition tracker
///
/// # Phase 30: LoopScopeShape 移行中
///
/// このBoxは将来 LoopScopeShape に吸収される予定。
/// 定義位置情報は LoopScopeShape::from_existing_boxes() 内部で集約され、
/// 変数分類に使用される。
///
/// **新規コード**: LoopScopeShape が利用可能な場合は、
/// `classify()` メソッドを直接使うことを推奨。
///
/// # Purpose
///
/// This box tracks which variables are defined in which basic blocks.
@ -145,6 +154,30 @@ impl LocalScopeInspectorBox {
.map(|blocks| blocks.len())
.unwrap_or(0)
}
// ========================================================================
// Phase 30 F-1.3: LoopScopeShape 委譲メソッド
// ========================================================================
/// Phase 30: LoopScopeShape を使って変数が全 exit pred で利用可能か判定
///
/// 新規コードでは LoopScopeShape::classify() を直接使うことを推奨。
/// このメソッドは LoopScopeShape が既にある場合の便利メソッド。
///
/// # Note
///
/// LoopScopeShape::classify() は既に exit_live 情報を含んでいるため、
/// 直接 classify() → needs_exit_phi() を使う方が効率的。
pub fn is_available_in_all_with_scope(
&self,
var_name: &str,
scope: &crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape,
) -> bool {
// LoopScopeShape の分類を使用
// Pinned/Carrier/BodyLocalExit → available in all (exit PHI needed)
// BodyLocalInternal → NOT available in all (no exit PHI)
scope.needs_exit_phi(var_name)
}
}
// ============================================================================

View File

@ -43,6 +43,13 @@ pub trait ExitLivenessProvider: Send + Sync {
/// Loop Exit Liveness BoxLegacy/Phase 1
///
/// # Phase 30: LoopScopeShape 移行中
///
/// このBoxは将来 LoopScopeShape に吸収される予定。
/// LoopScopeShape.exit_live が唯一の live_at_exit 情報源になる。
///
/// **新規コード**: `LoopScopeShape::exit_live` を直接参照すること。
///
/// # Purpose
/// Exit後で本当に使われる変数を決定する専門箱
///
@ -171,6 +178,29 @@ impl LoopExitLivenessBox {
live_vars
}
// ========================================================================
// Phase 30 F-1.2: LoopScopeShape 委譲メソッド
// ========================================================================
/// Phase 30: LoopScopeShape の exit_live を直接取得
///
/// 新規コードではこのメソッドを使うことを推奨。
/// 将来的には旧 compute_live_at_exit() メソッドを削除し、
/// LoopScopeShape::exit_live を直接参照するのが標準になる。
///
/// # Example
///
/// ```ignore
/// let scope = LoopScopeShape::from_existing_boxes(...)?;
/// let live = liveness_box.get_exit_live_from_scope(&scope);
/// ```
pub fn get_exit_live_from_scope(
&self,
scope: &crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape,
) -> BTreeSet<String> {
scope.exit_live.clone()
}
}
impl ExitLivenessProvider for LoopExitLivenessBox {

View File

@ -108,7 +108,12 @@ impl LoopVarClass {
/// Loop variable classifier box
///
/// # Usage
/// # Phase 30: LoopScopeShape 移行中
///
/// このBoxは将来 LoopScopeShape に吸収される予定。
/// 新規コードは `LoopScopeShape::classify()` を直接使うことを推奨。
///
/// # Usage (Legacy)
///
/// ```
/// let inspector = LocalScopeInspectorBox::new();
@ -127,6 +132,13 @@ impl LoopVarClass {
/// // Generate exit PHI
/// }
/// ```
///
/// # Usage (Phase 30 Recommended)
///
/// ```
/// // LoopScopeShape が利用可能な場合は直接使う
/// let class = scope.classify("ch");
/// ```
#[derive(Debug, Clone, Default)]
pub struct LoopVarClassBox;
@ -138,6 +150,11 @@ impl LoopVarClassBox {
/// Classify a variable for PHI generation decision
///
/// # Phase 30 TODO
///
/// このメソッドは将来 `LoopScopeShape::classify()` に置き換える予定。
/// 呼び出し側が LoopScopeShape を持っている場合は、そちらを直接使うこと。
///
/// # Parameters
///
/// - `var_name`: Variable to classify
@ -245,6 +262,38 @@ impl LoopVarClassBox {
.cloned()
.collect()
}
// ========================================================================
// Phase 30 F-1.1: LoopScopeShape 委譲メソッド
// ========================================================================
/// Phase 30: LoopScopeShape を使って変数を分類
///
/// 新規コードではこのメソッドを使うことを推奨。
/// 将来的には旧 classify() メソッドを削除し、このメソッドが標準になる。
///
/// # Example
///
/// ```ignore
/// let scope = LoopScopeShape::from_existing_boxes(...)?;
/// let class = classifier.classify_with_scope("ch", &scope);
/// ```
pub fn classify_with_scope(
&self,
var_name: &str,
scope: &crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape,
) -> LoopVarClass {
scope.classify(var_name)
}
/// Phase 30: LoopScopeShape を使って複数変数を一括分類
pub fn classify_all_with_scope(
&self,
var_names: &[String],
scope: &crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape,
) -> Vec<(String, LoopVarClass)> {
scope.classify_all(var_names)
}
}
// ============================================================================

View File

@ -51,8 +51,7 @@ use std::collections::BTreeMap;
pub struct PhiBuilderBox {
/// If PHI生成時のコンテキスト将来拡張用
if_context: Option<IfPhiContext>,
/// Loop PHI生成時のコンテキスト将来拡張用
loop_context: Option<LoopPhiContext>,
// Phase 30: loop_context 削除(完全未使用、将来 JoinIR で代替予定
}
/// If PHI生成コンテキストPhase 26-F-2: 箱理論による責務分離)
@ -85,19 +84,13 @@ pub struct IfPhiContext {
pub loop_carrier_names: std::collections::BTreeSet<String>,
}
/// Loop PHI生成コンテキスト将来拡張用
#[derive(Debug, Clone)]
struct LoopPhiContext {
/// 予約済みPhase 3で実装
_reserved: (),
}
// Phase 30: LoopPhiContext 削除(完全未使用、将来 JoinIR で代替予定
impl PhiBuilderBox {
/// 新しいPhiBuilderBoxを作成
pub fn new() -> Self {
Self {
if_context: None,
loop_context: None,
}
}
@ -576,7 +569,7 @@ mod tests {
fn test_phi_builder_box_creation() {
let builder = PhiBuilderBox::new();
assert!(builder.if_context.is_none());
assert!(builder.loop_context.is_none());
// Phase 30: loop_context 削除済み
}
#[test]