phase29ak(p1): guard pattern1 facts via planner context
This commit is contained in:
@ -16,7 +16,7 @@ Scope: Repo root の旧リンク互換。現行の入口は `docs/development/cu
|
||||
`./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` が唯一の integration gate。phase143_* は対象外(legacy pack で隔離)。phase286_pattern9_* は legacy pack (SKIP) で運用。
|
||||
|
||||
**PlanRuleOrder SSOT**
|
||||
single_planner の順序/名前 SSOT は `src/mir/builder/control_flow/plan/single_planner/rule_order.rs` に固定。PlannerContext は P0 では未使用で、P1 以降で段階的に利用する。
|
||||
single_planner の順序/名前 SSOT は `src/mir/builder/control_flow/plan/single_planner/rule_order.rs` に固定。PlannerContext で Pattern1 facts の抑制を開始し、残りの guard/filter は段階移行。
|
||||
|
||||
**2025-12-29: Phase 29aj P6 COMPLETE (JoinIR regression gate SSOT)**
|
||||
JoinIR 回帰の integration gate を phase29ae pack に固定し、phase143_* を legacy pack で隔離。
|
||||
@ -27,6 +27,9 @@ Pattern8 BoolPredicateScan の Facts→Planner-first を導入し、single_plann
|
||||
**2025-12-29: Phase 29ak P0 COMPLETE (PlanRuleOrder + PlannerContext plumbing)**
|
||||
PlanRuleOrder を SSOT 化し、PlannerContext を配線(未使用)。single_planner の手書きテーブルを撤去。
|
||||
|
||||
**2025-12-29: Phase 29ak P1 COMPLETE (Pattern1 facts guard via planner)**
|
||||
Pattern1 以外のループで pattern1_simplewhile facts 抽出を抑制。single_planner 側の guard は安全策として維持。
|
||||
|
||||
**2025-12-29: Phase 29aj P10 COMPLETE (single_planner unified shape)**
|
||||
single_planner を全パターンで planner-first → extractor フォールバックの共通形に統一(挙動不変)。
|
||||
|
||||
|
||||
@ -2,10 +2,15 @@
|
||||
|
||||
## Current Focus: Phase 29ak(PlanRuleOrder + PlannerContext)
|
||||
|
||||
Next: Phase 29ak P1(TBD)
|
||||
Next: Phase 29ak P2(TBD)
|
||||
運用ルール: integration filter で phase143_* は回さない(JoinIR 回帰は phase29ae pack のみ)
|
||||
運用ルール: phase286_pattern9_* は legacy pack (SKIP) を使う
|
||||
|
||||
**2025-12-29: Phase 29ak P1 完了** ✅
|
||||
- 目的: Pattern1 guard を planner 側へ移して facts 抽出を抑制(仕様不変)
|
||||
- 実装: `src/mir/builder/control_flow/plan/facts/loop_facts.rs` / `src/mir/builder/control_flow/plan/planner/outcome.rs`
|
||||
- 検証: `cargo build --release` / `./tools/smokes/v2/run.sh --profile quick` / `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` PASS
|
||||
|
||||
**2025-12-29: Phase 29ak P0 完了** ✅
|
||||
- 目的: PlanRuleOrder SSOT を新設し、PlannerContext の配線だけ先に導入(仕様不変)
|
||||
- 実装: `src/mir/builder/control_flow/plan/single_planner/rule_order.rs` / `src/mir/builder/control_flow/plan/planner/context.rs` / `src/mir/builder/control_flow/plan/single_planner/rules.rs`
|
||||
|
||||
@ -26,8 +26,8 @@ Related:
|
||||
|
||||
- **Phase 29ak(candidate): PlanRuleOrder SSOT + PlannerContext plumbing**
|
||||
- 入口: `docs/development/current/main/phases/phase-29ak/README.md`
|
||||
- 状況: P0 ✅ 完了
|
||||
- Next: Phase 29ak P1(TBD)
|
||||
- 状況: P0/P1 ✅ 完了
|
||||
- Next: Phase 29ak P2(TBD)
|
||||
|
||||
- **Phase 29ai(candidate): Plan/Frag single-planner(Facts SSOT)**
|
||||
- 入口: `docs/development/current/main/phases/phase-29ai/README.md`
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
# Phase 29ak P1: Guard Pattern1 facts via PlannerContext
|
||||
|
||||
Date: 2025-12-29
|
||||
Status: Ready for execution
|
||||
Scope: planner の Facts 入口で pattern_kind を参照して早期 Ok(None)(保守的)
|
||||
Goal: single_planner の guard 責務を減らし、planner 側へ寄せる第一歩にする(仕様不変)
|
||||
|
||||
## Objective
|
||||
|
||||
- PlannerContext.pattern_kind を使い、Pattern1 以外のループで Pattern1 facts 抽出を行わない
|
||||
- 挙動・ログは不変(single_planner 側の guard は維持)
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Pattern8 static box filter の移動
|
||||
- CandidateSet の順序SSOT化
|
||||
- extractor fallback の撤去
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Step 1: loop_facts に guard を追加
|
||||
|
||||
Update:
|
||||
- `src/mir/builder/control_flow/plan/facts/loop_facts.rs`
|
||||
|
||||
Add:
|
||||
- `try_build_loop_facts_with_ctx(ctx, condition, body)` を新設
|
||||
- ctx.pattern_kind が Pattern1 以外のとき pattern1_simplewhile 抽出を抑制
|
||||
|
||||
### Step 2: planner outcome から ctx 版を使う
|
||||
|
||||
Update:
|
||||
- `src/mir/builder/control_flow/plan/planner/outcome.rs`
|
||||
|
||||
Notes:
|
||||
- `build_plan_with_facts_ctx` は `try_build_loop_facts_with_ctx` を使う
|
||||
- `build_plan_with_facts` は既存挙動のまま
|
||||
|
||||
### Step 3: single_planner guard は残す
|
||||
|
||||
Update:
|
||||
- `src/mir/builder/control_flow/plan/single_planner/rules.rs`
|
||||
|
||||
Notes:
|
||||
- planner 側に移した旨を docs に明記(guard は冗長だが安全策として維持)
|
||||
|
||||
### Step 4: テストを追加
|
||||
|
||||
Add:
|
||||
- pattern_kind != Pattern1 のとき pattern1 facts が None になること
|
||||
- pattern_kind == Pattern1 のとき従来通り抽出できること
|
||||
|
||||
### Step 5: docs / CURRENT_TASK 更新
|
||||
|
||||
Update:
|
||||
- `docs/development/current/main/phases/phase-29ak/README.md`
|
||||
- `docs/development/current/main/10-Now.md`
|
||||
- `docs/development/current/main/30-Backlog.md`
|
||||
- `CURRENT_TASK.md`
|
||||
|
||||
## Verification
|
||||
|
||||
- `cargo build --release`
|
||||
- `./tools/smokes/v2/run.sh --profile quick`
|
||||
- `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh`
|
||||
|
||||
## Commit
|
||||
|
||||
- `git add -A && git commit -m "phase29ak(p1): guard pattern1 facts via planner context"`
|
||||
@ -8,3 +8,10 @@ Goal: single_planner の「順序・名前・ガード」の SSOT を 1 箇所
|
||||
- ねらい: rule_order.rs を順序/名前 SSOT に固定し、PlannerContext を配線(未使用)
|
||||
- 完了: PlanRuleOrder を追加し、single_planner の手書きテーブルを撤去
|
||||
- 検証: `cargo build --release` / `./tools/smokes/v2/run.sh --profile quick` / `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh`
|
||||
|
||||
## P1: Pattern1 guard を planner 側へ移動(facts 抽出抑制)
|
||||
|
||||
- 指示書: `docs/development/current/main/phases/phase-29ak/P1-PLANNER-PATTERN1-GUARD-INSTRUCTIONS.md`
|
||||
- ねらい: pattern_kind が Pattern1 以外のとき pattern1 facts 抽出を行わない
|
||||
- 完了: PlannerContext を参照して loop_facts 入口で Pattern1 を抑制(single_planner 側の guard は維持)
|
||||
- 検証: `cargo build --release` / `./tools/smokes/v2/run.sh --profile quick` / `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh`
|
||||
|
||||
@ -10,7 +10,8 @@
|
||||
use crate::ast::ASTNode;
|
||||
|
||||
use super::scan_shapes::{ConditionShape, StepShape};
|
||||
use crate::mir::builder::control_flow::plan::planner::Freeze;
|
||||
use crate::mir::builder::control_flow::plan::planner::{Freeze, PlannerContext};
|
||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||
use crate::ast::{BinaryOperator, LiteralValue};
|
||||
use super::scan_shapes::LengthMethod;
|
||||
use super::pattern1_simplewhile_facts::{
|
||||
@ -72,6 +73,27 @@ pub(in crate::mir::builder) struct SplitScanFacts {
|
||||
pub(in crate::mir::builder) fn try_build_loop_facts(
|
||||
condition: &ASTNode,
|
||||
body: &[ASTNode],
|
||||
) -> Result<Option<LoopFacts>, Freeze> {
|
||||
try_build_loop_facts_inner(condition, body, true)
|
||||
}
|
||||
|
||||
pub(in crate::mir::builder) fn try_build_loop_facts_with_ctx(
|
||||
ctx: &PlannerContext,
|
||||
condition: &ASTNode,
|
||||
body: &[ASTNode],
|
||||
) -> Result<Option<LoopFacts>, Freeze> {
|
||||
let allow_pattern1 = match ctx.pattern_kind {
|
||||
Some(LoopPatternKind::Pattern1SimpleWhile) => true,
|
||||
Some(_) => false,
|
||||
None => true,
|
||||
};
|
||||
try_build_loop_facts_inner(condition, body, allow_pattern1)
|
||||
}
|
||||
|
||||
fn try_build_loop_facts_inner(
|
||||
condition: &ASTNode,
|
||||
body: &[ASTNode],
|
||||
allow_pattern1: bool,
|
||||
) -> Result<Option<LoopFacts>, Freeze> {
|
||||
// Phase 29ai P4/P7: keep Facts conservative; only return Some when we can
|
||||
// build a concrete pattern fact set (no guesses / no hardcoded names).
|
||||
@ -81,7 +103,11 @@ pub(in crate::mir::builder) fn try_build_loop_facts(
|
||||
let step_shape = try_extract_step_shape(body)?.unwrap_or(StepShape::Unknown);
|
||||
let scan_with_init = try_extract_scan_with_init_facts(body, &condition_shape, &step_shape)?;
|
||||
let split_scan = try_extract_split_scan_facts(condition, body)?;
|
||||
let pattern1_simplewhile = try_extract_pattern1_simplewhile_facts(condition, body)?;
|
||||
let pattern1_simplewhile = if allow_pattern1 {
|
||||
try_extract_pattern1_simplewhile_facts(condition, body)?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let pattern3_ifphi = try_extract_pattern3_ifphi_facts(condition, body)?;
|
||||
let pattern4_continue = try_extract_pattern4_continue_facts(condition, body)?;
|
||||
let pattern5_infinite_early_exit =
|
||||
@ -642,6 +668,8 @@ fn is_i_increment_by_one(stmt: &ASTNode, i_var: &str) -> bool {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ast::Span;
|
||||
use crate::mir::builder::control_flow::plan::planner::PlannerContext;
|
||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||
|
||||
fn v(name: &str) -> ASTNode {
|
||||
ASTNode::Variable {
|
||||
@ -711,6 +739,75 @@ mod tests {
|
||||
assert!(facts.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loopfacts_ctx_skips_pattern1_when_kind_mismatch() {
|
||||
let condition = ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Less,
|
||||
left: Box::new(v("i")),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(3),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let step = ASTNode::Assignment {
|
||||
target: Box::new(v("i")),
|
||||
value: Box::new(ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(v("i")),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let ctx = PlannerContext {
|
||||
pattern_kind: Some(LoopPatternKind::Pattern2Break),
|
||||
in_static_box: false,
|
||||
debug: false,
|
||||
};
|
||||
|
||||
let facts = try_build_loop_facts_with_ctx(&ctx, &condition, &[step]).expect("Ok");
|
||||
assert!(facts.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loopfacts_ctx_allows_pattern1_when_kind_matches() {
|
||||
let condition = ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Less,
|
||||
left: Box::new(v("i")),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(3),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let step = ASTNode::Assignment {
|
||||
target: Box::new(v("i")),
|
||||
value: Box::new(ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(v("i")),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let ctx = PlannerContext {
|
||||
pattern_kind: Some(LoopPatternKind::Pattern1SimpleWhile),
|
||||
in_static_box: false,
|
||||
debug: false,
|
||||
};
|
||||
|
||||
let facts = try_build_loop_facts_with_ctx(&ctx, &condition, &[step]).expect("Ok");
|
||||
let facts = facts.expect("Some");
|
||||
assert!(facts.pattern1_simplewhile.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loopfacts_ok_none_when_condition_not_supported() {
|
||||
let condition = v("i"); // not `i < n`
|
||||
|
||||
@ -17,4 +17,6 @@ pub(in crate::mir::builder) mod pattern2_break_facts;
|
||||
pub(in crate::mir::builder) mod pattern2_loopbodylocal_facts;
|
||||
pub(in crate::mir::builder) mod scan_shapes;
|
||||
|
||||
pub(in crate::mir::builder) use loop_facts::{try_build_loop_facts, LoopFacts};
|
||||
pub(in crate::mir::builder) use loop_facts::{
|
||||
try_build_loop_facts, try_build_loop_facts_with_ctx, LoopFacts,
|
||||
};
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
//! 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::facts::{
|
||||
try_build_loop_facts, try_build_loop_facts_with_ctx, LoopFacts,
|
||||
};
|
||||
use crate::mir::builder::control_flow::plan::normalize::{
|
||||
canonicalize_loop_facts, CanonicalLoopFacts,
|
||||
};
|
||||
@ -21,7 +23,23 @@ 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 {
|
||||
let facts = try_build_loop_facts(condition, body)?;
|
||||
build_plan_from_facts_opt(facts)
|
||||
}
|
||||
|
||||
pub(in crate::mir::builder) fn build_plan_with_facts_ctx(
|
||||
ctx: &PlannerContext,
|
||||
condition: &ASTNode,
|
||||
body: &[ASTNode],
|
||||
) -> Result<PlanBuildOutcome, Freeze> {
|
||||
let facts = try_build_loop_facts_with_ctx(ctx, condition, body)?;
|
||||
build_plan_from_facts_opt(facts)
|
||||
}
|
||||
|
||||
fn build_plan_from_facts_opt(
|
||||
facts: Option<LoopFacts>,
|
||||
) -> Result<PlanBuildOutcome, Freeze> {
|
||||
let Some(facts) = facts else {
|
||||
return Ok(PlanBuildOutcome {
|
||||
facts: None,
|
||||
plan: None,
|
||||
@ -35,11 +53,3 @@ pub(in crate::mir::builder) fn build_plan_with_facts(
|
||||
plan,
|
||||
})
|
||||
}
|
||||
|
||||
pub(in crate::mir::builder) fn build_plan_with_facts_ctx(
|
||||
_ctx: &PlannerContext,
|
||||
condition: &ASTNode,
|
||||
body: &[ASTNode],
|
||||
) -> Result<PlanBuildOutcome, Freeze> {
|
||||
build_plan_with_facts(condition, body)
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ pub(super) fn try_build_domain_plan(ctx: &LoopPatternContext) -> Result<Option<D
|
||||
let rule_id = *rule_id;
|
||||
let name = rule_name(rule_id);
|
||||
// Phase 286 P2.6: Pattern1 Plan guard (structural Fail-Fast)
|
||||
// Phase 29ak P1: planner also suppresses Pattern1 facts; keep this guard as safety.
|
||||
// Pattern1 should only match Pattern1SimpleWhile pattern_kind
|
||||
// This prevents Pattern1 from incorrectly matching Pattern3 fixtures
|
||||
if matches!(rule_id, PlanRuleId::Pattern1)
|
||||
|
||||
Reference in New Issue
Block a user