phase29af(p1): add boundary hygiene contract checks

This commit is contained in:
2025-12-29 05:27:14 +09:00
parent 19f2c6b7f6
commit 9bc9454726
7 changed files with 121 additions and 14 deletions

View File

@ -1,6 +1,12 @@
# Self Current Task — Now (main)
## Current Focus: Phase 29af (Pattern2 Boundary Hygiene)
## Current Focus: Phase 29afBoundary 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 の責務境界を固定(仕様不変)

View File

@ -8,7 +8,7 @@ Related:
## 直近JoinIR/selfhost
- **Phase 29af P0in progress: Pattern2 Boundary HygieneSSOT固定**
- **Phase 29af P1✅ COMPLETE: Pattern2 Boundary Hygienecontract_checks 集約**
- 入口: `docs/development/current/main/phases/phase-29af/README.md`
- **Phase 29ae P1✅ COMPLETE: JoinIR Regression Pack (SSOT固定)**

View File

@ -2,6 +2,11 @@
Goal: Pattern2 の boundary 情報の歪みを SSOT で整理し、将来の回帰を防ぐ(仕様不変)。
## Status
- P0: ✅ COMPLETEcommit: `19f2c6b7f`
- P1: ✅ COMPLETEmerge `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

View File

@ -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) {

View File

@ -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(())
}

View File

@ -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;

View File

@ -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");
}