chore: Phase 25.1 完了 - LoopForm v2/Stage1 CLI/環境変数削減 + Phase 26-D からの変更

Phase 25.1 完了成果:
-  LoopForm v2 テスト・ドキュメント・コメント完備
  - 4ケース(A/B/C/D)完全テストカバレッジ
  - 最小再現ケース作成(SSAバグ調査用)
  - SSOT文書作成(loopform_ssot.md)
  - 全ソースに [LoopForm] コメントタグ追加

-  Stage-1 CLI デバッグ環境構築
  - stage1_cli.hako 実装
  - stage1_bridge.rs ブリッジ実装
  - デバッグツール作成(stage1_debug.sh/stage1_minimal.sh)
  - アーキテクチャ改善提案文書

-  環境変数削減計画策定
  - 25変数の完全調査・分類
  - 6段階削減ロードマップ(25→5、80%削減)
  - 即時削除可能変数特定(NYASH_CONFIG/NYASH_DEBUG)

Phase 26-D からの累積変更:
- PHI実装改善(ExitPhiBuilder/HeaderPhiBuilder等)
- MIRビルダーリファクタリング
- 型伝播・最適化パス改善
- その他約300ファイルの累積変更

🎯 技術的成果:
- SSAバグ根本原因特定(条件分岐内loop変数変更)
- Region+next_iパターン適用完了(UsingCollectorBox等)
- LoopFormパターン文書化・テスト化完了
- セルフホスティング基盤強化

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: ChatGPT <noreply@openai.com>
Co-Authored-By: Task Assistant <task@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-21 06:25:17 +09:00
parent baf028a94f
commit f9d100ce01
366 changed files with 14322 additions and 5236 deletions

View File

@ -36,9 +36,18 @@ pub(in super::super) fn annotate_call_result_from_func_name<S: AsRef<str>>(
builder.value_types.insert(dst, ret.clone());
if let MirType::Box(bx) = ret {
builder.value_origin_newbox.insert(dst, bx);
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
let bx = builder.value_origin_newbox.get(&dst).cloned().unwrap_or_default();
super::super::utils::builder_debug_log(&format!("annotate call dst={} from {} -> Box({})", dst.0, name, bx));
if super::super::utils::builder_debug_enabled()
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
{
let bx = builder
.value_origin_newbox
.get(&dst)
.cloned()
.unwrap_or_default();
super::super::utils::builder_debug_log(&format!(
"annotate call dst={} from {} -> Box({})",
dst.0, name, bx
));
}
}
return;
@ -48,32 +57,60 @@ pub(in super::super) fn annotate_call_result_from_func_name<S: AsRef<str>>(
if name == "JsonParser.parse/1" {
let ret = MirType::Box("JsonNode".into());
builder.value_types.insert(dst, ret.clone());
if let MirType::Box(bx) = ret { builder.value_origin_newbox.insert(dst, bx); }
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
super::super::utils::builder_debug_log(&format!("annotate call (fallback) dst={} from {} -> Box(JsonNode)", dst.0, name));
if let MirType::Box(bx) = ret {
builder.value_origin_newbox.insert(dst, bx);
}
if super::super::utils::builder_debug_enabled()
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
{
super::super::utils::builder_debug_log(&format!(
"annotate call (fallback) dst={} from {} -> Box(JsonNode)",
dst.0, name
));
}
} else if name == "JsonParser.current_token/0" {
let ret = MirType::Box("JsonToken".into());
builder.value_types.insert(dst, ret.clone());
if let MirType::Box(bx) = ret { builder.value_origin_newbox.insert(dst, bx); }
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
super::super::utils::builder_debug_log(&format!("annotate call (fallback) dst={} from {} -> Box(JsonToken)", dst.0, name));
if let MirType::Box(bx) = ret {
builder.value_origin_newbox.insert(dst, bx);
}
if super::super::utils::builder_debug_enabled()
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
{
super::super::utils::builder_debug_log(&format!(
"annotate call (fallback) dst={} from {} -> Box(JsonToken)",
dst.0, name
));
}
} else if name == "JsonTokenizer.tokenize/0" {
// Tokenize returns an ArrayBox of tokens
let ret = MirType::Box("ArrayBox".into());
builder.value_types.insert(dst, ret.clone());
if let MirType::Box(bx) = ret { builder.value_origin_newbox.insert(dst, bx); }
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
super::super::utils::builder_debug_log(&format!("annotate call (fallback) dst={} from {} -> Box(ArrayBox)", dst.0, name));
if let MirType::Box(bx) = ret {
builder.value_origin_newbox.insert(dst, bx);
}
if super::super::utils::builder_debug_enabled()
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
{
super::super::utils::builder_debug_log(&format!(
"annotate call (fallback) dst={} from {} -> Box(ArrayBox)",
dst.0, name
));
}
} else if name == "JsonParserModule.create_parser/0" {
// Fallback path for parser factory
let ret = MirType::Box("JsonParser".into());
builder.value_types.insert(dst, ret.clone());
if let MirType::Box(bx) = ret { builder.value_origin_newbox.insert(dst, bx); }
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
super::super::utils::builder_debug_log(&format!("annotate call (fallback) dst={} from {} -> Box(JsonParser)", dst.0, name));
if let MirType::Box(bx) = ret {
builder.value_origin_newbox.insert(dst, bx);
}
if super::super::utils::builder_debug_enabled()
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
{
super::super::utils::builder_debug_log(&format!(
"annotate call (fallback) dst={} from {} -> Box(JsonParser)",
dst.0, name
));
}
} else {
// Generic tiny whitelist for known primitive-like utilities (spec unchanged)

View File

@ -5,11 +5,11 @@
//! - build_method_call: メソッド呼び出し構築
//! - build_from_expression: from式構築
use crate::ast::{ASTNode, LiteralValue};
use super::super::{Effect, EffectMask, MirBuilder, MirInstruction, MirType, ValueId};
use crate::mir::TypeOpKind;
use super::CallTarget;
use super::special_handlers;
use super::CallTarget;
use crate::ast::{ASTNode, LiteralValue};
use crate::mir::TypeOpKind;
impl MirBuilder {
// Build function call: name(args)
@ -20,7 +20,11 @@ impl MirBuilder {
) -> Result<ValueId, String> {
// Dev trace
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
let cur_fun = self.current_function.as_ref().map(|f| f.signature.name.clone()).unwrap_or_else(|| "<none>".to_string());
let cur_fun = self
.current_function
.as_ref()
.map(|f| f.signature.name.clone())
.unwrap_or_else(|| "<none>".to_string());
eprintln!(
"[builder] function-call name={} static_ctx={} in_fn={}",
name,
@ -71,10 +75,16 @@ impl MirBuilder {
const MAX_METHOD_DEPTH: usize = 100;
self.recursion_depth += 1;
if self.recursion_depth > MAX_METHOD_DEPTH {
eprintln!("[FATAL] build_method_call recursion depth exceeded {}", MAX_METHOD_DEPTH);
eprintln!(
"[FATAL] build_method_call recursion depth exceeded {}",
MAX_METHOD_DEPTH
);
eprintln!("[FATAL] Current depth: {}", self.recursion_depth);
eprintln!("[FATAL] Method: {}", method);
return Err(format!("build_method_call recursion depth exceeded: {}", self.recursion_depth));
return Err(format!(
"build_method_call recursion depth exceeded: {}",
self.recursion_depth
));
}
let result = self.build_method_call_impl(object, method, arguments);
@ -96,7 +106,10 @@ impl MirBuilder {
ASTNode::Me { .. } => "Me",
_ => "Other",
};
eprintln!("[builder] method-call object kind={} method={}", kind, method);
eprintln!(
"[builder] method-call object kind={} method={}",
kind, method
);
}
// 0. Dev-only: __mir__.log / __mir__.mark → MirInstruction::DebugLog へ直接 lowering
@ -110,7 +123,9 @@ impl MirBuilder {
// 1. Static box method call: BoxName.method(args)
if let ASTNode::Variable { name: obj_name, .. } = &object {
if let Some(result) = self.try_build_static_method_call(obj_name, &method, &arguments)? {
if let Some(result) =
self.try_build_static_method_call(obj_name, &method, &arguments)?
{
return Ok(result);
}
}
@ -211,40 +226,43 @@ impl MirBuilder {
let mut math_args: Vec<ValueId> = Vec::new();
for a in raw_args.into_iter() {
match a {
ASTNode::New { class, arguments, .. } if class == "FloatBox" && arguments.len() == 1 => {
ASTNode::New {
class, arguments, ..
} if class == "FloatBox" && arguments.len() == 1 => {
match self.build_expression(arguments[0].clone()) {
v @ Ok(_) => math_args.push(v.unwrap()),
err @ Err(_) => return Some(err),
}
}
ASTNode::New { class, arguments, .. } if class == "IntegerBox" && arguments.len() == 1 => {
ASTNode::New {
class, arguments, ..
} if class == "IntegerBox" && arguments.len() == 1 => {
let iv = match self.build_expression(arguments[0].clone()) {
Ok(v) => v,
Err(e) => return Some(Err(e))
Err(e) => return Some(Err(e)),
};
let fv = self.next_value_id();
if let Err(e) = self.emit_instruction(MirInstruction::TypeOp {
dst: fv,
op: TypeOpKind::Cast,
value: iv,
ty: MirType::Float
ty: MirType::Float,
}) {
return Some(Err(e));
}
math_args.push(fv);
}
ASTNode::Literal { value: LiteralValue::Float(_), .. } => {
match self.build_expression(a) {
v @ Ok(_) => math_args.push(v.unwrap()),
err @ Err(_) => return Some(err),
}
}
other => {
match self.build_expression(other) {
v @ Ok(_) => math_args.push(v.unwrap()),
err @ Err(_) => return Some(err),
}
}
ASTNode::Literal {
value: LiteralValue::Float(_),
..
} => match self.build_expression(a) {
v @ Ok(_) => math_args.push(v.unwrap()),
err @ Err(_) => return Some(err),
},
other => match self.build_expression(other) {
v @ Ok(_) => math_args.push(v.unwrap()),
err @ Err(_) => return Some(err),
},
}
}
// new MathBox()
@ -252,7 +270,8 @@ impl MirBuilder {
if let Err(e) = self.emit_constructor_call(math_recv, "MathBox".to_string(), vec![]) {
return Some(Err(e));
}
self.value_origin_newbox.insert(math_recv, "MathBox".to_string());
self.value_origin_newbox
.insert(math_recv, "MathBox".to_string());
// birth()
if let Err(e) = self.emit_method_call(None, math_recv, "birth".to_string(), vec![]) {
return Some(Err(e));
@ -272,29 +291,40 @@ impl MirBuilder {
method: &str,
arguments: &Vec<ASTNode>,
) -> Option<Result<ValueId, String>> {
let ASTNode::FieldAccess { object: env_obj, field: env_field, .. } = object else {
let ASTNode::FieldAccess {
object: env_obj,
field: env_field,
..
} = object
else {
return None;
};
if let ASTNode::Variable { name: env_name, .. } = env_obj.as_ref() {
if env_name != "env" { return None; }
if env_name != "env" {
return None;
}
// Build arguments once
let mut arg_values = Vec::new();
for arg in arguments {
match self.build_expression(arg.clone()) {
Ok(v) => arg_values.push(v),
Err(e) => return Some(Err(e))
Err(e) => return Some(Err(e)),
}
}
let iface = env_field.as_str();
let m = method;
let mut extern_call = |iface_name: &str, method_name: &str, effects: EffectMask, returns: bool| -> Result<ValueId, String> {
let mut extern_call = |iface_name: &str,
method_name: &str,
effects: EffectMask,
returns: bool|
-> Result<ValueId, String> {
let result_id = self.next_value_id();
self.emit_instruction(MirInstruction::ExternCall {
dst: if returns { Some(result_id) } else { None },
iface_name: iface_name.to_string(),
method_name: method_name.to_string(),
args: arg_values.clone(),
effects
effects,
})?;
if returns {
Ok(result_id)
@ -351,7 +381,8 @@ impl MirBuilder {
if let Some(result) = self.try_tail_based_fallback(&name, &arg_values)? {
return Ok(result);
}
return Err(format!("Unresolved function: '{}'. {}",
return Err(format!(
"Unresolved function: '{}'. {}",
name,
super::super::call_resolution::suggest_resolution(&name)
));
@ -379,11 +410,7 @@ impl MirBuilder {
arg_values: Vec<ValueId>,
) -> Result<ValueId, String> {
let dst = self.next_value_id();
self.emit_unified_call(
Some(dst),
CallTarget::Global(name),
arg_values,
)?;
self.emit_unified_call(Some(dst), CallTarget::Global(name), arg_values)?;
Ok(dst)
}
@ -398,11 +425,17 @@ impl MirBuilder {
// Debug trace
if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") {
eprintln!("[DEBUG] try_build_static_method_call: obj_name={}, method={}", obj_name, method);
eprintln!(
"[DEBUG] try_build_static_method_call: obj_name={}, method={}",
obj_name, method
);
eprintln!("[DEBUG] is_local_var={}", is_local_var);
if is_local_var {
eprintln!("[DEBUG] variable_map contains '{}' - treating as local variable, will use method call", obj_name);
eprintln!("[DEBUG] variable_map keys: {:?}", self.variable_map.keys().collect::<Vec<_>>());
eprintln!(
"[DEBUG] variable_map keys: {:?}",
self.variable_map.keys().collect::<Vec<_>>()
);
} else {
eprintln!("[DEBUG] '{}' not in variable_map - treating as static box, will use global call", obj_name);
}
@ -488,7 +521,7 @@ impl MirBuilder {
self.emit_unified_call(
Some(dst),
CallTarget::Global(func_name),
arg_values.to_vec()
arg_values.to_vec(),
)?;
return Ok(Some(dst));
}
@ -517,7 +550,7 @@ impl MirBuilder {
self.emit_legacy_call(
Some(dst),
CallTarget::Global(func_name),
arg_values.to_vec()
arg_values.to_vec(),
)?;
return Ok(Some(dst));
}
@ -530,20 +563,31 @@ impl MirBuilder {
fn trace_receiver_if_enabled(&self, object: &ASTNode, object_value: ValueId) {
if std::env::var("NYASH_DEBUG_PARAM_RECEIVER").ok().as_deref() == Some("1") {
if let ASTNode::Variable { name, .. } = object {
eprintln!("[DEBUG/param-recv] build_method_call receiver '{}' → ValueId({})",
name, object_value.0);
eprintln!(
"[DEBUG/param-recv] build_method_call receiver '{}' → ValueId({})",
name, object_value.0
);
if let Some(origin) = self.value_origin_newbox.get(&object_value) {
eprintln!("[DEBUG/param-recv] origin: {}", origin);
}
if let Some(&mapped_id) = self.variable_map.get(name) {
eprintln!("[DEBUG/param-recv] variable_map['{}'] = ValueId({})", name, mapped_id.0);
eprintln!(
"[DEBUG/param-recv] variable_map['{}'] = ValueId({})",
name, mapped_id.0
);
if mapped_id != object_value {
eprintln!("[DEBUG/param-recv] ⚠️ MISMATCH! build_expression returned different ValueId!");
}
} else {
eprintln!("[DEBUG/param-recv] ⚠️ '{}' NOT FOUND in variable_map!", name);
eprintln!(
"[DEBUG/param-recv] ⚠️ '{}' NOT FOUND in variable_map!",
name
);
}
eprintln!("[DEBUG/param-recv] current_block: {:?}", self.current_block);
eprintln!(
"[DEBUG/param-recv] current_block: {:?}",
self.current_block
);
}
}
}

View File

@ -16,7 +16,7 @@ pub enum CallTarget {
/// Method call (box.method)
Method {
box_type: Option<String>, // None = infer from value
box_type: Option<String>, // None = infer from value
method: String,
receiver: ValueId,
},
@ -55,4 +55,4 @@ impl CallTarget {
CallTarget::Closure { .. } => "<closure>".to_string(),
}
}
}
}

View File

@ -5,12 +5,16 @@
* Replaces 6 different call instructions with a single unified system
*/
use crate::mir::{Callee, EffectMask, ValueId};
use crate::mir::definitions::call_unified::{CallFlags, MirCall};
use crate::mir::{Callee, EffectMask, ValueId};
/// Check if unified call system is enabled
pub fn is_unified_call_enabled() -> bool {
match std::env::var("NYASH_MIR_UNIFIED_CALL").ok().as_deref().map(|s| s.to_ascii_lowercase()) {
match std::env::var("NYASH_MIR_UNIFIED_CALL")
.ok()
.as_deref()
.map(|s| s.to_ascii_lowercase())
{
Some(s) if s == "0" || s == "false" || s == "off" => false,
_ => true, // default ON during development; explicit opt-out supported
}
@ -74,11 +78,7 @@ pub fn create_call_flags(callee: &Callee) -> CallFlags {
}
/// Create a MirCall instruction from components
pub fn create_mir_call(
dst: Option<ValueId>,
callee: Callee,
args: Vec<ValueId>,
) -> MirCall {
pub fn create_mir_call(dst: Option<ValueId>, callee: Callee, args: Vec<ValueId>) -> MirCall {
let effects = compute_call_effects(&callee);
let flags = create_call_flags(&callee);

View File

@ -12,9 +12,9 @@
* - 既知の関数・メソッドのエフェクト知識を集約
*/
use crate::mir::definitions::call_unified::Callee;
use crate::mir::builder::{Effect, EffectMask};
use super::extern_calls;
use crate::mir::builder::{Effect, EffectMask};
use crate::mir::definitions::call_unified::Callee;
/// エフェクト解析専用箱
///
@ -36,16 +36,16 @@ impl EffectsAnalyzerBox {
/// - WriteHeap: ヒープ書き込み
pub fn compute_call_effects(callee: &Callee) -> EffectMask {
match callee {
Callee::Global(name) => {
match name.as_str() {
"print" | "error" => EffectMask::IO,
"panic" | "exit" => EffectMask::IO.add(Effect::Control),
"gc_collect" => EffectMask::IO.add(Effect::Alloc),
_ => EffectMask::IO,
}
Callee::Global(name) => match name.as_str() {
"print" | "error" => EffectMask::IO,
"panic" | "exit" => EffectMask::IO.add(Effect::Control),
"gc_collect" => EffectMask::IO.add(Effect::Alloc),
_ => EffectMask::IO,
},
Callee::Method { method, box_name, .. } => {
Callee::Method {
method, box_name, ..
} => {
match method.as_str() {
"birth" => EffectMask::PURE.add(Effect::Alloc),
"get" | "length" | "size" => EffectMask::READ,
@ -59,7 +59,7 @@ impl EffectsAnalyzerBox {
}
}
}
},
}
Callee::Constructor { .. } => EffectMask::PURE.add(Effect::Alloc),
@ -68,7 +68,7 @@ impl EffectsAnalyzerBox {
Callee::Extern(name) => {
let (iface, method) = extern_calls::parse_extern_name(name);
extern_calls::compute_extern_effects(&iface, &method)
},
}
Callee::Value(_) => EffectMask::IO, // Conservative for dynamic calls
}
@ -100,8 +100,8 @@ impl EffectsAnalyzerBox {
#[cfg(test)]
mod tests {
use super::*;
use crate::mir::ValueId;
use crate::mir::definitions::call_unified::{CalleeBoxKind, TypeCertainty};
use crate::mir::ValueId;
#[test]
fn test_compute_effects_global() {
@ -143,7 +143,10 @@ mod tests {
};
let effects = EffectsAnalyzerBox::compute_call_effects(&callee);
// Constructor should have PURE + Alloc
assert_eq!(effects, EffectMask::PURE.add(crate::mir::builder::Effect::Alloc));
assert_eq!(
effects,
EffectMask::PURE.add(crate::mir::builder::Effect::Alloc)
);
}
#[test]

View File

@ -6,8 +6,8 @@
//! - emit_global_call/emit_method_call/emit_constructor_call: 便利ラッパー
use super::super::{EffectMask, MirBuilder, MirInstruction, ValueId};
use crate::mir::definitions::call_unified::Callee;
use super::CallTarget;
use crate::mir::definitions::call_unified::Callee;
impl MirBuilder {
/// Unified call emission - delegates to UnifiedCallEmitterBox
@ -21,7 +21,6 @@ impl MirBuilder {
super::unified_emitter::UnifiedCallEmitterBox::emit_unified_call(self, dst, target, args)
}
/// Legacy call fallback - preserves existing behavior
pub fn emit_legacy_call(
&mut self,
@ -30,7 +29,11 @@ impl MirBuilder {
args: Vec<ValueId>,
) -> Result<(), String> {
match target {
CallTarget::Method { receiver, method, box_type: _ } => {
CallTarget::Method {
receiver,
method,
box_type: _,
} => {
// LEGACY PATH (after unified migration):
// Instance→Function rewrite is centralized in unified call path.
// Legacy path no longer functionizes; always use Box/Plugin call here.
@ -38,10 +41,11 @@ impl MirBuilder {
// Set flag to prevent emit_box_or_plugin_call from calling emit_unified_call
let prev_flag = self.in_unified_boxcall_fallback;
self.in_unified_boxcall_fallback = true;
let result = self.emit_box_or_plugin_call(dst, receiver, method, None, args, EffectMask::IO);
let result =
self.emit_box_or_plugin_call(dst, receiver, method, None, args, EffectMask::IO);
self.in_unified_boxcall_fallback = prev_flag;
result
},
}
CallTarget::Constructor(box_type) => {
// Use existing NewBox
let dst = dst.ok_or("Constructor must have destination")?;
@ -50,7 +54,7 @@ impl MirBuilder {
box_type,
args,
})
},
}
CallTarget::Extern(name) => {
// Use existing ExternCall
let mut args = args;
@ -69,14 +73,22 @@ impl MirBuilder {
args,
effects: EffectMask::IO,
})
},
}
CallTarget::Global(name) => {
super::unified_emitter::UnifiedCallEmitterBox::emit_global_unified(self, dst, name, args)
},
super::unified_emitter::UnifiedCallEmitterBox::emit_global_unified(
self, dst, name, args,
)
}
CallTarget::Value(func_val) => {
super::unified_emitter::UnifiedCallEmitterBox::emit_value_unified(self, dst, func_val, args)
},
CallTarget::Closure { params, captures, me_capture } => {
super::unified_emitter::UnifiedCallEmitterBox::emit_value_unified(
self, dst, func_val, args,
)
}
CallTarget::Closure {
params,
captures,
me_capture,
} => {
let dst = dst.ok_or("Closure creation must have destination")?;
self.emit_instruction(MirInstruction::NewClosure {
dst,
@ -85,7 +97,7 @@ impl MirBuilder {
captures,
me: me_capture,
})
},
}
}
}
@ -127,11 +139,7 @@ impl MirBuilder {
box_type: String,
args: Vec<ValueId>,
) -> Result<(), String> {
self.emit_unified_call(
Some(dst),
CallTarget::Constructor(box_type),
args,
)
self.emit_unified_call(Some(dst), CallTarget::Constructor(box_type), args)
}
// ========================================
@ -146,7 +154,9 @@ impl MirBuilder {
name: &str,
args: &[ValueId],
) -> Result<Option<()>, String> {
super::materializer::CallMaterializerBox::try_global_fallback_handlers(self, dst, name, args)
super::materializer::CallMaterializerBox::try_global_fallback_handlers(
self, dst, name, args,
)
}
/// Ensure receiver is materialized in Callee::Method (delegates to CallMaterializerBox)

View File

@ -125,18 +125,8 @@ pub fn get_env_method_spec(
)),
// Direct env access
("env", "get") => Some((
"env".to_string(),
"get".to_string(),
EffectMask::READ,
true,
)),
("env", "set") => Some((
"env".to_string(),
"set".to_string(),
EffectMask::IO,
false,
)),
("env", "get") => Some(("env".to_string(), "get".to_string(), EffectMask::READ, true)),
("env", "set") => Some(("env".to_string(), "set".to_string(), EffectMask::IO, false)),
// Unknown
_ => None,
@ -157,9 +147,16 @@ 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" |
"env.canvas" | "env.task" | "env.future" | "env.process"
matches!(
name,
"env"
| "env.console"
| "env.fs"
| "env.net"
| "env.canvas"
| "env.task"
| "env.future"
| "env.process"
)
}
@ -167,13 +164,9 @@ pub fn is_env_interface(name: &str) -> bool {
pub fn compute_extern_effects(iface: &str, method: &str) -> EffectMask {
match (iface, method) {
// Pure reads
(_, m) if m.starts_with("get") || m == "argv" || m == "env" => {
EffectMask::READ
}
(_, m) if m.starts_with("get") || m == "argv" || m == "env" => EffectMask::READ,
// Control flow changes
(_, "exit") | (_, "panic") | (_, "throw") => {
EffectMask::IO.add(Effect::Control)
}
(_, "exit") | (_, "panic") | (_, "throw") => EffectMask::IO.add(Effect::Control),
// Memory allocation
(_, m) if m.starts_with("new") || m.starts_with("create") => {
EffectMask::IO.add(Effect::Alloc)

View File

@ -7,9 +7,9 @@
* Manages the complex state transitions during function lowering
*/
use super::special_handlers::contains_value_return;
use crate::ast::ASTNode;
use crate::mir::{Effect, EffectMask, FunctionSignature, MirType};
use super::special_handlers::contains_value_return;
/// Prepare function signature for a box method
/// Includes 'me' parameter as first parameter
@ -32,7 +32,7 @@ pub fn prepare_method_signature(
// Determine return type based on body analysis
let returns_value = contains_value_return(body);
let ret_ty = if returns_value {
MirType::Unknown // Will be inferred later
MirType::Unknown // Will be inferred later
} else {
MirType::Void
};
@ -62,7 +62,7 @@ pub fn prepare_static_method_signature(
// Determine return type based on body analysis
let returns_value = contains_value_return(body);
let ret_ty = if returns_value {
MirType::Unknown // Will be inferred later
MirType::Unknown // Will be inferred later
} else {
MirType::Void
};
@ -77,11 +77,7 @@ pub fn prepare_static_method_signature(
/// Generate canonical method name for MIR function
/// E.g., "StringBox.upper/0" for StringBox's upper method with 0 args
pub fn generate_method_function_name(
box_name: &str,
method_name: &str,
arity: usize,
) -> String {
pub fn generate_method_function_name(box_name: &str, method_name: &str, arity: usize) -> String {
format!("{}.{}/{}", box_name, method_name, arity)
}
@ -120,10 +116,9 @@ pub fn create_method_parameter_mapping(
}
/// Create parameter mapping for static method lowering
pub fn create_static_parameter_mapping(
params: &[String],
) -> Vec<(String, MirType)> {
params.iter()
pub fn create_static_parameter_mapping(params: &[String]) -> Vec<(String, MirType)> {
params
.iter()
.map(|p| (p.clone(), MirType::Unknown))
.collect()
}
@ -139,14 +134,24 @@ pub fn wrap_in_program(statements: Vec<ASTNode>) -> ASTNode {
/// Check if method name suggests it returns a value
pub fn method_likely_returns_value(method_name: &str) -> bool {
// Heuristic: methods that likely return values
method_name.starts_with("get") ||
method_name.starts_with("is") ||
method_name.starts_with("has") ||
method_name.starts_with("to") ||
matches!(method_name,
"length" | "size" | "count" |
"upper" | "lower" | "trim" |
"add" | "sub" | "mul" | "div" |
"min" | "max" | "abs"
)
method_name.starts_with("get")
|| method_name.starts_with("is")
|| method_name.starts_with("has")
|| method_name.starts_with("to")
|| matches!(
method_name,
"length"
| "size"
| "count"
| "upper"
| "lower"
| "trim"
| "add"
| "sub"
| "mul"
| "div"
| "min"
| "max"
| "abs"
)
}

View File

@ -12,8 +12,8 @@
* - receiver実体化の保証
*/
use crate::mir::{Callee, MirType, ValueId};
use crate::mir::definitions::call_unified::CalleeBoxKind;
use crate::mir::{Callee, MirType, ValueId};
use std::collections::HashMap;
/// 構造ガード専用箱
@ -49,31 +49,43 @@ impl<'a> CalleeGuardBox<'a> {
/// - StageBArgsBox.resolve_src内のargs.get(i)がStage1UsingResolverBox.getに
/// 化けるのを防ぐargs型はMapBox/ArrayBox → 正規化)
pub fn apply_static_runtime_guard(&self, callee: Callee) -> Result<Callee, String> {
if let Callee::Method { ref box_name, ref method, receiver: Some(recv), certainty, box_kind } = callee {
if let Callee::Method {
ref box_name,
ref method,
receiver: Some(recv),
certainty,
box_kind,
} = callee
{
// Only apply guard if box_kind is StaticCompiler
if box_kind == CalleeBoxKind::StaticCompiler {
// Check if receiver has a Box type
if let Some(MirType::Box(receiver_box)) = self.value_types.get(&recv) {
let trace_enabled = std::env::var("NYASH_CALLEE_RESOLVE_TRACE")
.ok()
.as_deref() == Some("1");
let trace_enabled =
std::env::var("NYASH_CALLEE_RESOLVE_TRACE").ok().as_deref() == Some("1");
// If receiver box type matches the static box name, this is a me-call
// Let it through for static method lowering (don't normalize)
if receiver_box == box_name {
if trace_enabled {
eprintln!("[static-runtime-guard] ME-CALL detected:");
eprintln!(" {}.{} with receiver type: {} (same as box_name)", box_name, method, receiver_box);
eprintln!(
" {}.{} with receiver type: {} (same as box_name)",
box_name, method, receiver_box
);
eprintln!(" → Allowing for static method lowering");
}
return Ok(callee); // Pass through unchanged
return Ok(callee); // Pass through unchanged
}
// Otherwise, this is a true mix-up: runtime box with static box name
// Normalize to the runtime box type
if trace_enabled {
eprintln!("[static-runtime-guard] CORRECTING mix-up:");
eprintln!(" Original: {}.{} (box_kind=StaticCompiler)", box_name, method);
eprintln!(
" Original: {}.{} (box_kind=StaticCompiler)",
box_name, method
);
eprintln!(" Receiver %{} has runtime type: {}", recv.0, receiver_box);
eprintln!(" Normalized: {}.{}", receiver_box, method);
}
@ -83,7 +95,7 @@ impl<'a> CalleeGuardBox<'a> {
method: method.clone(),
receiver: Some(recv),
certainty,
box_kind: CalleeBoxKind::RuntimeData, // Switch to runtime
box_kind: CalleeBoxKind::RuntimeData, // Switch to runtime
});
}
}
@ -188,7 +200,9 @@ mod tests {
// Mix-up → should normalize to MapBox
let result = guard.apply_static_runtime_guard(callee).unwrap();
match result {
Callee::Method { box_name, box_kind, .. } => {
Callee::Method {
box_name, box_kind, ..
} => {
assert_eq!(box_name, "MapBox");
assert_eq!(box_kind, CalleeBoxKind::RuntimeData);
}

View File

@ -5,11 +5,11 @@
//! - BoxCompilationContext による完全独立化
//! - パラメータ・型情報の適切な管理
use crate::ast::ASTNode;
use crate::mir::builder::{MirBuilder, MirType, MirInstruction};
use crate::mir::{MirValueKind, ValueId}; // Phase 26-A-3: ValueId型安全化
use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
use super::function_lowering;
use crate::ast::ASTNode;
use crate::mir::builder::{MirBuilder, MirInstruction, MirType};
use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
use crate::mir::{MirValueKind, ValueId}; // Phase 26-A-3: ValueId型安全化
use std::collections::HashMap;
/// 🎯 箱理論: Lowering Context準備と復元
@ -24,10 +24,7 @@ struct LoweringContext {
impl MirBuilder {
/// 🎯 箱理論: Step 1 - Lowering Context準備
fn prepare_lowering_context(
&mut self,
func_name: &str,
) -> LoweringContext {
fn prepare_lowering_context(&mut self, func_name: &str) -> LoweringContext {
// Static box context設定
let saved_static_ctx = self.current_static_box.clone();
if let Some(pos) = func_name.find('.') {
@ -77,11 +74,8 @@ impl MirBuilder {
body: &[ASTNode],
ctx: &mut LoweringContext,
) -> Result<(), String> {
let signature = function_lowering::prepare_static_method_signature(
func_name.clone(),
params,
body,
);
let signature =
function_lowering::prepare_static_method_signature(func_name.clone(), params, body);
let entry = self.block_gen.next();
let function = super::super::MirFunction::new(signature, entry);
@ -89,7 +83,10 @@ impl MirBuilder {
ctx.saved_function = self.current_function.take();
ctx.saved_block = self.current_block.take();
eprintln!("[DEBUG/create_function_skeleton] Creating function: {}", func_name);
eprintln!(
"[DEBUG/create_function_skeleton] Creating function: {}",
func_name
);
eprintln!("[DEBUG/create_function_skeleton] Entry block: {:?}", entry);
// 新しい関数に切り替え
@ -116,9 +113,15 @@ impl MirBuilder {
if let Some(ref mut f) = self.current_function {
// 📦 Hotfix 5: Use pre-populated params from MirFunction::new()
// Static methods have implicit receiver at params[0], so actual parameters start at offset
let receiver_offset = if f.params.is_empty() { 0 } else {
let receiver_offset = if f.params.is_empty() {
0
} else {
// If params already populated (by Hotfix 4+5), use them
if f.params.len() > params.len() { 1 } else { 0 }
if f.params.len() > params.len() {
1
} else {
0
}
};
let param_types = f.signature.params.clone();
@ -186,8 +189,7 @@ impl MirBuilder {
// 型推論
if let Some(ref mut f) = self.current_function {
if returns_value
&& matches!(f.signature.return_type, MirType::Void | MirType::Unknown)
if returns_value && matches!(f.signature.return_type, MirType::Void | MirType::Unknown)
{
let mut inferred: Option<MirType> = None;
'search: for (_bid, bb) in f.blocks.iter() {
@ -254,12 +256,8 @@ impl MirBuilder {
body: &[ASTNode],
ctx: &mut LoweringContext,
) -> Result<(), String> {
let signature = function_lowering::prepare_method_signature(
func_name,
box_name,
params,
body,
);
let signature =
function_lowering::prepare_method_signature(func_name, box_name, params, body);
let entry = self.block_gen.next();
let function = super::super::MirFunction::new(signature, entry);
@ -418,8 +416,7 @@ impl MirBuilder {
// 型推論Step 5の一部として
if let Some(ref mut f) = self.current_function {
if returns_value
&& matches!(f.signature.return_type, MirType::Void | MirType::Unknown)
if returns_value && matches!(f.signature.return_type, MirType::Void | MirType::Unknown)
{
let mut inferred: Option<MirType> = None;
'search: for (_bid, bb) in f.blocks.iter() {

View File

@ -12,7 +12,7 @@
* - Call発行前の準備処理全般
*/
use crate::mir::builder::{MirBuilder, ValueId, MirInstruction, EffectMask};
use crate::mir::builder::{EffectMask, MirBuilder, MirInstruction, ValueId};
use crate::mir::definitions::call_unified::Callee;
/// Call前処理・準備専用箱
@ -43,11 +43,17 @@ impl CallMaterializerBox {
let one = crate::mir::builder::emission::constant::emit_integer(builder, 1);
if dst.is_none() {
// If a destination was not provided, copy into the allocated dstv
builder.emit_instruction(MirInstruction::Copy { dst: dstv, src: one })?;
builder.emit_instruction(MirInstruction::Copy {
dst: dstv,
src: one,
})?;
crate::mir::builder::metadata::propagate::propagate(builder, one, dstv);
} else {
// If caller provided dst, ensure the computed value lands there
builder.emit_instruction(MirInstruction::Copy { dst: dstv, src: one })?;
builder.emit_instruction(MirInstruction::Copy {
dst: dstv,
src: one,
})?;
crate::mir::builder::metadata::propagate::propagate(builder, one, dstv);
}
return Ok(Some(()));
@ -57,7 +63,8 @@ impl CallMaterializerBox {
if let Some(ref module) = builder.current_module {
if module.functions.contains_key(name) {
let dstv = dst.unwrap_or_else(|| builder.next_value_id());
let name_const = crate::mir::builder::name_const::make_name_const_result(builder, name)?;
let name_const =
crate::mir::builder::name_const::make_name_const_result(builder, name)?;
builder.emit_instruction(MirInstruction::Call {
dst: Some(dstv),
func: name_const,
@ -82,7 +89,8 @@ impl CallMaterializerBox {
let func_name = format!("{}.{}{}", bx, name, format!("/{}", args.len()));
// Emit legacy call directly to preserve behavior
let dstv = dst.unwrap_or_else(|| builder.next_value_id());
let name_const = crate::mir::builder::name_const::make_name_const_result(builder, &func_name)?;
let name_const =
crate::mir::builder::name_const::make_name_const_result(builder, &func_name)?;
builder.emit_instruction(MirInstruction::Call {
dst: Some(dstv),
func: name_const,

View File

@ -53,12 +53,17 @@ pub fn resolve_call_target(
// or report a clear unresolved error.
// 6. Resolution failed - prevent runtime string-based resolution
Err(format!("Unresolved function: '{}'. {}", name, suggest_resolution(name)))
Err(format!(
"Unresolved function: '{}'. {}",
name,
suggest_resolution(name)
))
}
/// Check if function name is a built-in global function
pub fn is_builtin_function(name: &str) -> bool {
matches!(name,
matches!(
name,
"print" | "error" | "panic" | "exit" | "now" |
"gc_collect" | "gc_stats" |
// Math functions (handled specially)
@ -68,9 +73,7 @@ pub fn is_builtin_function(name: &str) -> bool {
/// Check if function name is an external/host function
pub fn is_extern_function(name: &str) -> bool {
name.starts_with("nyash.") ||
name.starts_with("env.") ||
name.starts_with("system.")
name.starts_with("nyash.") || name.starts_with("env.") || name.starts_with("system.")
}
/// Check if method is commonly shadowed (for warning generation)
@ -90,18 +93,12 @@ pub fn generate_self_recursion_warning(box_name: &str, method: &str) -> String {
/// Suggest resolution for unresolved function
pub fn suggest_resolution(name: &str) -> String {
match name {
n if n.starts_with("console") => {
"Did you mean 'env.console.log' or 'print'?".to_string()
}
"log" | "println" => {
"Did you mean 'print' or 'env.console.log'?".to_string()
}
n if n.starts_with("console") => "Did you mean 'env.console.log' or 'print'?".to_string(),
"log" | "println" => "Did you mean 'print' or 'env.console.log'?".to_string(),
n if n.contains('.') => {
"Qualified names should use 'env.' prefix for external calls.".to_string()
}
_ => {
"Check function name or ensure it's in scope.".to_string()
}
_ => "Check function name or ensure it's in scope.".to_string(),
}
}

View File

@ -17,24 +17,24 @@ pub mod method_resolution;
pub mod special_handlers;
// New refactored modules (Box Theory Phase 1 & 2 & 25.1d & Phase 3)
pub mod lowering;
pub mod utils;
pub mod emit; // Phase 2: Call emission
pub mod build; // Phase 2: Call building
pub mod guard; // Phase 25.1d: Structural guard (static/runtime box separation)
pub mod resolver; // Phase 25.1d: Callee resolution (CallTarget → Callee)
pub mod unified_emitter; // Phase 3-A: Unified call emitter (統一Call発行専用箱)
pub mod build; // Phase 2: Call building
pub mod effects_analyzer; // Phase 3-B: Effects analyzer (エフェクト解析専用箱)
pub mod materializer; // Phase 3-C: Call materializer (Call前処理・準備専用箱)
pub mod emit; // Phase 2: Call emission
pub mod guard; // Phase 25.1d: Structural guard (static/runtime box separation)
pub mod lowering;
pub mod materializer;
pub mod resolver; // Phase 25.1d: Callee resolution (CallTarget → Callee)
pub mod unified_emitter; // Phase 3-A: Unified call emitter (統一Call発行専用箱)
pub mod utils; // Phase 3-C: Call materializer (Call前処理・準備専用箱)
// Re-export public interfaces
#[allow(unused_imports)]
pub use build::*;
#[allow(unused_imports)]
pub use call_target::CallTarget;
#[allow(unused_imports)]
pub use emit::*;
#[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

@ -12,11 +12,11 @@
* - Call引数検証
*/
use crate::mir::{Callee, MirType, ValueId};
use super::method_resolution;
use crate::mir::builder::type_registry::TypeRegistry;
use crate::mir::builder::CallTarget;
use crate::mir::definitions::call_unified::{CalleeBoxKind, TypeCertainty};
use crate::mir::builder::type_registry::TypeRegistry;
use super::method_resolution;
use crate::mir::{Callee, MirType, ValueId};
use std::collections::HashMap;
/// Callee解決専用箱
@ -56,9 +56,7 @@ impl<'a> CalleeResolverBox<'a> {
///
/// 🎯 TypeRegistry対応: NYASH_USE_TYPE_REGISTRY=1 で registry 優先
pub fn resolve(&self, target: CallTarget) -> Result<Callee, String> {
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY")
.ok()
.as_deref() == Some("1");
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY").ok().as_deref() == Some("1");
match target {
CallTarget::Global(name) => {
@ -71,27 +69,36 @@ impl<'a> CalleeResolverBox<'a> {
// Module-local or static lowered function (e.g., "Box.method/N")
Ok(Callee::Global(name))
}
},
}
CallTarget::Method { box_type, method, receiver } => {
CallTarget::Method {
box_type,
method,
receiver,
} => {
// 🔍 Debug: trace box_name resolution
let trace_enabled = std::env::var("NYASH_CALLEE_RESOLVE_TRACE")
.ok()
.as_deref() == Some("1");
let trace_enabled =
std::env::var("NYASH_CALLEE_RESOLVE_TRACE").ok().as_deref() == Some("1");
if trace_enabled {
eprintln!("[callee-resolve] receiver=%{} method={}", receiver.0, method);
eprintln!(
"[callee-resolve] receiver=%{} method={}",
receiver.0, method
);
eprintln!("[callee-resolve] explicit box_type: {:?}", box_type);
eprintln!("[callee-resolve] use_registry: {}", use_registry);
}
let inferred_box_type = self.infer_box_type(receiver, box_type, trace_enabled, use_registry);
let inferred_box_type =
self.infer_box_type(receiver, box_type, trace_enabled, use_registry);
// Certainty is Known when we have explicit origin or Box型の型情報を持つ場合
let has_box_type = self.value_types
let has_box_type = self
.value_types
.get(&receiver)
.map(|t| matches!(t, MirType::Box(_)))
.unwrap_or(false);
let certainty = if self.value_origin_newbox.contains_key(&receiver) || has_box_type {
let certainty = if self.value_origin_newbox.contains_key(&receiver) || has_box_type
{
TypeCertainty::Known
} else {
TypeCertainty::Union
@ -101,7 +108,10 @@ impl<'a> CalleeResolverBox<'a> {
let box_kind = self.classify_box_kind(&inferred_box_type);
if trace_enabled {
eprintln!("[callee-resolve] inferred_box_name: {}", inferred_box_type);
eprintln!(
"[callee-resolve] inferred_box_name: {}",
inferred_box_type
);
eprintln!("[callee-resolve] box_kind: {:?}", box_kind);
}
@ -112,23 +122,23 @@ impl<'a> CalleeResolverBox<'a> {
certainty,
box_kind,
})
},
}
CallTarget::Constructor(box_type) => {
Ok(Callee::Constructor { box_type })
},
CallTarget::Constructor(box_type) => Ok(Callee::Constructor { box_type }),
CallTarget::Extern(name) => {
Ok(Callee::Extern(name))
},
CallTarget::Extern(name) => Ok(Callee::Extern(name)),
CallTarget::Value(func_val) => {
Ok(Callee::Value(func_val))
},
CallTarget::Value(func_val) => Ok(Callee::Value(func_val)),
CallTarget::Closure { params, captures, me_capture } => {
Ok(Callee::Closure { params, captures, me_capture })
},
CallTarget::Closure {
params,
captures,
me_capture,
} => Ok(Callee::Closure {
params,
captures,
me_capture,
}),
}
}
@ -183,14 +193,18 @@ impl<'a> CalleeResolverBox<'a> {
}
"exit" => {
if args.len() != 1 {
return Err("exit requires exactly one argument (exit code)".to_string());
return Err(
"exit requires exactly one argument (exit code)".to_string()
);
}
}
_ => {} // Unknown functions pass through
}
},
}
Callee::Method { box_name, method, .. } => {
Callee::Method {
box_name, method, ..
} => {
// Validate known methods
match (box_name.as_str(), method.as_str()) {
("ArrayBox", "get") | ("ArrayBox", "set") => {
@ -200,7 +214,7 @@ impl<'a> CalleeResolverBox<'a> {
}
_ => {} // Unknown methods pass through
}
},
}
_ => {} // Other callee types don't have validation yet
}
@ -240,12 +254,10 @@ impl<'a> CalleeResolverBox<'a> {
}
// 従来: HashMap から推論型情報を優先し、origin は補助とする)
let from_type = self.value_types
.get(&receiver)
.and_then(|t| match t {
MirType::Box(box_name) => Some(box_name.clone()),
_ => None,
});
let from_type = self.value_types.get(&receiver).and_then(|t| match t {
MirType::Box(box_name) => Some(box_name.clone()),
_ => None,
});
let from_origin = self.value_origin_newbox.get(&receiver).cloned();
if trace_enabled {
@ -254,14 +266,12 @@ impl<'a> CalleeResolverBox<'a> {
}
// 型情報MirTypeがある場合はそれを優先し、無い場合のみ origin にフォールバックする。
from_type
.or(from_origin)
.unwrap_or_else(|| {
if trace_enabled {
eprintln!("[callee-resolve] FALLBACK: UnknownBox");
}
"UnknownBox".to_string()
})
from_type.or(from_origin).unwrap_or_else(|| {
if trace_enabled {
eprintln!("[callee-resolve] FALLBACK: UnknownBox");
}
"UnknownBox".to_string()
})
})
}
}
@ -277,14 +287,26 @@ mod tests {
let resolver = CalleeResolverBox::new(&value_origin, &value_types, None);
// Stage-B boxes
assert_eq!(resolver.classify_box_kind("StageBArgsBox"), CalleeBoxKind::StaticCompiler);
assert_eq!(resolver.classify_box_kind("StageBDriverBox"), CalleeBoxKind::StaticCompiler);
assert_eq!(
resolver.classify_box_kind("StageBArgsBox"),
CalleeBoxKind::StaticCompiler
);
assert_eq!(
resolver.classify_box_kind("StageBDriverBox"),
CalleeBoxKind::StaticCompiler
);
// Stage-1 boxes
assert_eq!(resolver.classify_box_kind("Stage1UsingResolverBox"), CalleeBoxKind::StaticCompiler);
assert_eq!(
resolver.classify_box_kind("Stage1UsingResolverBox"),
CalleeBoxKind::StaticCompiler
);
// Parser boxes
assert_eq!(resolver.classify_box_kind("ParserBox"), CalleeBoxKind::StaticCompiler);
assert_eq!(
resolver.classify_box_kind("ParserBox"),
CalleeBoxKind::StaticCompiler
);
}
#[test]
@ -293,10 +315,22 @@ mod tests {
let value_types = HashMap::new();
let resolver = CalleeResolverBox::new(&value_origin, &value_types, None);
assert_eq!(resolver.classify_box_kind("MapBox"), CalleeBoxKind::RuntimeData);
assert_eq!(resolver.classify_box_kind("ArrayBox"), CalleeBoxKind::RuntimeData);
assert_eq!(resolver.classify_box_kind("StringBox"), CalleeBoxKind::RuntimeData);
assert_eq!(resolver.classify_box_kind("UnknownBox"), CalleeBoxKind::RuntimeData);
assert_eq!(
resolver.classify_box_kind("MapBox"),
CalleeBoxKind::RuntimeData
);
assert_eq!(
resolver.classify_box_kind("ArrayBox"),
CalleeBoxKind::RuntimeData
);
assert_eq!(
resolver.classify_box_kind("StringBox"),
CalleeBoxKind::RuntimeData
);
assert_eq!(
resolver.classify_box_kind("UnknownBox"),
CalleeBoxKind::RuntimeData
);
}
#[test]
@ -305,8 +339,14 @@ mod tests {
let value_types = HashMap::new();
let resolver = CalleeResolverBox::new(&value_origin, &value_types, None);
assert_eq!(resolver.classify_box_kind("MyCustomBox"), CalleeBoxKind::UserDefined);
assert_eq!(resolver.classify_box_kind("PersonBox"), CalleeBoxKind::UserDefined);
assert_eq!(
resolver.classify_box_kind("MyCustomBox"),
CalleeBoxKind::UserDefined
);
assert_eq!(
resolver.classify_box_kind("PersonBox"),
CalleeBoxKind::UserDefined
);
}
#[test]

View File

@ -12,7 +12,10 @@ use crate::mir::MirType;
/// Check if a function is a math function
pub fn is_math_function(name: &str) -> bool {
matches!(name, "sin" | "cos" | "abs" | "min" | "max" | "sqrt" | "pow" | "floor" | "ceil")
matches!(
name,
"sin" | "cos" | "abs" | "min" | "max" | "sqrt" | "pow" | "floor" | "ceil"
)
}
/// Check if a method is a type operation (.is() or .as())
@ -62,11 +65,17 @@ pub fn parse_type_name_to_mir(name: &str) -> MirType {
/// Check if a value is a numeric literal or numeric Box constructor
pub fn is_numeric_value(node: &ASTNode) -> bool {
match node {
ASTNode::Literal { value: LiteralValue::Integer(_), .. } => true,
ASTNode::Literal { value: LiteralValue::Float(_), .. } => true,
ASTNode::New { class, arguments, .. } => {
(class == "IntegerBox" || class == "FloatBox") && arguments.len() == 1
}
ASTNode::Literal {
value: LiteralValue::Integer(_),
..
} => true,
ASTNode::Literal {
value: LiteralValue::Float(_),
..
} => true,
ASTNode::New {
class, arguments, ..
} => (class == "IntegerBox" || class == "FloatBox") && arguments.len() == 1,
_ => false,
}
}
@ -74,8 +83,14 @@ pub fn is_numeric_value(node: &ASTNode) -> bool {
/// Extract numeric type from AST node
pub fn extract_numeric_type(node: &ASTNode) -> Option<MirType> {
match node {
ASTNode::Literal { value: LiteralValue::Integer(_), .. } => Some(MirType::Integer),
ASTNode::Literal { value: LiteralValue::Float(_), .. } => Some(MirType::Float),
ASTNode::Literal {
value: LiteralValue::Integer(_),
..
} => Some(MirType::Integer),
ASTNode::Literal {
value: LiteralValue::Float(_),
..
} => Some(MirType::Float),
ASTNode::New { class, .. } if class == "IntegerBox" => Some(MirType::Integer),
ASTNode::New { class, .. } if class == "FloatBox" => Some(MirType::Float),
_ => None,
@ -87,7 +102,11 @@ pub fn contains_value_return(nodes: &[ASTNode]) -> bool {
fn node_has_value_return(node: &ASTNode) -> bool {
match node {
ASTNode::Return { value: Some(_), .. } => true,
ASTNode::If { then_body, else_body, .. } => {
ASTNode::If {
then_body,
else_body,
..
} => {
contains_value_return(then_body)
|| else_body
.as_ref()
@ -125,9 +144,9 @@ pub fn make_function_name_with_arity(base_name: &str, arity: usize) -> String {
/// Check if a name is a reserved/special function
pub fn is_reserved_function(name: &str) -> bool {
matches!(name,
"birth" | "me" | "this" | "super" | "from" |
"new" | "delete" | "typeof" | "instanceof"
matches!(
name,
"birth" | "me" | "this" | "super" | "from" | "new" | "delete" | "typeof" | "instanceof"
)
}

View File

@ -13,10 +13,10 @@
* - emit_value_unified: 第一級関数呼び出し
*/
use crate::mir::builder::{MirBuilder, ValueId, MirInstruction, Effect, EffectMask};
use crate::mir::definitions::call_unified::Callee;
use super::CallTarget;
use super::call_unified;
use super::CallTarget;
use crate::mir::builder::{Effect, EffectMask, MirBuilder, MirInstruction, ValueId};
use crate::mir::definitions::call_unified::Callee;
/// 統一Call発行専用箱
///
@ -39,10 +39,16 @@ impl UnifiedCallEmitterBox {
const MAX_EMIT_DEPTH: usize = 100;
builder.recursion_depth += 1;
if builder.recursion_depth > MAX_EMIT_DEPTH {
eprintln!("[FATAL] emit_unified_call recursion depth exceeded {}", MAX_EMIT_DEPTH);
eprintln!(
"[FATAL] emit_unified_call recursion depth exceeded {}",
MAX_EMIT_DEPTH
);
eprintln!("[FATAL] Current depth: {}", builder.recursion_depth);
eprintln!("[FATAL] Target: {:?}", target);
return Err(format!("emit_unified_call recursion depth exceeded: {}", builder.recursion_depth));
return Err(format!(
"emit_unified_call recursion depth exceeded: {}",
builder.recursion_depth
));
}
// Check environment variable for unified call usage
@ -62,11 +68,16 @@ impl UnifiedCallEmitterBox {
target: CallTarget,
args: Vec<ValueId>,
) -> Result<(), String> {
// Emit resolve.try for method targets (dev-only; default OFF)
let arity_for_try = args.len();
if let CallTarget::Method { ref box_type, ref method, receiver } = target {
let recv_cls = box_type.clone()
if let CallTarget::Method {
ref box_type,
ref method,
receiver,
} = target
{
let recv_cls = box_type
.clone()
.or_else(|| builder.value_origin_newbox.get(&receiver).cloned())
.unwrap_or_default();
// Use indexed candidate lookup (tail → names)
@ -81,22 +92,60 @@ impl UnifiedCallEmitterBox {
}
// Centralized user-box rewrite for method targets (toString/stringify, equals/1, Known→unique)
if let CallTarget::Method { ref box_type, ref method, receiver } = target {
let class_name_opt = box_type.clone()
if let CallTarget::Method {
ref box_type,
ref method,
receiver,
} = target
{
let class_name_opt = box_type
.clone()
.or_else(|| builder.value_origin_newbox.get(&receiver).cloned())
.or_else(|| builder.value_types.get(&receiver).and_then(|t| if let crate::mir::MirType::Box(b) = t { Some(b.clone()) } else { None }));
.or_else(|| {
builder.value_types.get(&receiver).and_then(|t| {
if let crate::mir::MirType::Box(b) = t {
Some(b.clone())
} else {
None
}
})
});
// Early str-like
if let Some(res) = crate::mir::builder::rewrite::special::try_early_str_like_to_dst(
builder, dst, receiver, &class_name_opt, method, args.len(),
) { res?; return Ok(()); }
builder,
dst,
receiver,
&class_name_opt,
method,
args.len(),
) {
res?;
return Ok(());
}
// equals/1
if let Some(res) = crate::mir::builder::rewrite::special::try_special_equals_to_dst(
builder, dst, receiver, &class_name_opt, method, args.clone(),
) { res?; return Ok(()); }
builder,
dst,
receiver,
&class_name_opt,
method,
args.clone(),
) {
res?;
return Ok(());
}
// Known or unique
if let Some(res) = crate::mir::builder::rewrite::known::try_known_or_unique_to_dst(
builder, dst, receiver, &class_name_opt, method, args.clone(),
) { res?; return Ok(()); }
builder,
dst,
receiver,
&class_name_opt,
method,
args.clone(),
) {
res?;
return Ok(());
}
}
// Convert CallTarget to Callee using CalleeResolverBox
@ -105,14 +154,18 @@ impl UnifiedCallEmitterBox {
let resolver = super::resolver::CalleeResolverBox::new(
&builder.value_origin_newbox,
&builder.value_types,
Some(&builder.type_registry), // 🎯 TypeRegistry を渡す
Some(&builder.type_registry), // 🎯 TypeRegistry を渡す
);
let mut callee = match resolver.resolve(target.clone()) {
Ok(c) => c,
Err(e) => {
if let CallTarget::Global(ref name) = target {
// Try fallback handlers (via CallMaterializerBox)
if let Some(result) = super::materializer::CallMaterializerBox::try_global_fallback_handlers(builder, dst, name, &args)? {
if let Some(result) =
super::materializer::CallMaterializerBox::try_global_fallback_handlers(
builder, dst, name, &args,
)?
{
return Ok(result);
}
}
@ -121,7 +174,9 @@ impl UnifiedCallEmitterBox {
};
// Safety: ensure receiver is materialized even after callee conversion (via CallMaterializerBox)
callee = super::materializer::CallMaterializerBox::materialize_receiver_in_callee(builder, callee)?;
callee = super::materializer::CallMaterializerBox::materialize_receiver_in_callee(
builder, callee,
)?;
// Structural guard: prevent static compiler boxes from being called with runtime receivers
// 箱理論: CalleeGuardBox による構造的分離
@ -129,7 +184,13 @@ impl UnifiedCallEmitterBox {
callee = guard.apply_static_runtime_guard(callee)?;
// Emit resolve.choose for method callee (dev-only; default OFF)
if let Callee::Method { box_name, method, certainty, .. } = &callee {
if let Callee::Method {
box_name,
method,
certainty,
..
} = &callee
{
let chosen = format!("{}.{}{}", box_name, method, format!("/{}", arity_for_try));
let meta = serde_json::json!({
"recv_cls": box_name,
@ -152,11 +213,28 @@ impl UnifiedCallEmitterBox {
resolver.validate_args(&callee, &args)?;
// Stability guard: decide route via RouterPolicyBox (behavior-preserving rules)
if let Callee::Method { box_name, method, receiver: Some(r), certainty, .. } = &callee {
let route = crate::mir::builder::router::policy::choose_route(box_name, method, *certainty, arity_for_try);
if let Callee::Method {
box_name,
method,
receiver: Some(r),
certainty,
..
} = &callee
{
let route = crate::mir::builder::router::policy::choose_route(
box_name,
method,
*certainty,
arity_for_try,
);
if let crate::mir::builder::router::policy::Route::BoxCall = route {
if crate::mir::builder::utils::builder_debug_enabled() || std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
eprintln!("[router-guard] {}.{} → BoxCall fallback (recv=%{})", box_name, method, r.0);
if crate::mir::builder::utils::builder_debug_enabled()
|| std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1")
{
eprintln!(
"[router-guard] {}.{} → BoxCall fallback (recv=%{})",
box_name, method, r.0
);
}
let effects = EffectMask::READ.add(Effect::ReadHeap);
// Prevent BoxCall helper from bouncing back into emit_unified_call
@ -166,7 +244,8 @@ impl UnifiedCallEmitterBox {
// would otherwise choose Unified.
let prev_flag = builder.in_unified_boxcall_fallback;
builder.in_unified_boxcall_fallback = true;
let res = builder.emit_box_or_plugin_call(dst, *r, method.clone(), None, args, effects);
let res =
builder.emit_box_or_plugin_call(dst, *r, method.clone(), None, args, effects);
builder.in_unified_boxcall_fallback = prev_flag;
return res;
}
@ -175,13 +254,21 @@ impl UnifiedCallEmitterBox {
// Finalize operands in current block (EmitGuardBox wrapper)
let mut callee = callee;
let mut args_local: Vec<ValueId> = args;
crate::mir::builder::emit_guard::finalize_call_operands(builder, &mut callee, &mut args_local);
crate::mir::builder::emit_guard::finalize_call_operands(
builder,
&mut callee,
&mut args_local,
);
// 📦 Hotfix 7: Include receiver in args for Callee::Method
// VM's exec_function_inner expects receiver as the first parameter (ValueId(0))
// but finalize_call_operands keeps receiver in Callee, not in args.
// We must add it to args_local here so VM can bind it correctly.
if let Callee::Method { receiver: Some(recv), .. } = &callee {
if let Callee::Method {
receiver: Some(recv),
..
} = &callee
{
args_local.insert(0, *recv);
}
@ -189,11 +276,21 @@ impl UnifiedCallEmitterBox {
let mir_call = call_unified::create_mir_call(dst, callee.clone(), args_local.clone());
// Dev trace: show final callee/recv right before emission (guarded)
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") || crate::mir::builder::utils::builder_debug_enabled() {
if let Callee::Method { method, receiver, box_name, .. } = &callee {
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1")
|| crate::mir::builder::utils::builder_debug_enabled()
{
if let Callee::Method {
method,
receiver,
box_name,
..
} = &callee
{
if let Some(r) = receiver {
eprintln!("[vm-call-final] bb={:?} method={} recv=%{} class={}",
builder.current_block, method, r.0, box_name);
eprintln!(
"[vm-call-final] bb={:?} method={} recv=%{} class={}",
builder.current_block, method, r.0, box_name
);
}
}
}
@ -223,7 +320,11 @@ impl UnifiedCallEmitterBox {
// Create a string constant for the function name via NameConstBox
let name_const = crate::mir::builder::name_const::make_name_const_result(builder, &name)?;
// Allocate a destination if not provided so we can annotate it
let actual_dst = if let Some(d) = dst { d } else { builder.next_value_id() };
let actual_dst = if let Some(d) = dst {
d
} else {
builder.next_value_id()
};
let mut args = args;
crate::mir::builder::ssa::local::finalize_args(builder, &mut args);
builder.emit_instruction(MirInstruction::Call {