refactor(joinir): unify policy decisions and trim routing
This commit is contained in:
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use crate::mir::builder::MirBuilder;
|
use crate::mir::builder::MirBuilder;
|
||||||
|
use crate::mir::builder::control_flow::joinir::patterns::policies::PolicyDecision;
|
||||||
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
||||||
use crate::mir::join_ir::lowering::common::body_local_slot::{
|
use crate::mir::join_ir::lowering::common::body_local_slot::{
|
||||||
ReadOnlyBodyLocalSlot, ReadOnlyBodyLocalSlotBox,
|
ReadOnlyBodyLocalSlot, ReadOnlyBodyLocalSlotBox,
|
||||||
@ -19,14 +20,13 @@ use crate::mir::loop_pattern_detection::loop_condition_scope::{CondVarScope, Loo
|
|||||||
///
|
///
|
||||||
/// This is a "route" decision (not a fallback): we choose exactly one of the supported
|
/// This is a "route" decision (not a fallback): we choose exactly one of the supported
|
||||||
/// strategies and reject otherwise.
|
/// strategies and reject otherwise.
|
||||||
pub enum BodyLocalPolicyDecision {
|
pub enum BodyLocalRoute {
|
||||||
UsePromotion {
|
Promotion {
|
||||||
promoted_carrier: CarrierInfo,
|
promoted_carrier: CarrierInfo,
|
||||||
promoted_var: String,
|
promoted_var: String,
|
||||||
carrier_name: String,
|
carrier_name: String,
|
||||||
},
|
},
|
||||||
UseReadOnlySlot(ReadOnlyBodyLocalSlot),
|
ReadOnlySlot(ReadOnlyBodyLocalSlot),
|
||||||
Reject { reason: String, vars: Vec<String> },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn classify_for_pattern2(
|
pub fn classify_for_pattern2(
|
||||||
@ -36,7 +36,7 @@ pub fn classify_for_pattern2(
|
|||||||
break_condition_node: &ASTNode,
|
break_condition_node: &ASTNode,
|
||||||
cond_scope: &LoopConditionScope,
|
cond_scope: &LoopConditionScope,
|
||||||
body: &[ASTNode],
|
body: &[ASTNode],
|
||||||
) -> BodyLocalPolicyDecision {
|
) -> PolicyDecision<BodyLocalRoute> {
|
||||||
let vars: Vec<String> = cond_scope
|
let vars: Vec<String> = cond_scope
|
||||||
.vars
|
.vars
|
||||||
.iter()
|
.iter()
|
||||||
@ -60,19 +60,18 @@ pub fn classify_for_pattern2(
|
|||||||
carrier_info: promoted_carrier,
|
carrier_info: promoted_carrier,
|
||||||
promoted_var,
|
promoted_var,
|
||||||
carrier_name,
|
carrier_name,
|
||||||
} => BodyLocalPolicyDecision::UsePromotion {
|
} => PolicyDecision::Use(BodyLocalRoute::Promotion {
|
||||||
promoted_carrier,
|
promoted_carrier,
|
||||||
promoted_var,
|
promoted_var,
|
||||||
carrier_name,
|
carrier_name,
|
||||||
},
|
}),
|
||||||
ConditionPromotionResult::CannotPromote { reason, .. } => {
|
ConditionPromotionResult::CannotPromote { reason, .. } => {
|
||||||
match extract_body_local_inits_for_conditions(&vars, body) {
|
match extract_body_local_inits_for_conditions(&vars, body) {
|
||||||
Ok(Some(slot)) => BodyLocalPolicyDecision::UseReadOnlySlot(slot),
|
Ok(Some(slot)) => PolicyDecision::Use(BodyLocalRoute::ReadOnlySlot(slot)),
|
||||||
Ok(None) => BodyLocalPolicyDecision::Reject { reason, vars },
|
Ok(None) => PolicyDecision::Reject(reason),
|
||||||
Err(slot_err) => BodyLocalPolicyDecision::Reject {
|
Err(slot_err) => PolicyDecision::Reject(format!(
|
||||||
reason: format!("{reason}; read-only-slot rejected: {slot_err}"),
|
"{reason}; read-only-slot rejected: {slot_err}"
|
||||||
vars,
|
)),
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use crate::ast::ASTNode;
|
|||||||
use crate::mir::builder::MirBuilder;
|
use crate::mir::builder::MirBuilder;
|
||||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, CarrierInit};
|
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, CarrierInit};
|
||||||
use crate::mir::join_ir::lowering::condition_env::{ConditionBinding, ConditionEnv};
|
use crate::mir::join_ir::lowering::condition_env::{ConditionBinding, ConditionEnv};
|
||||||
use super::body_local_policy::{classify_for_pattern2, BodyLocalPolicyDecision};
|
use super::body_local_policy::{classify_for_pattern2, BodyLocalRoute};
|
||||||
use crate::mir::join_ir::lowering::common::body_local_slot::ReadOnlyBodyLocalSlot;
|
use crate::mir::join_ir::lowering::common::body_local_slot::ReadOnlyBodyLocalSlot;
|
||||||
use crate::mir::join_ir::lowering::common::body_local_derived_emitter::BodyLocalDerivedRecipe;
|
use crate::mir::join_ir::lowering::common::body_local_derived_emitter::BodyLocalDerivedRecipe;
|
||||||
use crate::mir::join_ir::lowering::debug_output_box::DebugOutputBox;
|
use crate::mir::join_ir::lowering::debug_output_box::DebugOutputBox;
|
||||||
@ -20,6 +20,7 @@ use crate::mir::ValueId;
|
|||||||
use super::policies::p5b_escape_derived_policy::{
|
use super::policies::p5b_escape_derived_policy::{
|
||||||
classify_p5b_escape_derived, P5bEscapeDerivedDecision,
|
classify_p5b_escape_derived, P5bEscapeDerivedDecision,
|
||||||
};
|
};
|
||||||
|
use super::policies::PolicyDecision;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
struct Pattern2DebugLog {
|
struct Pattern2DebugLog {
|
||||||
@ -283,6 +284,12 @@ fn promote_and_prepare_carriers(
|
|||||||
|
|
||||||
let log = Pattern2DebugLog::new(verbose);
|
let log = Pattern2DebugLog::new(verbose);
|
||||||
let mut promoted_pairs: Vec<(String, String)> = Vec::new();
|
let mut promoted_pairs: Vec<(String, String)> = Vec::new();
|
||||||
|
let cond_body_local_vars: Vec<String> = cond_scope
|
||||||
|
.vars
|
||||||
|
.iter()
|
||||||
|
.filter(|v| matches!(v.scope, crate::mir::loop_pattern_detection::loop_condition_scope::CondVarScope::LoopBodyLocal))
|
||||||
|
.map(|v| v.name.clone())
|
||||||
|
.collect();
|
||||||
|
|
||||||
if cond_scope.has_loop_body_local() {
|
if cond_scope.has_loop_body_local() {
|
||||||
match classify_for_pattern2(
|
match classify_for_pattern2(
|
||||||
@ -293,11 +300,11 @@ fn promote_and_prepare_carriers(
|
|||||||
&cond_scope,
|
&cond_scope,
|
||||||
body,
|
body,
|
||||||
) {
|
) {
|
||||||
BodyLocalPolicyDecision::UsePromotion {
|
PolicyDecision::Use(BodyLocalRoute::Promotion {
|
||||||
promoted_carrier,
|
promoted_carrier,
|
||||||
promoted_var,
|
promoted_var,
|
||||||
carrier_name,
|
carrier_name,
|
||||||
} => {
|
}) => {
|
||||||
// Phase 133 P1: Check if this is a Trim promotion (A-3 pattern)
|
// Phase 133 P1: Check if this is a Trim promotion (A-3 pattern)
|
||||||
// Trim promotions are handled by TrimLoopLowerer (apply_trim_and_normalize)
|
// Trim promotions are handled by TrimLoopLowerer (apply_trim_and_normalize)
|
||||||
// which provides SSOT for env/join_id, so we defer to that path.
|
// which provides SSOT for env/join_id, so we defer to that path.
|
||||||
@ -399,7 +406,7 @@ fn promote_and_prepare_carriers(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BodyLocalPolicyDecision::UseReadOnlySlot(slot) => {
|
PolicyDecision::Use(BodyLocalRoute::ReadOnlySlot(slot)) => {
|
||||||
log.log(
|
log.log(
|
||||||
"body_local_slot",
|
"body_local_slot",
|
||||||
format!(
|
format!(
|
||||||
@ -410,11 +417,12 @@ fn promote_and_prepare_carriers(
|
|||||||
inputs.allowed_body_locals_for_conditions = vec![slot.name.clone()];
|
inputs.allowed_body_locals_for_conditions = vec![slot.name.clone()];
|
||||||
inputs.read_only_body_local_slot = Some(slot);
|
inputs.read_only_body_local_slot = Some(slot);
|
||||||
}
|
}
|
||||||
BodyLocalPolicyDecision::Reject { reason, vars } => {
|
PolicyDecision::Reject(reason) => {
|
||||||
return Err(error_messages::format_error_pattern2_promotion_failed(
|
return Err(error_messages::format_error_pattern2_promotion_failed(
|
||||||
&vars, &reason,
|
&cond_body_local_vars, &reason,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
PolicyDecision::None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -817,7 +825,7 @@ impl MirBuilder {
|
|||||||
// Phase 94: Detect P5b escape-derived (`ch` reassignment + escape counter).
|
// Phase 94: Detect P5b escape-derived (`ch` reassignment + escape counter).
|
||||||
// Route is explicit; in strict mode, partial matches that require derived support must fail-fast.
|
// Route is explicit; in strict mode, partial matches that require derived support must fail-fast.
|
||||||
match classify_p5b_escape_derived(analysis_body, &inputs.loop_var_name) {
|
match classify_p5b_escape_derived(analysis_body, &inputs.loop_var_name) {
|
||||||
P5bEscapeDerivedDecision::UseDerived(recipe) => {
|
P5bEscapeDerivedDecision::Use(recipe) => {
|
||||||
log.log(
|
log.log(
|
||||||
"phase94",
|
"phase94",
|
||||||
format!(
|
format!(
|
||||||
|
|||||||
@ -85,8 +85,8 @@
|
|||||||
- Reject理由は`error_tags::freeze()`でタグ付与
|
- Reject理由は`error_tags::freeze()`でタグ付与
|
||||||
|
|
||||||
### Decision型の統一
|
### Decision型の統一
|
||||||
- `Decision` enum(None / Use(...) / Reject(String))を統一パターンとして使用
|
- `PolicyDecision<T>`(Use / Reject / None)をSSOTにする
|
||||||
- 例: `P5bEscapeDerivedDecision`, `TrimDecision`(将来)
|
- 例: `P5bEscapeDerivedDecision = PolicyDecision<BodyLocalDerivedRecipe>`, `TrimPolicyResult`
|
||||||
|
|
||||||
## 使用パターン
|
## 使用パターン
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,10 @@
|
|||||||
//! - ルーティング決定: 適用可能なLoweringパターンを決定
|
//! - ルーティング決定: 適用可能なLoweringパターンを決定
|
||||||
//! - Recipe生成: Pattern固有の情報(ConditionOnlyRecipe, BodyLocalDerivedRecipe, etc.)を生成
|
//! - Recipe生成: Pattern固有の情報(ConditionOnlyRecipe, BodyLocalDerivedRecipe, etc.)を生成
|
||||||
//!
|
//!
|
||||||
|
//! ## 決定型のSSOT
|
||||||
|
//! - `PolicyDecision<T>` に統一(Use / Reject / None)
|
||||||
|
//! - BodyLocal, Trim, P5b escape などすべてここ経由で route することで Pattern2 側の分岐を簡潔に保つ
|
||||||
|
//!
|
||||||
//! ## 設計原則
|
//! ## 設計原則
|
||||||
//! - **単一判断の原則**: 各policy箱は1つのパターン判断のみ
|
//! - **単一判断の原則**: 各policy箱は1つのパターン判断のみ
|
||||||
//! - **非破壊的判断**: 入力を変更せず、Decision型で結果を返す
|
//! - **非破壊的判断**: 入力を変更せず、Decision型で結果を返す
|
||||||
@ -17,11 +21,14 @@
|
|||||||
//! policies/ は「認識とルーティング決定(policy)」を分離する受け皿です。
|
//! policies/ は「認識とルーティング決定(policy)」を分離する受け皿です。
|
||||||
//! Phase 94(P5b derived)から段階的に移設を開始しました。
|
//! Phase 94(P5b derived)から段階的に移設を開始しました。
|
||||||
//!
|
//!
|
||||||
//! ### 段階的な移行計画
|
|
||||||
//! - Phase 1: ディレクトリ準備 ✅
|
|
||||||
//! - Phase 2: 既存policy箱の移動(進行中)
|
|
||||||
//! - Phase 3: インターフェース統一(将来)
|
|
||||||
//!
|
|
||||||
//! 詳細は [README.md](README.md) を参照してください。
|
//! 詳細は [README.md](README.md) を参照してください。
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum PolicyDecision<T> {
|
||||||
|
Use(T),
|
||||||
|
Reject(String),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
pub(in crate::mir::builder) mod p5b_escape_derived_policy;
|
pub(in crate::mir::builder) mod p5b_escape_derived_policy;
|
||||||
|
pub(in crate::mir::builder) mod trim_policy;
|
||||||
|
|||||||
@ -13,13 +13,9 @@ use crate::config::env::joinir_dev;
|
|||||||
use crate::mir::builder::control_flow::joinir::patterns::escape_pattern_recognizer::EscapeSkipPatternInfo;
|
use crate::mir::builder::control_flow::joinir::patterns::escape_pattern_recognizer::EscapeSkipPatternInfo;
|
||||||
use crate::mir::join_ir::lowering::common::body_local_derived_emitter::BodyLocalDerivedRecipe;
|
use crate::mir::join_ir::lowering::common::body_local_derived_emitter::BodyLocalDerivedRecipe;
|
||||||
use crate::mir::join_ir::lowering::error_tags;
|
use crate::mir::join_ir::lowering::error_tags;
|
||||||
|
use super::PolicyDecision;
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub type P5bEscapeDerivedDecision = PolicyDecision<BodyLocalDerivedRecipe>;
|
||||||
pub enum P5bEscapeDerivedDecision {
|
|
||||||
None,
|
|
||||||
UseDerived(BodyLocalDerivedRecipe),
|
|
||||||
Reject(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Detect a P5b derived body-local (`ch`) recipe from a Pattern2 loop body.
|
/// Detect a P5b derived body-local (`ch`) recipe from a Pattern2 loop body.
|
||||||
///
|
///
|
||||||
@ -43,7 +39,7 @@ pub fn classify_p5b_escape_derived(
|
|||||||
}
|
}
|
||||||
|
|
||||||
match build_recipe_from_info(body, &info) {
|
match build_recipe_from_info(body, &info) {
|
||||||
Ok(Some(recipe)) => P5bEscapeDerivedDecision::UseDerived(recipe),
|
Ok(Some(recipe)) => P5bEscapeDerivedDecision::Use(recipe),
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
// Escape pattern exists but there is no body-local reassignment to cover.
|
// Escape pattern exists but there is no body-local reassignment to cover.
|
||||||
P5bEscapeDerivedDecision::None
|
P5bEscapeDerivedDecision::None
|
||||||
@ -257,7 +253,7 @@ mod tests {
|
|||||||
];
|
];
|
||||||
|
|
||||||
match classify_p5b_escape_derived(&body, "i") {
|
match classify_p5b_escape_derived(&body, "i") {
|
||||||
P5bEscapeDerivedDecision::UseDerived(recipe) => {
|
P5bEscapeDerivedDecision::Use(recipe) => {
|
||||||
assert_eq!(recipe.name, "ch");
|
assert_eq!(recipe.name, "ch");
|
||||||
assert_eq!(recipe.loop_counter_name, "i");
|
assert_eq!(recipe.loop_counter_name, "i");
|
||||||
assert_eq!(recipe.pre_delta, 1);
|
assert_eq!(recipe.pre_delta, 1);
|
||||||
|
|||||||
@ -0,0 +1,51 @@
|
|||||||
|
//! Trim pattern policy box (判定専用)
|
||||||
|
//!
|
||||||
|
//! 目的: Trim 形状かどうかを判定し、ConditionScope を返すだけに責務を絞る。
|
||||||
|
//! 生成(lowering)は従来通り TrimLoopLowerer 側が担当する。
|
||||||
|
|
||||||
|
use crate::ast::ASTNode;
|
||||||
|
use crate::mir::builder::control_flow::joinir::patterns::trim_loop_lowering::TrimLoopLowerer;
|
||||||
|
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||||
|
use crate::mir::loop_pattern_detection::loop_condition_scope::{
|
||||||
|
CondVarInfo, CondVarScope, LoopConditionScope, LoopConditionScopeBox,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::PolicyDecision;
|
||||||
|
|
||||||
|
/// 判定結果(生成に必要な最小情報だけ運ぶ)
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TrimPolicyResult {
|
||||||
|
pub cond_scope: LoopConditionScope,
|
||||||
|
pub condition_body_locals: Vec<CondVarInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn classify_trim_like_loop(
|
||||||
|
scope: &LoopScopeShape,
|
||||||
|
loop_cond: &ASTNode,
|
||||||
|
break_cond: &ASTNode,
|
||||||
|
loop_var_name: &str,
|
||||||
|
) -> PolicyDecision<TrimPolicyResult> {
|
||||||
|
let cond_scope =
|
||||||
|
LoopConditionScopeBox::analyze(loop_var_name, &[loop_cond, break_cond], Some(scope));
|
||||||
|
|
||||||
|
if !cond_scope.has_loop_body_local() {
|
||||||
|
return PolicyDecision::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let condition_body_locals: Vec<_> = cond_scope
|
||||||
|
.vars
|
||||||
|
.iter()
|
||||||
|
.filter(|v| v.scope == CondVarScope::LoopBodyLocal)
|
||||||
|
.filter(|v| TrimLoopLowerer::is_var_used_in_condition(&v.name, break_cond))
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if condition_body_locals.is_empty() {
|
||||||
|
return PolicyDecision::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
PolicyDecision::Use(TrimPolicyResult {
|
||||||
|
cond_scope,
|
||||||
|
condition_body_locals,
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -44,8 +44,9 @@ use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
|||||||
use crate::mir::loop_pattern_detection::loop_body_carrier_promoter::{
|
use crate::mir::loop_pattern_detection::loop_body_carrier_promoter::{
|
||||||
LoopBodyCarrierPromoter, PromotionRequest, PromotionResult,
|
LoopBodyCarrierPromoter, PromotionRequest, PromotionResult,
|
||||||
};
|
};
|
||||||
use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScopeBox;
|
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
|
use super::policies::trim_policy::{classify_trim_like_loop, TrimPolicyResult};
|
||||||
|
use super::policies::PolicyDecision;
|
||||||
|
|
||||||
/// Trim pattern lowering orchestrator
|
/// Trim pattern lowering orchestrator
|
||||||
///
|
///
|
||||||
@ -96,7 +97,10 @@ impl TrimLoopLowerer {
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// `true` if `var_name` appears anywhere in `cond_node`, `false` otherwise
|
/// `true` if `var_name` appears anywhere in `cond_node`, `false` otherwise
|
||||||
fn is_var_used_in_condition(var_name: &str, cond_node: &ASTNode) -> bool {
|
pub(in crate::mir::builder::control_flow::joinir::patterns) fn is_var_used_in_condition(
|
||||||
|
var_name: &str,
|
||||||
|
cond_node: &ASTNode,
|
||||||
|
) -> bool {
|
||||||
match cond_node {
|
match cond_node {
|
||||||
ASTNode::Variable { name, .. } => name == var_name,
|
ASTNode::Variable { name, .. } => name == var_name,
|
||||||
ASTNode::BinaryOp { left, right, .. } => {
|
ASTNode::BinaryOp { left, right, .. } => {
|
||||||
@ -179,63 +183,14 @@ impl TrimLoopLowerer {
|
|||||||
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
|
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
|
||||||
let verbose = crate::config::env::joinir_dev_enabled() || trace.is_joinir_enabled();
|
let verbose = crate::config::env::joinir_dev_enabled() || trace.is_joinir_enabled();
|
||||||
|
|
||||||
// Phase 180-2: Skeleton implementation
|
let TrimPolicyResult {
|
||||||
// TODO: Phase 180-3 will implement full logic from Pattern2
|
cond_scope,
|
||||||
|
condition_body_locals,
|
||||||
// Step 1: Check if condition references LoopBodyLocal variables
|
} = match classify_trim_like_loop(scope, loop_cond, break_cond, loop_var_name) {
|
||||||
let cond_scope =
|
PolicyDecision::Use(res) => res,
|
||||||
LoopConditionScopeBox::analyze(loop_var_name, &[loop_cond, break_cond], Some(scope));
|
PolicyDecision::None => return Ok(None),
|
||||||
|
PolicyDecision::Reject(reason) => return Err(reason),
|
||||||
trace.emit_if(
|
};
|
||||||
"trim",
|
|
||||||
"scope",
|
|
||||||
&format!(
|
|
||||||
"Analyzing condition scope: {} variables",
|
|
||||||
cond_scope.vars.len()
|
|
||||||
),
|
|
||||||
verbose,
|
|
||||||
);
|
|
||||||
|
|
||||||
if !cond_scope.has_loop_body_local() {
|
|
||||||
// Not a Trim pattern - normal loop
|
|
||||||
trace.emit_if(
|
|
||||||
"trim",
|
|
||||||
"scope",
|
|
||||||
"No LoopBodyLocal detected, skipping Trim lowering",
|
|
||||||
verbose,
|
|
||||||
);
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
trace.emit_if(
|
|
||||||
"trim",
|
|
||||||
"scope",
|
|
||||||
"LoopBodyLocal detected in condition scope",
|
|
||||||
verbose,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Phase 183-2: Filter to only condition LoopBodyLocal (skip body-only)
|
|
||||||
use crate::mir::loop_pattern_detection::loop_condition_scope::CondVarScope;
|
|
||||||
let condition_body_locals: Vec<_> = cond_scope
|
|
||||||
.vars
|
|
||||||
.iter()
|
|
||||||
.filter(|v| v.scope == CondVarScope::LoopBodyLocal)
|
|
||||||
.filter(|v| {
|
|
||||||
// Check if variable is actually used in break condition
|
|
||||||
Self::is_var_used_in_condition(&v.name, break_cond)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if condition_body_locals.is_empty() {
|
|
||||||
// All LoopBodyLocal are body-only (not in conditions) → Not a Trim pattern
|
|
||||||
trace.emit_if(
|
|
||||||
"trim",
|
|
||||||
"phase183",
|
|
||||||
"All LoopBodyLocal are body-only (not in conditions), skipping Trim lowering",
|
|
||||||
verbose,
|
|
||||||
);
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
trace.emit_if(
|
trace.emit_if(
|
||||||
"trim",
|
"trim",
|
||||||
|
|||||||
Reference in New Issue
Block a user