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:
nyash-codex
2025-12-16 07:02:14 +09:00
parent 146f14a019
commit e404746612
106 changed files with 1475 additions and 1017 deletions

View File

@ -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,

View File

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

View File

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

View File

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

View File

@ -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 {

View File

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