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

@ -39,7 +39,10 @@ pub fn suggest_resolution(name: &str) -> String {
"Consider using ArrayBox methods or array.* functions".to_string()
}
_ => {
format!("Function '{}' not found. Check spelling or add explicit scope qualifier", name)
format!(
"Function '{}' not found. Check spelling or add explicit scope qualifier",
name
)
}
}
}
@ -53,7 +56,7 @@ pub fn is_commonly_shadowed_method(method: &str) -> bool {
"print" | "error" | "log" | "panic" | // Console methods
"length" | "size" | "count" | // Container methods
"toString" | "valueOf" | // Conversion methods
"equals" | "compare" // Comparison methods
"equals" | "compare" // Comparison methods
)
}

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 {

View File

@ -88,7 +88,8 @@ mod tests {
fn test_size_info() {
let mut ctx = BoxCompilationContext::new();
ctx.variable_map.insert("a".to_string(), ValueId::new(1));
ctx.value_origin_newbox.insert(ValueId::new(2), "StringBox".to_string());
ctx.value_origin_newbox
.insert(ValueId::new(2), "StringBox".to_string());
ctx.value_types.insert(ValueId::new(3), MirType::Integer);
let (vars, origins, types) = ctx.size_info();

View File

@ -148,7 +148,9 @@ impl super::MirBuilder {
// Exit block
self.start_new_block(exit_block)?;
let result = if self.return_deferred_emitted && !cleanup_terminated {
self.emit_instruction(MirInstruction::Return { value: Some(ret_slot) })?;
self.emit_instruction(MirInstruction::Return {
value: Some(ret_slot),
})?;
crate::mir::builder::emission::constant::emit_void(self)
} else {
crate::mir::builder::emission::constant::emit_void(self)

View File

@ -28,26 +28,34 @@ impl super::MirBuilder {
self.current_static_box = Some(box_name.clone());
// Look for the main() method
let out = if let Some(main_method) = methods.get("main") {
if let ASTNode::FunctionDeclaration { params, body, .. } = main_method {
// Optional: materialize a callable function entry "BoxName.main/N" for harness/PyVM.
// This static entryは通常の VM 実行では使用されず、過去の Hotfix 4 絡みの loop/control-flow
// バグの温床になっていたため、Phase 25.1m では明示トグルが立っている場合だけ生成する。
if std::env::var("NYASH_BUILD_STATIC_MAIN_ENTRY")
.ok()
.as_deref()
== Some("1")
{
let func_name = format!("{}.{}", box_name, "main");
eprintln!("[DEBUG] build_static_main_box: Before lower_static_method_as_function");
eprintln!("[DEBUG] params.len() = {}", params.len());
eprintln!("[DEBUG] body.len() = {}", body.len());
eprintln!("[DEBUG] variable_map = {:?}", self.variable_map);
// Note: Metadata clearing is now handled by BoxCompilationContext (箱理論)
// See lifecycle.rs and builder_calls.rs for context swap implementation
let _ = self.lower_static_method_as_function(func_name, params.clone(), body.clone());
eprintln!("[DEBUG] build_static_main_box: After lower_static_method_as_function");
eprintln!("[DEBUG] variable_map = {:?}", self.variable_map);
}
if let ASTNode::FunctionDeclaration { params, body, .. } = main_method {
// Optional: materialize a callable function entry "BoxName.main/N" for harness/PyVM.
// This static entryは通常の VM 実行では使用されず、過去の Hotfix 4 絡みの loop/control-flow
// バグの温床になっていたため、Phase 25.1m では明示トグルが立っている場合だけ生成する。
if std::env::var("NYASH_BUILD_STATIC_MAIN_ENTRY")
.ok()
.as_deref()
== Some("1")
{
let func_name = format!("{}.{}", box_name, "main");
eprintln!(
"[DEBUG] build_static_main_box: Before lower_static_method_as_function"
);
eprintln!("[DEBUG] params.len() = {}", params.len());
eprintln!("[DEBUG] body.len() = {}", body.len());
eprintln!("[DEBUG] variable_map = {:?}", self.variable_map);
// Note: Metadata clearing is now handled by BoxCompilationContext (箱理論)
// See lifecycle.rs and builder_calls.rs for context swap implementation
let _ = self.lower_static_method_as_function(
func_name,
params.clone(),
body.clone(),
);
eprintln!(
"[DEBUG] build_static_main_box: After lower_static_method_as_function"
);
eprintln!("[DEBUG] variable_map = {:?}", self.variable_map);
}
// Initialize local variables for Main.main() parameters
// Note: These are local variables in the wrapper main() function, NOT parameters
let saved_var_map = std::mem::take(&mut self.variable_map);
@ -63,10 +71,8 @@ impl super::MirBuilder {
box_type: "ArrayBox".to_string(),
args: vec![],
})?;
self.value_origin_newbox
.insert(pid, "ArrayBox".to_string());
self
.value_types
self.value_origin_newbox.insert(pid, "ArrayBox".to_string());
self.value_types
.insert(pid, super::MirType::Box("ArrayBox".to_string()));
// Explicitly call birth() to initialize internal state
self.emit_instruction(MirInstruction::BoxCall {
@ -79,7 +85,10 @@ impl super::MirBuilder {
})?;
if let Some(args) = script_args.as_ref() {
for arg in args {
let val = crate::mir::builder::emission::constant::emit_string(self, arg.clone());
let val = crate::mir::builder::emission::constant::emit_string(
self,
arg.clone(),
);
self.emit_instruction(MirInstruction::BoxCall {
dst: None,
box_val: pid,
@ -131,7 +140,10 @@ impl super::MirBuilder {
// Emit field metadata markers
for field in fields {
let _field_id = crate::mir::builder::emission::constant::emit_string(self, format!("__field_{}_{}", name, field));
let _field_id = crate::mir::builder::emission::constant::emit_string(
self,
format!("__field_{}_{}", name, field),
);
}
// Record weak fields for this box
@ -161,17 +173,21 @@ impl super::MirBuilder {
// Emit markers for declared methods (kept as metadata hints)
for (method_name, method_ast) in methods {
if let ASTNode::FunctionDeclaration { .. } = method_ast {
let _method_id = crate::mir::builder::emission::constant::emit_string(self, format!("__method_{}_{}", name, method_name));
let _method_id = crate::mir::builder::emission::constant::emit_string(
self,
format!("__method_{}_{}", name, method_name),
);
// Track unified member getters: __get_<prop> | __get_once_<prop> | __get_birth_<prop>
let kind_and_prop: Option<(super::PropertyKind, String)> = if let Some(rest) = method_name.strip_prefix("__get_once_") {
Some((super::PropertyKind::Once, rest.to_string()))
} else if let Some(rest) = method_name.strip_prefix("__get_birth_") {
Some((super::PropertyKind::BirthOnce, rest.to_string()))
} else if let Some(rest) = method_name.strip_prefix("__get_") {
Some((super::PropertyKind::Computed, rest.to_string()))
} else {
None
};
let kind_and_prop: Option<(super::PropertyKind, String)> =
if let Some(rest) = method_name.strip_prefix("__get_once_") {
Some((super::PropertyKind::Once, rest.to_string()))
} else if let Some(rest) = method_name.strip_prefix("__get_birth_") {
Some((super::PropertyKind::BirthOnce, rest.to_string()))
} else if let Some(rest) = method_name.strip_prefix("__get_") {
Some((super::PropertyKind::Computed, rest.to_string()))
} else {
None
};
if let Some((k, prop)) = kind_and_prop {
use std::collections::HashMap;
let entry: &mut HashMap<String, super::PropertyKind> = self

View File

@ -1,15 +1,24 @@
//! BranchEmissionBox — 分岐/ジャンプ命令発行の薄いヘルパ(仕様不変)
use crate::mir::{BasicBlockId, MirInstruction};
use crate::mir::builder::MirBuilder;
use crate::mir::{BasicBlockId, MirInstruction};
#[inline]
pub fn emit_conditional(b: &mut MirBuilder, cond: crate::mir::ValueId, then_bb: BasicBlockId, else_bb: BasicBlockId) -> Result<(), String> {
pub fn emit_conditional(
b: &mut MirBuilder,
cond: crate::mir::ValueId,
then_bb: BasicBlockId,
else_bb: BasicBlockId,
) -> Result<(), String> {
if let (Some(func), Some(cur_bb)) = (b.current_function.as_mut(), b.current_block) {
crate::mir::ssot::cf_common::set_branch(func, cur_bb, cond, then_bb, else_bb);
Ok(())
} else {
b.emit_instruction(MirInstruction::Branch { condition: cond, then_bb, else_bb })
b.emit_instruction(MirInstruction::Branch {
condition: cond,
then_bb,
else_bb,
})
}
}

View File

@ -1,10 +1,16 @@
//! CompareEmissionBox — 比較命令発行の薄いヘルパ(仕様不変)
use crate::mir::{CompareOp, MirInstruction, MirType, ValueId};
use crate::mir::builder::MirBuilder;
use crate::mir::{CompareOp, MirInstruction, MirType, ValueId};
#[inline]
pub fn emit_to(b: &mut MirBuilder, dst: ValueId, op: CompareOp, lhs: ValueId, rhs: ValueId) -> Result<(), String> {
pub fn emit_to(
b: &mut MirBuilder,
dst: ValueId,
op: CompareOp,
lhs: ValueId,
rhs: ValueId,
) -> Result<(), String> {
if let (Some(func), Some(cur_bb)) = (b.current_function.as_mut(), b.current_block) {
crate::mir::ssot::cf_common::emit_compare_func(func, cur_bb, dst, op, lhs, rhs);
} else {
@ -18,12 +24,22 @@ pub fn emit_to(b: &mut MirBuilder, dst: ValueId, op: CompareOp, lhs: ValueId, rh
// Convenience wrappers (明示関数名が読みやすい箇所用)
#[inline]
#[allow(dead_code)]
pub fn emit_eq_to(b: &mut MirBuilder, dst: ValueId, lhs: ValueId, rhs: ValueId) -> Result<(), String> {
pub fn emit_eq_to(
b: &mut MirBuilder,
dst: ValueId,
lhs: ValueId,
rhs: ValueId,
) -> Result<(), String> {
emit_to(b, dst, CompareOp::Eq, lhs, rhs)
}
#[inline]
#[allow(dead_code)]
pub fn emit_ne_to(b: &mut MirBuilder, dst: ValueId, lhs: ValueId, rhs: ValueId) -> Result<(), String> {
pub fn emit_ne_to(
b: &mut MirBuilder,
dst: ValueId,
lhs: ValueId,
rhs: ValueId,
) -> Result<(), String> {
emit_to(b, dst, CompareOp::Ne, lhs, rhs)
}

View File

@ -3,47 +3,65 @@
//! ✅ Phase 25.1b Fix: All constant emission now uses function-local ID generator
//! when inside a function context to ensure proper SSA verification.
use crate::mir::{ConstValue, MirInstruction, ValueId};
use crate::mir::builder::MirBuilder;
use crate::mir::{ConstValue, MirInstruction, ValueId};
#[inline]
pub fn emit_integer(b: &mut MirBuilder, val: i64) -> ValueId {
let dst = b.next_value_id();
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Integer(val) });
let _ = b.emit_instruction(MirInstruction::Const {
dst,
value: ConstValue::Integer(val),
});
dst
}
#[inline]
pub fn emit_bool(b: &mut MirBuilder, val: bool) -> ValueId {
let dst = b.next_value_id();
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Bool(val) });
let _ = b.emit_instruction(MirInstruction::Const {
dst,
value: ConstValue::Bool(val),
});
dst
}
#[inline]
pub fn emit_float(b: &mut MirBuilder, val: f64) -> ValueId {
let dst = b.next_value_id();
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Float(val) });
let _ = b.emit_instruction(MirInstruction::Const {
dst,
value: ConstValue::Float(val),
});
dst
}
#[inline]
pub fn emit_string<S: Into<String>>(b: &mut MirBuilder, s: S) -> ValueId {
let dst = b.next_value_id();
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::String(s.into()) });
let _ = b.emit_instruction(MirInstruction::Const {
dst,
value: ConstValue::String(s.into()),
});
dst
}
#[inline]
pub fn emit_null(b: &mut MirBuilder) -> ValueId {
let dst = b.next_value_id();
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Null });
let _ = b.emit_instruction(MirInstruction::Const {
dst,
value: ConstValue::Null,
});
dst
}
#[inline]
pub fn emit_void(b: &mut MirBuilder) -> ValueId {
let dst = b.next_value_id();
let _ = b.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Void });
let _ = b.emit_instruction(MirInstruction::Const {
dst,
value: ConstValue::Void,
});
dst
}

View File

@ -3,6 +3,6 @@
//! - compare.rs: Compare命令の薄い発行
//! - branch.rs: Branch/Jump 発行の薄い関数
pub mod constant;
pub mod compare;
pub mod branch;
pub mod compare;
pub mod constant;

View File

@ -3,7 +3,11 @@ use crate::mir::definitions::call_unified::Callee;
use crate::mir::ValueId;
/// Finalize call operands (receiver/args) using LocalSSA; thin wrapper to centralize usage.
pub fn finalize_call_operands(builder: &mut MirBuilder, callee: &mut Callee, args: &mut Vec<ValueId>) {
pub fn finalize_call_operands(
builder: &mut MirBuilder,
callee: &mut Callee,
args: &mut Vec<ValueId>,
) {
// Step 1: Receiver materialization (pin slot + LocalSSA) in a dedicated box
crate::mir::builder::receiver::finalize_method_receiver(builder, callee);

View File

@ -1,6 +1,8 @@
// Expression lowering split from builder.rs to keep files lean
use super::{MirInstruction, ValueId};
use crate::ast::{ASTNode, AssignStmt, ReturnStmt, BinaryExpr, CallExpr, MethodCallExpr, FieldAccessExpr};
use crate::ast::{
ASTNode, AssignStmt, BinaryExpr, CallExpr, FieldAccessExpr, MethodCallExpr, ReturnStmt,
};
impl super::MirBuilder {
// Main expression dispatcher
@ -16,9 +18,7 @@ impl super::MirBuilder {
// Sequentially lower statements and return last value (or Void)
self.cf_block(statements)
}
ASTNode::Print { expression, .. } => {
self.build_print_statement(*expression)
}
ASTNode::Print { expression, .. } => self.build_print_statement(*expression),
ASTNode::If {
condition,
then_body,
@ -36,13 +36,17 @@ impl super::MirBuilder {
});
self.cf_if(*condition, then_node, else_node)
}
ASTNode::Loop { condition, body, .. } => {
ASTNode::Loop {
condition, body, ..
} => {
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[exprs.rs:35] FIRST Loop pattern matched");
}
self.cf_loop(*condition, body)
}
ASTNode::While { condition, body, .. } => {
ASTNode::While {
condition, body, ..
} => {
// Desugar Stage-3 while into legacy loop(condition) { body }
self.cf_loop(*condition, body)
}
@ -85,8 +89,17 @@ impl super::MirBuilder {
let obj_val = self.build_expression_impl(*m.object.clone())?;
let ty = Self::parse_type_name_to_mir(&type_name);
let dst = self.next_value_id();
let op = if m.method == "is" { crate::mir::TypeOpKind::Check } else { crate::mir::TypeOpKind::Cast };
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?;
let op = if m.method == "is" {
crate::mir::TypeOpKind::Check
} else {
crate::mir::TypeOpKind::Cast
};
self.emit_instruction(MirInstruction::TypeOp {
dst,
op,
value: obj_val,
ty,
})?;
return Ok(dst);
}
}
@ -106,7 +119,11 @@ impl super::MirBuilder {
if let ASTNode::FieldAccess { object, field, .. } = stmt.target.as_ref() {
self.build_field_assignment(*object.clone(), field.clone(), *stmt.value.clone())
} else if let ASTNode::Index { target, index, .. } = stmt.target.as_ref() {
self.build_index_assignment(*target.clone(), *index.clone(), *stmt.value.clone())
self.build_index_assignment(
*target.clone(),
*index.clone(),
*stmt.value.clone(),
)
} else if let ASTNode::Variable { name, .. } = stmt.target.as_ref() {
self.build_assignment(name.clone(), *stmt.value.clone())
} else {
@ -280,8 +297,7 @@ impl super::MirBuilder {
})?;
self.value_origin_newbox
.insert(arr_id, "ArrayBox".to_string());
self
.value_types
self.value_types
.insert(arr_id, super::MirType::Box("ArrayBox".to_string()));
for e in elements {
let v = self.build_expression_impl(e)?;
@ -312,11 +328,9 @@ impl super::MirBuilder {
args: vec![],
effects: super::EffectMask::MUT,
})?;
self
.value_origin_newbox
self.value_origin_newbox
.insert(map_id, "MapBox".to_string());
self
.value_types
self.value_types
.insert(map_id, super::MirType::Box("MapBox".to_string()));
for (k, expr) in entries {
// const string key

View File

@ -32,7 +32,9 @@ impl super::MirBuilder {
self.emit_instruction(super::MirInstruction::Call {
dst: Some(dst),
func: callee_id,
callee: Some(crate::mir::definitions::call_unified::Callee::Value(callee_id)),
callee: Some(crate::mir::definitions::call_unified::Callee::Value(
callee_id,
)),
args: arg_ids,
effects: super::EffectMask::PURE,
})?;

View File

@ -69,16 +69,35 @@ impl super::MirBuilder {
// In current dispatch block, compare and branch
self.start_new_block(cur_dispatch)?;
let lit_id = match label {
LiteralValue::String(s) => crate::mir::builder::emission::constant::emit_string(self, s),
LiteralValue::Integer(i) => crate::mir::builder::emission::constant::emit_integer(self, i),
LiteralValue::Bool(b) => crate::mir::builder::emission::constant::emit_bool(self, b),
LiteralValue::Float(f) => crate::mir::builder::emission::constant::emit_float(self, f),
LiteralValue::String(s) => {
crate::mir::builder::emission::constant::emit_string(self, s)
}
LiteralValue::Integer(i) => {
crate::mir::builder::emission::constant::emit_integer(self, i)
}
LiteralValue::Bool(b) => {
crate::mir::builder::emission::constant::emit_bool(self, b)
}
LiteralValue::Float(f) => {
crate::mir::builder::emission::constant::emit_float(self, f)
}
LiteralValue::Null => crate::mir::builder::emission::constant::emit_null(self),
LiteralValue::Void => crate::mir::builder::emission::constant::emit_void(self),
};
let cond_id = self.next_value_id();
crate::mir::builder::emission::compare::emit_to(self, cond_id, super::CompareOp::Eq, scr_val, lit_id)?;
crate::mir::builder::emission::branch::emit_conditional(self, cond_id, then_block, else_target)?;
crate::mir::builder::emission::compare::emit_to(
self,
cond_id,
super::CompareOp::Eq,
scr_val,
lit_id,
)?;
crate::mir::builder::emission::branch::emit_conditional(
self,
cond_id,
then_block,
else_target,
)?;
// then arm
self.start_new_block(then_block)?;
@ -102,7 +121,10 @@ impl super::MirBuilder {
if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) {
crate::mir::ssot::cf_common::insert_phi_at_head(func, cur_bb, result_val, phi_inputs);
} else {
self.emit_instruction(super::MirInstruction::Phi { dst: result_val, inputs: phi_inputs })?;
self.emit_instruction(super::MirInstruction::Phi {
dst: result_val,
inputs: phi_inputs,
})?;
}
Ok(result_val)
}

View File

@ -21,7 +21,9 @@ impl super::MirBuilder {
let then_block = self.block_gen.next();
let else_block = self.block_gen.next();
let ok_local = self.local_ssa_ensure(ok_id, 4);
crate::mir::builder::emission::branch::emit_conditional(self, ok_local, then_block, else_block)?;
crate::mir::builder::emission::branch::emit_conditional(
self, ok_local, then_block, else_block,
)?;
self.start_new_block(then_block)?;
self.emit_instruction(super::MirInstruction::Return {
value: Some(res_local),

View File

@ -30,11 +30,16 @@ impl super::MirBuilder {
}
// Emit: field name const (boxed)
let field_name_id = crate::mir::builder::emission::constant::emit_string(self, field.clone());
let field_name_id =
crate::mir::builder::emission::constant::emit_string(self, field.clone());
// Finalize operands (base + args) in current block
let mut base = object_value;
let mut args_vec = vec![field_name_id];
crate::mir::builder::ssa::local::finalize_field_base_and_args(self, &mut base, &mut args_vec);
crate::mir::builder::ssa::local::finalize_field_base_and_args(
self,
&mut base,
&mut args_vec,
);
// BoxCall: getField(name)
let field_val = self.next_value_id();
self.emit_instruction(MirInstruction::BoxCall {
@ -60,8 +65,13 @@ impl super::MirBuilder {
.get(&(base_cls.clone(), field.clone()))
.cloned()
{
if super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
super::utils::builder_debug_log(&format!("field-origin hit by box-level map: base={} .{} -> {}", base_cls, field, fcls));
if super::utils::builder_debug_enabled()
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
{
super::utils::builder_debug_log(&format!(
"field-origin hit by box-level map: base={} .{} -> {}",
base_cls, field, fcls
));
}
self.value_origin_newbox.insert(field_val, fcls);
}
@ -127,11 +137,16 @@ impl super::MirBuilder {
}
// Emit: field name const
let field_name_id = crate::mir::builder::emission::constant::emit_string(self, field.clone());
let field_name_id =
crate::mir::builder::emission::constant::emit_string(self, field.clone());
// Finalize operands (base + args) in current block
let mut base = object_value;
let mut args_vec = vec![field_name_id, value_result];
crate::mir::builder::ssa::local::finalize_field_base_and_args(self, &mut base, &mut args_vec);
crate::mir::builder::ssa::local::finalize_field_base_and_args(
self,
&mut base,
&mut args_vec,
);
// Set the field via BoxCall: setField(name, value)
self.emit_instruction(MirInstruction::BoxCall {
dst: None,

View File

@ -1,6 +1,6 @@
use super::{MirBuilder, ValueId};
use crate::mir::loop_api::LoopBuilderApi; // for current_block()
use crate::ast::{ASTNode, BinaryOperator};
use crate::mir::loop_api::LoopBuilderApi; // for current_block()
impl MirBuilder {
/// Lower an if/else using a structured IfForm (header→then/else→merge).
@ -17,25 +17,31 @@ impl MirBuilder {
// so that subsequent branches can safely reuse these values across blocks.
// This leverages existing variable_map merges (PHI) at the merge block.
if crate::config::env::mir_pre_pin_compare_operands() {
if let ASTNode::BinaryOp { operator, left, right, .. } = &condition {
match operator {
BinaryOperator::Equal
| BinaryOperator::NotEqual
| BinaryOperator::Less
| BinaryOperator::LessEqual
| BinaryOperator::Greater
| BinaryOperator::GreaterEqual => {
if let Ok(lhs_v) = self.build_expression((**left).clone()) {
let _ = self.pin_to_slot(lhs_v, "@if_lhs");
}
if let Ok(rhs_v) = self.build_expression((**right).clone()) {
let _ = self.pin_to_slot(rhs_v, "@if_rhs");
if let ASTNode::BinaryOp {
operator,
left,
right,
..
} = &condition
{
match operator {
BinaryOperator::Equal
| BinaryOperator::NotEqual
| BinaryOperator::Less
| BinaryOperator::LessEqual
| BinaryOperator::Greater
| BinaryOperator::GreaterEqual => {
if let Ok(lhs_v) = self.build_expression((**left).clone()) {
let _ = self.pin_to_slot(lhs_v, "@if_lhs");
}
if let Ok(rhs_v) = self.build_expression((**right).clone()) {
let _ = self.pin_to_slot(rhs_v, "@if_rhs");
}
}
_ => {}
}
_ => {}
}
}
}
let condition_val = self.build_expression(condition)?;
let condition_val = self.local_cond(condition_val);
@ -49,7 +55,12 @@ impl MirBuilder {
let pre_branch_bb = self.current_block()?;
let mut condition_val = condition_val;
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut condition_val);
crate::mir::builder::emission::branch::emit_conditional(self, condition_val, then_block, else_block)?;
crate::mir::builder::emission::branch::emit_conditional(
self,
condition_val,
then_block,
else_block,
)?;
// Snapshot variables before entering branches
let pre_if_var_map = self.variable_map.clone();
@ -93,40 +104,41 @@ impl MirBuilder {
self.debug_push_region(format!("join#{}", join_id) + "/else");
// Scope enter for else-branch
self.hint_scope_enter(0);
let (else_value_raw, else_ast_for_analysis, else_var_map_end_opt) = if let Some(else_ast) = else_branch {
// Reset variable_map BEFORE materializing PHI nodes (same pattern as then-branch)
self.variable_map = pre_if_var_map.clone();
// Materialize all variables at block entry via single-pred Phi (correctness-first)
for (name, &pre_v) in pre_if_var_map.iter() {
let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
self.variable_map.insert(name.clone(), phi_val);
if trace_if {
eprintln!(
"[if-trace] else-entry phi var={} pre={:?} -> dst={:?}",
name, pre_v, phi_val
);
let (else_value_raw, else_ast_for_analysis, else_var_map_end_opt) =
if let Some(else_ast) = else_branch {
// Reset variable_map BEFORE materializing PHI nodes (same pattern as then-branch)
self.variable_map = pre_if_var_map.clone();
// Materialize all variables at block entry via single-pred Phi (correctness-first)
for (name, &pre_v) in pre_if_var_map.iter() {
let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
self.variable_map.insert(name.clone(), phi_val);
if trace_if {
eprintln!(
"[if-trace] else-entry phi var={} pre={:?} -> dst={:?}",
name, pre_v, phi_val
);
}
}
}
let val = self.build_expression(else_ast.clone())?;
(val, Some(else_ast), Some(self.variable_map.clone()))
} else {
// No else branch: materialize PHI nodes for the empty else block
self.variable_map = pre_if_var_map.clone();
for (name, &pre_v) in pre_if_var_map.iter() {
let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
self.variable_map.insert(name.clone(), phi_val);
if trace_if {
eprintln!(
"[if-trace] else-entry phi var={} pre={:?} -> dst={:?}",
name, pre_v, phi_val
);
let val = self.build_expression(else_ast.clone())?;
(val, Some(else_ast), Some(self.variable_map.clone()))
} else {
// No else branch: materialize PHI nodes for the empty else block
self.variable_map = pre_if_var_map.clone();
for (name, &pre_v) in pre_if_var_map.iter() {
let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
self.variable_map.insert(name.clone(), phi_val);
if trace_if {
eprintln!(
"[if-trace] else-entry phi var={} pre={:?} -> dst={:?}",
name, pre_v, phi_val
);
}
}
}
let void_val = crate::mir::builder::emission::constant::emit_void(self);
// Phase 25.1c/k: Pass PHI-renamed variable_map for empty else branch
// This ensures merge_modified_vars uses correct ValueIds after PHI renaming
(void_val, None, Some(self.variable_map.clone()))
};
let void_val = crate::mir::builder::emission::constant::emit_void(self);
// Phase 25.1c/k: Pass PHI-renamed variable_map for empty else branch
// This ensures merge_modified_vars uses correct ValueIds after PHI renaming
(void_val, None, Some(self.variable_map.clone()))
};
let else_exit_block = self.current_block()?;
let else_reaches_merge = !self.is_current_block_terminated();
if else_reaches_merge {
@ -146,7 +158,8 @@ impl MirBuilder {
self.push_if_merge(merge_block);
// Pre-analysis: identify then/else assigned var for skip and hints
let assigned_then_pre = crate::mir::phi_core::if_phi::extract_assigned_var(&then_ast_for_analysis);
let assigned_then_pre =
crate::mir::phi_core::if_phi::extract_assigned_var(&then_ast_for_analysis);
let assigned_else_pre = else_ast_for_analysis
.as_ref()
.and_then(|e| crate::mir::phi_core::if_phi::extract_assigned_var(e));
@ -157,8 +170,16 @@ impl MirBuilder {
let result_val = self.normalize_if_else_phi(
then_block,
else_block,
if then_reaches_merge { Some(then_exit_block) } else { None },
if else_reaches_merge { Some(else_exit_block) } else { None },
if then_reaches_merge {
Some(then_exit_block)
} else {
None
},
if else_reaches_merge {
Some(else_exit_block)
} else {
None
},
then_value_raw,
else_value_raw,
&pre_if_var_map,
@ -172,12 +193,16 @@ impl MirBuilder {
// Hint: join result variable(s)
// 1) Primary: if both branches assign to the same variable name, emit a hint for that name
if let (Some(tn), Some(en)) = (assigned_then_pre.as_deref(), assigned_else_pre.as_deref()) {
if tn == en { self.hint_join_result(tn); }
if tn == en {
self.hint_join_result(tn);
}
}
// 2) Secondary: if both branches assign multiple variables, hint全件制限なし
if let Some(ref else_map_end) = else_var_map_end_opt {
for name in then_var_map_end.keys() {
if Some(name.as_str()) == assigned_then_pre.as_deref() { continue; }
if Some(name.as_str()) == assigned_then_pre.as_deref() {
continue;
}
if else_map_end.contains_key(name) {
self.hint_join_result(name.as_str());
}
@ -189,8 +214,16 @@ impl MirBuilder {
self.merge_modified_vars(
then_block,
else_block,
if then_reaches_merge { Some(then_exit_block) } else { None },
if else_reaches_merge { Some(else_exit_block) } else { None },
if then_reaches_merge {
Some(then_exit_block)
} else {
None
},
if else_reaches_merge {
Some(else_exit_block)
} else {
None
},
&pre_if_var_map,
&then_var_map_end,
&else_var_map_end_opt,

View File

@ -1,4 +1,7 @@
use super::{EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId, BasicBlockId};
use super::{
BasicBlockId, EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule, MirType,
ValueId,
};
use crate::ast::ASTNode;
// Lifecycle routines extracted from builder.rs
@ -6,7 +9,13 @@ fn has_main_static(ast: &ASTNode) -> bool {
use crate::ast::ASTNode as N;
if let N::Program { statements, .. } = ast {
for st in statements {
if let N::BoxDeclaration { name, methods, is_static, .. } = st {
if let N::BoxDeclaration {
name,
methods,
is_static,
..
} = st
{
if *is_static && name == "Main" {
if let Some(m) = methods.get("main") {
if let N::FunctionDeclaration { .. } = m {
@ -31,7 +40,12 @@ impl super::MirBuilder {
self.index_declarations(st);
}
}
ASTNode::BoxDeclaration { name, methods, is_static, .. } => {
ASTNode::BoxDeclaration {
name,
methods,
is_static,
..
} => {
if !*is_static {
self.user_defined_boxes.insert(name.clone());
} else {
@ -106,7 +120,8 @@ impl super::MirBuilder {
ASTNode::Program { statements, .. } => {
use crate::ast::ASTNode as N;
// First pass: lower declarations (static boxes except Main, and instance boxes)
let mut main_static: Option<(String, std::collections::HashMap<String, ASTNode>)> = None;
let mut main_static: Option<(String, std::collections::HashMap<String, ASTNode>)> =
None;
for st in &statements {
if let N::BoxDeclaration {
name,
@ -135,9 +150,20 @@ impl super::MirBuilder {
// Lower all static methods into standalone functions: BoxName.method/Arity
for (mname, mast) in methods.iter() {
if let N::FunctionDeclaration { params, body, .. } = mast {
let func_name = format!("{}.{}{}", name, mname, format!("/{}", params.len()));
self.lower_static_method_as_function(func_name, params.clone(), body.clone())?;
if let N::FunctionDeclaration { params, body, .. } =
mast
{
let func_name = format!(
"{}.{}{}",
name,
mname,
format!("/{}", params.len())
);
self.lower_static_method_as_function(
func_name,
params.clone(),
body.clone(),
)?;
self.static_method_index
.entry(mname.clone())
.or_insert_with(Vec::new)
@ -174,9 +200,20 @@ impl super::MirBuilder {
}
}
for (mname, mast) in methods.iter() {
if let N::FunctionDeclaration { params, body, is_static, .. } = mast {
if let N::FunctionDeclaration {
params,
body,
is_static,
..
} = mast
{
if !*is_static {
let func_name = format!("{}.{}{}", name, mname, format!("/{}", params.len()));
let func_name = format!(
"{}.{}{}",
name,
mname,
format!("/{}", params.len())
);
self.lower_method_as_function(
func_name,
name.clone(),
@ -208,10 +245,7 @@ impl super::MirBuilder {
}
}
pub(super) fn finalize_module(
&mut self,
result_value: ValueId,
) -> Result<MirModule, String> {
pub(super) fn finalize_module(&mut self, result_value: ValueId) -> Result<MirModule, String> {
// Hint: scope leave at function end (id=0 for main)
self.hint_scope_leave(0);
if let Some(block_id) = self.current_block {
@ -280,7 +314,12 @@ impl super::MirBuilder {
let insns = &bb.instructions;
let mut idx = 0usize;
while idx < insns.len() {
if let MirInstruction::NewBox { dst, box_type, args } = &insns[idx] {
if let MirInstruction::NewBox {
dst,
box_type,
args,
} = &insns[idx]
{
// Skip StringBox (literal optimization path)
if box_type != "StringBox" {
let expect_tail = format!("{}.birth/{}", box_type, args.len());
@ -290,16 +329,26 @@ impl super::MirBuilder {
let mut last_const_name: Option<String> = None;
while j < insns.len() && j <= idx + 3 {
match &insns[j] {
MirInstruction::BoxCall { box_val, method, .. } => {
if method == "birth" && box_val == dst { ok = true; break; }
MirInstruction::BoxCall {
box_val, method, ..
} => {
if method == "birth" && box_val == dst {
ok = true;
break;
}
}
MirInstruction::Const { value, .. } => {
if let super::ConstValue::String(s) = value { last_const_name = Some(s.clone()); }
if let super::ConstValue::String(s) = value {
last_const_name = Some(s.clone());
}
}
MirInstruction::Call { func: _, .. } => {
// If immediately preceded by matching Const String, accept
if let Some(prev) = last_const_name.as_ref() {
if prev == &expect_tail { ok = true; break; }
if prev == &expect_tail {
ok = true;
break;
}
}
// Heuristic: in some forms, builder may reuse a shared const; best-effort only
}
@ -317,7 +366,10 @@ impl super::MirBuilder {
}
}
if warn_count > 0 {
eprintln!("[warn] dev verify: NewBox→birth invariant warnings: {}", warn_count);
eprintln!(
"[warn] dev verify: NewBox→birth invariant warnings: {}",
warn_count
);
}
}

View File

@ -62,7 +62,10 @@ pub(crate) fn add_predecessor(
bb.add_predecessor(pred);
return Ok(());
}
return Err(format!("Block {} not found (impossible after auto-create)", block));
return Err(format!(
"Block {} not found (impossible after auto-create)",
block
));
}
Err("No current function".to_string())
}

View File

@ -2,4 +2,3 @@
//! - value_types と value_origin_newbox の伝播を一箇所に集約する。
pub mod propagate;

View File

@ -4,16 +4,14 @@
//! 🎯 箱理論: TypeRegistryBox 統合対応
//! NYASH_USE_TYPE_REGISTRY=1 で TypeRegistry 経由に切り替え(段階的移行)
use crate::mir::{MirType, ValueId};
use crate::mir::builder::MirBuilder;
use crate::mir::{MirType, ValueId};
/// src から dst へ builder 内メタデータvalue_types / value_origin_newboxを伝播する。
/// 🎯 TypeRegistry 経由モード対応NYASH_USE_TYPE_REGISTRY=1
#[inline]
pub fn propagate(builder: &mut MirBuilder, src: ValueId, dst: ValueId) {
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");
if use_registry {
// 🎯 新: TypeRegistry 経由(トレース可能)
@ -34,9 +32,7 @@ pub fn propagate(builder: &mut MirBuilder, src: ValueId, dst: ValueId) {
#[inline]
#[allow(dead_code)]
pub fn propagate_with_override(builder: &mut MirBuilder, dst: ValueId, ty: MirType) {
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY")
.ok()
.as_deref() == Some("1");
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY").ok().as_deref() == Some("1");
if use_registry {
// 🎯 新: TypeRegistry 経由

View File

@ -4,10 +4,10 @@
//! following the Single Responsibility Principle.
use crate::ast::ASTNode;
use crate::mir::builder::{MirBuilder, ValueId};
use crate::mir::builder::builder_calls::CallTarget;
use crate::mir::{MirInstruction, TypeOpKind};
use crate::mir::builder::calls::function_lowering;
use crate::mir::builder::{MirBuilder, ValueId};
use crate::mir::{MirInstruction, TypeOpKind};
/// Me-call 専用のポリシー箱。
///
@ -34,8 +34,7 @@ impl MeCallPolicyBox {
arg_values.push(builder.build_expression(a.clone())?);
}
let arity = arg_values.len();
let fname =
function_lowering::generate_method_function_name(cls, method, arity);
let fname = function_lowering::generate_method_function_name(cls, method, arity);
let exists = if let Some(ref module) = builder.current_module {
module.functions.contains_key(&fname)
} else {
@ -63,8 +62,7 @@ impl MeCallPolicyBox {
// static box 文脈では実体インスタンスが存在しないため UndefinedValue を生みやすい。
// - ここでは receiver を持たない Global call に揃え、FuncScannerBox などの
// static ヘルパー呼び出しを安全に扱う。
let static_dst =
builder.handle_static_method_call(cls, method, arguments)?;
let static_dst = builder.handle_static_method_call(cls, method, arguments)?;
return Ok(Some(static_dst));
}
@ -161,7 +159,11 @@ impl MirBuilder {
let dst = self.next_value_id();
self.emit_unified_call(
Some(dst),
CallTarget::Method { box_type: None, method, receiver: object_value },
CallTarget::Method {
box_type: None,
method,
receiver: object_value,
},
arg_values,
)?;
Ok(dst)

View File

@ -1,5 +1,5 @@
use crate::mir::ValueId;
use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;
/// Emit a string Const (function name const) and return its ValueId.
/// Behavior-preserving wrapper around Const emission with String value.

View File

@ -3,6 +3,5 @@
//! - ssa: PHI/SSA related debug emissions
//! - resolve: method resolution try/choose既存呼び出しの置換は段階的に
pub mod ssa;
pub mod resolve;
pub mod ssa;

View File

@ -23,14 +23,20 @@ fn sample_every() -> usize {
/// Devonly: emit a resolve.try eventcandidates inspection
pub(crate) fn emit_try(builder: &MirBuilder, meta: serde_json::Value) {
let fn_name = builder.current_function.as_ref().map(|f| f.signature.name.as_str());
let fn_name = builder
.current_function
.as_ref()
.map(|f| f.signature.name.as_str());
let region = builder.debug_current_region_id();
crate::debug::hub::emit("resolve", "try", fn_name, region.as_deref(), meta);
}
/// Devonly: emit a resolve.choose eventdecision
pub(crate) fn emit_choose(builder: &MirBuilder, meta: serde_json::Value) {
let fn_name = builder.current_function.as_ref().map(|f| f.signature.name.as_str());
let fn_name = builder
.current_function
.as_ref()
.map(|f| f.signature.name.as_str());
let region = builder.debug_current_region_id();
// KPI (dev-only)
record_kpi(&meta);
@ -40,7 +46,9 @@ pub(crate) fn emit_choose(builder: &MirBuilder, meta: serde_json::Value) {
/// Internal: Call from emit_choose wrapper to record KPI if enabled.
#[allow(dead_code)]
fn record_kpi(meta: &serde_json::Value) {
if !kpi_enabled() { return; }
if !kpi_enabled() {
return;
}
let total = TOTAL_CHOOSE.fetch_add(1, Ordering::Relaxed) + 1;
let certainty = meta.get("certainty").and_then(|v| v.as_str()).unwrap_or("");
if certainty == "Known" {
@ -49,7 +57,14 @@ fn record_kpi(meta: &serde_json::Value) {
let n = sample_every();
if n > 0 && total % n == 0 {
let known = KNOWN_CHOOSE.load(Ordering::Relaxed);
let rate = if total > 0 { (known as f64) * 100.0 / (total as f64) } else { 0.0 };
eprintln!("[NYASH-KPI] resolve.choose Known={} Total={} ({:.1}%)", known, total, rate);
let rate = if total > 0 {
(known as f64) * 100.0 / (total as f64)
} else {
0.0
};
eprintln!(
"[NYASH-KPI] resolve.choose Known={} Total={} ({:.1}%)",
known, total, rate
);
}
}

View File

@ -1,6 +1,6 @@
use super::{MirInstruction, MirType, ValueId};
use crate::mir::loop_api::LoopBuilderApi; // for current_block()
use crate::ast::{ASTNode, BinaryOperator};
use crate::mir::loop_api::LoopBuilderApi; // for current_block()
use crate::mir::{BinaryOp, CompareOp, TypeOpKind, UnaryOp};
// Internal classification for binary operations
@ -38,7 +38,10 @@ impl super::MirBuilder {
let mir_op = self.convert_binary_operator(operator)?;
let all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL").ok().as_deref() == Some("1");
let all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL")
.ok()
.as_deref()
== Some("1");
match mir_op {
// Arithmetic operations
@ -51,11 +54,19 @@ impl super::MirBuilder {
.unwrap_or(false);
if matches!(op, crate::mir::BinaryOp::Add)
&& !in_add_op
&& (all_call || std::env::var("NYASH_BUILDER_OPERATOR_BOX_ADD_CALL").ok().as_deref() == Some("1"))
&& (all_call
|| std::env::var("NYASH_BUILDER_OPERATOR_BOX_ADD_CALL")
.ok()
.as_deref()
== Some("1"))
{
// AddOperator.apply/2(lhs, rhs)
let name = "AddOperator.apply/2".to_string();
self.emit_legacy_call(Some(dst), super::builder_calls::CallTarget::Global(name), vec![lhs, rhs])?;
self.emit_legacy_call(
Some(dst),
super::builder_calls::CallTarget::Global(name),
vec![lhs, rhs],
)?;
// 型注釈(従来と同等)
let lhs_is_str = match self.value_types.get(&lhs) {
Some(MirType::String) => true,
@ -81,21 +92,39 @@ impl super::MirBuilder {
crate::mir::BinaryOp::Mod => ("ModOperator.apply/2", "ModOperator.apply/"),
crate::mir::BinaryOp::Shl => ("ShlOperator.apply/2", "ShlOperator.apply/"),
crate::mir::BinaryOp::Shr => ("ShrOperator.apply/2", "ShrOperator.apply/"),
crate::mir::BinaryOp::BitAnd => ("BitAndOperator.apply/2", "BitAndOperator.apply/"),
crate::mir::BinaryOp::BitOr => ("BitOrOperator.apply/2", "BitOrOperator.apply/"),
crate::mir::BinaryOp::BitXor => ("BitXorOperator.apply/2", "BitXorOperator.apply/"),
crate::mir::BinaryOp::BitAnd => {
("BitAndOperator.apply/2", "BitAndOperator.apply/")
}
crate::mir::BinaryOp::BitOr => {
("BitOrOperator.apply/2", "BitOrOperator.apply/")
}
crate::mir::BinaryOp::BitXor => {
("BitXorOperator.apply/2", "BitXorOperator.apply/")
}
_ => ("", ""),
};
if !name.is_empty() {
let in_guard = self.current_function.as_ref().map(|f| f.signature.name.starts_with(guard_prefix)).unwrap_or(false);
let in_guard = self
.current_function
.as_ref()
.map(|f| f.signature.name.starts_with(guard_prefix))
.unwrap_or(false);
if !in_guard {
self.emit_legacy_call(Some(dst), super::builder_calls::CallTarget::Global(name.to_string()), vec![lhs, rhs])?;
self.emit_legacy_call(
Some(dst),
super::builder_calls::CallTarget::Global(name.to_string()),
vec![lhs, rhs],
)?;
// 型注釈: 算術はおおむね整数Addは上で注釈済み
self.value_types.insert(dst, MirType::Integer);
} else {
// guard中は従来のBinOp
if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) {
crate::mir::ssot::binop_lower::emit_binop_to_dst(func, cur_bb, dst, op, lhs, rhs);
if let (Some(func), Some(cur_bb)) =
(self.current_function.as_mut(), self.current_block)
{
crate::mir::ssot::binop_lower::emit_binop_to_dst(
func, cur_bb, dst, op, lhs, rhs,
);
} else {
self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
}
@ -103,8 +132,12 @@ impl super::MirBuilder {
}
} else {
// 既存の算術経路
if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) {
crate::mir::ssot::binop_lower::emit_binop_to_dst(func, cur_bb, dst, op, lhs, rhs);
if let (Some(func), Some(cur_bb)) =
(self.current_function.as_mut(), self.current_block)
{
crate::mir::ssot::binop_lower::emit_binop_to_dst(
func, cur_bb, dst, op, lhs, rhs,
);
} else {
self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
}
@ -130,8 +163,12 @@ impl super::MirBuilder {
}
} else {
// 既存の算術経路
if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) {
crate::mir::ssot::binop_lower::emit_binop_to_dst(func, cur_bb, dst, op, lhs, rhs);
if let (Some(func), Some(cur_bb)) =
(self.current_function.as_mut(), self.current_block)
{
crate::mir::ssot::binop_lower::emit_binop_to_dst(
func, cur_bb, dst, op, lhs, rhs,
);
} else {
self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
}
@ -165,7 +202,12 @@ impl super::MirBuilder {
.map(|f| f.signature.name.starts_with("CompareOperator.apply/"))
.unwrap_or(false);
if !in_cmp_op
&& (all_call || std::env::var("NYASH_BUILDER_OPERATOR_BOX_COMPARE_CALL").ok().as_deref() == Some("1")) {
&& (all_call
|| std::env::var("NYASH_BUILDER_OPERATOR_BOX_COMPARE_CALL")
.ok()
.as_deref()
== Some("1"))
{
// op名の文字列化
let opname = match op {
CompareOp::Eq => "Eq",
@ -175,10 +217,15 @@ impl super::MirBuilder {
CompareOp::Gt => "Gt",
CompareOp::Ge => "Ge",
};
let op_const = crate::mir::builder::emission::constant::emit_string(self, opname);
let op_const =
crate::mir::builder::emission::constant::emit_string(self, opname);
// そのまま値を渡す(型変換/slot化は演算子内orVMで行う
let name = "CompareOperator.apply/3".to_string();
self.emit_legacy_call(Some(dst), super::builder_calls::CallTarget::Global(name), vec![op_const, lhs, rhs])?;
self.emit_legacy_call(
Some(dst),
super::builder_calls::CallTarget::Global(name),
vec![op_const, lhs, rhs],
)?;
self.value_types.insert(dst, MirType::Bool);
} else {
// 既存の比較経路(安全のための型注釈/slot化含む
@ -245,7 +292,9 @@ impl super::MirBuilder {
// Branch on LHS truthiness (runtime to_bool semantics in interpreter/LLVM)
let mut lhs_cond = self.local_cond(lhs_val);
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut lhs_cond);
crate::mir::builder::emission::branch::emit_conditional(self, lhs_cond, then_block, else_block)?;
crate::mir::builder::emission::branch::emit_conditional(
self, lhs_cond, then_block, else_block,
)?;
// Record predecessor block for branch (for single-pred PHI materialization)
let pre_branch_bb = self.current_block()?;
@ -273,7 +322,9 @@ impl super::MirBuilder {
let rhs_val = self.build_expression(right.clone())?;
let mut rhs_cond = self.local_cond(rhs_val);
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut rhs_cond);
crate::mir::builder::emission::branch::emit_conditional(self, rhs_cond, rhs_true, rhs_false)?;
crate::mir::builder::emission::branch::emit_conditional(
self, rhs_cond, rhs_true, rhs_false,
)?;
// true path
self.start_new_block(rhs_true)?;
let t_id = crate::mir::builder::emission::constant::emit_bool(self, true);
@ -286,7 +337,9 @@ impl super::MirBuilder {
let rhs_false_exit = self.current_block()?;
// join rhs result into a single bool
self.start_new_block(rhs_join)?;
if let Some(func) = self.current_function.as_mut() { func.update_cfg(); }
if let Some(func) = self.current_function.as_mut() {
func.update_cfg();
}
let rhs_bool = self.insert_phi_binary(rhs_true_exit, t_id, rhs_false_exit, f_id)?;
self.value_types.insert(rhs_bool, MirType::Bool);
rhs_bool
@ -323,7 +376,9 @@ impl super::MirBuilder {
let rhs_val = self.build_expression(right)?;
let mut rhs_cond = self.local_cond(rhs_val);
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut rhs_cond);
crate::mir::builder::emission::branch::emit_conditional(self, rhs_cond, rhs_true, rhs_false)?;
crate::mir::builder::emission::branch::emit_conditional(
self, rhs_cond, rhs_true, rhs_false,
)?;
// true path
self.start_new_block(rhs_true)?;
let t_id = crate::mir::builder::emission::constant::emit_bool(self, true);
@ -336,7 +391,9 @@ impl super::MirBuilder {
let rhs_false_exit = self.current_block()?;
// join rhs result into a single bool
self.start_new_block(rhs_join)?;
if let Some(func) = self.current_function.as_mut() { func.update_cfg(); }
if let Some(func) = self.current_function.as_mut() {
func.update_cfg();
}
let rhs_bool = self.insert_phi_binary(rhs_true_exit, t_id, rhs_false_exit, f_id)?;
self.value_types.insert(rhs_bool, MirType::Bool);
rhs_bool
@ -357,10 +414,16 @@ impl super::MirBuilder {
// Result PHI (bool) — consider only reachable predecessors
let mut inputs: Vec<(crate::mir::BasicBlockId, ValueId)> = Vec::new();
if then_reaches_merge { inputs.push((then_exit_block, then_value_raw)); }
if else_reaches_merge { inputs.push((else_exit_block, else_value_raw)); }
if then_reaches_merge {
inputs.push((then_exit_block, then_value_raw));
}
if else_reaches_merge {
inputs.push((else_exit_block, else_value_raw));
}
let result_val = if inputs.len() >= 2 {
if let Some(func) = self.current_function.as_mut() { func.update_cfg(); }
if let Some(func) = self.current_function.as_mut() {
func.update_cfg();
}
let dst = self.insert_phi(inputs)?;
self.value_types.insert(dst, MirType::Bool);
dst
@ -375,8 +438,16 @@ impl super::MirBuilder {
self.merge_modified_vars(
then_block,
else_block,
if then_reaches_merge { Some(then_exit_block) } else { None },
if else_reaches_merge { Some(else_exit_block) } else { None },
if then_reaches_merge {
Some(then_exit_block)
} else {
None
},
if else_reaches_merge {
Some(else_exit_block)
} else {
None
},
&pre_if_var_map,
&then_var_map_end,
&Some(else_var_map_end),
@ -394,19 +465,38 @@ impl super::MirBuilder {
operand: ASTNode,
) -> Result<ValueId, String> {
let operand_val = self.build_expression(operand)?;
let all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL").ok().as_deref() == Some("1");
let all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL")
.ok()
.as_deref()
== Some("1");
if all_call {
let (name, guard_prefix, rett) = match operator.as_str() {
"-" => ("NegOperator.apply/1", "NegOperator.apply/", MirType::Integer),
"-" => (
"NegOperator.apply/1",
"NegOperator.apply/",
MirType::Integer,
),
"!" | "not" => ("NotOperator.apply/1", "NotOperator.apply/", MirType::Bool),
"~" => ("BitNotOperator.apply/1", "BitNotOperator.apply/", MirType::Integer),
"~" => (
"BitNotOperator.apply/1",
"BitNotOperator.apply/",
MirType::Integer,
),
_ => ("", "", MirType::Integer),
};
if !name.is_empty() {
let in_guard = self.current_function.as_ref().map(|f| f.signature.name.starts_with(guard_prefix)).unwrap_or(false);
let in_guard = self
.current_function
.as_ref()
.map(|f| f.signature.name.starts_with(guard_prefix))
.unwrap_or(false);
let dst = self.next_value_id();
if !in_guard {
self.emit_legacy_call(Some(dst), super::builder_calls::CallTarget::Global(name.to_string()), vec![operand_val])?;
self.emit_legacy_call(
Some(dst),
super::builder_calls::CallTarget::Global(name.to_string()),
vec![operand_val],
)?;
self.value_types.insert(dst, rett);
return Ok(dst);
}

View File

@ -7,13 +7,17 @@ use super::super::{MirBuilder, MirType, ValueId};
pub(crate) fn annotate_me_origin(builder: &mut MirBuilder, me_id: ValueId) {
let mut cls: Option<String> = None;
if let Some(c) = builder.current_static_box.clone() {
if !c.is_empty() { cls = Some(c); }
if !c.is_empty() {
cls = Some(c);
}
}
if cls.is_none() {
if let Some(ref fun) = builder.current_function {
if let Some(dot) = fun.signature.name.find('.') {
let c = fun.signature.name[..dot].to_string();
if !c.is_empty() { cls = Some(c); }
if !c.is_empty() {
cls = Some(c);
}
}
}
}

View File

@ -10,4 +10,3 @@
pub mod infer;
pub mod phi;

View File

@ -1,4 +1,4 @@
use super::super::{MirBuilder, MirType, ValueId, BasicBlockId};
use super::super::{BasicBlockId, MirBuilder, MirType, ValueId};
/// Lightweight propagation at PHI when all inputs agree型/起源)。
/// 仕様は不変: 一致時のみ dst にコピーする(不一致/未知は何もしない)。
@ -15,13 +15,21 @@ pub(crate) fn propagate_phi_meta(
match &common_ty {
None => common_ty = Some(t),
Some(ct) => {
if ct != &t { ty_agree = false; break; }
if ct != &t {
ty_agree = false;
break;
}
}
}
} else { ty_agree = false; break; }
} else {
ty_agree = false;
break;
}
}
if ty_agree {
if let Some(ct) = common_ty { builder.value_types.insert(dst, ct); }
if let Some(ct) = common_ty {
builder.value_types.insert(dst, ct);
}
}
// Origin一致のときだけコピー
let mut common_cls: Option<String> = None;
@ -30,9 +38,21 @@ pub(crate) fn propagate_phi_meta(
if let Some(c) = builder.value_origin_newbox.get(v).cloned() {
match &common_cls {
None => common_cls = Some(c),
Some(cc) => { if cc != &c { cls_agree = false; break; } }
Some(cc) => {
if cc != &c {
cls_agree = false;
break;
}
}
}
} else { cls_agree = false; break; }
} else {
cls_agree = false;
break;
}
}
if cls_agree {
if let Some(cc) = common_cls {
builder.value_origin_newbox.insert(dst, cc);
}
}
if cls_agree { if let Some(cc) = common_cls { builder.value_origin_newbox.insert(dst, cc); } }
}

View File

@ -31,28 +31,38 @@ impl MirBuilder {
let changed_set: HashSet<String> = conservative.changed_vars.iter().cloned().collect();
// Use PhiMergeHelper for unified variable merging
let mut helper = super::phi_merge::PhiMergeHelper::new(
self,
then_exit_block_opt,
else_exit_block_opt,
);
let mut helper =
super::phi_merge::PhiMergeHelper::new(self, then_exit_block_opt, else_exit_block_opt);
helper.merge_all_vars(pre_if_snapshot, then_map_end, else_map_end_opt, skip_var)?;
// Ensure pinned synthetic slots ("__pin$...") have a block-local definition at the merge,
// even if their values did not change across branches. This avoids undefined uses when
// subsequent blocks re-use pinned values without modifications.
for (pin_name, pre_val) in pre_if_snapshot.iter() {
if !pin_name.starts_with("__pin$") { continue; }
if skip_var.map(|s| s == pin_name.as_str()).unwrap_or(false) { continue; }
if changed_set.contains(pin_name) { continue; }
let then_v = then_map_end.get(pin_name.as_str()).copied().unwrap_or(*pre_val);
if !pin_name.starts_with("__pin$") {
continue;
}
if skip_var.map(|s| s == pin_name.as_str()).unwrap_or(false) {
continue;
}
if changed_set.contains(pin_name) {
continue;
}
let then_v = then_map_end
.get(pin_name.as_str())
.copied()
.unwrap_or(*pre_val);
let else_v = else_map_end_opt
.as_ref()
.and_then(|m| m.get(pin_name.as_str()).copied())
.unwrap_or(*pre_val);
let mut inputs: Vec<(super::BasicBlockId, super::ValueId)> = Vec::new();
if let Some(tp) = then_exit_block_opt { inputs.push((tp, then_v)); }
if let Some(ep) = else_exit_block_opt { inputs.push((ep, else_v)); }
if let Some(tp) = then_exit_block_opt {
inputs.push((tp, then_v));
}
if let Some(ep) = else_exit_block_opt {
inputs.push((ep, else_v));
}
match inputs.len() {
0 => {}
1 => {
@ -60,15 +70,27 @@ impl MirBuilder {
self.variable_map.insert(pin_name.clone(), v);
}
_ => {
if let Some(func) = self.current_function.as_mut() { func.update_cfg(); }
if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) {
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs);
if let Some(func) = self.current_function.as_mut() {
func.update_cfg();
}
if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block)
{
crate::mir::phi_core::common::debug_verify_phi_inputs(
func, cur_bb, &inputs,
);
}
let merged = self.next_value_id();
if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) {
crate::mir::ssot::cf_common::insert_phi_at_head(func, cur_bb, merged, inputs);
if let (Some(func), Some(cur_bb)) =
(self.current_function.as_mut(), self.current_block)
{
crate::mir::ssot::cf_common::insert_phi_at_head(
func, cur_bb, merged, inputs,
);
} else {
self.emit_instruction(MirInstruction::Phi { dst: merged, inputs })?;
self.emit_instruction(MirInstruction::Phi {
dst: merged,
inputs,
})?;
}
self.variable_map.insert(pin_name.clone(), merged);
}
@ -95,7 +117,8 @@ impl MirBuilder {
) -> Result<ValueId, String> {
// If only the then-branch assigns a variable (e.g., `if c { x = ... }`) and the else
// does not assign the same variable, bind that variable to a Phi of (then_value, pre_if_value).
let assigned_var_then = crate::mir::phi_core::if_phi::extract_assigned_var(then_ast_for_analysis);
let assigned_var_then =
crate::mir::phi_core::if_phi::extract_assigned_var(then_ast_for_analysis);
let assigned_var_else = else_ast_for_analysis
.as_ref()
.and_then(|a| crate::mir::phi_core::if_phi::extract_assigned_var(a));
@ -131,8 +154,12 @@ impl MirBuilder {
};
// Build inputs from reachable predecessors only
let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
if let Some(tp) = then_exit_block_opt { inputs.push((tp, then_value_for_var)); }
if let Some(ep) = else_exit_block_opt { inputs.push((ep, else_value_for_var)); }
if let Some(tp) = then_exit_block_opt {
inputs.push((tp, then_value_for_var));
}
if let Some(ep) = else_exit_block_opt {
inputs.push((ep, else_value_for_var));
}
match inputs.len() {
0 => {}
1 => {
@ -142,9 +169,14 @@ impl MirBuilder {
return Ok(inputs[0].1);
}
_ => {
if let Some(func) = self.current_function.as_mut() { func.update_cfg(); }
if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) {
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs);
if let Some(func) = self.current_function.as_mut() {
func.update_cfg();
}
if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block)
{
crate::mir::phi_core::common::debug_verify_phi_inputs(
func, cur_bb, &inputs,
);
}
self.insert_phi_with_dst(result_val, inputs)?;
}
@ -154,10 +186,15 @@ impl MirBuilder {
} else {
// No variable assignment pattern detected just emit Phi for expression result
let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
if let Some(tp) = then_exit_block_opt { inputs.push((tp, then_value_raw)); }
if let Some(ep) = else_exit_block_opt { inputs.push((ep, else_value_raw)); }
if let Some(tp) = then_exit_block_opt {
inputs.push((tp, then_value_raw));
}
if let Some(ep) = else_exit_block_opt {
inputs.push((ep, else_value_raw));
}
match inputs.len() {
0 => { /* leave result_val as fresh, but unused; synthesize void */
0 => {
/* leave result_val as fresh, but unused; synthesize void */
let v = crate::mir::builder::emission::constant::emit_void(self);
return Ok(v);
}
@ -165,9 +202,14 @@ impl MirBuilder {
return Ok(inputs[0].1);
}
_ => {
if let Some(func) = self.current_function.as_mut() { func.update_cfg(); }
if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) {
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs);
if let Some(func) = self.current_function.as_mut() {
func.update_cfg();
}
if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block)
{
crate::mir::phi_core::common::debug_verify_phi_inputs(
func, cur_bb, &inputs,
);
}
self.insert_phi_with_dst(result_val, inputs)?;
}

View File

@ -9,7 +9,14 @@ use crate::mir::definitions::call_unified::Callee;
/// - Ensure the receiver has an in-block definition via LocalSSA (Copy in the current block).
/// - Args の LocalSSA は別レイヤssa::localで扱う。
pub fn finalize_method_receiver(builder: &mut MirBuilder, callee: &mut Callee) {
if let Callee::Method { box_name, method, receiver: Some(r), certainty, box_kind } = callee.clone() {
if let Callee::Method {
box_name,
method,
receiver: Some(r),
certainty,
box_kind,
} = callee.clone()
{
// Pin to a named slot so start_new_block や LoopBuilder が slot 経由で追跡できる
let r_pinned = builder.pin_to_slot(r, "@recv").unwrap_or(r);
@ -40,7 +47,12 @@ pub fn finalize_method_receiver(builder: &mut MirBuilder, callee: &mut Callee) {
// LocalSSA: ensure an in-block definition in the current block
let r_local = crate::mir::builder::ssa::local::recv(builder, r_pinned);
*callee = Callee::Method { box_name, method, receiver: Some(r_local), certainty, box_kind };
*callee = Callee::Method {
box_name,
method,
receiver: Some(r_local),
certainty,
box_kind,
};
}
}

View File

@ -5,12 +5,20 @@ fn rewrite_enabled() -> bool {
// New primary flag (P4): NYASH_REWRITE_KNOWN_DEFAULT (default ON; allow explicit OFF)
if let Ok(v) = std::env::var("NYASH_REWRITE_KNOWN_DEFAULT") {
let s = v.to_ascii_lowercase();
if s == "0" || s == "false" || s == "off" { return false; }
if s == "1" || s == "true" || s == "on" { return true; }
if s == "0" || s == "false" || s == "off" {
return false;
}
if s == "1" || s == "true" || s == "on" {
return true;
}
// fallthrough to legacy if malformed
}
// Legacy flag (kept for compatibility): NYASH_BUILDER_REWRITE_INSTANCE (default ON)
match std::env::var("NYASH_BUILDER_REWRITE_INSTANCE").ok().as_deref().map(|v| v.to_ascii_lowercase()) {
match std::env::var("NYASH_BUILDER_REWRITE_INSTANCE")
.ok()
.as_deref()
.map(|v| v.to_ascii_lowercase())
{
Some(ref s) if s == "0" || s == "false" || s == "off" => false,
Some(ref s) if s == "1" || s == "true" || s == "on" => true,
_ => true, // default ON (spec unchanged; can opt out by setting ...=0)
@ -40,13 +48,23 @@ pub(crate) fn try_known_rewrite(
return None;
}
// Policy gates従来互換
let allow_userbox_rewrite = std::env::var("NYASH_DEV_REWRITE_USERBOX").ok().as_deref() == Some("1");
let allow_new_origin = std::env::var("NYASH_DEV_REWRITE_NEW_ORIGIN").ok().as_deref() == Some("1");
let allow_userbox_rewrite =
std::env::var("NYASH_DEV_REWRITE_USERBOX").ok().as_deref() == Some("1");
let allow_new_origin = std::env::var("NYASH_DEV_REWRITE_NEW_ORIGIN")
.ok()
.as_deref()
== Some("1");
let from_new_origin = builder.value_origin_newbox.get(&object_value).is_some();
let arity = arg_values.len();
let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(cls, method, arity);
let module_has = if let Some(ref module) = builder.current_module { module.functions.contains_key(&fname) } else { false };
if !( (module_has || allow_userbox_rewrite) || (from_new_origin && allow_new_origin) ) {
let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(
cls, method, arity,
);
let module_has = if let Some(ref module) = builder.current_module {
module.functions.contains_key(&fname)
} else {
false
};
if !((module_has || allow_userbox_rewrite) || (from_new_origin && allow_new_origin)) {
return None;
}
// Materialize function call: pass 'me' first, then args (unified call)
@ -59,7 +77,9 @@ pub(crate) fn try_known_rewrite(
Some(dst),
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
call_args,
) { return Some(Err(e)); }
) {
return Some(Err(e));
}
// Annotate and emit choose
let chosen = fname.clone();
builder.annotate_call_result_from_func_name(dst, &chosen);
@ -84,16 +104,34 @@ pub(crate) fn try_known_rewrite_to_dst(
method: &str,
mut arg_values: Vec<ValueId>,
) -> Option<Result<ValueId, String>> {
if !rewrite_enabled() { return None; }
if builder.value_origin_newbox.get(&object_value).is_none() { return None; }
if !builder.user_defined_boxes.contains(cls) { return None; }
let allow_userbox_rewrite = std::env::var("NYASH_DEV_REWRITE_USERBOX").ok().as_deref() == Some("1");
let allow_new_origin = std::env::var("NYASH_DEV_REWRITE_NEW_ORIGIN").ok().as_deref() == Some("1");
if !rewrite_enabled() {
return None;
}
if builder.value_origin_newbox.get(&object_value).is_none() {
return None;
}
if !builder.user_defined_boxes.contains(cls) {
return None;
}
let allow_userbox_rewrite =
std::env::var("NYASH_DEV_REWRITE_USERBOX").ok().as_deref() == Some("1");
let allow_new_origin = std::env::var("NYASH_DEV_REWRITE_NEW_ORIGIN")
.ok()
.as_deref()
== Some("1");
let from_new_origin = builder.value_origin_newbox.get(&object_value).is_some();
let arity = arg_values.len();
let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(cls, method, arity);
let module_has = if let Some(ref module) = builder.current_module { module.functions.contains_key(&fname) } else { false };
if !((module_has || allow_userbox_rewrite) || (from_new_origin && allow_new_origin)) { return None; }
let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(
cls, method, arity,
);
let module_has = if let Some(ref module) = builder.current_module {
module.functions.contains_key(&fname)
} else {
false
};
if !((module_has || allow_userbox_rewrite) || (from_new_origin && allow_new_origin)) {
return None;
}
// unified global function call (module-local)
let mut call_args = Vec::with_capacity(arity + 1);
call_args.push(object_value);
@ -104,7 +142,9 @@ pub(crate) fn try_known_rewrite_to_dst(
Some(actual_dst),
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
call_args,
) { return Some(Err(e)); }
) {
return Some(Err(e));
}
builder.annotate_call_result_from_func_name(actual_dst, &fname);
let meta = serde_json::json!({
"recv_cls": cls,
@ -157,7 +197,9 @@ pub(crate) fn try_unique_suffix_rewrite(
Some(dst),
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
call_args,
) { return Some(Err(e)); }
) {
return Some(Err(e));
}
builder.annotate_call_result_from_func_name(dst, &fname);
let meta = serde_json::json!({
"recv_cls": builder.value_origin_newbox.get(&object_value).cloned().unwrap_or_default(),
@ -179,13 +221,26 @@ pub(crate) fn try_unique_suffix_rewrite_to_dst(
method: &str,
mut arg_values: Vec<ValueId>,
) -> Option<Result<ValueId, String>> {
if !rewrite_enabled() { return None; }
if builder.value_origin_newbox.get(&object_value).is_none() { return None; }
if !rewrite_enabled() {
return None;
}
if builder.value_origin_newbox.get(&object_value).is_none() {
return None;
}
let mut cands: Vec<String> = builder.method_candidates(method, arg_values.len());
if cands.len() != 1 { return None; }
if cands.len() != 1 {
return None;
}
let fname = cands.remove(0);
if let Some((bx, _)) = fname.split_once('.') { if !builder.user_defined_boxes.contains(bx) { return None; } } else { return None; }
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
if let Some((bx, _)) = fname.split_once('.') {
if !builder.user_defined_boxes.contains(bx) {
return None;
}
} else {
return None;
}
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname)
{
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
@ -199,7 +254,9 @@ pub(crate) fn try_unique_suffix_rewrite_to_dst(
Some(actual_dst),
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
call_args,
) { return Some(Err(e)); }
) {
return Some(Err(e));
}
builder.annotate_call_result_from_func_name(actual_dst, &fname);
let meta = serde_json::json!({
"recv_cls": builder.value_origin_newbox.get(&object_value).cloned().unwrap_or_default(),
@ -223,7 +280,8 @@ pub(crate) fn try_known_or_unique(
arg_values: Vec<ValueId>,
) -> Option<Result<ValueId, String>> {
if let Some(cls) = class_name_opt.as_ref() {
if let Some(res) = try_known_rewrite(builder, object_value, cls, method, arg_values.clone()) {
if let Some(res) = try_known_rewrite(builder, object_value, cls, method, arg_values.clone())
{
return Some(res);
}
}
@ -240,7 +298,14 @@ pub(crate) fn try_known_or_unique_to_dst(
arg_values: Vec<ValueId>,
) -> Option<Result<ValueId, String>> {
if let Some(cls) = class_name_opt.as_ref() {
if let Some(res) = try_known_rewrite_to_dst(builder, want_dst, object_value, cls, method, arg_values.clone()) {
if let Some(res) = try_known_rewrite_to_dst(
builder,
want_dst,
object_value,
cls,
method,
arg_values.clone(),
) {
return Some(res);
}
}

View File

@ -7,4 +7,3 @@
pub mod known;
pub mod special;

View File

@ -13,11 +13,21 @@ pub(crate) fn try_early_str_like(
if !(method == "toString" || method == "stringify") || arity != 0 {
return None;
}
let module = match &builder.current_module { Some(m) => m, None => return None };
let module = match &builder.current_module {
Some(m) => m,
None => return None,
};
// Prefer class-qualified str if we can infer class; fallback to stringify for互換
if let Some(cls) = class_name_opt.clone() {
let str_name = crate::mir::builder::calls::function_lowering::generate_method_function_name(&cls, "str", 0);
let compat_stringify = crate::mir::builder::calls::function_lowering::generate_method_function_name(&cls, "stringify", 0);
let str_name = crate::mir::builder::calls::function_lowering::generate_method_function_name(
&cls, "str", 0,
);
let compat_stringify =
crate::mir::builder::calls::function_lowering::generate_method_function_name(
&cls,
"stringify",
0,
);
let have_str = module.functions.contains_key(&str_name);
let have_compat = module.functions.contains_key(&compat_stringify);
if have_str || (!have_str && have_compat) {
@ -32,7 +42,7 @@ pub(crate) fn try_early_str_like(
"certainty": "Known",
});
super::super::observe::resolve::emit_choose(builder, meta);
// unified
// unified
let mut call_args = Vec::with_capacity(1);
call_args.push(object_value);
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
@ -41,7 +51,9 @@ pub(crate) fn try_early_str_like(
Some(dst),
crate::mir::builder::builder_calls::CallTarget::Global(chosen.clone()),
call_args,
) { return Some(Err(e)); }
) {
return Some(Err(e));
}
builder.annotate_call_result_from_func_name(dst, &chosen);
return Some(Ok(dst));
}
@ -71,7 +83,9 @@ pub(crate) fn try_early_str_like(
Some(dst),
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
call_args,
) { return Some(Err(e)); }
) {
return Some(Err(e));
}
builder.annotate_call_result_from_func_name(dst, &fname);
return Some(Ok(dst));
} else if cands.len() > 1 {
@ -95,7 +109,9 @@ pub(crate) fn try_early_str_like(
Some(dst),
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
call_args,
) { return Some(Err(e)); }
) {
return Some(Err(e));
}
builder.annotate_call_result_from_func_name(dst, &fname);
return Some(Ok(dst));
}
@ -113,10 +129,14 @@ pub(crate) fn try_special_equals(
method: &str,
args: Vec<super::super::ValueId>,
) -> Option<Result<super::super::ValueId, String>> {
if method != "equals" || args.len() != 1 { return None; }
if method != "equals" || args.len() != 1 {
return None;
}
// First, Known rewrite if possible
if let Some(cls) = class_name_opt.as_ref() {
if let Some(res) = super::known::try_known_rewrite(builder, object_value, cls, method, args.clone()) {
if let Some(res) =
super::known::try_known_rewrite(builder, object_value, cls, method, args.clone())
{
return Some(res);
}
}
@ -136,10 +156,20 @@ pub(crate) fn try_early_str_like_to_dst(
if !(method == "toString" || method == "stringify") || arity != 0 {
return None;
}
let module = match &builder.current_module { Some(m) => m, None => return None };
let module = match &builder.current_module {
Some(m) => m,
None => return None,
};
if let Some(cls) = class_name_opt.clone() {
let str_name = crate::mir::builder::calls::function_lowering::generate_method_function_name(&cls, "str", 0);
let compat_stringify = crate::mir::builder::calls::function_lowering::generate_method_function_name(&cls, "stringify", 0);
let str_name = crate::mir::builder::calls::function_lowering::generate_method_function_name(
&cls, "str", 0,
);
let compat_stringify =
crate::mir::builder::calls::function_lowering::generate_method_function_name(
&cls,
"stringify",
0,
);
let have_str = module.functions.contains_key(&str_name);
let have_compat = module.functions.contains_key(&compat_stringify);
if have_str || (!have_str && have_compat) {
@ -153,10 +183,11 @@ pub(crate) fn try_early_str_like_to_dst(
"certainty": "Known",
});
super::super::observe::resolve::emit_choose(builder, meta);
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &chosen) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
let _name_const =
match crate::mir::builder::name_const::make_name_const_result(builder, &chosen) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
let mut call_args = Vec::with_capacity(1);
call_args.push(object_value);
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
@ -165,7 +196,9 @@ pub(crate) fn try_early_str_like_to_dst(
Some(actual_dst),
crate::mir::builder::builder_calls::CallTarget::Global(chosen.clone()),
call_args,
) { return Some(Err(e)); }
) {
return Some(Err(e));
}
builder.annotate_call_result_from_func_name(actual_dst, &chosen);
return Some(Ok(actual_dst));
}
@ -184,10 +217,11 @@ pub(crate) fn try_early_str_like_to_dst(
"certainty": "Heuristic",
});
super::super::observe::resolve::emit_choose(builder, meta);
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
let _name_const =
match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
let mut call_args = Vec::with_capacity(1);
call_args.push(object_value);
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
@ -196,7 +230,9 @@ pub(crate) fn try_early_str_like_to_dst(
Some(actual_dst),
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
call_args,
) { return Some(Err(e)); }
) {
return Some(Err(e));
}
builder.annotate_call_result_from_func_name(actual_dst, &fname);
return Some(Ok(actual_dst));
} else if cands.len() > 1 {
@ -211,19 +247,22 @@ pub(crate) fn try_early_str_like_to_dst(
"certainty": "Heuristic",
});
super::super::observe::resolve::emit_choose(builder, meta);
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
let mut call_args = Vec::with_capacity(1);
call_args.push(object_value);
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
let _name_const =
match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
let mut call_args = Vec::with_capacity(1);
call_args.push(object_value);
crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
let actual_dst = want_dst.unwrap_or_else(|| builder.next_value_id());
if let Err(e) = builder.emit_unified_call(
Some(actual_dst),
crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
call_args,
) { return Some(Err(e)); }
) {
return Some(Err(e));
}
builder.annotate_call_result_from_func_name(actual_dst, &fname);
return Some(Ok(actual_dst));
}
@ -240,9 +279,18 @@ pub(crate) fn try_special_equals_to_dst(
method: &str,
args: Vec<super::super::ValueId>,
) -> Option<Result<super::super::ValueId, String>> {
if method != "equals" || args.len() != 1 { return None; }
if method != "equals" || args.len() != 1 {
return None;
}
if let Some(cls) = class_name_opt.as_ref() {
if let Some(res) = super::known::try_known_rewrite_to_dst(builder, want_dst, object_value, cls, method, args.clone()) {
if let Some(res) = super::known::try_known_rewrite_to_dst(
builder,
want_dst,
object_value,
cls,
method,
args.clone(),
) {
return Some(res);
}
}

View File

@ -1,3 +1,2 @@
//! Router policy module
pub mod policy;

View File

@ -8,11 +8,17 @@ pub struct BlockScheduleBox;
impl BlockScheduleBox {
/// Insert a Copy immediately after PHI nodes. Returns the local value id.
#[allow(dead_code)]
pub fn ensure_after_phis_copy(builder: &mut MirBuilder, src: ValueId) -> Result<ValueId, String> {
pub fn ensure_after_phis_copy(
builder: &mut MirBuilder,
src: ValueId,
) -> Result<ValueId, String> {
if let Some(bb) = builder.current_block {
if let Some(&cached) = builder.schedule_mat_map.get(&(bb, src)) {
if std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") {
eprintln!("[schedule/after-phis] bb={:?} src=%{} cached dst=%{}", bb, src.0, cached.0);
eprintln!(
"[schedule/after-phis] bb={:?} src=%{} cached dst=%{}",
bb, src.0, cached.0
);
}
return Ok(cached);
}
@ -31,13 +37,18 @@ impl BlockScheduleBox {
/// Emit a Copy right before the next emitted instruction (best-effort):
/// place it at the tail of the current block. Returns the local value id.
#[allow(dead_code)]
pub fn emit_before_call_copy(builder: &mut MirBuilder, src: ValueId) -> Result<ValueId, String> {
pub fn emit_before_call_copy(
builder: &mut MirBuilder,
src: ValueId,
) -> Result<ValueId, String> {
// Prefer to reuse the after-phis materialized id for this src in this block
let base = Self::ensure_after_phis_copy(builder, src)?;
let dst = builder.next_value_id();
if std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") {
eprintln!("[schedule/before-call] bb={:?} src=%{} base=%{} dst=%{} (emitting Copy)",
builder.current_block, src.0, base.0, dst.0);
eprintln!(
"[schedule/before-call] bb={:?} src=%{} base=%{} dst=%{} (emitting Copy)",
builder.current_block, src.0, base.0, dst.0
);
}
builder.emit_instruction(MirInstruction::Copy { dst, src: base })?;
// Propagate metadata to keep dst consistent with base
@ -54,8 +65,12 @@ impl BlockScheduleBox {
return;
}
let (f_opt, bb_opt) = (builder.current_function.as_ref(), builder.current_block);
let (Some(fun), Some(bb_id)) = (f_opt, bb_opt) else { return; };
let Some(bb) = fun.get_block(bb_id) else { return; };
let (Some(fun), Some(bb_id)) = (f_opt, bb_opt) else {
return;
};
let Some(bb) = fun.get_block(bb_id) else {
return;
};
// 1) PHI group must be at head
let mut seen_non_phi = false;
@ -66,27 +81,27 @@ impl BlockScheduleBox {
eprintln!("[block-schedule][verify] WARN: PHI found after non-PHI at bb={:?} idx={}", bb_id, idx);
}
}
_ => { seen_non_phi = true; }
_ => {
seen_non_phi = true;
}
}
}
// 2) If a Copy is immediately before a Call-like, prefer it to be derived from after-PHIs copy
let is_call_like = |mi: &MirInstruction| -> bool {
matches!(mi,
MirInstruction::Call { .. } |
MirInstruction::BoxCall { .. } |
MirInstruction::PluginInvoke { .. } |
MirInstruction::ExternCall { .. }
matches!(
mi,
MirInstruction::Call { .. }
| MirInstruction::BoxCall { .. }
| MirInstruction::PluginInvoke { .. }
| MirInstruction::ExternCall { .. }
)
};
for w in bb.instructions.windows(2) {
if let [MirInstruction::Copy { dst: _, src }, call] = w {
if is_call_like(call) {
// best-effort: src should be one of the after-PHIs materialized ids for this bb
let derived_ok = builder
.schedule_mat_map
.values()
.any(|&v| v == *src);
let derived_ok = builder.schedule_mat_map.values().any(|&v| v == *src);
if !derived_ok {
eprintln!(
"[block-schedule][verify] WARN: tail Copy src=%{} is not from after-PHIs in bb={:?}",

View File

@ -1,2 +1 @@
pub mod block;

View File

@ -45,8 +45,10 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
// CRITICAL: Check for errors - if block creation fails, return original value.
if let Err(e) = builder.ensure_block_exists(bb) {
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
eprintln!("[local-ssa] ensure_block_exists FAILED bb={:?} kind={:?} v=%{} err={}",
bb, kind, v.0, e);
eprintln!(
"[local-ssa] ensure_block_exists FAILED bb={:?} kind={:?} v=%{} err={}",
bb, kind, v.0, e
);
}
return v;
}
@ -60,7 +62,9 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
// and then look up the current ValueId for that slot.
let mut slot_name_opt: Option<String> = None;
let names_for_v: Vec<String> = builder.variable_map.iter()
let names_for_v: Vec<String> = builder
.variable_map
.iter()
.filter(|(k, &vid)| vid == v && k.starts_with("__pin$"))
.map(|(k, _)| k.clone())
.collect();
@ -77,8 +81,10 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
// The slot has been updated (likely by a PHI or header rewrite).
// Use the updated value instead of the stale pinned ValueId.
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
eprintln!("[local-ssa] phi-redirect bb={:?} kind={:?} slot={} %{} -> %{}",
bb, kind, slot_name, v.0, current_val.0);
eprintln!(
"[local-ssa] phi-redirect bb={:?} kind={:?} slot={} %{} -> %{}",
bb, kind, slot_name, v.0, current_val.0
);
}
builder.local_ssa_map.insert(key, current_val);
return current_val;
@ -89,7 +95,9 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
let loc = builder.next_value_id();
// CRITICAL: Check emit_instruction result - if Copy fails, return original value
// to avoid returning undefined ValueId.
if let Err(e) = builder.emit_instruction(crate::mir::MirInstruction::Copy { dst: loc, src: v }) {
if let Err(e) =
builder.emit_instruction(crate::mir::MirInstruction::Copy { dst: loc, src: v })
{
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
eprintln!("[local-ssa] emit_instruction Copy FAILED bb={:?} kind={:?} v=%{} dst=%{} err={}",
bb, kind, v.0, loc.0, e);
@ -98,7 +106,10 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
return v;
}
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
eprintln!("[local-ssa] copy bb={:?} kind={:?} %{} -> %{}", bb, kind, v.0, loc.0);
eprintln!(
"[local-ssa] copy bb={:?} kind={:?} %{} -> %{}",
bb, kind, v.0, loc.0
);
}
// Success: register metadata and cache
if let Some(t) = builder.value_types.get(&v).cloned() {
@ -115,19 +126,29 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
}
#[inline]
pub fn recv(builder: &mut MirBuilder, v: ValueId) -> ValueId { ensure(builder, v, LocalKind::Recv) }
pub fn recv(builder: &mut MirBuilder, v: ValueId) -> ValueId {
ensure(builder, v, LocalKind::Recv)
}
#[inline]
pub fn arg(builder: &mut MirBuilder, v: ValueId) -> ValueId { ensure(builder, v, LocalKind::Arg) }
pub fn arg(builder: &mut MirBuilder, v: ValueId) -> ValueId {
ensure(builder, v, LocalKind::Arg)
}
#[inline]
pub fn cond(builder: &mut MirBuilder, v: ValueId) -> ValueId { ensure(builder, v, LocalKind::Cond) }
pub fn cond(builder: &mut MirBuilder, v: ValueId) -> ValueId {
ensure(builder, v, LocalKind::Cond)
}
#[inline]
pub fn field_base(builder: &mut MirBuilder, v: ValueId) -> ValueId { ensure(builder, v, LocalKind::FieldBase) }
pub fn field_base(builder: &mut MirBuilder, v: ValueId) -> ValueId {
ensure(builder, v, LocalKind::FieldBase)
}
#[inline]
pub fn cmp_operand(builder: &mut MirBuilder, v: ValueId) -> ValueId { ensure(builder, v, LocalKind::CompareOperand) }
pub fn cmp_operand(builder: &mut MirBuilder, v: ValueId) -> ValueId {
ensure(builder, v, LocalKind::CompareOperand)
}
/// Finalize only the args (legacy Call paths)
pub fn finalize_args(builder: &mut MirBuilder, args: &mut Vec<ValueId>) {
@ -141,7 +162,12 @@ pub fn finalize_args(builder: &mut MirBuilder, args: &mut Vec<ValueId>) {
pub fn finalize_branch_cond(builder: &mut MirBuilder, condition_v: &mut ValueId) {
*condition_v = cond(builder, *condition_v);
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
if let Some(bb) = builder.current_block { eprintln!("[local-ssa] finalize-branch bb={:?} cond=%{}", bb, condition_v.0); }
if let Some(bb) = builder.current_block {
eprintln!(
"[local-ssa] finalize-branch bb={:?} cond=%{}",
bb, condition_v.0
);
}
}
}
@ -151,15 +177,33 @@ pub fn finalize_compare(builder: &mut MirBuilder, lhs: &mut ValueId, rhs: &mut V
*lhs = cmp_operand(builder, *lhs);
*rhs = cmp_operand(builder, *rhs);
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
if let Some(bb) = builder.current_block { eprintln!("[local-ssa] finalize-compare bb={:?} lhs=%{} rhs=%{}", bb, lhs.0, rhs.0); }
if let Some(bb) = builder.current_block {
eprintln!(
"[local-ssa] finalize-compare bb={:?} lhs=%{} rhs=%{}",
bb, lhs.0, rhs.0
);
}
}
}
/// Finalize field use sites: ensure base and all args are in the current block.
pub fn finalize_field_base_and_args(builder: &mut MirBuilder, base: &mut ValueId, args: &mut Vec<ValueId>) {
pub fn finalize_field_base_and_args(
builder: &mut MirBuilder,
base: &mut ValueId,
args: &mut Vec<ValueId>,
) {
*base = field_base(builder, *base);
for a in args.iter_mut() { *a = arg(builder, *a); }
for a in args.iter_mut() {
*a = arg(builder, *a);
}
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
if let Some(bb) = builder.current_block { eprintln!("[local-ssa] finalize-field bb={:?} base=%{} argc={}", bb, base.0, args.len()); }
if let Some(bb) = builder.current_block {
eprintln!(
"[local-ssa] finalize-field bb={:?} base=%{} argc={}",
bb,
base.0,
args.len()
);
}
}
}

View File

@ -1,2 +1 @@
pub mod local;

View File

@ -1,7 +1,7 @@
use super::{Effect, EffectMask, MirInstruction, ValueId};
use crate::ast::{ASTNode, CallExpr};
use crate::mir::TypeOpKind;
use crate::mir::utils::is_current_block_terminated;
use crate::mir::TypeOpKind;
impl super::MirBuilder {
// Print statement: env.console.log(value) with early TypeOp handling
@ -10,15 +10,34 @@ impl super::MirBuilder {
// Prefer wrapper for simple function-call pattern (non-breaking refactor)
if let Ok(call) = CallExpr::try_from(expression.clone()) {
if (call.name == "isType" || call.name == "asType") && call.arguments.len() == 2 {
super::utils::builder_debug_log("pattern: print(FunctionCall isType|asType) [via wrapper]");
if let Some(type_name) = super::MirBuilder::extract_string_literal(&call.arguments[1]) {
super::utils::builder_debug_log(&format!("extract_string_literal OK: {}", type_name));
super::utils::builder_debug_log(
"pattern: print(FunctionCall isType|asType) [via wrapper]",
);
if let Some(type_name) =
super::MirBuilder::extract_string_literal(&call.arguments[1])
{
super::utils::builder_debug_log(&format!(
"extract_string_literal OK: {}",
type_name
));
let val = self.build_expression(call.arguments[0].clone())?;
let ty = super::MirBuilder::parse_type_name_to_mir(&type_name);
let dst = self.next_value_id();
let op = if call.name == "isType" { TypeOpKind::Check } else { TypeOpKind::Cast };
super::utils::builder_debug_log(&format!("emit TypeOp {:?} value={} dst= {}", op, val, dst));
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?;
let op = if call.name == "isType" {
TypeOpKind::Check
} else {
TypeOpKind::Cast
};
super::utils::builder_debug_log(&format!(
"emit TypeOp {:?} value={} dst= {}",
op, val, dst
));
self.emit_instruction(MirInstruction::TypeOp {
dst,
op,
value: val,
ty,
})?;
self.emit_instruction(MirInstruction::ExternCall {
dst: None,
iface_name: "env.console".to_string(),
@ -129,7 +148,7 @@ impl super::MirBuilder {
if use_unified {
// New unified path - treat print as global function
self.emit_unified_call(
None, // print returns nothing
None, // print returns nothing
super::builder_calls::CallTarget::Global("print".to_string()),
vec![value],
)?;
@ -155,14 +174,24 @@ impl super::MirBuilder {
let total = statements.len();
eprintln!("[DEBUG/build_block] Processing {} statements", total);
for (idx, statement) in statements.into_iter().enumerate() {
eprintln!("[DEBUG/build_block] Statement {}/{} current_block={:?} current_function={}",
idx+1, total, self.current_block,
self.current_function.as_ref().map(|f| f.signature.name.as_str()).unwrap_or("none"));
eprintln!(
"[DEBUG/build_block] Statement {}/{} current_block={:?} current_function={}",
idx + 1,
total,
self.current_block,
self.current_function
.as_ref()
.map(|f| f.signature.name.as_str())
.unwrap_or("none")
);
last_value = Some(self.build_statement(statement)?);
// If the current block was terminated by this statement (e.g., return/throw),
// do not emit any further instructions for this block.
if is_current_block_terminated(self)? {
eprintln!("[DEBUG/build_block] Block terminated after statement {}", idx+1);
eprintln!(
"[DEBUG/build_block] Block terminated after statement {}",
idx + 1
);
break;
}
}
@ -190,7 +219,6 @@ impl super::MirBuilder {
}
}
// Local declarations with optional initializers
pub(super) fn build_local_statement(
&mut self,
@ -198,7 +226,11 @@ impl super::MirBuilder {
initial_values: Vec<Option<Box<ASTNode>>>,
) -> Result<ValueId, String> {
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[build_local_statement] ENTRY: variables={:?}, initial_values.len()={}", variables, initial_values.len());
eprintln!(
"[build_local_statement] ENTRY: variables={:?}, initial_values.len()={}",
variables,
initial_values.len()
);
}
let mut last_value = None;
for (i, var_name) in variables.iter().enumerate() {
@ -212,12 +244,15 @@ impl super::MirBuilder {
let var_id = self.next_value_id();
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[build_local_statement] '{}': init_val={:?}, allocated var_id={:?}", var_name, init_val, var_id);
eprintln!(
"[build_local_statement] '{}': init_val={:?}, allocated var_id={:?}",
var_name, init_val, var_id
);
}
self.emit_instruction(crate::mir::MirInstruction::Copy {
dst: var_id,
src: init_val
src: init_val,
})?;
// Propagate metadata (type/origin) from initializer to variable
@ -228,13 +263,19 @@ impl super::MirBuilder {
// Create a concrete register for uninitialized locals (Void)
let void_id = crate::mir::builder::emission::constant::emit_void(self);
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[build_local_statement] '{}': uninitialized, void_id={:?}", var_name, void_id);
eprintln!(
"[build_local_statement] '{}': uninitialized, void_id={:?}",
var_name, void_id
);
}
void_id
};
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[build_local_statement] Inserting '{}' -> {:?} into variable_map", var_name, var_id);
eprintln!(
"[build_local_statement] Inserting '{}' -> {:?} into variable_map",
var_name, var_id
);
}
self.variable_map.insert(var_name.clone(), var_id);
// SlotRegistry にもローカル変数スロットを登録しておくよ(観測専用)
@ -267,7 +308,10 @@ impl super::MirBuilder {
// Defer: copy into slot and jump to target
if let (Some(slot), Some(target)) = (self.return_defer_slot, self.return_defer_target) {
self.return_deferred_emitted = true;
self.emit_instruction(MirInstruction::Copy { dst: slot, src: return_value })?;
self.emit_instruction(MirInstruction::Copy {
dst: slot,
src: return_value,
})?;
crate::mir::builder::metadata::propagate::propagate(self, return_value, slot);
if !self.is_current_block_terminated() {
crate::mir::builder::emission::branch::emit_jump(self, target)?;
@ -275,12 +319,16 @@ impl super::MirBuilder {
Ok(return_value)
} else {
// Fallback: no configured slot/target; emit a real return
self.emit_instruction(MirInstruction::Return { value: Some(return_value) })?;
self.emit_instruction(MirInstruction::Return {
value: Some(return_value),
})?;
Ok(return_value)
}
} else {
// Normal return
self.emit_instruction(MirInstruction::Return { value: Some(return_value) })?;
self.emit_instruction(MirInstruction::Return {
value: Some(return_value),
})?;
Ok(return_value)
}
}
@ -299,7 +347,8 @@ impl super::MirBuilder {
} = expression.clone()
{
let recv_val = self.build_expression(*object)?;
let mname_id = crate::mir::builder::emission::constant::emit_string(self, method.clone());
let mname_id =
crate::mir::builder::emission::constant::emit_string(self, method.clone());
let mut arg_vals: Vec<ValueId> = Vec::with_capacity(2 + arguments.len());
arg_vals.push(recv_val);
arg_vals.push(mname_id);

View File

@ -12,7 +12,7 @@ use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct TraceEntry {
pub vid: ValueId,
pub source: String, // "newbox:MapBox", "param:args", "propagate:from_%123"
pub source: String, // "newbox:MapBox", "param:args", "propagate:from_%123"
#[allow(dead_code)]
pub timestamp: usize,
}
@ -36,9 +36,7 @@ pub struct TypeRegistry {
impl TypeRegistry {
/// 新しいレジストリを作成
pub fn new() -> Self {
let trace_enabled = std::env::var("NYASH_TYPE_REGISTRY_TRACE")
.ok()
.as_deref() == Some("1");
let trace_enabled = std::env::var("NYASH_TYPE_REGISTRY_TRACE").ok().as_deref() == Some("1");
Self {
origins: HashMap::new(),
@ -194,7 +192,10 @@ impl TypeRegistry {
// フォールバック: コンテキスト名(警告付き)
if let Some(ctx) = fallback_context {
if self.trace_enabled {
eprintln!("[type-registry] WARNING: fallback to context '{}' for %{}", ctx, vid.0);
eprintln!(
"[type-registry] WARNING: fallback to context '{}' for %{}",
ctx, vid.0
);
}
return ctx.to_string();
}
@ -222,9 +223,15 @@ impl TypeRegistry {
/// 全トレースログを表示
#[allow(dead_code)]
pub fn dump_trace(&self) {
eprintln!("[type-registry] === Trace Log ({} entries) ===", self.trace_log.len());
eprintln!(
"[type-registry] === Trace Log ({} entries) ===",
self.trace_log.len()
);
for entry in &self.trace_log {
eprintln!("[type-registry] #{:04} %{}{}", entry.timestamp, entry.vid.0, entry.source);
eprintln!(
"[type-registry] #{:04} %{}{}",
entry.timestamp, entry.vid.0, entry.source
);
}
}
@ -234,7 +241,10 @@ impl TypeRegistry {
eprintln!("[type-registry] === Statistics ===");
eprintln!("[type-registry] Origins: {} entries", self.origins.len());
eprintln!("[type-registry] Types: {} entries", self.types.len());
eprintln!("[type-registry] Trace log: {} entries", self.trace_log.len());
eprintln!(
"[type-registry] Trace log: {} entries",
self.trace_log.len()
);
}
// ============================================================

View File

@ -1,7 +1,7 @@
//! TypeAnnotationBox — MIR 値への型注釈(仕様不変の最小)
use crate::mir::{MirType, ValueId};
use crate::mir::builder::MirBuilder;
use crate::mir::{MirType, ValueId};
/// 直接的に MirType を設定する(仕様不変)。
#[inline]
@ -22,18 +22,42 @@ pub fn annotate_from_function(builder: &mut MirBuilder, dst: ValueId, func_name:
fn infer_return_type(func_name: &str) -> Option<MirType> {
// Very small whitelist; 仕様不変(既知の戻り型のみ)
// Normalize forms like "JsonNode.str/0" or "StringBox.length/0" if needed
if func_name.ends_with(".str/0") { return Some(MirType::String); }
if func_name.ends_with(".length/0") { return Some(MirType::Integer); }
if func_name.ends_with(".size/0") { return Some(MirType::Integer); }
if func_name.ends_with(".len/0") { return Some(MirType::Integer); }
if func_name.ends_with(".substring/2") { return Some(MirType::String); }
if func_name.ends_with(".esc_json/0") { return Some(MirType::String); }
if func_name.ends_with(".indexOf/1") { return Some(MirType::Integer); }
if func_name.ends_with(".lastIndexOf/1") { return Some(MirType::Integer); }
if func_name.ends_with(".is_digit_char/1") { return Some(MirType::Bool); }
if func_name.ends_with(".is_hex_digit_char/1") { return Some(MirType::Bool); }
if func_name.ends_with(".is_alpha_char/1") { return Some(MirType::Bool); }
if func_name.ends_with("MapBox.has/1") { return Some(MirType::Bool); }
if func_name.ends_with(".str/0") {
return Some(MirType::String);
}
if func_name.ends_with(".length/0") {
return Some(MirType::Integer);
}
if func_name.ends_with(".size/0") {
return Some(MirType::Integer);
}
if func_name.ends_with(".len/0") {
return Some(MirType::Integer);
}
if func_name.ends_with(".substring/2") {
return Some(MirType::String);
}
if func_name.ends_with(".esc_json/0") {
return Some(MirType::String);
}
if func_name.ends_with(".indexOf/1") {
return Some(MirType::Integer);
}
if func_name.ends_with(".lastIndexOf/1") {
return Some(MirType::Integer);
}
if func_name.ends_with(".is_digit_char/1") {
return Some(MirType::Bool);
}
if func_name.ends_with(".is_hex_digit_char/1") {
return Some(MirType::Bool);
}
if func_name.ends_with(".is_alpha_char/1") {
return Some(MirType::Bool);
}
if func_name.ends_with("MapBox.has/1") {
return Some(MirType::Bool);
}
// Fallback: none (変更なし)
None
}

View File

@ -3,4 +3,3 @@
//! - inference.rs後段、挙動不変の観測強化と最小推論
pub mod annotation;

View File

@ -17,7 +17,9 @@ pub(super) fn builder_debug_log(msg: &str) {
if let Ok(cap_s) = std::env::var("NYASH_BUILDER_DEBUG_LIMIT") {
if let Ok(cap) = cap_s.parse::<usize>() {
let n = BUILDER_DEBUG_COUNT.fetch_add(1, Ordering::Relaxed);
if n >= cap { return; }
if n >= cap {
return;
}
}
}
eprintln!("[BUILDER] {}", msg);
@ -32,28 +34,38 @@ impl super::MirBuilder {
#[inline]
pub(crate) fn next_value_id(&mut self) -> super::ValueId {
if let Some(ref mut f) = self.current_function {
f.next_value_id() // Function context
f.next_value_id() // Function context
} else {
self.value_gen.next() // Module context
self.value_gen.next() // Module context
}
}
// ---- LocalSSA convenience (readability helpers) ----
#[allow(dead_code)]
#[inline]
pub(crate) fn local_recv(&mut self, v: super::ValueId) -> super::ValueId { super::ssa::local::recv(self, v) }
pub(crate) fn local_recv(&mut self, v: super::ValueId) -> super::ValueId {
super::ssa::local::recv(self, v)
}
#[allow(dead_code)]
#[inline]
pub(crate) fn local_arg(&mut self, v: super::ValueId) -> super::ValueId { super::ssa::local::arg(self, v) }
pub(crate) fn local_arg(&mut self, v: super::ValueId) -> super::ValueId {
super::ssa::local::arg(self, v)
}
#[allow(dead_code)]
#[inline]
pub(crate) fn local_cmp_operand(&mut self, v: super::ValueId) -> super::ValueId { super::ssa::local::cmp_operand(self, v) }
pub(crate) fn local_cmp_operand(&mut self, v: super::ValueId) -> super::ValueId {
super::ssa::local::cmp_operand(self, v)
}
#[allow(dead_code)]
#[inline]
pub(crate) fn local_field_base(&mut self, v: super::ValueId) -> super::ValueId { super::ssa::local::field_base(self, v) }
pub(crate) fn local_field_base(&mut self, v: super::ValueId) -> super::ValueId {
super::ssa::local::field_base(self, v)
}
#[allow(dead_code)]
#[inline]
pub(crate) fn local_cond(&mut self, v: super::ValueId) -> super::ValueId { super::ssa::local::cond(self, v) }
pub(crate) fn local_cond(&mut self, v: super::ValueId) -> super::ValueId {
super::ssa::local::cond(self, v)
}
/// Ensure a basic block exists in the current function
pub(crate) fn ensure_block_exists(&mut self, block_id: BasicBlockId) -> Result<(), String> {
if let Some(ref mut function) = self.current_function {
@ -93,12 +105,15 @@ impl super::MirBuilder {
// and LoopBuilder/IfBuilder already manage PHIs for ALL variables in variable_map,
// including pinned slots.
}
if false && !self.suppress_pin_entry_copy_next { // Keep old code for reference
if false && !self.suppress_pin_entry_copy_next {
// Keep old code for reference
// First pass: copy all pin slots and remember old->new mapping
let names: Vec<String> = self.variable_map.keys().cloned().collect();
let mut pin_renames: Vec<(super::ValueId, super::ValueId)> = Vec::new();
for name in names.iter() {
if !name.starts_with("__pin$") { continue; }
if !name.starts_with("__pin$") {
continue;
}
if let Some(&src) = self.variable_map.get(name) {
let dst = self.next_value_id();
self.emit_instruction(super::MirInstruction::Copy { dst, src })?;
@ -169,8 +184,13 @@ impl super::MirBuilder {
crate::mir::definitions::call_unified::TypeCertainty::Union,
args.len(),
);
if super::utils::builder_debug_enabled() || std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
if matches!(method.as_str(), "parse" | "substring" | "has_errors" | "length") {
if super::utils::builder_debug_enabled()
|| std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1")
{
if matches!(
method.as_str(),
"parse" | "substring" | "has_errors" | "length"
) {
eprintln!(
"[boxcall-decision] method={} bb={:?} recv=%{} class_hint={:?} prefer_legacy={}",
method,
@ -314,18 +334,27 @@ impl super::MirBuilder {
/// Pin a block-crossing ephemeral value into a pseudo local slot and register it in variable_map
/// so it participates in PHI merges across branches/blocks. Safe default for correctness-first.
pub(crate) fn pin_to_slot(&mut self, v: super::ValueId, hint: &str) -> Result<super::ValueId, String> {
pub(crate) fn pin_to_slot(
&mut self,
v: super::ValueId,
hint: &str,
) -> Result<super::ValueId, String> {
self.temp_slot_counter = self.temp_slot_counter.wrapping_add(1);
let slot_name = format!("__pin${}${}", self.temp_slot_counter, hint);
// Phase 25.1b: Use function-local ID allocator to avoid SSA verification failures
let dst = if let Some(ref mut f) = self.current_function {
f.next_value_id() // Function context: use local ID
f.next_value_id() // Function context: use local ID
} else {
self.value_gen.next() // Module context: use global ID
self.value_gen.next() // Module context: use global ID
};
self.emit_instruction(super::MirInstruction::Copy { dst, src: v })?;
if super::utils::builder_debug_enabled() || std::env::var("NYASH_PIN_TRACE").ok().as_deref() == Some("1") {
super::utils::builder_debug_log(&format!("pin slot={} src={} dst={}", slot_name, v.0, dst.0));
if super::utils::builder_debug_enabled()
|| std::env::var("NYASH_PIN_TRACE").ok().as_deref() == Some("1")
{
super::utils::builder_debug_log(&format!(
"pin slot={} src={} dst={}",
slot_name, v.0, dst.0
));
}
// Propagate lightweight metadata so downstream resolution/type inference remains stable
crate::mir::builder::metadata::propagate::propagate(self, v, dst);
@ -339,12 +368,15 @@ impl super::MirBuilder {
/// Ensure a value has a local definition in the current block by inserting a Copy.
#[allow(dead_code)]
pub(crate) fn materialize_local(&mut self, v: super::ValueId) -> Result<super::ValueId, String> {
pub(crate) fn materialize_local(
&mut self,
v: super::ValueId,
) -> Result<super::ValueId, String> {
// Phase 25.1b: Use function-local ID allocator to avoid SSA verification failures
let dst = if let Some(ref mut f) = self.current_function {
f.next_value_id() // Function context: use local ID
f.next_value_id() // Function context: use local ID
} else {
self.value_gen.next() // Module context: use global ID
self.value_gen.next() // Module context: use global ID
};
self.emit_instruction(super::MirInstruction::Copy { dst, src: v })?;
// Propagate metadata (type/origin) from source to the new local copy
@ -354,11 +386,18 @@ impl super::MirBuilder {
/// Insert a Copy immediately after PHI nodes in the current block (position-stable).
#[allow(dead_code)]
pub(crate) fn insert_copy_after_phis(&mut self, dst: super::ValueId, src: super::ValueId) -> Result<(), String> {
if let (Some(ref mut function), Some(bb)) = (&mut self.current_function, self.current_block) {
pub(crate) fn insert_copy_after_phis(
&mut self,
dst: super::ValueId,
src: super::ValueId,
) -> Result<(), String> {
if let (Some(ref mut function), Some(bb)) = (&mut self.current_function, self.current_block)
{
if std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") {
eprintln!("[utils/insert-copy-after-phis] bb={:?} dst=%{} src=%{} attempting...",
bb, dst.0, src.0);
eprintln!(
"[utils/insert-copy-after-phis] bb={:?} dst=%{} src=%{} attempting...",
bb, dst.0, src.0
);
}
if let Some(block) = function.get_block_mut(bb) {
if std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") {
@ -382,7 +421,11 @@ impl super::MirBuilder {
/// Ensure a value is safe to use in the current block by slotifying (pinning) it.
/// Currently correctness-first: always pin to get a block-local def and PHI participation.
pub(crate) fn ensure_slotified_for_use(&mut self, v: super::ValueId, hint: &str) -> Result<super::ValueId, String> {
pub(crate) fn ensure_slotified_for_use(
&mut self,
v: super::ValueId,
hint: &str,
) -> Result<super::ValueId, String> {
self.pin_to_slot(v, hint)
}