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:
Selfhosting Dev
2025-09-23 07:13:32 +09:00
parent 7ab1e59450
commit 2e93403de0
12 changed files with 2070 additions and 99 deletions

View File

@ -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 &params {
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 &params {
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;
}
}
}

View File

@ -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)?;

View File

@ -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)]

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