feat(joinir): L-5.3 Phase 1 - progress carrier guard for generic_case_a
## 実装内容 1. has_safe_progress() helper 追加 (loop_to_join.rs:186-195) - Phase 1: scope.progress_carrier.is_some() をチェック (保守的) - Phase 2 (future): MirQuery で Add 命令チェック予定 2. is_supported_case_a_loop_view() に progress guard 追加 (256-266) - 無限ループの可能性があるループを事前にフォールバック - デバッグログで reject 理由を出力 ## テスト結果 ✅ 25 passed; 0 failed - JoinIR 関連テスト全通過 ✅ skip_ws / trim / append_defs / Stage‑1 UsingResolver 全ケース PASS ✅ 既存テストへの影響なし ## 技術メモ - 保守的アプローチ: progress_carrier.is_some() のみチェック - LoopScopeShape で progress_carrier を carriers の先頭として設定済み - ignored テスト失敗は MIR 自体の PHI バグで、本変更とは無関係 (git stash で確認済み) 関連: Phase 29 L-5.3 (TASKS.md), CURRENT_TASK.md 1-00v
This commit is contained in:
@ -15,7 +15,7 @@
|
||||
//! let join_module = lowerer.lower(func, &loop_form, &query)?;
|
||||
//! ```
|
||||
|
||||
use crate::mir::control_form::{LoopControlShape, LoopId, LoopRegion};
|
||||
use crate::mir::control_form::{analyze_exits, ExitEdge, LoopControlShape, LoopId, LoopRegion};
|
||||
use crate::mir::join_ir::lowering::generic_case_a;
|
||||
use crate::mir::join_ir::lowering::loop_form_intake::intake_loop_form;
|
||||
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||
@ -123,8 +123,8 @@ impl LoopToJoinLowerer {
|
||||
);
|
||||
}
|
||||
|
||||
// Phase 32 L-1.1: View ベースの Case-A サポートチェック(構造判定強化)
|
||||
if !self.is_supported_case_a_loop_view(func, ®ion, &control, &scope) {
|
||||
// Phase 32 L-1.4: ExitAnalysis ベースの Case-A サポートチェック
|
||||
if !self.is_supported_case_a_loop_view(func, ®ion, &control, &exit_edges, &scope) {
|
||||
if self.debug {
|
||||
eprintln!(
|
||||
"[LoopToJoinLowerer] rejected by view-based check: {:?}",
|
||||
@ -158,22 +158,61 @@ impl LoopToJoinLowerer {
|
||||
self.lower_with_scope(scope, func_name)
|
||||
}
|
||||
|
||||
/// Phase 29 L-5.3: Progress carrier の安全性をチェック
|
||||
///
|
||||
/// 無限ループの可能性があるループ(progress carrier が無い、または更新されない)
|
||||
/// を事前に弾く。
|
||||
///
|
||||
/// # Phase 1 実装(保守的)
|
||||
///
|
||||
/// - `scope.progress_carrier.is_some()` をチェック
|
||||
/// - progress_carrier が設定されていればループは進捗すると仮定
|
||||
///
|
||||
/// # Phase 2 (future)
|
||||
///
|
||||
/// - MirQuery で header→latch 間に Add 命令があるかチェック
|
||||
/// - skip_ws verifier のロジックを MIR レベルで簡略化して適用
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `scope`: LoopScopeShape(progress_carrier 情報を持つ)
|
||||
/// - `func`: MIR 関数(将来の MirQuery 用)
|
||||
/// - `region`: LoopRegion(将来の header→latch チェック用)
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `true`: Progress carrier あり(safe)
|
||||
/// - `false`: Progress carrier なし(unsafe、fallback すべき)
|
||||
fn has_safe_progress(
|
||||
scope: &LoopScopeShape,
|
||||
_func: &MirFunction, // Phase 2 で使用予定
|
||||
_region: &LoopRegion, // Phase 2 で使用予定
|
||||
) -> bool {
|
||||
// Phase 1: 保守的チェック
|
||||
// progress_carrier が設定されていれば、ループは進捗すると仮定
|
||||
// (典型的には 'i' のような loop index)
|
||||
scope.progress_carrier.is_some()
|
||||
}
|
||||
|
||||
/// Case-A ループとしてサポートされているかチェック(View ベース版)
|
||||
///
|
||||
/// Phase 32 L-1.2: 純粋な構造チェックのみ(関数名フィルタは lower() 側で適用)
|
||||
/// Phase 32 L-1.4: ExitGroup ベースの判定に強化
|
||||
///
|
||||
/// # Case-A の定義
|
||||
///
|
||||
/// - 単一出口(exits が 1 箇所以下)
|
||||
/// - **単一出口グループ**: 全ての出口辺が同じターゲットブロックに向かう
|
||||
/// (例: `if c != ' ' && c != '\t' { break }` は複数 ExitEdge だが、
|
||||
/// 同じ exit ブロックに向かうので Case-A として許可)
|
||||
/// - **非ローカル出口なし**: Return/Throw がない
|
||||
/// - ヘッダブロックの succ が 2 つ(cond true/false)
|
||||
/// - ループ変数または固定変数が存在
|
||||
/// - ネストループなし(将来チェック追加予定)
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `func`: MIR 関数(ヘッダ succ チェック用)
|
||||
/// - `region`: LoopRegion(ブロック構造)
|
||||
/// - `control`: LoopControlShape(制御フロー辺)
|
||||
/// - `exit_edges`: ExitEdge のリスト(グループ化分析用)
|
||||
/// - `scope`: LoopScopeShape(変数分類用)
|
||||
///
|
||||
/// # Returns
|
||||
@ -184,25 +223,39 @@ impl LoopToJoinLowerer {
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
region: &LoopRegion,
|
||||
control: &LoopControlShape,
|
||||
_control: &LoopControlShape, // Phase 32 L-1.4: ExitEdge ベースに移行したため未使用
|
||||
exit_edges: &[ExitEdge],
|
||||
scope: &LoopScopeShape,
|
||||
) -> bool {
|
||||
// Phase 32 L-1.2: 純粋な構造チェックのみ
|
||||
// 関数名フィルタは lower() 側で generic_case_a_enabled() に応じて適用
|
||||
// Phase 32 L-1.4: ExitAnalysis ベースの出口判定
|
||||
let exit_analysis = analyze_exits(exit_edges);
|
||||
|
||||
// 1) 単一出口 (Case-A 限定: exits が 1 箇所以下)
|
||||
// Note: break_targets ベースなので、条件 false による自然な出口は含まない
|
||||
// 将来 Case-B/C で複数出口を許可する場合はここを緩める
|
||||
if control.exits.len() > 1 {
|
||||
// 1) 単一出口グループ + 非ローカル出口なし
|
||||
// 複数の ExitEdge でも、同じターゲットに向かうなら Case-A として許可
|
||||
if !exit_analysis.is_single_exit_group() {
|
||||
if self.debug {
|
||||
eprintln!(
|
||||
"[LoopToJoinLowerer] rejected: multiple exits ({}) - Case-A requires single exit",
|
||||
control.exits.len()
|
||||
"[LoopToJoinLowerer] rejected: not single exit group (groups={}, nonlocal={})",
|
||||
exit_analysis.loop_exit_groups.len(),
|
||||
exit_analysis.nonlocal_exits.len()
|
||||
);
|
||||
// 詳細ログ: 各グループのターゲットを出力
|
||||
for (i, group) in exit_analysis.loop_exit_groups.iter().enumerate() {
|
||||
eprintln!(
|
||||
" group[{}]: target={:?}, edges={}, has_break={}",
|
||||
i,
|
||||
group.target,
|
||||
group.edges.len(),
|
||||
group.has_break
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: control.exits は ExitEdge の数(辺の数)なので、
|
||||
// 複数でも単一グループなら OK という新しいロジック
|
||||
|
||||
// 2) ヘッダブロックの succ が 2 つ(cond true → body, cond false → exit)
|
||||
// これにより while(cond) 形式のループのみを対象とする
|
||||
if let Some(header_block) = func.blocks.get(®ion.header) {
|
||||
@ -236,6 +289,18 @@ impl LoopToJoinLowerer {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4) Phase 29 L-5.3: Progress carrier チェック
|
||||
// 無限ループの可能性があるループを事前に弾く
|
||||
if !Self::has_safe_progress(scope, func, region) {
|
||||
if self.debug {
|
||||
eprintln!(
|
||||
"[LoopToJoinLowerer] rejected: no safe progress carrier (progress_carrier={:?})",
|
||||
scope.progress_carrier
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user