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:
nyash-codex
2025-11-18 18:56:35 +09:00
parent 8b37e9711d
commit d3cbc71c9b
81 changed files with 907 additions and 147 deletions

View File

@ -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;
// ========================================

View File

@ -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"));
}
}
}

View File

@ -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 {

View File

@ -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,

View File

@ -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" |

View File

@ -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"
)
}
}

View File

@ -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,

View File

@ -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::*;

View File

@ -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),
}
}
}

View File

@ -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(),

View File

@ -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 {

View File

@ -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)
}

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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])

View File

@ -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,

View File

@ -19,6 +19,7 @@ fn rewrite_enabled() -> bool {
/// Try Knownroute 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,

View File

@ -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)),
};

View File

@ -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)?;

View File

@ -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();

View File

@ -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);
}

View File

@ -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") {