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:
@ -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)
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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"
|
||||
)
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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::*;
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user