feat(mir): Phase 25.1f完了 - Conservative PHI + ControlForm観測レイヤー
🎉 Conservative PHI Box理論による完全SSA構築 **Phase 7-B: Conservative PHI実装** - 片方branchのみ定義変数に対応(emit_void使用) - 全変数にPHI生成(Conservative Box理論) - Stage-1 resolver全テスト緑化(3/3 PASS) **Phase 25.1f: ControlForm観測レイヤー** - LoopShape/IfShape/ControlForm構造定義 - Loop/If統一インターフェース実装 - debug_dump/debug_validate機能追加 - NYASH_CONTROL_FORM_TRACE環境変数対応 **主な変更**: - src/mir/builder/phi.rs: Conservative PHI実装 - src/mir/control_form.rs: ControlForm構造(NEW) - src/mir/loop_builder.rs: LoopForm v2デフォルト化 **テスト結果**: ✅ mir_stage1_using_resolver_min_fragment_verifies ✅ mir_stage1_using_resolver_full_collect_entries_verifies ✅ mir_parserbox_parse_program2_harness_parses_minimal_source 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: ChatGPT <chatgpt@openai.com>
This commit is contained in:
@ -510,11 +510,11 @@ impl MirBuilder {
|
||||
let mut instruction = instruction;
|
||||
|
||||
// Precompute debug metadata to avoid borrow conflicts later
|
||||
let dbg_fn_name = self
|
||||
let _dbg_fn_name = self
|
||||
.current_function
|
||||
.as_ref()
|
||||
.map(|f| f.signature.name.clone());
|
||||
let dbg_region_id = self.debug_current_region_id();
|
||||
let _dbg_region_id = self.debug_current_region_id();
|
||||
// P0: PHI の軽量補強と観測は、関数ブロック取得前に実施して借用競合を避ける
|
||||
if let MirInstruction::Phi { dst, inputs } = &instruction {
|
||||
origin::phi::propagate_phi_meta(self, *dst, inputs);
|
||||
|
||||
@ -18,7 +18,6 @@
|
||||
//! 4. ✅ 巨大関数は分割: 100行超える関数を30-50行目標で分割
|
||||
|
||||
// Import from new modules (refactored with Box Theory)
|
||||
use super::calls::*;
|
||||
pub use super::calls::call_target::CallTarget;
|
||||
|
||||
// ========================================
|
||||
|
||||
@ -46,6 +46,7 @@ pub fn suggest_resolution(name: &str) -> String {
|
||||
|
||||
/// Check if a method name is commonly shadowed by global functions
|
||||
/// Used for generating warnings about potential self-recursion
|
||||
#[allow(dead_code)]
|
||||
pub fn is_commonly_shadowed_method(method: &str) -> bool {
|
||||
matches!(
|
||||
method,
|
||||
@ -57,6 +58,7 @@ pub fn is_commonly_shadowed_method(method: &str) -> bool {
|
||||
}
|
||||
|
||||
/// Generate warning message for potential self-recursion
|
||||
#[allow(dead_code)]
|
||||
pub fn generate_self_recursion_warning(box_name: &str, method: &str) -> String {
|
||||
format!(
|
||||
"Warning: Potential self-recursion detected in {}.{}(). \
|
||||
@ -102,4 +104,4 @@ mod tests {
|
||||
assert!(warning.contains("::print()"));
|
||||
assert!(warning.contains("self-recursion"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,11 +5,8 @@
|
||||
* Replaces 6 different call instructions with a single unified system
|
||||
*/
|
||||
|
||||
use crate::mir::{Callee, Effect, EffectMask, ValueId};
|
||||
use crate::mir::definitions::call_unified::{CallFlags, MirCall, TypeCertainty};
|
||||
use super::call_target::CallTarget;
|
||||
use super::method_resolution;
|
||||
use super::extern_calls;
|
||||
use crate::mir::{Callee, EffectMask, ValueId};
|
||||
use crate::mir::definitions::call_unified::{CallFlags, MirCall};
|
||||
|
||||
/// Check if unified call system is enabled
|
||||
pub fn is_unified_call_enabled() -> bool {
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
//! - emit_legacy_call: レガシーCall発行(既存互換)
|
||||
//! - emit_global_call/emit_method_call/emit_constructor_call: 便利ラッパー
|
||||
|
||||
use super::super::{Effect, EffectMask, MirBuilder, MirInstruction, ValueId};
|
||||
use super::super::{EffectMask, MirBuilder, MirInstruction, ValueId};
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
use super::{CallTarget, call_unified};
|
||||
use super::CallTarget;
|
||||
|
||||
impl MirBuilder {
|
||||
/// Unified call emission - delegates to UnifiedCallEmitterBox
|
||||
@ -139,6 +139,7 @@ impl MirBuilder {
|
||||
// ========================================
|
||||
|
||||
/// Try fallback handlers for global functions (delegates to CallMaterializerBox)
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn try_global_fallback_handlers(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
@ -149,6 +150,7 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
/// Ensure receiver is materialized in Callee::Method (delegates to CallMaterializerBox)
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn materialize_receiver_in_callee(
|
||||
&mut self,
|
||||
callee: Callee,
|
||||
|
||||
@ -155,6 +155,7 @@ pub fn parse_extern_name(name: &str) -> (String, String) {
|
||||
}
|
||||
|
||||
/// Check if a name refers to an environment interface
|
||||
#[allow(dead_code)]
|
||||
pub fn is_env_interface(name: &str) -> bool {
|
||||
matches!(name,
|
||||
"env" | "env.console" | "env.fs" | "env.net" |
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
/*!
|
||||
* Function Lowering Utilities
|
||||
*
|
||||
@ -147,4 +149,4 @@ pub fn method_likely_returns_value(method_name: &str) -> bool {
|
||||
"add" | "sub" | "mul" | "div" |
|
||||
"min" | "max" | "abs"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,6 +96,7 @@ impl<'a> CalleeGuardBox<'a> {
|
||||
/// receiver型の検証(ヘルパー)
|
||||
///
|
||||
/// 指定されたreceiverがBox型を持っているか確認
|
||||
#[allow(dead_code)]
|
||||
pub fn has_box_type(&self, receiver: ValueId) -> bool {
|
||||
matches!(self.value_types.get(&receiver), Some(MirType::Box(_)))
|
||||
}
|
||||
@ -103,6 +104,7 @@ impl<'a> CalleeGuardBox<'a> {
|
||||
/// receiver型の取得(ヘルパー)
|
||||
///
|
||||
/// 指定されたreceiverのBox型名を返す(存在しない場合はNone)
|
||||
#[allow(dead_code)]
|
||||
pub fn get_box_type(&self, receiver: ValueId) -> Option<&String> {
|
||||
match self.value_types.get(&receiver) {
|
||||
Some(MirType::Box(box_name)) => Some(box_name),
|
||||
@ -114,6 +116,7 @@ impl<'a> CalleeGuardBox<'a> {
|
||||
///
|
||||
/// box_name と receiver型が一致するか判定
|
||||
/// (静的メソッド呼び出しの検出用)
|
||||
#[allow(dead_code)]
|
||||
pub fn is_me_call(&self, box_name: &str, receiver: ValueId) -> bool {
|
||||
match self.get_box_type(receiver) {
|
||||
Some(recv_box) => recv_box == box_name,
|
||||
|
||||
@ -28,8 +28,13 @@ pub mod effects_analyzer; // Phase 3-B: Effects analyzer (エフェクト解析
|
||||
pub mod materializer; // Phase 3-C: Call materializer (Call前処理・準備専用箱)
|
||||
|
||||
// Re-export public interfaces
|
||||
#[allow(unused_imports)]
|
||||
pub use call_target::CallTarget;
|
||||
#[allow(unused_imports)]
|
||||
pub use lowering::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use utils::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use emit::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use build::*;
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
/*!
|
||||
* Special Call Handlers
|
||||
*
|
||||
@ -137,4 +139,4 @@ pub fn suggest_alternative_for_reserved(name: &str) -> String {
|
||||
"from" => "Use 'from Parent.method()' syntax for delegation".to_string(),
|
||||
_ => format!("'{}' is a reserved keyword", name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,16 +23,19 @@ use std::collections::HashMap;
|
||||
pub struct BoxCompilationContext {
|
||||
/// 変数名 → ValueId マッピング
|
||||
/// 例: "args" → ValueId(0), "result" → ValueId(42)
|
||||
#[allow(dead_code)]
|
||||
pub variable_map: HashMap<String, ValueId>,
|
||||
|
||||
/// ValueId → 起源Box名 マッピング
|
||||
/// NewBox命令で生成されたValueIdがどのBox型から来たかを追跡
|
||||
/// 例: ValueId(10) → "ParserBox"
|
||||
#[allow(dead_code)]
|
||||
pub value_origin_newbox: HashMap<ValueId, String>,
|
||||
|
||||
/// ValueId → MIR型 マッピング
|
||||
/// 各ValueIdの型情報を保持
|
||||
/// 例: ValueId(5) → MirType::Integer
|
||||
#[allow(dead_code)]
|
||||
pub value_types: HashMap<ValueId, MirType>,
|
||||
}
|
||||
|
||||
@ -43,6 +46,7 @@ impl BoxCompilationContext {
|
||||
}
|
||||
|
||||
/// コンテキストが空(未使用)かどうかを判定
|
||||
#[allow(dead_code)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.variable_map.is_empty()
|
||||
&& self.value_origin_newbox.is_empty()
|
||||
@ -50,6 +54,7 @@ impl BoxCompilationContext {
|
||||
}
|
||||
|
||||
/// デバッグ用:コンテキストのサイズ情報を取得
|
||||
#[allow(dead_code)]
|
||||
pub fn size_info(&self) -> (usize, usize, usize) {
|
||||
(
|
||||
self.variable_map.len(),
|
||||
|
||||
@ -38,11 +38,6 @@ impl super::MirBuilder {
|
||||
let _ = self.lower_static_method_as_function(func_name, params.clone(), body.clone());
|
||||
eprintln!("[DEBUG] build_static_main_box: After lower_static_method_as_function");
|
||||
eprintln!("[DEBUG] variable_map = {:?}", self.variable_map);
|
||||
// Convert the method body to a Program AST node and lower it
|
||||
let program_ast = ASTNode::Program {
|
||||
statements: body.clone(),
|
||||
span: crate::ast::Span::unknown(),
|
||||
};
|
||||
// Initialize local variables for Main.main() parameters
|
||||
// Note: These are local variables in the wrapper main() function, NOT parameters
|
||||
let saved_var_map = std::mem::take(&mut self.variable_map);
|
||||
@ -117,7 +112,7 @@ impl super::MirBuilder {
|
||||
weak_fields: Vec<String>,
|
||||
) -> Result<(), String> {
|
||||
// Create a type registration constant (marker)
|
||||
let type_id = crate::mir::builder::emission::constant::emit_string(self, format!("__box_type_{}", name));
|
||||
crate::mir::builder::emission::constant::emit_string(self, format!("__box_type_{}", name));
|
||||
|
||||
// Emit field metadata markers
|
||||
for field in fields {
|
||||
|
||||
@ -17,11 +17,13 @@ pub fn emit_to(b: &mut MirBuilder, dst: ValueId, op: CompareOp, lhs: ValueId, rh
|
||||
|
||||
// Convenience wrappers (明示関数名が読みやすい箇所用)
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn emit_eq_to(b: &mut MirBuilder, dst: ValueId, lhs: ValueId, rhs: ValueId) -> Result<(), String> {
|
||||
emit_to(b, dst, CompareOp::Eq, lhs, rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn emit_ne_to(b: &mut MirBuilder, dst: ValueId, lhs: ValueId, rhs: ValueId) -> Result<(), String> {
|
||||
emit_to(b, dst, CompareOp::Ne, lhs, rhs)
|
||||
}
|
||||
|
||||
@ -288,7 +288,7 @@ impl super::MirBuilder {
|
||||
MirInstruction::Const { value, .. } => {
|
||||
if let super::ConstValue::String(s) = value { last_const_name = Some(s.clone()); }
|
||||
}
|
||||
MirInstruction::Call { func, .. } => {
|
||||
MirInstruction::Call { func: _, .. } => {
|
||||
// If immediately preceded by matching Const String, accept
|
||||
if let Some(prev) = last_const_name.as_ref() {
|
||||
if prev == &expect_tail { ok = true; break; }
|
||||
@ -318,7 +318,7 @@ impl super::MirBuilder {
|
||||
// Dev stub: provide condition_fn when missing to satisfy predicate calls in JSON lexers
|
||||
// Returns integer 1 (truthy) and accepts one argument (unused).
|
||||
if module.functions.get("condition_fn").is_none() {
|
||||
let mut sig = FunctionSignature {
|
||||
let sig = FunctionSignature {
|
||||
name: "condition_fn".to_string(),
|
||||
params: vec![MirType::Integer], // accept one i64-like arg
|
||||
return_type: MirType::Integer,
|
||||
|
||||
@ -32,6 +32,7 @@ pub fn propagate(builder: &mut MirBuilder, src: ValueId, dst: ValueId) {
|
||||
/// dst に型注釈を明示的に設定し、必要ならば起源情報を消去/維持する。
|
||||
/// 🎯 TypeRegistry 経由モード対応(NYASH_USE_TYPE_REGISTRY=1)
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn propagate_with_override(builder: &mut MirBuilder, dst: ValueId, ty: MirType) {
|
||||
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY")
|
||||
.ok()
|
||||
@ -45,4 +46,3 @@ pub fn propagate_with_override(builder: &mut MirBuilder, dst: ValueId, ty: MirTy
|
||||
builder.value_types.insert(dst, ty);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -62,6 +62,7 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
/// Check if this is a TypeOp method call
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn is_typeop_method(method: &str, arguments: &[ASTNode]) -> Option<String> {
|
||||
if (method == "is" || method == "as") && arguments.len() == 1 {
|
||||
Self::extract_string_literal(&arguments[0])
|
||||
|
||||
@ -12,7 +12,7 @@ impl MirBuilder {
|
||||
pub(super) fn merge_modified_vars(
|
||||
&mut self,
|
||||
_then_block: super::BasicBlockId,
|
||||
else_block: super::BasicBlockId,
|
||||
_else_block: super::BasicBlockId,
|
||||
then_exit_block_opt: Option<super::BasicBlockId>,
|
||||
else_exit_block_opt: Option<super::BasicBlockId>,
|
||||
pre_if_snapshot: &std::collections::HashMap<String, super::ValueId>,
|
||||
@ -179,8 +179,8 @@ impl MirBuilder {
|
||||
/// This handles variable reassignment patterns and ensures a single exit value.
|
||||
pub(super) fn normalize_if_else_phi(
|
||||
&mut self,
|
||||
then_block: BasicBlockId,
|
||||
else_block: BasicBlockId,
|
||||
_then_block: BasicBlockId,
|
||||
_else_block: BasicBlockId,
|
||||
then_exit_block_opt: Option<BasicBlockId>,
|
||||
else_exit_block_opt: Option<BasicBlockId>,
|
||||
then_value_raw: ValueId,
|
||||
|
||||
@ -19,6 +19,7 @@ fn rewrite_enabled() -> bool {
|
||||
|
||||
/// Try Known‑route instance→function rewrite.
|
||||
/// 既存の安全ガード(user_defined/存在確認/ENV)を尊重して関数化する。
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn try_known_rewrite(
|
||||
builder: &mut MirBuilder,
|
||||
object_value: ValueId,
|
||||
@ -119,6 +120,7 @@ pub(crate) fn try_known_rewrite_to_dst(
|
||||
|
||||
/// Fallback: when exactly one user-defined method matches by name/arity across the module,
|
||||
/// resolve to that even if class inference failed. Deterministic via uniqueness and user-box prefix.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn try_unique_suffix_rewrite(
|
||||
builder: &mut MirBuilder,
|
||||
object_value: ValueId,
|
||||
@ -183,7 +185,7 @@ pub(crate) fn try_unique_suffix_rewrite_to_dst(
|
||||
if cands.len() != 1 { return None; }
|
||||
let fname = cands.remove(0);
|
||||
if let Some((bx, _)) = fname.split_once('.') { if !builder.user_defined_boxes.contains(bx) { return None; } } else { return None; }
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
@ -212,6 +214,7 @@ pub(crate) fn try_unique_suffix_rewrite_to_dst(
|
||||
}
|
||||
|
||||
/// Unified entry: try Known rewrite first, then unique-suffix fallback.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn try_known_or_unique(
|
||||
builder: &mut MirBuilder,
|
||||
object_value: ValueId,
|
||||
|
||||
@ -2,6 +2,7 @@ use super::super::MirBuilder;
|
||||
|
||||
/// Early special-case: toString/stringify → str(互換)を処理。
|
||||
/// 戻り値: Some(result_id) なら処理済み。None なら通常経路へ委譲。
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn try_early_str_like(
|
||||
builder: &mut MirBuilder,
|
||||
object_value: super::super::ValueId,
|
||||
@ -104,6 +105,7 @@ pub(crate) fn try_early_str_like(
|
||||
|
||||
/// Special-case for equals/1: prefer Known rewrite; otherwise allow unique-suffix fallback
|
||||
/// when it is deterministic (single candidate). This centralizes equals handling.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn try_special_equals(
|
||||
builder: &mut MirBuilder,
|
||||
object_value: super::super::ValueId,
|
||||
@ -151,7 +153,7 @@ pub(crate) fn try_early_str_like_to_dst(
|
||||
"certainty": "Known",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &chosen) {
|
||||
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &chosen) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
@ -182,7 +184,7 @@ pub(crate) fn try_early_str_like_to_dst(
|
||||
"certainty": "Heuristic",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
@ -209,7 +211,7 @@ pub(crate) fn try_early_str_like_to_dst(
|
||||
"certainty": "Heuristic",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
|
||||
@ -7,6 +7,7 @@ pub struct BlockScheduleBox;
|
||||
|
||||
impl BlockScheduleBox {
|
||||
/// Insert a Copy immediately after PHI nodes. Returns the local value id.
|
||||
#[allow(dead_code)]
|
||||
pub fn ensure_after_phis_copy(builder: &mut MirBuilder, src: ValueId) -> Result<ValueId, String> {
|
||||
if let Some(bb) = builder.current_block {
|
||||
if let Some(&cached) = builder.schedule_mat_map.get(&(bb, src)) {
|
||||
@ -29,6 +30,7 @@ impl BlockScheduleBox {
|
||||
|
||||
/// Emit a Copy right before the next emitted instruction (best-effort):
|
||||
/// place it at the tail of the current block. Returns the local value id.
|
||||
#[allow(dead_code)]
|
||||
pub fn emit_before_call_copy(builder: &mut MirBuilder, src: ValueId) -> Result<ValueId, String> {
|
||||
// Prefer to reuse the after-phis materialized id for this src in this block
|
||||
let base = Self::ensure_after_phis_copy(builder, src)?;
|
||||
|
||||
@ -13,6 +13,7 @@ use std::collections::HashMap;
|
||||
pub struct TraceEntry {
|
||||
pub vid: ValueId,
|
||||
pub source: String, // "newbox:MapBox", "param:args", "propagate:from_%123"
|
||||
#[allow(dead_code)]
|
||||
pub timestamp: usize,
|
||||
}
|
||||
|
||||
@ -52,6 +53,7 @@ impl TypeRegistry {
|
||||
// ============================================================
|
||||
|
||||
/// NewBox起源を記録
|
||||
#[allow(dead_code)]
|
||||
pub fn record_newbox(&mut self, vid: ValueId, class: String) {
|
||||
self.origins.insert(vid, class.clone());
|
||||
|
||||
@ -67,6 +69,7 @@ impl TypeRegistry {
|
||||
}
|
||||
|
||||
/// パラメータ型を記録
|
||||
#[allow(dead_code)]
|
||||
pub fn record_param(&mut self, vid: ValueId, param_name: &str, param_type: Option<MirType>) {
|
||||
if let Some(ty) = param_type.clone() {
|
||||
self.types.insert(vid, ty.clone());
|
||||
@ -107,6 +110,7 @@ impl TypeRegistry {
|
||||
}
|
||||
|
||||
/// 起源を明示的に設定(推論結果など)
|
||||
#[allow(dead_code)]
|
||||
pub fn record_origin(&mut self, vid: ValueId, origin: String, reason: &str) {
|
||||
self.origins.insert(vid, origin.clone());
|
||||
|
||||
@ -164,11 +168,13 @@ impl TypeRegistry {
|
||||
// ============================================================
|
||||
|
||||
/// 起源クラス名を取得
|
||||
#[allow(dead_code)]
|
||||
pub fn get_origin(&self, vid: ValueId) -> Option<&String> {
|
||||
self.origins.get(&vid)
|
||||
}
|
||||
|
||||
/// 型情報を取得
|
||||
#[allow(dead_code)]
|
||||
pub fn get_type(&self, vid: ValueId) -> Option<&MirType> {
|
||||
self.types.get(&vid)
|
||||
}
|
||||
@ -214,6 +220,7 @@ impl TypeRegistry {
|
||||
}
|
||||
|
||||
/// 全トレースログを表示
|
||||
#[allow(dead_code)]
|
||||
pub fn dump_trace(&self) {
|
||||
eprintln!("[type-registry] === Trace Log ({} entries) ===", self.trace_log.len());
|
||||
for entry in &self.trace_log {
|
||||
@ -222,6 +229,7 @@ impl TypeRegistry {
|
||||
}
|
||||
|
||||
/// 統計情報を表示
|
||||
#[allow(dead_code)]
|
||||
pub fn dump_stats(&self) {
|
||||
eprintln!("[type-registry] === Statistics ===");
|
||||
eprintln!("[type-registry] Origins: {} entries", self.origins.len());
|
||||
@ -234,6 +242,7 @@ impl TypeRegistry {
|
||||
// ============================================================
|
||||
|
||||
/// 起源情報のみクリア(型情報は保持)
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_origins(&mut self) {
|
||||
self.origins.clear();
|
||||
if self.trace_enabled {
|
||||
@ -242,6 +251,7 @@ impl TypeRegistry {
|
||||
}
|
||||
|
||||
/// 全情報クリア
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_all(&mut self) {
|
||||
self.origins.clear();
|
||||
self.types.clear();
|
||||
|
||||
@ -5,6 +5,7 @@ use crate::mir::builder::MirBuilder;
|
||||
|
||||
/// 直接的に MirType を設定する(仕様不変)。
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_type(builder: &mut MirBuilder, dst: ValueId, ty: MirType) {
|
||||
builder.value_types.insert(dst, ty);
|
||||
}
|
||||
|
||||
@ -334,6 +334,7 @@ impl super::MirBuilder {
|
||||
}
|
||||
|
||||
/// Ensure a value has a local definition in the current block by inserting a Copy.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn materialize_local(&mut self, v: super::ValueId) -> Result<super::ValueId, String> {
|
||||
// Phase 25.1b: Use function-local ID allocator to avoid SSA verification failures
|
||||
let dst = if let Some(ref mut f) = self.current_function {
|
||||
@ -348,6 +349,7 @@ impl super::MirBuilder {
|
||||
}
|
||||
|
||||
/// Insert a Copy immediately after PHI nodes in the current block (position-stable).
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn insert_copy_after_phis(&mut self, dst: super::ValueId, src: super::ValueId) -> Result<(), String> {
|
||||
if let (Some(ref mut function), Some(bb)) = (&mut self.current_function, self.current_block) {
|
||||
if std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") {
|
||||
|
||||
222
src/mir/control_form.rs
Normal file
222
src/mir/control_form.rs
Normal file
@ -0,0 +1,222 @@
|
||||
/*!
|
||||
* ControlForm – 共通制御構造ビュー(Loop / If の箱化レイヤ)
|
||||
*
|
||||
* 目的:
|
||||
* - LoopForm v2(ループ)と If 降下を、1段上の「制御構造の形」として統一的に眺めるための薄いレイヤだよ。
|
||||
* - Conservative PHI Box や将来の可視化/検証ロジックが、Loop 専用 / If 専用に分かれず、
|
||||
* ControlForm という SSOT から情報を取れるようにするのがねらいだよ。
|
||||
*
|
||||
* このモジュール自体は構造定義とデバッグ用のユーティリティのみを提供し、
|
||||
* 既存の LoopBuilder / If 降下の挙動は変えないよ(Phase 25.1f では観測レイヤ専用)。
|
||||
*/
|
||||
|
||||
use crate::mir::{BasicBlock, BasicBlockId, MirFunction};
|
||||
|
||||
/// ループ構造の形だけを表す箱だよ。
|
||||
///
|
||||
/// - `preheader` : ループ直前のブロック(キャリア/ピン変数のコピー元)
|
||||
/// - `header` : ループヘッダ(条件判定と header PHI が置かれる)
|
||||
/// - `body` : 代表的なループ本体ブロック(最初の body など)
|
||||
/// - `latch` : ヘッダへ戻るバックエッジを張るブロック
|
||||
/// - `exit` : ループを抜けた先のブロック
|
||||
/// - `continue_targets` : continue がジャンプするブロック群(通常は latch か header)
|
||||
/// - `break_targets` : break がジャンプするブロック群(通常は exit)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LoopShape {
|
||||
pub preheader: BasicBlockId,
|
||||
pub header: BasicBlockId,
|
||||
pub body: BasicBlockId,
|
||||
pub latch: BasicBlockId,
|
||||
pub exit: BasicBlockId,
|
||||
pub continue_targets: Vec<BasicBlockId>,
|
||||
pub break_targets: Vec<BasicBlockId>,
|
||||
}
|
||||
|
||||
/// if/else 構造の形だけを表す箱だよ。
|
||||
///
|
||||
/// - `cond_block` : 条件式を評価するブロック
|
||||
/// - `then_block` : then ブランチの先頭ブロック
|
||||
/// - `else_block` : else ブランチの先頭ブロック(無ければ None)
|
||||
/// - `merge_block`: then/else の合流ブロック
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IfShape {
|
||||
pub cond_block: BasicBlockId,
|
||||
pub then_block: BasicBlockId,
|
||||
pub else_block: Option<BasicBlockId>,
|
||||
pub merge_block: BasicBlockId,
|
||||
}
|
||||
|
||||
/// 制御構造の種別だよ。
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ControlKind {
|
||||
Loop(LoopShape),
|
||||
If(IfShape),
|
||||
}
|
||||
|
||||
/// ループ / if / 将来の switch などを、共通のビューとして扱う箱だよ。
|
||||
///
|
||||
/// - `entry` : 構造に入る入口ブロック
|
||||
/// - `exits` : 構造を抜けたあとのブロック群
|
||||
/// - `kind` : Loop / If などの種別ごとの Shape
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ControlForm {
|
||||
pub entry: BasicBlockId,
|
||||
pub exits: Vec<BasicBlockId>,
|
||||
pub kind: ControlKind,
|
||||
}
|
||||
|
||||
impl ControlForm {
|
||||
/// ループ用 Shape から ControlForm を生成するよ。
|
||||
///
|
||||
/// ループの entry は preheader、exit は exit ブロック 1 つとみなす。
|
||||
pub fn from_loop(shape: LoopShape) -> Self {
|
||||
ControlForm {
|
||||
entry: shape.preheader,
|
||||
exits: vec![shape.exit],
|
||||
kind: ControlKind::Loop(shape),
|
||||
}
|
||||
}
|
||||
|
||||
/// If 用 Shape から ControlForm を生成するよ。
|
||||
///
|
||||
/// If の entry は cond_block、exit は merge_block 1 つとみなす。
|
||||
pub fn from_if(shape: IfShape) -> Self {
|
||||
ControlForm {
|
||||
entry: shape.cond_block,
|
||||
exits: vec![shape.merge_block],
|
||||
kind: ControlKind::If(shape),
|
||||
}
|
||||
}
|
||||
|
||||
/// これはループかな?という軽い判定だよ。
|
||||
pub fn is_loop(&self) -> bool {
|
||||
matches!(self.kind, ControlKind::Loop(_))
|
||||
}
|
||||
|
||||
/// これは if 構造かな?という軽い判定だよ。
|
||||
pub fn is_if(&self) -> bool {
|
||||
matches!(self.kind, ControlKind::If(_))
|
||||
}
|
||||
|
||||
/// デバッグ用に構造をダンプするよ。
|
||||
///
|
||||
/// 呼び出し側で `NYASH_CONTROL_FORM_TRACE=1` を見る想定なので、
|
||||
/// ここでは単純に eprintln! するだけにしておく。
|
||||
pub fn debug_dump(&self) {
|
||||
match &self.kind {
|
||||
ControlKind::Loop(shape) => {
|
||||
eprintln!(
|
||||
"[ControlForm::Loop] entry={:?} preheader={:?} header={:?} body={:?} latch={:?} exit={:?} continue={:?} break={:?}",
|
||||
self.entry,
|
||||
shape.preheader,
|
||||
shape.header,
|
||||
shape.body,
|
||||
shape.latch,
|
||||
shape.exit,
|
||||
shape.continue_targets,
|
||||
shape.break_targets,
|
||||
);
|
||||
}
|
||||
ControlKind::If(shape) => {
|
||||
eprintln!(
|
||||
"[ControlForm::If] entry={:?} cond={:?} then={:?} else={:?} merge={:?} exits={:?}",
|
||||
self.entry,
|
||||
shape.cond_block,
|
||||
shape.then_block,
|
||||
shape.else_block,
|
||||
shape.merge_block,
|
||||
self.exits,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ControlForm の invariant を軽く検査するための CFG 抽象だよ。
|
||||
///
|
||||
/// 実装は MirFunction などに持たせて、`debug_validate` から使う想定。
|
||||
pub trait CfgLike {
|
||||
fn has_edge(&self, from: BasicBlockId, to: BasicBlockId) -> bool;
|
||||
fn predecessors_len(&self, block: BasicBlockId) -> usize;
|
||||
}
|
||||
|
||||
impl CfgLike for MirFunction {
|
||||
fn has_edge(&self, from: BasicBlockId, to: BasicBlockId) -> bool {
|
||||
self.blocks
|
||||
.get(&from)
|
||||
.map(|bb: &BasicBlock| bb.successors.contains(&to))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn predecessors_len(&self, block: BasicBlockId) -> usize {
|
||||
self.blocks
|
||||
.get(&block)
|
||||
.map(|bb: &BasicBlock| bb.predecessors.len())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// ControlForm トレース用の環境フラグを判定するヘルパーだよ。
|
||||
///
|
||||
/// - 未設定 → 既定で ON
|
||||
/// - "0" / "false" → OFF
|
||||
/// - それ以外 → ON
|
||||
pub fn is_control_form_trace_on() -> bool {
|
||||
std::env::var("NYASH_CONTROL_FORM_TRACE")
|
||||
.map(|v| v != "0" && v.to_lowercase() != "false")
|
||||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
impl LoopShape {
|
||||
/// Debug ビルドでだけ呼ぶ用の簡易 invariant チェックだよ。
|
||||
///
|
||||
/// - preheader → header にエッジがあること
|
||||
/// - latch → header にバックエッジがあること
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn debug_validate<C: CfgLike>(&self, cfg: &C) {
|
||||
debug_assert!(
|
||||
cfg.has_edge(self.preheader, self.header),
|
||||
"LoopShape invalid: preheader -> header edge missing: {:?} -> {:?}",
|
||||
self.preheader,
|
||||
self.header
|
||||
);
|
||||
debug_assert!(
|
||||
cfg.has_edge(self.latch, self.header),
|
||||
"LoopShape invalid: latch -> header backedge missing: {:?} -> {:?}",
|
||||
self.latch,
|
||||
self.header
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl IfShape {
|
||||
/// Debug ビルドでだけ呼ぶ用の簡易 invariant チェックだよ。
|
||||
///
|
||||
/// - cond → then / else にエッジがあること
|
||||
/// - merge については、predecessor 情報がまだ配線途中のケースもあるので
|
||||
/// ここでは「0 ならログだけ出す(panic しない)」ことにするよ。
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn debug_validate<C: CfgLike>(&self, cfg: &C) {
|
||||
debug_assert!(
|
||||
cfg.has_edge(self.cond_block, self.then_block),
|
||||
"IfShape invalid: cond -> then edge missing: {:?} -> {:?}",
|
||||
self.cond_block,
|
||||
self.then_block
|
||||
);
|
||||
if let Some(else_blk) = self.else_block {
|
||||
debug_assert!(
|
||||
cfg.has_edge(self.cond_block, else_blk),
|
||||
"IfShape invalid: cond -> else edge missing: {:?} -> {:?}",
|
||||
self.cond_block,
|
||||
else_blk
|
||||
);
|
||||
}
|
||||
let preds = cfg.predecessors_len(self.merge_block);
|
||||
if preds == 0 && std::env::var("NYASH_CONTROL_FORM_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[ControlForm::IfShape] WARN: merge block {:?} has no predecessors yet",
|
||||
self.merge_block
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
use super::{BasicBlockId, ConstValue, MirInstruction, ValueId};
|
||||
use crate::mir::control_form::{ControlForm, IfShape, LoopShape, is_control_form_trace_on};
|
||||
use crate::mir::phi_core::loop_phi::IncompletePhi;
|
||||
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
|
||||
use crate::ast::ASTNode;
|
||||
@ -342,7 +343,6 @@ impl<'a> LoopBuilder<'a> {
|
||||
|
||||
// Jump to latch if not already terminated
|
||||
let actual_latch_id = if !is_current_block_terminated(self.parent_builder)? {
|
||||
let cur_body_end = self.current_block()?;
|
||||
self.emit_jump(latch_id)?;
|
||||
latch_id
|
||||
} else {
|
||||
@ -375,6 +375,38 @@ impl<'a> LoopBuilder<'a> {
|
||||
// Pop loop context
|
||||
crate::mir::builder::loops::pop_loop_context(self.parent_builder);
|
||||
|
||||
// ControlForm 観測: 環境フラグ(未設定時は既定ON)のとき LoopShape をダンプ
|
||||
if is_control_form_trace_on() {
|
||||
// continue / break のターゲットブロックをユニーク化して収集
|
||||
use std::collections::HashSet;
|
||||
let mut cont_set: HashSet<BasicBlockId> = HashSet::new();
|
||||
let mut break_set: HashSet<BasicBlockId> = HashSet::new();
|
||||
for (bb, _) in &self.continue_snapshots {
|
||||
cont_set.insert(*bb);
|
||||
}
|
||||
for (bb, _) in &self.exit_snapshots {
|
||||
break_set.insert(*bb);
|
||||
}
|
||||
let continue_targets: Vec<BasicBlockId> = cont_set.into_iter().collect();
|
||||
let break_targets: Vec<BasicBlockId> = break_set.into_iter().collect();
|
||||
|
||||
let loop_shape = LoopShape {
|
||||
preheader: preheader_id,
|
||||
header: header_id,
|
||||
body: body_id,
|
||||
latch: latch_id,
|
||||
exit: exit_id,
|
||||
continue_targets,
|
||||
break_targets,
|
||||
};
|
||||
let form = ControlForm::from_loop(loop_shape.clone());
|
||||
form.debug_dump();
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(ref func) = self.parent_builder.current_function {
|
||||
loop_shape.debug_validate(func);
|
||||
}
|
||||
}
|
||||
|
||||
// Return void value
|
||||
let void_dst = self.new_value();
|
||||
self.emit_const(void_dst, ConstValue::Void)?;
|
||||
@ -486,7 +518,7 @@ impl<'a> LoopBuilder<'a> {
|
||||
}
|
||||
|
||||
// Add PHI nodes for new pinned variables in header block
|
||||
for (name, value, preheader_value) in new_pinned_vars {
|
||||
for (name, _value, preheader_value) in new_pinned_vars {
|
||||
let phi_id = self.new_value();
|
||||
self.emit_phi_at_block_start(header_id, phi_id, vec![(preheader_id, preheader_value)])?;
|
||||
// Update variable map to use PHI value
|
||||
@ -1156,6 +1188,22 @@ impl<'a> LoopBuilder<'a> {
|
||||
&else_var_map_end_opt,
|
||||
None,
|
||||
)?;
|
||||
|
||||
// ControlForm 観測: 環境フラグ(未設定時は既定ON)のとき IfShape をダンプ
|
||||
if is_control_form_trace_on() {
|
||||
let if_shape = IfShape {
|
||||
cond_block: pre_branch_bb,
|
||||
then_block: then_bb,
|
||||
else_block: Some(else_bb),
|
||||
merge_block: merge_bb,
|
||||
};
|
||||
let form = ControlForm::from_if(if_shape.clone());
|
||||
form.debug_dump();
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(ref func) = self.parent_builder.current_function {
|
||||
if_shape.debug_validate(func);
|
||||
}
|
||||
}
|
||||
let void_id = self.new_value();
|
||||
self.emit_const(void_id, ConstValue::Void)?;
|
||||
// Pop merge debug region
|
||||
|
||||
@ -34,6 +34,7 @@ pub mod slot_registry; // Phase 9.79b.1: method slot resolution (IDs)
|
||||
pub mod value_id;
|
||||
pub mod verification;
|
||||
pub mod verification_types; // extracted error types // Optimization subpasses (e.g., type_hints)
|
||||
pub mod control_form; // Phase 25.1f: Loop/If 共通ビュー(ControlForm)
|
||||
|
||||
// Re-export main types for easy access
|
||||
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
|
||||
|
||||
@ -145,7 +145,7 @@ pub fn merge_modified_at_merge_with<O: PhiMergeOps>(
|
||||
ops: &mut O,
|
||||
merge_bb: crate::mir::BasicBlockId,
|
||||
_then_block: crate::mir::BasicBlockId,
|
||||
else_block: crate::mir::BasicBlockId,
|
||||
_else_block: crate::mir::BasicBlockId,
|
||||
then_pred_opt: Option<crate::mir::BasicBlockId>,
|
||||
else_pred_opt: Option<crate::mir::BasicBlockId>,
|
||||
pre_if_snapshot: &HashMap<String, ValueId>,
|
||||
|
||||
@ -193,7 +193,7 @@ pub fn prepare_loop_variables_with<O: LoopPhiOps>(
|
||||
ops.emit_copy_at_preheader(preheader_id, pre_copy, value_before)?;
|
||||
|
||||
let phi_id = ops.new_value();
|
||||
let mut inc = IncompletePhi {
|
||||
let inc = IncompletePhi {
|
||||
phi_id,
|
||||
var_name: var_name.clone(),
|
||||
known_inputs: vec![(preheader_id, pre_copy)], // ensure def at preheader
|
||||
|
||||
@ -67,6 +67,7 @@ pub fn compute_dominators(function: &MirFunction) -> HashMap<BasicBlockId, HashS
|
||||
dom
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn compute_reachable_blocks(function: &MirFunction) -> HashSet<BasicBlockId> {
|
||||
let mut reachable = HashSet::new();
|
||||
let mut worklist = vec![function.entry_block];
|
||||
|
||||
Reference in New Issue
Block a user