Stabilize joinir tests and env guards

This commit is contained in:
2025-12-27 17:41:30 +09:00
parent 88ead0df9f
commit 0d6229d5a2
28 changed files with 444 additions and 127 deletions

View File

@ -110,7 +110,7 @@ mod tests {
let err = result.unwrap_err();
assert!(
err.contains("[joinir/contract/B1]"),
err.contains("contract/B1"),
"Error should have B1 tag: {}",
err
);
@ -147,7 +147,7 @@ mod tests {
let err = result.unwrap_err();
assert!(
err.contains("[joinir/contract/B1]"),
err.contains("contract/B1"),
"Error should have B1 tag: {}",
err
);

View File

@ -272,6 +272,7 @@ impl ExitLineReconnector {
variable_map: &BTreeMap<String, ValueId>,
) {
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
use crate::mir::ValueId;
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
for binding in &boundary.exit_bindings {
@ -286,6 +287,17 @@ impl ExitLineReconnector {
);
continue;
}
// Phase 247-EX: Skip carriers without host slots (loop-local or FromHost placeholders).
if binding.host_slot == ValueId(0) {
trace.stderr_if(
&format!(
"[JoinIR/ExitLine/Contract] Phase 247-EX: Skipping carrier '{}' (no host slot)",
binding.carrier_name
),
true,
);
continue;
}
// Contract 1: carrier_phis must contain this carrier
let phi_dst = carrier_phis.get(&binding.carrier_name);

View File

@ -932,7 +932,7 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
if let Some(ref func) = builder.scope_ctx.current_function {
debug_assertions::verify_joinir_contracts(
func,
entry_block,
loop_header_phi_info.header_block,
exit_block_id,
&loop_header_phi_info,
boundary,

View File

@ -74,6 +74,7 @@ pub fn sync_spans(
mod tests {
use super::*;
use crate::ast::Span;
use crate::mir::ValueId;
#[test]
fn test_sync_spans_exact_match() {

View File

@ -49,11 +49,12 @@ pub(super) fn resolve_target_func_name<'a>(
.find_map(|(fname, &entry_block)| (entry_block == target_block).then(|| fname.as_str()))
}
/// Check if block is MAIN's entry block (pure lexical check)
pub(super) fn is_joinir_main_entry_block(
/// Check if the source function is the host (not the loop header function).
///
/// Used to classify LoopEntry for tail-call handling.
pub(super) fn is_joinir_loop_entry_source(
func_name: &str,
func: &MirFunction,
old_block_id: BasicBlockId,
entry_func_name: Option<&str>,
) -> bool {
func_name == canonical_names::MAIN && old_block_id == func.entry_block
entry_func_name.map(|name| name != func_name).unwrap_or(false)
}

View File

@ -8,7 +8,7 @@
//! - Record latch incoming for loop header PHI
// Import helpers from entry_resolver
use super::entry_resolver::{resolve_target_func_name, is_joinir_main_entry_block};
use super::entry_resolver::{resolve_target_func_name, is_joinir_loop_entry_source};
// Rewriter siblings (2 super:: up from plan/ to stages/, then 1 more to rewriter/)
use super::super::super::{
@ -43,8 +43,8 @@ pub(super) fn process_tail_call_params(
new_block: &mut BasicBlock,
tail_call_target: (BasicBlockId, &[ValueId]),
func_name: &str,
func: &MirFunction,
old_block_id: BasicBlockId,
_func: &MirFunction,
_old_block_id: BasicBlockId,
function_params: &BTreeMap<String, Vec<ValueId>>,
entry_func_name: Option<&str>,
continuation_candidates: &BTreeSet<String>,
@ -83,7 +83,7 @@ pub(super) fn process_tail_call_params(
// Phase 287 P2: Calculate tail_call_kind early for latch incoming logic
// Only treat MAIN's entry block as entry-like (not loop_step's entry block)
let is_entry_like_block_for_latch =
is_joinir_main_entry_block(func_name, func, old_block_id);
is_joinir_loop_entry_source(func_name, entry_func_name);
// CRITICAL: Argument order must match merge level classify_tail_call()
let tail_call_kind = classify_tail_call(

View File

@ -9,7 +9,7 @@
//! - Collect carrier inputs for exit jumps
// Import helpers from entry_resolver
use super::entry_resolver::{resolve_target_func_name, is_joinir_main_entry_block};
use super::entry_resolver::{resolve_target_func_name, is_joinir_loop_entry_source};
// Rewriter siblings (2 super:: up from plan/ to stages/, then 1 more to rewriter/)
use super::super::super::{
@ -51,8 +51,8 @@ pub(super) fn process_block_terminator(
found_tail_call: bool,
tail_call_target: Option<(BasicBlockId, &[ValueId])>,
func_name: &str,
func: &MirFunction,
old_block_id: BasicBlockId,
_func: &MirFunction,
_old_block_id: BasicBlockId,
new_block_id: BasicBlockId,
remapper: &JoinIrIdRemapper,
local_block_map: &BTreeMap<BasicBlockId, BasicBlockId>,
@ -257,9 +257,9 @@ pub(super) fn process_block_terminator(
.map(|name| entry_func_name == Some(name))
.unwrap_or(false);
// Phase 287 P2: main の entry block からの呼び出しを LoopEntry 扱いにする
// Only treat MAIN's entry block as entry-like (not loop_step's entry block)
let is_entry_like_block = is_joinir_main_entry_block(func_name, func, old_block_id);
// Phase 287 P2: host entry block からの呼び出しを LoopEntry 扱いにする
// (loop header func の entry block は含めない)
let is_entry_like_block = is_joinir_loop_entry_source(func_name, entry_func_name);
// CRITICAL: Argument order must match merge level classify_tail_call()
let tail_call_kind = classify_tail_call(