phase29aj(p0): expose planner outcome facts for strict observability

This commit is contained in:
2025-12-29 12:17:57 +09:00
parent 2e1bb938b4
commit 5988374ecf
11 changed files with 187 additions and 48 deletions

View File

@ -4,10 +4,10 @@
use crate::ast::ASTNode;
use crate::mir::builder::control_flow::plan::facts::try_build_loop_facts;
use crate::mir::builder::control_flow::plan::normalize::{canonicalize_loop_facts, CanonicalLoopFacts};
use crate::mir::builder::control_flow::plan::normalize::CanonicalLoopFacts;
use super::candidates::{CandidateSet, PlanCandidate};
use super::outcome::build_plan_with_facts;
use super::Freeze;
use crate::mir::builder::control_flow::plan::{
DomainPlan, Pattern2BreakPlan, Pattern2PromotionHint, ScanDirection, ScanWithInitPlan,
@ -21,11 +21,7 @@ pub(in crate::mir::builder) fn build_plan(
condition: &ASTNode,
body: &[ASTNode],
) -> Result<Option<DomainPlan>, Freeze> {
let Some(facts) = try_build_loop_facts(condition, body)? else {
return Ok(None);
};
let canonical = canonicalize_loop_facts(facts);
build_plan_from_facts(canonical)
Ok(build_plan_with_facts(condition, body)?.plan)
}
pub(in crate::mir::builder) fn build_plan_from_facts(

View File

@ -8,6 +8,8 @@
pub(in crate::mir::builder) mod build;
pub(in crate::mir::builder) mod candidates;
pub(in crate::mir::builder) mod freeze;
pub(in crate::mir::builder) mod outcome;
pub(in crate::mir::builder) use build::build_plan;
pub(in crate::mir::builder) use freeze::Freeze;
pub(in crate::mir::builder) use outcome::{build_plan_with_facts, PlanBuildOutcome};

View File

@ -0,0 +1,40 @@
//! Phase 29aj P0: Planner outcome (facts + plan) SSOT
use crate::ast::ASTNode;
use crate::mir::builder::control_flow::plan::facts::try_build_loop_facts;
use crate::mir::builder::control_flow::plan::normalize::{
canonicalize_loop_facts, CanonicalLoopFacts,
};
use crate::mir::builder::control_flow::plan::DomainPlan;
use super::build::build_plan_from_facts;
use super::Freeze;
#[derive(Debug, Clone)]
pub(in crate::mir::builder) struct PlanBuildOutcome {
pub facts: Option<CanonicalLoopFacts>,
pub plan: Option<DomainPlan>,
#[allow(dead_code)]
pub chosen_rule: Option<&'static str>,
}
pub(in crate::mir::builder) fn build_plan_with_facts(
condition: &ASTNode,
body: &[ASTNode],
) -> Result<PlanBuildOutcome, Freeze> {
let Some(facts) = try_build_loop_facts(condition, body)? else {
return Ok(PlanBuildOutcome {
facts: None,
plan: None,
chosen_rule: None,
});
};
let canonical = canonicalize_loop_facts(facts);
let plan = build_plan_from_facts(canonical.clone())?;
Ok(PlanBuildOutcome {
facts: Some(canonical),
plan,
chosen_rule: None,
})
}

View File

@ -7,17 +7,20 @@ use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternCont
use crate::mir::loop_pattern_detection::LoopPatternKind;
use super::legacy_rules;
use crate::mir::builder::control_flow::plan::facts::pattern2_loopbodylocal_facts::{
try_extract_pattern2_loopbodylocal_facts, LoopBodyLocalShape,
};
use crate::mir::builder::control_flow::plan::facts::pattern2_loopbodylocal_facts::LoopBodyLocalShape;
use crate::mir::builder::control_flow::plan::planner;
use crate::mir::builder::control_flow::plan::{DomainPlan, Pattern2PromotionHint};
use crate::mir::builder::control_flow::plan::DomainPlan;
pub(super) fn try_build_domain_plan(ctx: &LoopPatternContext) -> Result<Option<DomainPlan>, String> {
use crate::mir::builder::control_flow::joinir::trace;
let planner_opt = planner::build_plan(ctx.condition, ctx.body)
let outcome = planner::build_plan_with_facts(ctx.condition, ctx.body)
.map_err(|freeze| freeze.to_string())?;
let planner_opt = outcome.plan.clone();
let promotion_facts = outcome
.facts
.as_ref()
.and_then(|facts| facts.facts.pattern2_loopbodylocal.as_ref());
// NOTE: Names must match the previous PLAN_EXTRACTORS entries exactly.
// Order must match exactly.
@ -94,36 +97,9 @@ pub(super) fn try_build_domain_plan(ctx: &LoopPatternContext) -> Result<Option<D
let promotion_tag = if matches!(entry.kind, RuleKind::Pattern2)
&& crate::config::env::joinir_dev::strict_enabled()
{
let from_plan = match plan_opt.as_ref() {
Some(DomainPlan::Pattern2Break(plan)) => {
if let Some(Pattern2PromotionHint::LoopBodyLocal(facts)) = &plan.promotion {
Some(match facts.shape {
LoopBodyLocalShape::TrimSeg { .. } => {
"[plan/pattern2/promotion_hint:TrimSeg]"
}
LoopBodyLocalShape::DigitPos { .. } => {
"[plan/pattern2/promotion_hint:DigitPos]"
}
})
} else {
None
}
}
_ => None,
};
from_plan.or_else(|| {
try_extract_pattern2_loopbodylocal_facts(ctx.condition, ctx.body)
.ok()
.and_then(|facts| {
facts.map(|facts| match facts.shape {
LoopBodyLocalShape::TrimSeg { .. } => {
"[plan/pattern2/promotion_hint:TrimSeg]"
}
LoopBodyLocalShape::DigitPos { .. } => {
"[plan/pattern2/promotion_hint:DigitPos]"
}
})
})
promotion_facts.map(|facts| match facts.shape {
LoopBodyLocalShape::TrimSeg { .. } => "[plan/pattern2/promotion_hint:TrimSeg]",
LoopBodyLocalShape::DigitPos { .. } => "[plan/pattern2/promotion_hint:DigitPos]",
})
} else {
None