diff --git a/docs/private b/docs/private index 1e9d5ca5..6abd9ec4 160000 --- a/docs/private +++ b/docs/private @@ -1 +1 @@ -Subproject commit 1e9d5ca5bb2e3275e785c7c2877325f61bc8b8c8 +Subproject commit 6abd9ec4f27afb375052e1000b8326a6cf24fd71 diff --git a/src/mir/join_ir/lowering/skip_ws.rs b/src/mir/join_ir/lowering/skip_ws.rs index 408167a3..827262b9 100644 --- a/src/mir/join_ir/lowering/skip_ws.rs +++ b/src/mir/join_ir/lowering/skip_ws.rs @@ -229,6 +229,9 @@ fn lower_skip_ws_handwritten(module: &crate::mir::MirModule) -> Option Option { + use crate::mir::query::{MirQuery, MirQueryBox}; + use crate::mir::MirInstruction; + // Step 1: "Main.skip/1" を探す let target_func = module.functions.get("Main.skip/1")?; @@ -246,6 +249,34 @@ fn lower_skip_ws_from_mir(module: &crate::mir::MirModule) -> Option return lower_skip_ws_handwritten(module); } + // Phase 27.8-4: Lightweight CFG sanity checks + // MirQueryBox を使ってパターンマッチング + let query = MirQueryBox::new(target_func); + let entry_id = target_func.entry_block; + + // Check 1: Entry block has at least 1 successor + let entry_succs = query.succs(entry_id); + if entry_succs.is_empty() { + eprintln!("[joinir/skip_ws/mir] unexpected MIR shape: entry has no successors, falling back to handwritten"); + return lower_skip_ws_handwritten(module); + } + + // Check 2: First block contains Const(0) and BoxCall(String.length) + let entry_insts = query.insts_in_block(entry_id); + let has_const_0 = entry_insts.iter().any(|inst| { + matches!(inst, MirInstruction::Const { value: crate::mir::ConstValue::Integer(0), .. }) + }); + let has_string_length = entry_insts.iter().any(|inst| { + matches!(inst, MirInstruction::BoxCall { method, .. } if method == "length") + }); + + if !has_const_0 || !has_string_length { + eprintln!("[joinir/skip_ws/mir] unexpected MIR shape: entry block missing Const(0) or String.length, falling back to handwritten"); + return lower_skip_ws_handwritten(module); + } + + eprintln!("[joinir/skip_ws/mir] CFG sanity checks passed ✅"); + // JoinIR の ValueId は手書き版と同じレンジを使い、既存テストと互換にする let skip_id = JoinFuncId::new(0); let loop_step_id = JoinFuncId::new(1);