From c1aa3c8c2bfb475b658d1aa3601cd91383dc5b14 Mon Sep 17 00:00:00 2001 From: nyash-codex Date: Sun, 23 Nov 2025 18:03:33 +0900 Subject: [PATCH] =?UTF-8?q?feat(joinir):=20Phase=2027.8-4/5=20=E2=80=94=20?= =?UTF-8?q?skip=5Fws=20CFG=20sanity=20checks=20+=20fallback=20design?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MirQuery API を使用した軽量 CFG チェック実装 - Entry block successors >= 1 - Const(0) 存在確認 - String.length() 存在確認 - 予期しない MIR 構造時の手書き版 fallback 設計 - A/B テスト完了: 手書き版・MIR-based 版両方 PASS ✅ - ドキュメント更新: joinir_coverage.md + IMPLEMENTATION_LOG.md Phase 27.9 modular refactoring: commit 3d5979c7 Phase 27.8-4/5 verification: CFG checks + docs updates --- docs/private | 2 +- src/mir/join_ir/lowering/skip_ws.rs | 31 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) 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);