phase29an(p4): require skeleton/features in loop facts

This commit is contained in:
2025-12-29 18:23:28 +09:00
parent d114117a36
commit 5ea120ca19
2 changed files with 107 additions and 34 deletions

View File

@ -43,8 +43,8 @@ use super::feature_facts::{LoopFeatureFacts, try_extract_loop_feature_facts};
pub(in crate::mir::builder) struct LoopFacts {
pub condition_shape: ConditionShape,
pub step_shape: StepShape,
pub skeleton: Option<SkeletonFacts>,
pub features: Option<LoopFeatureFacts>,
pub skeleton: SkeletonFacts,
pub features: LoopFeatureFacts,
pub scan_with_init: Option<ScanWithInitFacts>,
pub split_scan: Option<SplitScanFacts>,
pub pattern1_simplewhile: Option<Pattern1SimpleWhileFacts>,
@ -146,8 +146,15 @@ fn try_build_loop_facts_inner(
return Ok(None);
}
let skeleton = try_extract_loop_skeleton_facts(condition, body)?;
let features = Some(try_extract_loop_feature_facts(body)?);
let skeleton = match try_extract_loop_skeleton_facts(condition, body)? {
Some(skeleton) => skeleton,
None => {
return Err(Freeze::bug(
"loop facts require skeleton when patterns are present",
));
}
};
let features = try_extract_loop_feature_facts(body)?;
Ok(Some(LoopFacts {
condition_shape,
@ -259,6 +266,56 @@ fn try_extract_step_shape(body: &[ASTNode]) -> Result<Option<StepShape>, Freeze>
}))
}
#[cfg(test)]
mod tests {
use super::{try_build_loop_facts, LoopFacts};
use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span};
use crate::mir::builder::control_flow::plan::facts::skeleton_facts::SkeletonKind;
fn v(name: &str) -> ASTNode {
ASTNode::Variable {
name: name.to_string(),
span: Span::unknown(),
}
}
fn lit_int(value: i64) -> ASTNode {
ASTNode::Literal {
value: LiteralValue::Integer(value),
span: Span::unknown(),
}
}
#[test]
fn loop_facts_require_skeleton_and_features_when_present() {
let condition = ASTNode::BinaryOp {
operator: BinaryOperator::Less,
left: Box::new(v("i")),
right: Box::new(lit_int(3)),
span: Span::unknown(),
};
let body = vec![ASTNode::Assignment {
target: Box::new(v("i")),
value: Box::new(ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left: Box::new(v("i")),
right: Box::new(lit_int(1)),
span: Span::unknown(),
}),
span: Span::unknown(),
}];
let facts = try_build_loop_facts(&condition, &body)
.expect("Ok")
.expect("Some");
assert_eq!(facts.skeleton.kind, SkeletonKind::Loop);
assert!(!facts.features.exit_usage.has_break);
assert!(!facts.features.exit_usage.has_continue);
assert!(!facts.features.exit_usage.has_return);
let _: LoopFacts = facts;
}
}
fn try_extract_scan_with_init_facts(
body: &[ASTNode],
condition_shape: &ConditionShape,

View File

@ -70,15 +70,11 @@ pub(in crate::mir::builder) fn build_plan_from_facts_ctx(
}
fn infer_skeleton_kind(facts: &CanonicalLoopFacts) -> Option<SkeletonKind> {
facts.facts.skeleton.as_ref().map(|s| s.kind)
Some(facts.facts.skeleton.kind)
}
fn infer_exit_usage(facts: &CanonicalLoopFacts) -> Option<ExitUsageFacts> {
facts
.facts
.features
.as_ref()
.map(|features| features.exit_usage.clone())
Some(facts.facts.features.exit_usage.clone())
}
fn push_scan_with_init(candidates: &mut CandidateSet, facts: &CanonicalLoopFacts) {
@ -318,8 +314,10 @@ mod tests {
let facts = LoopFacts {
condition_shape: ConditionShape::Unknown,
step_shape: StepShape::Unknown,
skeleton: None,
features: None,
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts::default(),
scan_with_init: None,
split_scan: Some(SplitScanFacts {
s_var: "s".to_string(),
@ -357,8 +355,10 @@ mod tests {
let facts = LoopFacts {
condition_shape: ConditionShape::Unknown,
step_shape: StepShape::Unknown,
skeleton: None,
features: None,
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts::default(),
scan_with_init: None,
split_scan: None,
pattern1_simplewhile: None,
@ -380,8 +380,10 @@ mod tests {
let facts = LoopFacts {
condition_shape: ConditionShape::Unknown,
step_shape: StepShape::Unknown,
skeleton: None,
features: None,
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts::default(),
scan_with_init: Some(ScanWithInitFacts {
loop_var: "i".to_string(),
haystack: "s".to_string(),
@ -413,10 +415,10 @@ mod tests {
let facts = LoopFacts {
condition_shape: ConditionShape::Unknown,
step_shape: StepShape::Unknown,
skeleton: Some(SkeletonFacts {
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
}),
features: Some(LoopFeatureFacts {
},
features: LoopFeatureFacts {
exit_usage: ExitUsageFacts {
has_break: true,
has_continue: false,
@ -424,7 +426,7 @@ mod tests {
},
value_join: None,
cleanup: None,
}),
},
scan_with_init: Some(ScanWithInitFacts {
loop_var: "i".to_string(),
haystack: "s".to_string(),
@ -467,8 +469,10 @@ mod tests {
let facts = LoopFacts {
condition_shape: ConditionShape::Unknown,
step_shape: StepShape::Unknown,
skeleton: None,
features: None,
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts::default(),
scan_with_init: None,
split_scan: None,
pattern1_simplewhile: Some(Pattern1SimpleWhileFacts {
@ -531,8 +535,10 @@ mod tests {
let facts = LoopFacts {
condition_shape: ConditionShape::Unknown,
step_shape: StepShape::Unknown,
skeleton: None,
features: None,
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts::default(),
scan_with_init: None,
split_scan: None,
pattern1_simplewhile: None,
@ -594,8 +600,10 @@ mod tests {
let facts = LoopFacts {
condition_shape: ConditionShape::Unknown,
step_shape: StepShape::Unknown,
skeleton: None,
features: None,
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts::default(),
scan_with_init: None,
split_scan: None,
pattern1_simplewhile: None,
@ -643,8 +651,10 @@ mod tests {
let facts = LoopFacts {
condition_shape: ConditionShape::Unknown,
step_shape: StepShape::Unknown,
skeleton: None,
features: None,
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts::default(),
scan_with_init: None,
split_scan: None,
pattern1_simplewhile: None,
@ -693,8 +703,10 @@ mod tests {
let facts = LoopFacts {
condition_shape: ConditionShape::Unknown,
step_shape: StepShape::Unknown,
skeleton: None,
features: None,
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts::default(),
scan_with_init: None,
split_scan: None,
pattern1_simplewhile: None,
@ -751,8 +763,10 @@ mod tests {
let facts = LoopFacts {
condition_shape: ConditionShape::Unknown,
step_shape: StepShape::Unknown,
skeleton: None,
features: None,
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts::default(),
scan_with_init: None,
split_scan: None,
pattern1_simplewhile: None,
@ -815,8 +829,10 @@ mod tests {
let facts = LoopFacts {
condition_shape: ConditionShape::Unknown,
step_shape: StepShape::Unknown,
skeleton: None,
features: None,
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts::default(),
scan_with_init: None,
split_scan: None,
pattern1_simplewhile: None,