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

@ -13,11 +13,10 @@
/// - **SSOT**: Single function for all CarrierInit → ValueId generation
/// - **Testability**: Pure function, easy to unit test
/// - **Consistency**: Uniform debug output format
use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::lowering::carrier_info::CarrierInit;
use crate::mir::value_id::ValueId;
use crate::mir::types::ConstValue;
use crate::mir::value_id::ValueId;
use crate::mir::MirInstruction;
/// Generate a ValueId for the given CarrierInit policy
@ -120,7 +119,13 @@ mod tests {
let mut builder = MirBuilder::new();
let host_id = ValueId(999); // Dummy host_id (not used for BoolConst)
let result = init_value(&mut builder, &CarrierInit::BoolConst(true), host_id, "test", false);
let result = init_value(
&mut builder,
&CarrierInit::BoolConst(true),
host_id,
"test",
false,
);
assert_ne!(result, host_id, "BoolConst should emit new ValueId");
}
@ -131,7 +136,13 @@ mod tests {
let mut builder = MirBuilder::new();
let host_id = ValueId(999);
let result = init_value(&mut builder, &CarrierInit::BoolConst(false), host_id, "flag", false);
let result = init_value(
&mut builder,
&CarrierInit::BoolConst(false),
host_id,
"flag",
false,
);
assert_ne!(result, host_id, "BoolConst should emit new ValueId");
}
@ -142,7 +153,13 @@ mod tests {
let mut builder = MirBuilder::new();
let host_id = ValueId(999);
let result = init_value(&mut builder, &CarrierInit::LoopLocalZero, host_id, "digit", false);
let result = init_value(
&mut builder,
&CarrierInit::LoopLocalZero,
host_id,
"digit",
false,
);
assert_ne!(result, host_id, "LoopLocalZero should emit new ValueId");
}
@ -153,12 +170,36 @@ mod tests {
let mut builder = MirBuilder::new();
let host_id = ValueId(100);
let result1 = init_value(&mut builder, &CarrierInit::BoolConst(true), host_id, "flag1", false);
let result2 = init_value(&mut builder, &CarrierInit::BoolConst(false), host_id, "flag2", false);
let result3 = init_value(&mut builder, &CarrierInit::LoopLocalZero, host_id, "counter", false);
let result1 = init_value(
&mut builder,
&CarrierInit::BoolConst(true),
host_id,
"flag1",
false,
);
let result2 = init_value(
&mut builder,
&CarrierInit::BoolConst(false),
host_id,
"flag2",
false,
);
let result3 = init_value(
&mut builder,
&CarrierInit::LoopLocalZero,
host_id,
"counter",
false,
);
assert_ne!(result1, result2, "Different BoolConst calls should produce different ValueIds");
assert_ne!(result2, result3, "BoolConst and LoopLocalZero should produce different ValueIds");
assert_ne!(
result1, result2,
"Different BoolConst calls should produce different ValueIds"
);
assert_ne!(
result2, result3,
"BoolConst and LoopLocalZero should produce different ValueIds"
);
assert_ne!(result1, result3, "All ValueIds should be unique");
}
@ -168,7 +209,13 @@ mod tests {
let mut builder = MirBuilder::new();
let host_id = ValueId(42);
let _ = init_value(&mut builder, &CarrierInit::FromHost, host_id, "debug_test", true);
let _ = init_value(
&mut builder,
&CarrierInit::FromHost,
host_id,
"debug_test",
true,
);
// Expected stderr output: "[carrier_init_builder] 'debug_test': FromHost -> ValueId(42)"
// (This test just verifies it doesn't crash)
}
@ -179,7 +226,13 @@ mod tests {
let mut builder = MirBuilder::new();
let host_id = ValueId(999);
let _result = init_value(&mut builder, &CarrierInit::BoolConst(true), host_id, "debug_bool", true);
let _result = init_value(
&mut builder,
&CarrierInit::BoolConst(true),
host_id,
"debug_bool",
true,
);
// Expected stderr output: "[carrier_init_builder] 'debug_bool': BoolConst(true) -> ValueId(N)"
// (This test just verifies it doesn't crash)
}
@ -190,7 +243,13 @@ mod tests {
let mut builder = MirBuilder::new();
let host_id = ValueId(999);
let _result = init_value(&mut builder, &CarrierInit::LoopLocalZero, host_id, "debug_zero", true);
let _result = init_value(
&mut builder,
&CarrierInit::LoopLocalZero,
host_id,
"debug_zero",
true,
);
// Expected stderr output: "[carrier_init_builder] 'debug_zero': LoopLocalZero -> ValueId(N)"
// (This test just verifies it doesn't crash)
}

View File

@ -143,7 +143,11 @@ impl ExitLineReconnector {
// Update variable_ctx.variable_map with PHI dst
if let Some(&phi_value) = phi_dst {
if let Some(var_vid) = builder.variable_ctx.variable_map.get_mut(&binding.carrier_name) {
if let Some(var_vid) = builder
.variable_ctx
.variable_map
.get_mut(&binding.carrier_name)
{
// Phase 177-STRUCT: Always log for debugging
if verbose {
eprintln!(

View File

@ -149,14 +149,15 @@ pub(super) fn merge_and_rewrite(
// Phase 195 FIX: Reuse existing block if present (preserves PHI from JoinIR Select lowering)
// ultrathink "finalizer集約案": Don't overwrite blocks with BasicBlock::new()
let mut new_block = if let Some(ref mut current_func) = builder.scope_ctx.current_function {
current_func
.blocks
.remove(&new_block_id)
.unwrap_or_else(|| BasicBlock::new(new_block_id))
} else {
BasicBlock::new(new_block_id)
};
let mut new_block =
if let Some(ref mut current_func) = builder.scope_ctx.current_function {
current_func
.blocks
.remove(&new_block_id)
.unwrap_or_else(|| BasicBlock::new(new_block_id))
} else {
BasicBlock::new(new_block_id)
};
// Phase 33-16: Identify loop entry point
//

View File

@ -96,12 +96,17 @@ impl LoopHeaderPhiBuilder {
// Phase 131-11-H: Set PHI type from entry incoming (init value) only
// Ignore backedge to avoid circular dependency in type inference
if let Some(init_type) = builder.type_ctx.value_types.get(&loop_var_init).cloned() {
builder.type_ctx.value_types.insert(loop_var_phi_dst, init_type.clone());
builder
.type_ctx
.value_types
.insert(loop_var_phi_dst, init_type.clone());
if debug || std::env::var("NYASH_CARRIER_PHI_DEBUG").ok().as_deref() == Some("1") {
eprintln!(
"[carrier/phi] Loop var '{}': dst=%{} entry_type={:?} (backedge ignored)",
loop_var_name, loop_var_phi_dst.as_u32(), init_type
loop_var_name,
loop_var_phi_dst.as_u32(),
init_type
);
}
}
@ -128,13 +133,8 @@ impl LoopHeaderPhiBuilder {
// Allocate PHIs for other carriers
for (name, host_id, init, role) in carriers {
// Phase 86: Use centralized CarrierInit builder
let init_value = super::carrier_init_builder::init_value(
builder,
&init,
*host_id,
&name,
debug,
);
let init_value =
super::carrier_init_builder::init_value(builder, &init, *host_id, &name, debug);
let phi_dst = builder.next_value_id();
@ -145,12 +145,17 @@ impl LoopHeaderPhiBuilder {
// Phase 131-11-H: Set PHI type from entry incoming (init value) only
// Ignore backedge to avoid circular dependency in type inference
if let Some(init_type) = builder.type_ctx.value_types.get(&init_value).cloned() {
builder.type_ctx.value_types.insert(phi_dst, init_type.clone());
builder
.type_ctx
.value_types
.insert(phi_dst, init_type.clone());
if debug || std::env::var("NYASH_CARRIER_PHI_DEBUG").ok().as_deref() == Some("1") {
eprintln!(
"[carrier/phi] Carrier '{}': dst=%{} entry_type={:?} (backedge ignored)",
name, phi_dst.as_u32(), init_type
name,
phi_dst.as_u32(),
init_type
);
}
}
@ -223,7 +228,8 @@ impl LoopHeaderPhiBuilder {
// Get the header block from current function
let current_func = builder
.scope_ctx.current_function
.scope_ctx
.current_function
.as_mut()
.ok_or("Phase 33-16: No current function when finalizing header PHIs")?;

View File

@ -141,20 +141,24 @@ mod tests {
#[test]
fn test_parity_check_skip_whitespace_match() {
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
use crate::mir::builder::control_flow::joinir::patterns::ast_feature_extractor as ast_features;
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
let loop_ast = build_skip_whitespace_loop();
// Extract condition and body
let (condition, body) = match &loop_ast {
ASTNode::Loop { condition, body, .. } => (condition.as_ref(), body.as_slice()),
ASTNode::Loop {
condition, body, ..
} => (condition.as_ref(), body.as_slice()),
_ => panic!("Expected loop node"),
};
// Run canonicalizer
let (_, canonical_decision) = canonicalize_loop_expr(&loop_ast).unwrap();
let canonical_pattern = canonical_decision.chosen.expect("Canonicalizer should succeed");
let canonical_pattern = canonical_decision
.chosen
.expect("Canonicalizer should succeed");
// Run router's pattern detection
let has_continue = ast_features::detect_continue_in_body(body);
@ -175,13 +179,16 @@ mod tests {
crate::mir::loop_pattern_detection::LoopPatternKind::Pattern2Break,
"Router should classify as Pattern2Break for has_break=true"
);
assert_eq!(canonical_pattern, actual_pattern, "Phase 137-5: Canonicalizer and router should agree (SSOT policy)");
assert_eq!(
canonical_pattern, actual_pattern,
"Phase 137-5: Canonicalizer and router should agree (SSOT policy)"
);
}
#[test]
fn test_parity_check_match_simple_while() {
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
use crate::mir::builder::control_flow::joinir::patterns::ast_feature_extractor as ast_features;
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
// Simple while loop: no break, no continue, no if
let loop_ast = ASTNode::Loop {
@ -221,7 +228,9 @@ mod tests {
// Extract condition and body
let (condition, body) = match &loop_ast {
ASTNode::Loop { condition, body, .. } => (condition.as_ref(), body.as_slice()),
ASTNode::Loop {
condition, body, ..
} => (condition.as_ref(), body.as_slice()),
_ => panic!("Expected loop node"),
};
@ -243,6 +252,9 @@ mod tests {
// Canonicalizer should fail (not implemented yet for Pattern1)
assert!(canonical_result.is_ok());
let (_, decision) = canonical_result.unwrap();
assert!(decision.is_fail_fast(), "Canonicalizer should fail for simple patterns (Phase 3 only supports skip_whitespace)");
assert!(
decision.is_fail_fast(),
"Canonicalizer should fail for simple patterns (Phase 3 only supports skip_whitespace)"
);
}
}

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

View File

@ -6,7 +6,6 @@ use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;
impl MirBuilder {
/// Phase 49: Try JoinIR Frontend for mainline integration
///
/// Returns `Ok(Some(value))` if the loop is successfully lowered via JoinIR,
@ -29,7 +28,8 @@ impl MirBuilder {
) -> Result<Option<ValueId>, String> {
// Get current function name
let func_name = self
.scope_ctx.current_function
.scope_ctx
.current_function
.as_ref()
.map(|f| f.signature.name.clone())
.unwrap_or_default();
@ -138,17 +138,38 @@ impl MirBuilder {
match canonicalize_loop_expr(&loop_ast) {
Ok((skeleton, decision)) => {
eprintln!("[loop_canonicalizer] Function: {}", func_name);
eprintln!("[loop_canonicalizer] Skeleton steps: {}", skeleton.steps.len());
eprintln!("[loop_canonicalizer] Carriers: {}", skeleton.carriers.len());
eprintln!("[loop_canonicalizer] Has exits: {}", skeleton.exits.has_any_exit());
eprintln!("[loop_canonicalizer] Decision: {}",
if decision.is_success() { "SUCCESS" } else { "FAIL_FAST" });
eprintln!(
"[loop_canonicalizer] Skeleton steps: {}",
skeleton.steps.len()
);
eprintln!(
"[loop_canonicalizer] Carriers: {}",
skeleton.carriers.len()
);
eprintln!(
"[loop_canonicalizer] Has exits: {}",
skeleton.exits.has_any_exit()
);
eprintln!(
"[loop_canonicalizer] Decision: {}",
if decision.is_success() {
"SUCCESS"
} else {
"FAIL_FAST"
}
);
if let Some(pattern) = decision.chosen {
eprintln!("[loop_canonicalizer] Chosen pattern: {:?}", pattern);
}
eprintln!("[loop_canonicalizer] Missing caps: {:?}", decision.missing_caps);
eprintln!(
"[loop_canonicalizer] Missing caps: {:?}",
decision.missing_caps
);
if decision.is_fail_fast() {
eprintln!("[loop_canonicalizer] Reason: {}", decision.notes.join("; "));
eprintln!(
"[loop_canonicalizer] Reason: {}",
decision.notes.join("; ")
);
}
// Phase 137-4: Router parity verification
@ -183,7 +204,11 @@ impl MirBuilder {
func_name,
&format!(
"fn_body_ast is {}",
if fn_body_clone.is_some() { "SOME" } else { "NONE" }
if fn_body_clone.is_some() {
"SOME"
} else {
"NONE"
}
),
);
let ctx = if let Some(ref fn_body) = fn_body_clone {