refactor(mir): Phase 139-P3-B - RoutingDecision を enum 対応 + レガシー削除
- RoutingDecision の missing_caps を Vec<CapabilityTag> に変更(型安全化) - error_tags は to_tag() メソッドで自動生成 - 全 callsite を enum variant に修正 - capability_tags モジュール(文字列定数群)を完全削除 - 全テスト PASS(型安全性向上を確認) - フォーマット適用
This commit is contained in:
@ -106,7 +106,10 @@ impl<'a> ExitBindingBuilder<'a> {
|
||||
///
|
||||
/// Success or error if boundary cannot be updated
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn apply_to_boundary(&self, boundary: &mut JoinInlineBoundary) -> Result<(), String> {
|
||||
pub(crate) fn apply_to_boundary(
|
||||
&self,
|
||||
boundary: &mut JoinInlineBoundary,
|
||||
) -> Result<(), String> {
|
||||
// Phase 222.5-C: Delegate to applicator module
|
||||
apply_exit_bindings_to_boundary(
|
||||
self.carrier_info,
|
||||
|
||||
@ -77,14 +77,17 @@ impl MirBuilder {
|
||||
//
|
||||
// Phase 132: Add exit_bindings to enable ExitLineReconnector
|
||||
// This ensures `return i` after loop returns the final value (3) instead of initial (0)
|
||||
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
|
||||
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
|
||||
use crate::mir::join_ir::lowering::join_value_space::PARAM_MIN;
|
||||
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
||||
|
||||
// Phase 132-Post: Extract k_exit's parameter ValueId from join_module (Box-First)
|
||||
let k_exit_func = join_module.require_function("k_exit", "Pattern 1");
|
||||
let join_exit_value = k_exit_func.params.first().copied()
|
||||
let join_exit_value = k_exit_func
|
||||
.params
|
||||
.first()
|
||||
.copied()
|
||||
.expect("k_exit must have parameter for exit value");
|
||||
|
||||
// Phase 132: Create exit binding for loop variable
|
||||
@ -98,7 +101,7 @@ impl MirBuilder {
|
||||
let boundary = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(
|
||||
vec![ValueId(PARAM_MIN)], // JoinIR's main() parameter (loop variable, Param region)
|
||||
vec![ctx.loop_var_id], // Host's loop variable
|
||||
vec![ctx.loop_var_id], // Host's loop variable
|
||||
)
|
||||
.with_exit_bindings(vec![exit_binding]) // Phase 132: Enable exit PHI & variable_map update
|
||||
.with_loop_var_name(Some(ctx.loop_var_name.clone())) // Phase 33-16: Enable header PHI generation for SSA correctness
|
||||
|
||||
@ -9,8 +9,8 @@ use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
||||
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
|
||||
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||
use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
|
||||
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
|
||||
use crate::mir::loop_pattern_detection::error_messages;
|
||||
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
@ -42,7 +42,9 @@ fn prepare_pattern2_inputs(
|
||||
verbose: bool,
|
||||
) -> Result<Pattern2Inputs, String> {
|
||||
use super::condition_env_builder::ConditionEnvBuilder;
|
||||
use crate::mir::loop_pattern_detection::function_scope_capture::{analyze_captured_vars_v2, CapturedEnv};
|
||||
use crate::mir::loop_pattern_detection::function_scope_capture::{
|
||||
analyze_captured_vars_v2, CapturedEnv,
|
||||
};
|
||||
|
||||
let loop_var_name = ctx.loop_var_name.clone();
|
||||
let loop_var_id = ctx.loop_var_id;
|
||||
@ -88,7 +90,10 @@ fn prepare_pattern2_inputs(
|
||||
log_pattern2(
|
||||
verbose,
|
||||
"capture",
|
||||
format!("Phase 200-C: Captured {} variables", captured_env.vars.len()),
|
||||
format!(
|
||||
"Phase 200-C: Captured {} variables",
|
||||
captured_env.vars.len()
|
||||
),
|
||||
);
|
||||
for var in &captured_env.vars {
|
||||
log_pattern2(
|
||||
@ -747,16 +752,8 @@ impl MirBuilder {
|
||||
|
||||
trace::trace().varmap("pattern2_start", &self.variable_ctx.variable_map);
|
||||
|
||||
let mut inputs =
|
||||
prepare_pattern2_inputs(self, condition, _body, fn_body, &ctx, verbose)?;
|
||||
promote_and_prepare_carriers(
|
||||
self,
|
||||
condition,
|
||||
_body,
|
||||
&mut inputs,
|
||||
debug,
|
||||
verbose,
|
||||
)?;
|
||||
let mut inputs = prepare_pattern2_inputs(self, condition, _body, fn_body, &ctx, verbose)?;
|
||||
promote_and_prepare_carriers(self, condition, _body, &mut inputs, debug, verbose)?;
|
||||
let (effective_break_condition, normalized_body) =
|
||||
apply_trim_and_normalize(self, condition, _body, &mut inputs, verbose)?;
|
||||
let analysis_body = normalized_body.as_deref().unwrap_or(_body);
|
||||
@ -771,10 +768,10 @@ impl MirBuilder {
|
||||
verbose,
|
||||
"updates",
|
||||
format!(
|
||||
"Phase 176-3: Analyzed {} carrier updates",
|
||||
carrier_updates.len()
|
||||
),
|
||||
);
|
||||
"Phase 176-3: Analyzed {} carrier updates",
|
||||
carrier_updates.len()
|
||||
),
|
||||
);
|
||||
|
||||
let original_carrier_count = inputs.carrier_info.carriers.len();
|
||||
filter_carriers_for_updates(&mut inputs.carrier_info, &carrier_updates);
|
||||
@ -977,11 +974,26 @@ mod tests {
|
||||
use crate::mir::ValueId;
|
||||
|
||||
let mut builder = MirBuilder::new();
|
||||
builder.variable_ctx.variable_map.insert("i".to_string(), ValueId(1));
|
||||
builder.variable_ctx.variable_map.insert("len".to_string(), ValueId(2));
|
||||
builder.variable_ctx.variable_map.insert("s".to_string(), ValueId(3));
|
||||
builder.variable_ctx.variable_map.insert("digits".to_string(), ValueId(4));
|
||||
builder.variable_ctx.variable_map.insert("result".to_string(), ValueId(5));
|
||||
builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.insert("i".to_string(), ValueId(1));
|
||||
builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.insert("len".to_string(), ValueId(2));
|
||||
builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.insert("s".to_string(), ValueId(3));
|
||||
builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.insert("digits".to_string(), ValueId(4));
|
||||
builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.insert("result".to_string(), ValueId(5));
|
||||
|
||||
let condition = bin(BinaryOperator::Less, var("i"), var("len"));
|
||||
|
||||
@ -1008,7 +1020,9 @@ mod tests {
|
||||
|
||||
let break_if = ASTNode::If {
|
||||
condition: Box::new(bin(BinaryOperator::Less, var("digit_pos"), lit_i(0))),
|
||||
then_body: vec![ASTNode::Break { span: Span::unknown() }],
|
||||
then_body: vec![ASTNode::Break {
|
||||
span: Span::unknown(),
|
||||
}],
|
||||
else_body: None,
|
||||
span: Span::unknown(),
|
||||
};
|
||||
@ -1035,9 +1049,8 @@ mod tests {
|
||||
|
||||
let ctx = build_pattern_context(&mut builder, &condition, &body, PatternVariant::Pattern2)
|
||||
.expect("build_pattern_context");
|
||||
let mut inputs =
|
||||
prepare_pattern2_inputs(&builder, &condition, &body, None, &ctx, false)
|
||||
.expect("prepare_pattern2_inputs");
|
||||
let mut inputs = prepare_pattern2_inputs(&builder, &condition, &body, None, &ctx, false)
|
||||
.expect("prepare_pattern2_inputs");
|
||||
|
||||
promote_and_prepare_carriers(&mut builder, &condition, &body, &mut inputs, false, false)
|
||||
.expect("promote_and_prepare_carriers");
|
||||
|
||||
@ -183,7 +183,8 @@ impl MirBuilder {
|
||||
// Collect parent-defined variables from function scope
|
||||
// For now, use all variables in variable_map except loop_var
|
||||
let parent_defined: Vec<String> = self
|
||||
.variable_ctx.variable_map
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.keys()
|
||||
.filter(|name| *name != &loop_var_name)
|
||||
.cloned()
|
||||
@ -196,9 +197,11 @@ impl MirBuilder {
|
||||
condition_bindings.iter().map(|b| b.name.clone()).collect();
|
||||
|
||||
// Run consistency checks
|
||||
if let Err(e) =
|
||||
check_ownership_plan_consistency(&plan, &ctx.carrier_info, &condition_binding_names)
|
||||
{
|
||||
if let Err(e) = check_ownership_plan_consistency(
|
||||
&plan,
|
||||
&ctx.carrier_info,
|
||||
&condition_binding_names,
|
||||
) {
|
||||
eprintln!("[phase64/ownership] Consistency check failed: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
@ -50,7 +50,13 @@ fn count_breaks_and_continues(body: &[ASTNode]) -> (usize, usize, bool) {
|
||||
let mut continue_count = 0;
|
||||
let mut has_nested_loop = false;
|
||||
|
||||
fn scan_node(node: &ASTNode, break_count: &mut usize, continue_count: &mut usize, has_nested_loop: &mut bool, depth: usize) {
|
||||
fn scan_node(
|
||||
node: &ASTNode,
|
||||
break_count: &mut usize,
|
||||
continue_count: &mut usize,
|
||||
has_nested_loop: &mut bool,
|
||||
depth: usize,
|
||||
) {
|
||||
match node {
|
||||
ASTNode::Break { .. } => {
|
||||
*break_count += 1;
|
||||
@ -61,13 +67,29 @@ fn count_breaks_and_continues(body: &[ASTNode]) -> (usize, usize, bool) {
|
||||
ASTNode::Loop { .. } if depth > 0 => {
|
||||
*has_nested_loop = true;
|
||||
}
|
||||
ASTNode::If { then_body, else_body, .. } => {
|
||||
ASTNode::If {
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} => {
|
||||
for stmt in then_body {
|
||||
scan_node(stmt, break_count, continue_count, has_nested_loop, depth + 1);
|
||||
scan_node(
|
||||
stmt,
|
||||
break_count,
|
||||
continue_count,
|
||||
has_nested_loop,
|
||||
depth + 1,
|
||||
);
|
||||
}
|
||||
if let Some(else_body) = else_body {
|
||||
for stmt in else_body {
|
||||
scan_node(stmt, break_count, continue_count, has_nested_loop, depth + 1);
|
||||
scan_node(
|
||||
stmt,
|
||||
break_count,
|
||||
continue_count,
|
||||
has_nested_loop,
|
||||
depth + 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -76,7 +98,13 @@ fn count_breaks_and_continues(body: &[ASTNode]) -> (usize, usize, bool) {
|
||||
}
|
||||
|
||||
for stmt in body {
|
||||
scan_node(stmt, &mut break_count, &mut continue_count, &mut has_nested_loop, 0);
|
||||
scan_node(
|
||||
stmt,
|
||||
&mut break_count,
|
||||
&mut continue_count,
|
||||
&mut has_nested_loop,
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
(break_count, continue_count, has_nested_loop)
|
||||
@ -95,7 +123,12 @@ fn validate_continue_position(body: &[ASTNode]) -> bool {
|
||||
/// Phase 131-11-D: Validate break is in simple if pattern
|
||||
fn validate_break_pattern(body: &[ASTNode]) -> bool {
|
||||
for stmt in body {
|
||||
if let ASTNode::If { then_body, else_body, .. } = stmt {
|
||||
if let ASTNode::If {
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} = stmt
|
||||
{
|
||||
// Check then branch for simple break
|
||||
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
|
||||
// Ensure no else-break pattern
|
||||
@ -132,13 +165,17 @@ pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &LoopPatternContext) -> bool
|
||||
// Step 2: Shape guard - infinite loop condition (true literal)
|
||||
if !ctx.features.is_infinite_loop {
|
||||
if debug {
|
||||
trace::trace().debug("pattern5/detect", "Not an infinite loop (condition != true)");
|
||||
trace::trace().debug(
|
||||
"pattern5/detect",
|
||||
"Not an infinite loop (condition != true)",
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Phase 131-11-D: Enhanced real count validation
|
||||
let (real_break_count, real_continue_count, has_nested_loop) = count_breaks_and_continues(ctx.body);
|
||||
let (real_break_count, real_continue_count, has_nested_loop) =
|
||||
count_breaks_and_continues(ctx.body);
|
||||
|
||||
// Step 3: Shape guard - exactly 1 break (real count)
|
||||
if real_break_count != 1 {
|
||||
@ -232,11 +269,21 @@ fn extract_counter_name(body: &[ASTNode]) -> Result<String, String> {
|
||||
use crate::ast::BinaryOperator;
|
||||
|
||||
for stmt in body {
|
||||
if let ASTNode::If { condition, then_body, .. } = stmt {
|
||||
if let ASTNode::If {
|
||||
condition,
|
||||
then_body,
|
||||
..
|
||||
} = stmt
|
||||
{
|
||||
// Check if then_body contains just break
|
||||
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
|
||||
// Extract counter from condition
|
||||
if let ASTNode::BinaryOp { operator: BinaryOperator::Equal, left, .. } = condition.as_ref() {
|
||||
if let ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Equal,
|
||||
left,
|
||||
..
|
||||
} = condition.as_ref()
|
||||
{
|
||||
if let ASTNode::Variable { name, .. } = left.as_ref() {
|
||||
return Ok(name.clone());
|
||||
}
|
||||
@ -256,12 +303,26 @@ fn extract_limit_value(body: &[ASTNode]) -> Result<i64, String> {
|
||||
use crate::ast::{BinaryOperator, LiteralValue};
|
||||
|
||||
for stmt in body {
|
||||
if let ASTNode::If { condition, then_body, .. } = stmt {
|
||||
if let ASTNode::If {
|
||||
condition,
|
||||
then_body,
|
||||
..
|
||||
} = stmt
|
||||
{
|
||||
// Check if then_body contains just break
|
||||
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
|
||||
// Extract limit from condition
|
||||
if let ASTNode::BinaryOp { operator: BinaryOperator::Equal, right, .. } = condition.as_ref() {
|
||||
if let ASTNode::Literal { value: LiteralValue::Integer(limit), .. } = right.as_ref() {
|
||||
if let ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Equal,
|
||||
right,
|
||||
..
|
||||
} = condition.as_ref()
|
||||
{
|
||||
if let ASTNode::Literal {
|
||||
value: LiteralValue::Integer(limit),
|
||||
..
|
||||
} = right.as_ref()
|
||||
{
|
||||
return Ok(*limit);
|
||||
}
|
||||
}
|
||||
@ -317,9 +378,17 @@ pub(crate) fn lower(
|
||||
}
|
||||
|
||||
// Step 2: Get counter ValueId from variable_ctx.variable_map
|
||||
let counter_id = builder.variable_ctx.variable_map.get(&counter_name).copied().ok_or_else(|| {
|
||||
format!("Counter variable '{}' not found in variable_ctx.variable_map", counter_name)
|
||||
})?;
|
||||
let counter_id = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&counter_name)
|
||||
.copied()
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Counter variable '{}' not found in variable_ctx.variable_map",
|
||||
counter_name
|
||||
)
|
||||
})?;
|
||||
|
||||
if debug {
|
||||
trace::trace().debug(
|
||||
@ -340,7 +409,8 @@ pub(crate) fn lower(
|
||||
// Step 4: Generate JoinIR
|
||||
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
||||
use crate::mir::join_ir::{
|
||||
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, MirLikeInst,
|
||||
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
|
||||
MirLikeInst,
|
||||
};
|
||||
|
||||
let mut join_value_space = JoinValueSpace::new();
|
||||
@ -356,14 +426,14 @@ pub(crate) fn lower(
|
||||
// ValueId allocation
|
||||
// main() locals
|
||||
let counter_init = alloc_value(); // ValueId(1000) - initial counter
|
||||
let loop_result = alloc_value(); // ValueId(1001) - result from loop_step
|
||||
let loop_result = alloc_value(); // ValueId(1001) - result from loop_step
|
||||
|
||||
// loop_step locals
|
||||
let counter_param = alloc_value(); // ValueId(1002) - parameter
|
||||
let const_1 = alloc_value(); // ValueId(1003) - increment constant
|
||||
let counter_next = alloc_value(); // ValueId(1004) - counter + 1
|
||||
let const_limit = alloc_value(); // ValueId(1005) - limit constant
|
||||
let break_cond = alloc_value(); // ValueId(1006) - counter_next == LIMIT
|
||||
let const_1 = alloc_value(); // ValueId(1003) - increment constant
|
||||
let counter_next = alloc_value(); // ValueId(1004) - counter + 1
|
||||
let const_limit = alloc_value(); // ValueId(1005) - limit constant
|
||||
let break_cond = alloc_value(); // ValueId(1006) - counter_next == LIMIT
|
||||
|
||||
// k_exit locals
|
||||
let counter_exit = alloc_value(); // ValueId(1007) - exit parameter
|
||||
@ -397,33 +467,42 @@ pub(crate) fn lower(
|
||||
// ==================================================================
|
||||
// loop_step(counter) function - post-increment pattern
|
||||
// ==================================================================
|
||||
let mut loop_step_func = JoinFunction::new(loop_step_id, "loop_step".to_string(), vec![counter_param]);
|
||||
let mut loop_step_func =
|
||||
JoinFunction::new(loop_step_id, "loop_step".to_string(), vec![counter_param]);
|
||||
|
||||
// counter_next = counter + 1
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_1,
|
||||
value: ConstValue::Integer(1),
|
||||
}));
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_1,
|
||||
value: ConstValue::Integer(1),
|
||||
}));
|
||||
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: counter_next,
|
||||
op: BinOpKind::Add,
|
||||
lhs: counter_param,
|
||||
rhs: const_1,
|
||||
}));
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: counter_next,
|
||||
op: BinOpKind::Add,
|
||||
lhs: counter_param,
|
||||
rhs: const_1,
|
||||
}));
|
||||
|
||||
// break_cond = (counter_next == LIMIT)
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_limit,
|
||||
value: ConstValue::Integer(limit),
|
||||
}));
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_limit,
|
||||
value: ConstValue::Integer(limit),
|
||||
}));
|
||||
|
||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: break_cond,
|
||||
op: CompareOp::Eq,
|
||||
lhs: counter_next,
|
||||
rhs: const_limit,
|
||||
}));
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: break_cond,
|
||||
op: CompareOp::Eq,
|
||||
lhs: counter_next,
|
||||
rhs: const_limit,
|
||||
}));
|
||||
|
||||
// Jump(k_exit, [counter_next], cond=break_cond)
|
||||
loop_step_func.body.push(JoinInst::Jump {
|
||||
|
||||
@ -241,7 +241,8 @@ impl TrimLoopLowerer {
|
||||
// Step 3: Convert to CarrierInfo and merge
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
// Phase 136 Step 4/7: Use binding_ctx for binding_map reference
|
||||
let promoted_carrier = trim_info.to_carrier_info(Some(builder.binding_ctx.binding_map()));
|
||||
let promoted_carrier =
|
||||
trim_info.to_carrier_info(Some(builder.binding_ctx.binding_map()));
|
||||
#[cfg(not(feature = "normalized_dev"))]
|
||||
let promoted_carrier = trim_info.to_carrier_info();
|
||||
carrier_info.merge_from(&promoted_carrier);
|
||||
@ -352,10 +353,12 @@ impl TrimLoopLowerer {
|
||||
);
|
||||
|
||||
// Get ValueIds for string and start
|
||||
let s_id =
|
||||
builder.variable_ctx.variable_map.get(&s_name).copied().ok_or_else(|| {
|
||||
format!("[TrimLoopLowerer] String variable '{}' not found", s_name)
|
||||
})?;
|
||||
let s_id = builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get(&s_name)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[TrimLoopLowerer] String variable '{}' not found", s_name))?;
|
||||
|
||||
// Compile start expression to get ValueId
|
||||
let start_id = builder.build_expression_impl(*start_expr)?;
|
||||
@ -403,7 +406,8 @@ impl TrimLoopLowerer {
|
||||
|
||||
// Register carrier in variable_ctx.variable_map
|
||||
builder
|
||||
.variable_ctx.variable_map
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.insert(trim_helper.carrier_name.clone(), is_ch_match0);
|
||||
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user