phase15: implement Phase S root treatment for break control flow
🔧 **Phase S (Immediate Stabilization) Implementation** - Create control flow utilities module (src/mir/utils/) - Refactor loop_builder.rs duplicated code to utilities - Fix PHI incoming predecessor capture per ChatGPT Pro analysis 📊 **AI Collaborative Analysis Complete** - Task agent: Root cause identification - Gemini: Strategic 3-phase approach - codex: Advanced type inference solution (archived) - ChatGPT Pro: Definitive staged treatment strategy 🗂️ **Documentation & Archive** - Strategy document: docs/development/strategies/break-control-flow-strategy.md - codex solutions: archive/codex-solutions/ (100+ lines changes) - Update CLAUDE.md with 2025-09-23 progress ⚡ **Expected Impact** - Resolve collect_prints null return issue - Eliminate code duplication (4 locations unified) - Foundation for Phase M (PHI unification) and Phase L (BuildOutcome) 🎯 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -1,6 +1,41 @@
|
||||
// Extracted call-related builders from builder.rs to keep files lean
|
||||
use super::{Effect, EffectMask, FunctionSignature, MirInstruction, MirType, ValueId};
|
||||
use crate::ast::{ASTNode, LiteralValue, MethodCallExpr};
|
||||
|
||||
fn contains_value_return(nodes: &[ASTNode]) -> bool {
|
||||
fn node_has_value_return(node: &ASTNode) -> bool {
|
||||
match node {
|
||||
ASTNode::Return { value: Some(_), .. } => true,
|
||||
ASTNode::If { then_body, else_body, .. } => {
|
||||
contains_value_return(then_body)
|
||||
|| else_body
|
||||
.as_ref()
|
||||
.map_or(false, |body| contains_value_return(body))
|
||||
}
|
||||
ASTNode::Loop { body, .. } => contains_value_return(body),
|
||||
ASTNode::TryCatch {
|
||||
try_body,
|
||||
catch_clauses,
|
||||
finally_body,
|
||||
..
|
||||
} => {
|
||||
contains_value_return(try_body)
|
||||
|| catch_clauses
|
||||
.iter()
|
||||
.any(|clause| contains_value_return(&clause.body))
|
||||
|| finally_body
|
||||
.as_ref()
|
||||
.map_or(false, |body| contains_value_return(body))
|
||||
}
|
||||
ASTNode::Program { statements, .. } => contains_value_return(statements),
|
||||
ASTNode::ScopeBox { body, .. } => contains_value_return(body),
|
||||
ASTNode::FunctionDeclaration { body, .. } => contains_value_return(body),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
nodes.iter().any(node_has_value_return)
|
||||
}
|
||||
use crate::mir::{slot_registry, TypeOpKind};
|
||||
|
||||
impl super::MirBuilder {
|
||||
@ -321,13 +356,7 @@ impl super::MirBuilder {
|
||||
for _ in ¶ms {
|
||||
param_types.push(MirType::Unknown);
|
||||
}
|
||||
let mut returns_value = false;
|
||||
for st in &body {
|
||||
if let ASTNode::Return { value: Some(_), .. } = st {
|
||||
returns_value = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let returns_value = contains_value_return(&body);
|
||||
let ret_ty = if returns_value {
|
||||
MirType::Unknown
|
||||
} else {
|
||||
@ -365,17 +394,39 @@ impl super::MirBuilder {
|
||||
span: crate::ast::Span::unknown(),
|
||||
};
|
||||
let _last = self.build_expression(program_ast)?;
|
||||
if !returns_value && !self.is_current_block_terminated() {
|
||||
let void_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: void_val,
|
||||
value: super::ConstValue::Void,
|
||||
})?;
|
||||
self.emit_instruction(MirInstruction::Return {
|
||||
value: Some(void_val),
|
||||
})?;
|
||||
}
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
if let Some(block) = f.get_block(self.current_block.unwrap()) {
|
||||
if !block.is_terminated() {
|
||||
let void_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: void_val,
|
||||
value: super::ConstValue::Void,
|
||||
})?;
|
||||
self.emit_instruction(MirInstruction::Return {
|
||||
value: Some(void_val),
|
||||
})?;
|
||||
if returns_value
|
||||
&& matches!(f.signature.return_type, MirType::Void | MirType::Unknown)
|
||||
{
|
||||
let mut inferred: Option<MirType> = None;
|
||||
'search: for (_bid, bb) in f.blocks.iter() {
|
||||
for inst in bb.instructions.iter() {
|
||||
if let MirInstruction::Return { value: Some(v) } = inst {
|
||||
if let Some(mt) = self.value_types.get(v).cloned() {
|
||||
inferred = Some(mt);
|
||||
break 'search;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(MirInstruction::Return { value: Some(v) }) = &bb.terminator {
|
||||
if let Some(mt) = self.value_types.get(v).cloned() {
|
||||
inferred = Some(mt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(mt) = inferred {
|
||||
f.signature.return_type = mt;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -401,13 +452,7 @@ impl super::MirBuilder {
|
||||
for _ in ¶ms {
|
||||
param_types.push(MirType::Unknown);
|
||||
}
|
||||
let mut returns_value = false;
|
||||
for st in &body {
|
||||
if let ASTNode::Return { value: Some(_), .. } = st {
|
||||
returns_value = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let returns_value = contains_value_return(&body);
|
||||
let ret_ty = if returns_value {
|
||||
MirType::Unknown
|
||||
} else {
|
||||
@ -441,17 +486,45 @@ impl super::MirBuilder {
|
||||
span: crate::ast::Span::unknown(),
|
||||
};
|
||||
let _last = self.build_expression(program_ast)?;
|
||||
if !returns_value {
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
if let Some(block) = f.get_block(self.current_block.unwrap()) {
|
||||
if !block.is_terminated() {
|
||||
let void_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: void_val,
|
||||
value: super::ConstValue::Void,
|
||||
})?;
|
||||
self.emit_instruction(MirInstruction::Return {
|
||||
value: Some(void_val),
|
||||
})?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
if let Some(block) = f.get_block(self.current_block.unwrap()) {
|
||||
if !block.is_terminated() {
|
||||
let void_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: void_val,
|
||||
value: super::ConstValue::Void,
|
||||
})?;
|
||||
self.emit_instruction(MirInstruction::Return {
|
||||
value: Some(void_val),
|
||||
})?;
|
||||
if returns_value
|
||||
&& matches!(f.signature.return_type, MirType::Void | MirType::Unknown)
|
||||
{
|
||||
let mut inferred: Option<MirType> = None;
|
||||
'search: for (_bid, bb) in f.blocks.iter() {
|
||||
for inst in bb.instructions.iter() {
|
||||
if let MirInstruction::Return { value: Some(v) } = inst {
|
||||
if let Some(mt) = self.value_types.get(v).cloned() {
|
||||
inferred = Some(mt);
|
||||
break 'search;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(MirInstruction::Return { value: Some(v) }) = &bb.terminator {
|
||||
if let Some(mt) = self.value_types.get(v).cloned() {
|
||||
inferred = Some(mt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(mt) = inferred {
|
||||
f.signature.return_type = mt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,13 @@ use super::{BasicBlockId, ConstValue, MirInstruction, ValueId};
|
||||
use crate::ast::ASTNode;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
// Phase 15 段階的根治戦略:制御フローユーティリティ
|
||||
use super::utils::{
|
||||
is_current_block_terminated,
|
||||
capture_actual_predecessor_and_jump,
|
||||
collect_phi_incoming_if_reachable,
|
||||
};
|
||||
|
||||
/// 不完全なPhi nodeの情報
|
||||
#[derive(Debug, Clone)]
|
||||
struct IncompletePhi {
|
||||
@ -516,34 +523,17 @@ impl<'a> LoopBuilder<'a> {
|
||||
self.set_current_block(then_bb)?;
|
||||
for s in then_body.iter().cloned() {
|
||||
let _ = self.build_statement(s)?;
|
||||
// Stop if block terminated
|
||||
let cur_id = self.current_block()?;
|
||||
let terminated = {
|
||||
if let Some(ref fun_ro) = self.parent_builder.current_function {
|
||||
if let Some(bb) = fun_ro.get_block(cur_id) { bb.is_terminated() } else { false }
|
||||
} else { false }
|
||||
};
|
||||
if terminated { break; }
|
||||
// フェーズS修正:統一終端検出ユーティリティ使用
|
||||
if is_current_block_terminated(self.parent_builder)? {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let then_var_map_end = self.get_current_variable_map();
|
||||
// Only jump to merge if not already terminated (e.g., continue/break)
|
||||
// Capture the actual predecessor block that reaches merge (entry block may not be the exit).
|
||||
let then_pred_to_merge: Option<BasicBlockId> = {
|
||||
let cur_id = self.current_block()?;
|
||||
let need_jump = {
|
||||
if let Some(ref fun_ro) = self.parent_builder.current_function {
|
||||
if let Some(bb) = fun_ro.get_block(cur_id) { !bb.is_terminated() } else { false }
|
||||
} else { false }
|
||||
};
|
||||
if need_jump {
|
||||
// Emit the edge now; record the real predecessor (cur_id), not the entry then_bb.
|
||||
self.emit_jump(merge_bb)?;
|
||||
Some(cur_id)
|
||||
} else {
|
||||
// Terminated path (e.g., continue/break) — no incoming to merge.
|
||||
None
|
||||
}
|
||||
};
|
||||
// フェーズS修正:最強モード指摘の「実到達predecessor捕捉」を統一
|
||||
let then_pred_to_merge = capture_actual_predecessor_and_jump(
|
||||
self.parent_builder,
|
||||
merge_bb
|
||||
)?;
|
||||
|
||||
// else branch
|
||||
self.set_current_block(else_bb)?;
|
||||
@ -551,30 +541,18 @@ impl<'a> LoopBuilder<'a> {
|
||||
if let Some(es) = else_body.clone() {
|
||||
for s in es.into_iter() {
|
||||
let _ = self.build_statement(s)?;
|
||||
let cur_id = self.current_block()?;
|
||||
let terminated = {
|
||||
if let Some(ref fun_ro) = self.parent_builder.current_function {
|
||||
if let Some(bb) = fun_ro.get_block(cur_id) { bb.is_terminated() } else { false }
|
||||
} else { false }
|
||||
};
|
||||
if terminated { break; }
|
||||
// フェーズS修正:統一終端検出ユーティリティ使用
|
||||
if is_current_block_terminated(self.parent_builder)? {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else_var_map_end_opt = Some(self.get_current_variable_map());
|
||||
}
|
||||
let else_pred_to_merge: Option<BasicBlockId> = {
|
||||
let cur_id = self.current_block()?;
|
||||
let need_jump = {
|
||||
if let Some(ref fun_ro) = self.parent_builder.current_function {
|
||||
if let Some(bb) = fun_ro.get_block(cur_id) { !bb.is_terminated() } else { false }
|
||||
} else { false }
|
||||
};
|
||||
if need_jump {
|
||||
self.emit_jump(merge_bb)?;
|
||||
Some(cur_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
// フェーズS修正:else branchでも統一実到達predecessor捕捉
|
||||
let else_pred_to_merge = capture_actual_predecessor_and_jump(
|
||||
self.parent_builder,
|
||||
merge_bb
|
||||
)?;
|
||||
|
||||
// Continue at merge
|
||||
self.set_current_block(merge_bb)?;
|
||||
|
||||
@ -18,6 +18,7 @@ pub mod types; // core MIR enums (ConstValue, Ops, MirType)
|
||||
pub mod loop_api; // Minimal LoopBuilder facade (adapter-ready)
|
||||
pub mod loop_builder; // SSA loop construction with phi nodes
|
||||
pub mod optimizer;
|
||||
pub mod utils; // Phase 15 control flow utilities for root treatment
|
||||
pub mod optimizer_passes; // optimizer passes (normalize/diagnostics)
|
||||
pub mod optimizer_stats; // extracted stats struct
|
||||
pub mod passes;
|
||||
@ -44,6 +45,13 @@ pub use slot_registry::{BoxTypeId, MethodSlot};
|
||||
pub use value_id::{LocalId, ValueId, ValueIdGenerator};
|
||||
pub use verification::MirVerifier;
|
||||
pub use verification_types::VerificationError;
|
||||
// Phase 15 control flow utilities (段階的根治戦略)
|
||||
pub use utils::{
|
||||
is_current_block_terminated,
|
||||
capture_actual_predecessor_and_jump,
|
||||
collect_phi_incoming_if_reachable,
|
||||
execute_statement_with_termination_check,
|
||||
};
|
||||
|
||||
/// MIR compilation result
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
122
src/mir/utils/control_flow.rs
Normal file
122
src/mir/utils/control_flow.rs
Normal file
@ -0,0 +1,122 @@
|
||||
/*!
|
||||
* Control Flow Utilities - 制御フロー処理の共通ユーティリティ
|
||||
*
|
||||
* PHI incoming修正とブロック終端検出の汎用関数群
|
||||
* フェーズS(即効止血)からフェーズL(根本解決)まで共通利用
|
||||
*/
|
||||
|
||||
use super::super::{BasicBlockId, MirBuilder};
|
||||
|
||||
/// **外部関数**: 現在のブロックが終端済みかチェック
|
||||
///
|
||||
/// loop_builder.rsで3箇所重複していた処理を統一
|
||||
///
|
||||
/// # 使用例
|
||||
/// ```rust
|
||||
/// if is_current_block_terminated(builder)? {
|
||||
/// break; // 早期終了
|
||||
/// }
|
||||
/// ```
|
||||
pub fn is_current_block_terminated(builder: &MirBuilder) -> Result<bool, String> {
|
||||
let cur_id = builder.current_block
|
||||
.ok_or_else(|| "No current block".to_string())?;
|
||||
|
||||
if let Some(ref function) = builder.current_function {
|
||||
if let Some(bb) = function.get_block(cur_id) {
|
||||
Ok(bb.is_terminated())
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// **外部関数**: 実到達ブロックを捕捉してJump発行
|
||||
///
|
||||
/// 最強モード指摘の「実到達predecessor捕捉」を汎用化
|
||||
/// break/continue後の到達不能ブロックは除外
|
||||
///
|
||||
/// # 戻り値
|
||||
/// - `Some(predecessor_id)`: Jump発行済み、PHI incomingに使用可能
|
||||
/// - `None`: 既に終端済み、PHI incomingから除外すべき
|
||||
///
|
||||
/// # 使用例
|
||||
/// ```rust
|
||||
/// if let Some(pred_id) = capture_actual_predecessor_and_jump(builder, merge_bb)? {
|
||||
/// phi_incomings.push((pred_id, value));
|
||||
/// }
|
||||
/// ```
|
||||
pub fn capture_actual_predecessor_and_jump(
|
||||
builder: &mut MirBuilder,
|
||||
target_block: BasicBlockId,
|
||||
) -> Result<Option<BasicBlockId>, String> {
|
||||
let cur_id = builder.current_block
|
||||
.ok_or_else(|| "No current block".to_string())?;
|
||||
|
||||
let need_jump = !is_current_block_terminated(builder)?;
|
||||
|
||||
if need_jump {
|
||||
// Jump発行前に実到達ブロックID捕捉(重要!)
|
||||
// 既存control_flowモジュールと同じパターンを使用
|
||||
builder.emit_instruction(super::super::MirInstruction::Jump {
|
||||
target: target_block
|
||||
})?;
|
||||
Ok(Some(cur_id))
|
||||
} else {
|
||||
// 既に終端済み(break/continue等)、PHI incomingから除外
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// **外部関数**: 条件付きPHI incoming収集
|
||||
///
|
||||
/// 到達可能な場合のみincomingをリストに追加
|
||||
/// フェーズM、フェーズLでの型安全性向上にも対応
|
||||
///
|
||||
/// # 使用例
|
||||
/// ```rust
|
||||
/// let mut incomings = Vec::new();
|
||||
/// collect_phi_incoming_if_reachable(&mut incomings, then_pred, then_value);
|
||||
/// collect_phi_incoming_if_reachable(&mut incomings, else_pred, else_value);
|
||||
/// ```
|
||||
pub fn collect_phi_incoming_if_reachable(
|
||||
incomings: &mut Vec<(BasicBlockId, super::super::ValueId)>,
|
||||
predecessor: Option<BasicBlockId>,
|
||||
value: super::super::ValueId,
|
||||
) {
|
||||
if let Some(pred_id) = predecessor {
|
||||
incomings.push((pred_id, value));
|
||||
}
|
||||
// None(到達不能)の場合は何もしない
|
||||
}
|
||||
|
||||
/// **外部関数**: 終端チェック付きステートメント実行
|
||||
///
|
||||
/// build_statement後の終端チェックを自動化
|
||||
/// フェーズSでの「終端ガード徹底」を支援
|
||||
///
|
||||
/// # 戻り値
|
||||
/// - `Ok(true)`: 正常実行、継続可能
|
||||
/// - `Ok(false)`: 終端済み、ループ脱出すべき
|
||||
/// - `Err(_)`: エラー
|
||||
pub fn execute_statement_with_termination_check(
|
||||
builder: &mut MirBuilder,
|
||||
statement: crate::ast::ASTNode,
|
||||
) -> Result<bool, String> {
|
||||
let _result = builder.build_expression(statement)?;
|
||||
|
||||
// 終端チェック(統一処理)
|
||||
let terminated = is_current_block_terminated(builder)?;
|
||||
Ok(!terminated)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// ユニットテスト(将来追加)
|
||||
// - 終端検出の正確性
|
||||
// - 実到達ブロック捕捉の正確性
|
||||
// - PHI incoming除外の正確性
|
||||
}
|
||||
19
src/mir/utils/mod.rs
Normal file
19
src/mir/utils/mod.rs
Normal file
@ -0,0 +1,19 @@
|
||||
/*!
|
||||
* MIR Utilities - Phase 15 段階的根治戦略の共通ユーティリティ
|
||||
*
|
||||
* フェーズS: 即効止血
|
||||
* フェーズM: PHI一本化
|
||||
* フェーズL: 根本解決
|
||||
*
|
||||
* 全フェーズで使用する汎用関数を提供
|
||||
*/
|
||||
|
||||
pub mod control_flow;
|
||||
|
||||
// 外部公開API
|
||||
pub use control_flow::{
|
||||
is_current_block_terminated,
|
||||
capture_actual_predecessor_and_jump,
|
||||
collect_phi_incoming_if_reachable,
|
||||
execute_statement_with_termination_check,
|
||||
};
|
||||
Reference in New Issue
Block a user