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

@ -36,9 +36,6 @@ impl MirInterpreter {
let saved_fn = self.cur_fn.clone();
self.cur_fn = Some(func.signature.name.clone());
// Check if this is a static box method call
let static_box_name = self.is_static_box_method(&func.signature.name);
match arg_vals {
Some(args) => {
// Regular parameter binding: params and args are 1:1

View File

@ -278,6 +278,7 @@ impl MirInterpreter {
}
// moved: try_handle_map_box → handlers/boxes_map.rs
#[allow(dead_code)]
fn try_handle_map_box(
&mut self,
dst: Option<ValueId>,
@ -289,6 +290,7 @@ impl MirInterpreter {
}
// moved: try_handle_string_box → handlers/boxes_string.rs
#[allow(dead_code)]
fn try_handle_string_box(
&mut self,
dst: Option<ValueId>,
@ -300,6 +302,7 @@ impl MirInterpreter {
}
// moved: try_handle_array_box → handlers/boxes_array.rs
#[allow(dead_code)]
fn try_handle_array_box(
&mut self,
dst: Option<ValueId>,

View File

@ -102,7 +102,7 @@ pub(super) fn try_handle_instance_box(
} else {
// Conservative fallback: search unique function by name tail ".method/arity"
let tail = format!(".{}{}", method, format!("/{}", args.len()));
let mut cands: Vec<String> = this
let cands: Vec<String> = this
.functions
.keys()
.filter(|k| k.ends_with(&tail))

View File

@ -1,5 +1,4 @@
use super::*;
use crate::box_trait::NyashBox;
pub(super) fn try_handle_object_fields(
this: &mut MirInterpreter,

View File

@ -1,5 +1,4 @@
use super::*;
use crate::box_trait::NyashBox;
pub(super) fn try_handle_string_box(
this: &mut MirInterpreter,

View File

@ -3,6 +3,7 @@ use serde_json::Value as JsonValue;
impl MirInterpreter {
#[inline]
#[allow(dead_code)]
fn ensure_mir_json_version_field(s: &str) -> String {
match serde_json::from_str::<JsonValue>(s) {
Ok(mut v) => {
@ -28,6 +29,12 @@ impl MirInterpreter {
let mbase = super::super::utils::normalize_arity_suffix(method);
match (iface, mbase) {
("env", "get") => {
// Prefer provider-based resolution when available, fall back to process env.
if let Some(provider_res) = self.extern_provider_dispatch("env.get", args) {
let result = provider_res?;
self.write_result(dst, result);
return Ok(());
}
if let Some(a0) = args.get(0) {
let key = self.reg_load(*a0)?.to_string();
let val = std::env::var(&key).ok();
@ -135,14 +142,6 @@ impl MirInterpreter {
}
Ok(())
}
("env", "get") => {
// Delegate to provider
let ret = self
.extern_provider_dispatch("env.get", args)
.unwrap_or(Ok(VMValue::Void))?;
self.write_result(dst, ret);
Ok(())
}
("env", "set") => {
// Delegate to provider
let ret = self

View File

@ -110,6 +110,7 @@ impl MirInterpreter {
/// Check if a function name represents a static box method
/// Format: "BoxName.method/Arity"
#[allow(dead_code)]
fn is_static_box_method(&self, func_name: &str) -> Option<String> {
if let Some((box_name, _rest)) = func_name.split_once('.') {
if self.static_box_decls.contains_key(box_name) {

View File

@ -44,6 +44,7 @@ impl MirInterpreter {
/// # Returns
/// 引数数が範囲内の場合はOk(())、そうでない場合はエラー
#[inline]
#[allow(dead_code)]
pub(crate) fn validate_args_range(
&self,
method: &str,
@ -71,6 +72,7 @@ impl MirInterpreter {
/// # Returns
/// 引数数が最小値以上の場合はOk(())、そうでない場合はエラー
#[inline]
#[allow(dead_code)]
pub(crate) fn validate_args_min(
&self,
method: &str,

View File

@ -42,6 +42,7 @@ impl MirInterpreter {
/// # Errors
/// * 値が整数でない場合はエラー
#[inline]
#[allow(dead_code)]
pub(crate) fn load_as_int(&mut self, vid: ValueId) -> Result<i64, VMError> {
match self.reg_load(vid)? {
VMValue::Integer(i) => Ok(i),
@ -71,6 +72,7 @@ impl MirInterpreter {
/// # Errors
/// * 値がboolでない場合はエラー
#[inline]
#[allow(dead_code)]
pub(crate) fn load_as_bool(&mut self, vid: ValueId) -> Result<bool, VMError> {
match self.reg_load(vid)? {
VMValue::Bool(b) => Ok(b),
@ -112,6 +114,7 @@ impl MirInterpreter {
/// # Returns
/// * `Result<Vec<VMValue>, VMError>` - 読み込んだVMValueのVec
#[inline]
#[allow(dead_code)]
pub(crate) fn load_args_as_values(
&mut self,
vids: &[ValueId],

View File

@ -13,6 +13,7 @@ impl MirInterpreter {
/// * `dst` - 書き込み先のValueId (Noneの場合は何もしない)
/// * `result` - 書き込むBox
#[inline]
#[allow(dead_code)]
pub(crate) fn write_box_result(
&mut self,
dst: Option<ValueId>,

View File

@ -40,6 +40,7 @@ impl ErrorBuilder {
/// // => "get expects Integer type, got String"
/// ```
#[inline]
#[allow(dead_code)]
pub fn type_mismatch(method: &str, expected: &str, actual: &str) -> VMError {
VMError::InvalidInstruction(format!("{} expects {} type, got {}", method, expected, actual))
}
@ -57,6 +58,7 @@ impl ErrorBuilder {
/// // => "get index out of bounds: 5 >= 3"
/// ```
#[inline]
#[allow(dead_code)]
pub fn out_of_bounds(method: &str, index: usize, len: usize) -> VMError {
VMError::InvalidInstruction(format!("{} index out of bounds: {} >= {}", method, index, len))
}
@ -93,6 +95,7 @@ impl ErrorBuilder {
/// // => "receiver must be ArrayBox"
/// ```
#[inline]
#[allow(dead_code)]
pub fn receiver_type_error(expected: &str) -> VMError {
VMError::InvalidInstruction(format!("receiver must be {}", expected))
}
@ -123,6 +126,7 @@ impl ErrorBuilder {
/// // => "link_object expects at least 1 arg, got 0"
/// ```
#[inline]
#[allow(dead_code)]
pub fn arg_count_min(method: &str, min: usize, actual: usize) -> VMError {
VMError::InvalidInstruction(format!(
"{} expects at least {} arg{}, got {}",
@ -153,6 +157,7 @@ impl ErrorBuilder {
/// // => "link_object: <error message>"
/// ```
#[inline]
#[allow(dead_code)]
pub fn from_error(operation: &str, error: &dyn std::error::Error) -> VMError {
VMError::InvalidInstruction(format!("{}: {}", operation, error))
}
@ -178,6 +183,7 @@ impl super::super::MirInterpreter {
/// return Err(self.err_type_mismatch("get", "Integer", actual_type));
/// ```
#[inline]
#[allow(dead_code)]
pub(crate) fn err_type_mismatch(&self, method: &str, expected: &str, actual: &str) -> VMError {
ErrorBuilder::type_mismatch(method, expected, actual)
}
@ -189,6 +195,7 @@ impl super::super::MirInterpreter {
/// return Err(self.err_out_of_bounds("get", idx, len));
/// ```
#[inline]
#[allow(dead_code)]
pub(crate) fn err_out_of_bounds(&self, method: &str, index: usize, len: usize) -> VMError {
ErrorBuilder::out_of_bounds(method, index, len)
}

View File

@ -15,6 +15,7 @@ impl MirInterpreter {
/// # Returns
/// 変換成功時はBox、失敗時はエラー
#[inline]
#[allow(dead_code)]
pub(crate) fn convert_to_box(
&mut self,
receiver: ValueId,