phase29af(p1): add boundary hygiene contract checks
This commit is contained in:
@ -1,6 +1,12 @@
|
||||
# Self Current Task — Now (main)
|
||||
|
||||
## Current Focus: Phase 29af (Pattern2 Boundary Hygiene)
|
||||
## Current Focus: Phase 29af(Boundary Hygiene SSOT 固定)
|
||||
|
||||
**2025-12-29: Phase 29af P1 完了** ✅
|
||||
- 目的: boundary hygiene を merge 入口(`contract_checks`)へ集約して再発検知を SSOT 化(仕様不変)
|
||||
- 実装: `src/mir/builder/control_flow/joinir/merge/contract_checks/boundary_hygiene.rs`(strict/dev のみ)
|
||||
- 配線: `src/mir/builder/control_flow/joinir/merge/contract_checks/boundary_creation.rs`
|
||||
- 検証: `cargo build --release` / `./tools/smokes/v2/run.sh --profile quick` / `./tools/smokes/v2/run.sh --profile integration --filter "phase29ab_pattern2_"` / `./tools/smokes/v2/run.sh --profile integration --filter "phase1883_"` PASS
|
||||
|
||||
**2025-12-29: Phase 29af P0 完了** ✅
|
||||
- 目的: Pattern2 の boundary 情報の歪みを SSOT 化し、exit/header/latch の責務境界を固定(仕様不変)
|
||||
|
||||
@ -8,7 +8,7 @@ Related:
|
||||
|
||||
## 直近(JoinIR/selfhost)
|
||||
|
||||
- **Phase 29af P0(in progress): Pattern2 Boundary Hygiene(SSOT固定)**
|
||||
- **Phase 29af P1(✅ COMPLETE): Pattern2 Boundary Hygiene(contract_checks 集約)**
|
||||
- 入口: `docs/development/current/main/phases/phase-29af/README.md`
|
||||
|
||||
- **Phase 29ae P1(✅ COMPLETE): JoinIR Regression Pack (SSOT固定)**
|
||||
|
||||
@ -2,6 +2,11 @@
|
||||
|
||||
Goal: Pattern2 の boundary 情報の歪みを SSOT で整理し、将来の回帰を防ぐ(仕様不変)。
|
||||
|
||||
## Status
|
||||
|
||||
- P0: ✅ COMPLETE(commit: `19f2c6b7f`)
|
||||
- P1: ✅ COMPLETE(merge `contract_checks` への集約)
|
||||
|
||||
## Boundary Contract (SSOT)
|
||||
|
||||
- Header PHI 対象:
|
||||
@ -20,8 +25,14 @@ Goal: Pattern2 の boundary 情報の歪みを SSOT で整理し、将来の回
|
||||
|
||||
- boundary 構築: `src/mir/builder/control_flow/joinir/patterns/pattern2_steps/emit_joinir_step_box.rs`
|
||||
- header PHI 事前構築: `src/mir/builder/control_flow/joinir/merge/header_phi_prebuild.rs`
|
||||
- exit_bindings 収集: `src/mir/builder/control_flow/joinir/merge/exit_line/meta_collector.rs`
|
||||
- latch 記録: `src/mir/builder/control_flow/joinir/merge/rewriter/{tail_call_policy,latch_incoming_recorder}.rs`
|
||||
|
||||
## P1: Contract Checks (merge 入口)
|
||||
|
||||
P0 で確定した boundary hygiene を、merge 入口の `contract_checks` に集約する(仕様不変)。
|
||||
|
||||
- 実装: `src/mir/builder/control_flow/joinir/merge/contract_checks/boundary_hygiene.rs`
|
||||
- 配線: `src/mir/builder/control_flow/joinir/merge/contract_checks/boundary_creation.rs`
|
||||
- 実行条件: `joinir_strict` または `joinir_dev` のみ Fail-Fast
|
||||
|
||||
## Verification
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
||||
use super::verify_boundary_hygiene;
|
||||
|
||||
/// Phase 286 P1: Boundary contract validation (B1/C2 invariants)
|
||||
///
|
||||
@ -31,6 +32,9 @@ pub(in crate::mir::builder::control_flow::joinir) fn verify_boundary_contract_at
|
||||
eprintln!(" (This should help identify which pattern is causing the issue)");
|
||||
}
|
||||
|
||||
// Phase 29af P1: Boundary hygiene checks (strict/dev only)
|
||||
verify_boundary_hygiene(boundary)?;
|
||||
|
||||
// B1: join_inputs in Param region (100-999)
|
||||
for (i, join_id) in boundary.join_inputs.iter().enumerate() {
|
||||
if !(PARAM_MIN..=PARAM_MAX).contains(&join_id.0) {
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
/// Phase 29af P1: Boundary hygiene contract checks (strict/dev only).
|
||||
pub(in crate::mir::builder::control_flow::joinir::merge) fn verify_boundary_hygiene(
|
||||
boundary: &JoinInlineBoundary,
|
||||
) -> Result<(), String> {
|
||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole};
|
||||
use crate::mir::join_ir::lowering::error_tags;
|
||||
|
||||
let strict = crate::config::env::joinir_strict_enabled()
|
||||
|| crate::config::env::joinir_dev_enabled();
|
||||
if !strict {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut exit_names = BTreeSet::new();
|
||||
for binding in &boundary.exit_bindings {
|
||||
if binding.role != CarrierRole::LoopState {
|
||||
return Err(error_tags::freeze_with_hint(
|
||||
"phase29af/boundary_hygiene/exit_binding_role",
|
||||
&format!(
|
||||
"exit_binding '{}' role {:?} is not LoopState",
|
||||
binding.carrier_name, binding.role
|
||||
),
|
||||
"exclude ConditionOnly carriers from exit_bindings",
|
||||
));
|
||||
}
|
||||
if !exit_names.insert(binding.carrier_name.clone()) {
|
||||
return Err(error_tags::freeze_with_hint(
|
||||
"phase29af/boundary_hygiene/exit_binding_duplicate",
|
||||
&format!("duplicate exit_binding carrier '{}'", binding.carrier_name),
|
||||
"deduplicate exit_bindings by carrier_name",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(carrier_info) = &boundary.carrier_info {
|
||||
let mut carrier_names = BTreeSet::new();
|
||||
carrier_names.insert(carrier_info.loop_var_name.clone());
|
||||
|
||||
for carrier in &carrier_info.carriers {
|
||||
carrier_names.insert(carrier.name.clone());
|
||||
if carrier.init == CarrierInit::FromHost && carrier.host_id == ValueId(0) {
|
||||
return Err(error_tags::freeze_with_hint(
|
||||
"phase29af/boundary_hygiene/fromhost_host_id",
|
||||
&format!(
|
||||
"carrier '{}' has FromHost init with host_id=ValueId(0)",
|
||||
carrier.name
|
||||
),
|
||||
"assign a valid host ValueId for FromHost carriers",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
for binding in &boundary.exit_bindings {
|
||||
if !carrier_names.contains(&binding.carrier_name) {
|
||||
return Err(error_tags::freeze_with_hint(
|
||||
"phase29af/boundary_hygiene/exit_binding_unknown",
|
||||
&format!(
|
||||
"exit_binding '{}' not found in carrier_info",
|
||||
binding.carrier_name
|
||||
),
|
||||
"ensure exit_bindings carriers are declared in carrier_info",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -14,6 +14,7 @@ mod terminator_targets;
|
||||
mod exit_bindings;
|
||||
mod carrier_inputs;
|
||||
mod boundary_creation;
|
||||
mod boundary_hygiene;
|
||||
mod entry_params;
|
||||
mod entry_like_policy;
|
||||
|
||||
@ -21,6 +22,7 @@ mod entry_like_policy;
|
||||
pub(super) use terminator_targets::verify_all_terminator_targets_exist;
|
||||
pub(super) use exit_bindings::verify_exit_bindings_have_exit_phis;
|
||||
pub(super) use carrier_inputs::verify_carrier_inputs_complete;
|
||||
pub(super) use boundary_hygiene::verify_boundary_hygiene;
|
||||
pub(super) use entry_like_policy::is_entry_like_source;
|
||||
pub(in crate::mir::builder::control_flow::joinir) use boundary_creation::verify_boundary_contract_at_creation;
|
||||
pub(in crate::mir::builder::control_flow::joinir) use entry_params::run_all_pipeline_checks;
|
||||
|
||||
@ -332,17 +332,29 @@ impl Pattern2InputsFactsBox {
|
||||
}
|
||||
}
|
||||
|
||||
if verbose {
|
||||
log.log(
|
||||
"phase100_p2",
|
||||
format!("Promoting '{}' to mutable LoopState carrier", spec.target_name),
|
||||
);
|
||||
}
|
||||
if spec.target_name == loop_var_name {
|
||||
if verbose {
|
||||
log.log(
|
||||
"phase100_p2",
|
||||
format!(
|
||||
"Skip promoting loop var '{}' to carrier (already loop_var)",
|
||||
spec.target_name
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if verbose {
|
||||
log.log(
|
||||
"phase100_p2",
|
||||
format!("Promoting '{}' to mutable LoopState carrier", spec.target_name),
|
||||
);
|
||||
}
|
||||
|
||||
use crate::mir::join_ir::lowering::carrier_info::CarrierVar;
|
||||
carrier_info
|
||||
.carriers
|
||||
.push(CarrierVar::new(spec.target_name.clone(), *target_id));
|
||||
use crate::mir::join_ir::lowering::carrier_info::CarrierVar;
|
||||
carrier_info
|
||||
.carriers
|
||||
.push(CarrierVar::new(spec.target_name.clone(), *target_id));
|
||||
}
|
||||
} else if verbose {
|
||||
log.log("phase100_p2", "No mutable accumulator pattern detected");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user