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:
@ -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)
|
||||
}
|
||||
|
||||
@ -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!(
|
||||
|
||||
@ -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
|
||||
//
|
||||
|
||||
@ -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")?;
|
||||
|
||||
|
||||
@ -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)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user