phase29af(p0): pattern2 boundary hygiene ssot

This commit is contained in:
2025-12-29 05:12:15 +09:00
parent 62bd42b87f
commit 19f2c6b7f6
14 changed files with 244 additions and 98 deletions

View File

@ -27,6 +27,14 @@
- `CarrierInit::BoolConst(_)` / `CarrierInit::LoopLocalZero` -> host binding is skipped
- ConditionOnly carriers must not use `FromHost`
## Boundary hygiene (Phase 29af)
- Header PHI 対象: `carrier_info` の carriersLoopState + ConditionOnly + LoopLocalZero
- Exit reconnection 対象: LoopState のみConditionOnly は exit_bindings に入れない)
- Host binding 対象: `CarrierInit::FromHost` のみBoolConst / LoopLocalZero は host slot 不要)
- Fail-Fast: exit_bindings の `carrier_name` 重複は禁止debug_assert
- Fail-Fast: `CarrierInit::FromHost``host_id=0` の場合は Err
- SSOT: `docs/development/current/main/phases/phase-29af/README.md`
## Out of scope
- multiple breaks / continue / return in the loop body
- reassigned LoopBodyLocal outside the derived-slot shape

View File

@ -66,7 +66,25 @@ impl EmitJoinIRStepBox {
let exit_meta = &fragment_meta.exit_meta;
use crate::mir::builder::control_flow::joinir::merge::exit_line::ExitMetaCollector;
let exit_bindings = ExitMetaCollector::collect(builder, exit_meta, Some(&inputs.carrier_info), debug);
let mut exit_bindings =
ExitMetaCollector::collect(builder, exit_meta, Some(&inputs.carrier_info), debug);
// Phase 29af P0: Exit reconnection targets LoopState only.
exit_bindings.retain(|binding| {
binding.role == crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState
});
// Phase 29af P0: Reject duplicate carrier_name in exit_bindings.
#[cfg(debug_assertions)]
{
use std::collections::HashSet;
let mut seen = HashSet::new();
for binding in &exit_bindings {
debug_assert!(
seen.insert(&binding.carrier_name),
"Phase 29af Fail-Fast: duplicate exit_binding carrier '{}'",
binding.carrier_name
);
}
}
// Phase 256.8.5: Use JoinModule.entry.params as SSOT (no hardcoded ValueIds)
use super::super::common::get_entry_function;
@ -78,7 +96,22 @@ impl EmitJoinIRStepBox {
// Build host_input_values in same order (loop_var + carriers)
let mut host_input_values = vec![inputs.loop_var_id];
for carrier in inputs.carrier_info.carriers.iter() {
host_input_values.push(carrier.host_id);
use super::super::common::{decide_carrier_binding_policy, CarrierBindingPolicy};
match decide_carrier_binding_policy(carrier) {
CarrierBindingPolicy::BindFromHost => {
if carrier.host_id == crate::mir::ValueId(0) {
return Err(format!(
"[emit_joinir] Phase 29af Fail-Fast: FromHost carrier '{}' has host_id=0",
carrier.name
));
}
host_input_values.push(carrier.host_id);
}
CarrierBindingPolicy::SkipBinding => {
// Placeholder: SkipBinding does not require a host slot.
host_input_values.push(crate::mir::ValueId(0));
}
}
}
// Verify count consistency (fail-fast)