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:
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -36,9 +36,18 @@ pub(in super::super) fn annotate_call_result_from_func_name<S: AsRef<str>>(
|
||||
builder.value_types.insert(dst, ret.clone());
|
||||
if let MirType::Box(bx) = ret {
|
||||
builder.value_origin_newbox.insert(dst, bx);
|
||||
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
||||
let bx = builder.value_origin_newbox.get(&dst).cloned().unwrap_or_default();
|
||||
super::super::utils::builder_debug_log(&format!("annotate call dst={} from {} -> Box({})", dst.0, name, bx));
|
||||
if super::super::utils::builder_debug_enabled()
|
||||
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
|
||||
{
|
||||
let bx = builder
|
||||
.value_origin_newbox
|
||||
.get(&dst)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
super::super::utils::builder_debug_log(&format!(
|
||||
"annotate call dst={} from {} -> Box({})",
|
||||
dst.0, name, bx
|
||||
));
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -48,32 +57,60 @@ pub(in super::super) fn annotate_call_result_from_func_name<S: AsRef<str>>(
|
||||
if name == "JsonParser.parse/1" {
|
||||
let ret = MirType::Box("JsonNode".into());
|
||||
builder.value_types.insert(dst, ret.clone());
|
||||
if let MirType::Box(bx) = ret { builder.value_origin_newbox.insert(dst, bx); }
|
||||
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
||||
super::super::utils::builder_debug_log(&format!("annotate call (fallback) dst={} from {} -> Box(JsonNode)", dst.0, name));
|
||||
if let MirType::Box(bx) = ret {
|
||||
builder.value_origin_newbox.insert(dst, bx);
|
||||
}
|
||||
if super::super::utils::builder_debug_enabled()
|
||||
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
|
||||
{
|
||||
super::super::utils::builder_debug_log(&format!(
|
||||
"annotate call (fallback) dst={} from {} -> Box(JsonNode)",
|
||||
dst.0, name
|
||||
));
|
||||
}
|
||||
} else if name == "JsonParser.current_token/0" {
|
||||
let ret = MirType::Box("JsonToken".into());
|
||||
builder.value_types.insert(dst, ret.clone());
|
||||
if let MirType::Box(bx) = ret { builder.value_origin_newbox.insert(dst, bx); }
|
||||
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
||||
super::super::utils::builder_debug_log(&format!("annotate call (fallback) dst={} from {} -> Box(JsonToken)", dst.0, name));
|
||||
if let MirType::Box(bx) = ret {
|
||||
builder.value_origin_newbox.insert(dst, bx);
|
||||
}
|
||||
if super::super::utils::builder_debug_enabled()
|
||||
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
|
||||
{
|
||||
super::super::utils::builder_debug_log(&format!(
|
||||
"annotate call (fallback) dst={} from {} -> Box(JsonToken)",
|
||||
dst.0, name
|
||||
));
|
||||
}
|
||||
} else if name == "JsonTokenizer.tokenize/0" {
|
||||
// Tokenize returns an ArrayBox of tokens
|
||||
let ret = MirType::Box("ArrayBox".into());
|
||||
builder.value_types.insert(dst, ret.clone());
|
||||
if let MirType::Box(bx) = ret { builder.value_origin_newbox.insert(dst, bx); }
|
||||
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
||||
super::super::utils::builder_debug_log(&format!("annotate call (fallback) dst={} from {} -> Box(ArrayBox)", dst.0, name));
|
||||
if let MirType::Box(bx) = ret {
|
||||
builder.value_origin_newbox.insert(dst, bx);
|
||||
}
|
||||
if super::super::utils::builder_debug_enabled()
|
||||
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
|
||||
{
|
||||
super::super::utils::builder_debug_log(&format!(
|
||||
"annotate call (fallback) dst={} from {} -> Box(ArrayBox)",
|
||||
dst.0, name
|
||||
));
|
||||
}
|
||||
} else if name == "JsonParserModule.create_parser/0" {
|
||||
// Fallback path for parser factory
|
||||
let ret = MirType::Box("JsonParser".into());
|
||||
builder.value_types.insert(dst, ret.clone());
|
||||
if let MirType::Box(bx) = ret { builder.value_origin_newbox.insert(dst, bx); }
|
||||
if super::super::utils::builder_debug_enabled() || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
||||
super::super::utils::builder_debug_log(&format!("annotate call (fallback) dst={} from {} -> Box(JsonParser)", dst.0, name));
|
||||
if let MirType::Box(bx) = ret {
|
||||
builder.value_origin_newbox.insert(dst, bx);
|
||||
}
|
||||
if super::super::utils::builder_debug_enabled()
|
||||
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
|
||||
{
|
||||
super::super::utils::builder_debug_log(&format!(
|
||||
"annotate call (fallback) dst={} from {} -> Box(JsonParser)",
|
||||
dst.0, name
|
||||
));
|
||||
}
|
||||
} else {
|
||||
// Generic tiny whitelist for known primitive-like utilities (spec unchanged)
|
||||
|
||||
@ -5,11 +5,11 @@
|
||||
//! - build_method_call: メソッド呼び出し構築
|
||||
//! - build_from_expression: from式構築
|
||||
|
||||
use crate::ast::{ASTNode, LiteralValue};
|
||||
use super::super::{Effect, EffectMask, MirBuilder, MirInstruction, MirType, ValueId};
|
||||
use crate::mir::TypeOpKind;
|
||||
use super::CallTarget;
|
||||
use super::special_handlers;
|
||||
use super::CallTarget;
|
||||
use crate::ast::{ASTNode, LiteralValue};
|
||||
use crate::mir::TypeOpKind;
|
||||
|
||||
impl MirBuilder {
|
||||
// Build function call: name(args)
|
||||
@ -20,7 +20,11 @@ impl MirBuilder {
|
||||
) -> Result<ValueId, String> {
|
||||
// Dev trace
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
let cur_fun = self.current_function.as_ref().map(|f| f.signature.name.clone()).unwrap_or_else(|| "<none>".to_string());
|
||||
let cur_fun = self
|
||||
.current_function
|
||||
.as_ref()
|
||||
.map(|f| f.signature.name.clone())
|
||||
.unwrap_or_else(|| "<none>".to_string());
|
||||
eprintln!(
|
||||
"[builder] function-call name={} static_ctx={} in_fn={}",
|
||||
name,
|
||||
@ -71,10 +75,16 @@ impl MirBuilder {
|
||||
const MAX_METHOD_DEPTH: usize = 100;
|
||||
self.recursion_depth += 1;
|
||||
if self.recursion_depth > MAX_METHOD_DEPTH {
|
||||
eprintln!("[FATAL] build_method_call recursion depth exceeded {}", MAX_METHOD_DEPTH);
|
||||
eprintln!(
|
||||
"[FATAL] build_method_call recursion depth exceeded {}",
|
||||
MAX_METHOD_DEPTH
|
||||
);
|
||||
eprintln!("[FATAL] Current depth: {}", self.recursion_depth);
|
||||
eprintln!("[FATAL] Method: {}", method);
|
||||
return Err(format!("build_method_call recursion depth exceeded: {}", self.recursion_depth));
|
||||
return Err(format!(
|
||||
"build_method_call recursion depth exceeded: {}",
|
||||
self.recursion_depth
|
||||
));
|
||||
}
|
||||
|
||||
let result = self.build_method_call_impl(object, method, arguments);
|
||||
@ -96,7 +106,10 @@ impl MirBuilder {
|
||||
ASTNode::Me { .. } => "Me",
|
||||
_ => "Other",
|
||||
};
|
||||
eprintln!("[builder] method-call object kind={} method={}", kind, method);
|
||||
eprintln!(
|
||||
"[builder] method-call object kind={} method={}",
|
||||
kind, method
|
||||
);
|
||||
}
|
||||
|
||||
// 0. Dev-only: __mir__.log / __mir__.mark → MirInstruction::DebugLog へ直接 lowering
|
||||
@ -110,7 +123,9 @@ impl MirBuilder {
|
||||
|
||||
// 1. Static box method call: BoxName.method(args)
|
||||
if let ASTNode::Variable { name: obj_name, .. } = &object {
|
||||
if let Some(result) = self.try_build_static_method_call(obj_name, &method, &arguments)? {
|
||||
if let Some(result) =
|
||||
self.try_build_static_method_call(obj_name, &method, &arguments)?
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
@ -211,40 +226,43 @@ impl MirBuilder {
|
||||
let mut math_args: Vec<ValueId> = Vec::new();
|
||||
for a in raw_args.into_iter() {
|
||||
match a {
|
||||
ASTNode::New { class, arguments, .. } if class == "FloatBox" && arguments.len() == 1 => {
|
||||
ASTNode::New {
|
||||
class, arguments, ..
|
||||
} if class == "FloatBox" && arguments.len() == 1 => {
|
||||
match self.build_expression(arguments[0].clone()) {
|
||||
v @ Ok(_) => math_args.push(v.unwrap()),
|
||||
err @ Err(_) => return Some(err),
|
||||
}
|
||||
}
|
||||
ASTNode::New { class, arguments, .. } if class == "IntegerBox" && arguments.len() == 1 => {
|
||||
ASTNode::New {
|
||||
class, arguments, ..
|
||||
} if class == "IntegerBox" && arguments.len() == 1 => {
|
||||
let iv = match self.build_expression(arguments[0].clone()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e))
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
let fv = self.next_value_id();
|
||||
if let Err(e) = self.emit_instruction(MirInstruction::TypeOp {
|
||||
dst: fv,
|
||||
op: TypeOpKind::Cast,
|
||||
value: iv,
|
||||
ty: MirType::Float
|
||||
ty: MirType::Float,
|
||||
}) {
|
||||
return Some(Err(e));
|
||||
}
|
||||
math_args.push(fv);
|
||||
}
|
||||
ASTNode::Literal { value: LiteralValue::Float(_), .. } => {
|
||||
match self.build_expression(a) {
|
||||
v @ Ok(_) => math_args.push(v.unwrap()),
|
||||
err @ Err(_) => return Some(err),
|
||||
}
|
||||
}
|
||||
other => {
|
||||
match self.build_expression(other) {
|
||||
v @ Ok(_) => math_args.push(v.unwrap()),
|
||||
err @ Err(_) => return Some(err),
|
||||
}
|
||||
}
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Float(_),
|
||||
..
|
||||
} => match self.build_expression(a) {
|
||||
v @ Ok(_) => math_args.push(v.unwrap()),
|
||||
err @ Err(_) => return Some(err),
|
||||
},
|
||||
other => match self.build_expression(other) {
|
||||
v @ Ok(_) => math_args.push(v.unwrap()),
|
||||
err @ Err(_) => return Some(err),
|
||||
},
|
||||
}
|
||||
}
|
||||
// new MathBox()
|
||||
@ -252,7 +270,8 @@ impl MirBuilder {
|
||||
if let Err(e) = self.emit_constructor_call(math_recv, "MathBox".to_string(), vec![]) {
|
||||
return Some(Err(e));
|
||||
}
|
||||
self.value_origin_newbox.insert(math_recv, "MathBox".to_string());
|
||||
self.value_origin_newbox
|
||||
.insert(math_recv, "MathBox".to_string());
|
||||
// birth()
|
||||
if let Err(e) = self.emit_method_call(None, math_recv, "birth".to_string(), vec![]) {
|
||||
return Some(Err(e));
|
||||
@ -272,29 +291,40 @@ impl MirBuilder {
|
||||
method: &str,
|
||||
arguments: &Vec<ASTNode>,
|
||||
) -> Option<Result<ValueId, String>> {
|
||||
let ASTNode::FieldAccess { object: env_obj, field: env_field, .. } = object else {
|
||||
let ASTNode::FieldAccess {
|
||||
object: env_obj,
|
||||
field: env_field,
|
||||
..
|
||||
} = object
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
if let ASTNode::Variable { name: env_name, .. } = env_obj.as_ref() {
|
||||
if env_name != "env" { return None; }
|
||||
if env_name != "env" {
|
||||
return None;
|
||||
}
|
||||
// Build arguments once
|
||||
let mut arg_values = Vec::new();
|
||||
for arg in arguments {
|
||||
match self.build_expression(arg.clone()) {
|
||||
Ok(v) => arg_values.push(v),
|
||||
Err(e) => return Some(Err(e))
|
||||
Err(e) => return Some(Err(e)),
|
||||
}
|
||||
}
|
||||
let iface = env_field.as_str();
|
||||
let m = method;
|
||||
let mut extern_call = |iface_name: &str, method_name: &str, effects: EffectMask, returns: bool| -> Result<ValueId, String> {
|
||||
let mut extern_call = |iface_name: &str,
|
||||
method_name: &str,
|
||||
effects: EffectMask,
|
||||
returns: bool|
|
||||
-> Result<ValueId, String> {
|
||||
let result_id = self.next_value_id();
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: if returns { Some(result_id) } else { None },
|
||||
iface_name: iface_name.to_string(),
|
||||
method_name: method_name.to_string(),
|
||||
args: arg_values.clone(),
|
||||
effects
|
||||
effects,
|
||||
})?;
|
||||
if returns {
|
||||
Ok(result_id)
|
||||
@ -351,7 +381,8 @@ impl MirBuilder {
|
||||
if let Some(result) = self.try_tail_based_fallback(&name, &arg_values)? {
|
||||
return Ok(result);
|
||||
}
|
||||
return Err(format!("Unresolved function: '{}'. {}",
|
||||
return Err(format!(
|
||||
"Unresolved function: '{}'. {}",
|
||||
name,
|
||||
super::super::call_resolution::suggest_resolution(&name)
|
||||
));
|
||||
@ -379,11 +410,7 @@ impl MirBuilder {
|
||||
arg_values: Vec<ValueId>,
|
||||
) -> Result<ValueId, String> {
|
||||
let dst = self.next_value_id();
|
||||
self.emit_unified_call(
|
||||
Some(dst),
|
||||
CallTarget::Global(name),
|
||||
arg_values,
|
||||
)?;
|
||||
self.emit_unified_call(Some(dst), CallTarget::Global(name), arg_values)?;
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
@ -398,11 +425,17 @@ impl MirBuilder {
|
||||
|
||||
// Debug trace
|
||||
if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[DEBUG] try_build_static_method_call: obj_name={}, method={}", obj_name, method);
|
||||
eprintln!(
|
||||
"[DEBUG] try_build_static_method_call: obj_name={}, method={}",
|
||||
obj_name, method
|
||||
);
|
||||
eprintln!("[DEBUG] is_local_var={}", is_local_var);
|
||||
if is_local_var {
|
||||
eprintln!("[DEBUG] variable_map contains '{}' - treating as local variable, will use method call", obj_name);
|
||||
eprintln!("[DEBUG] variable_map keys: {:?}", self.variable_map.keys().collect::<Vec<_>>());
|
||||
eprintln!(
|
||||
"[DEBUG] variable_map keys: {:?}",
|
||||
self.variable_map.keys().collect::<Vec<_>>()
|
||||
);
|
||||
} else {
|
||||
eprintln!("[DEBUG] '{}' not in variable_map - treating as static box, will use global call", obj_name);
|
||||
}
|
||||
@ -488,7 +521,7 @@ impl MirBuilder {
|
||||
self.emit_unified_call(
|
||||
Some(dst),
|
||||
CallTarget::Global(func_name),
|
||||
arg_values.to_vec()
|
||||
arg_values.to_vec(),
|
||||
)?;
|
||||
return Ok(Some(dst));
|
||||
}
|
||||
@ -517,7 +550,7 @@ impl MirBuilder {
|
||||
self.emit_legacy_call(
|
||||
Some(dst),
|
||||
CallTarget::Global(func_name),
|
||||
arg_values.to_vec()
|
||||
arg_values.to_vec(),
|
||||
)?;
|
||||
return Ok(Some(dst));
|
||||
}
|
||||
@ -530,20 +563,31 @@ impl MirBuilder {
|
||||
fn trace_receiver_if_enabled(&self, object: &ASTNode, object_value: ValueId) {
|
||||
if std::env::var("NYASH_DEBUG_PARAM_RECEIVER").ok().as_deref() == Some("1") {
|
||||
if let ASTNode::Variable { name, .. } = object {
|
||||
eprintln!("[DEBUG/param-recv] build_method_call receiver '{}' → ValueId({})",
|
||||
name, object_value.0);
|
||||
eprintln!(
|
||||
"[DEBUG/param-recv] build_method_call receiver '{}' → ValueId({})",
|
||||
name, object_value.0
|
||||
);
|
||||
if let Some(origin) = self.value_origin_newbox.get(&object_value) {
|
||||
eprintln!("[DEBUG/param-recv] origin: {}", origin);
|
||||
}
|
||||
if let Some(&mapped_id) = self.variable_map.get(name) {
|
||||
eprintln!("[DEBUG/param-recv] variable_map['{}'] = ValueId({})", name, mapped_id.0);
|
||||
eprintln!(
|
||||
"[DEBUG/param-recv] variable_map['{}'] = ValueId({})",
|
||||
name, mapped_id.0
|
||||
);
|
||||
if mapped_id != object_value {
|
||||
eprintln!("[DEBUG/param-recv] ⚠️ MISMATCH! build_expression returned different ValueId!");
|
||||
}
|
||||
} else {
|
||||
eprintln!("[DEBUG/param-recv] ⚠️ '{}' NOT FOUND in variable_map!", name);
|
||||
eprintln!(
|
||||
"[DEBUG/param-recv] ⚠️ '{}' NOT FOUND in variable_map!",
|
||||
name
|
||||
);
|
||||
}
|
||||
eprintln!("[DEBUG/param-recv] current_block: {:?}", self.current_block);
|
||||
eprintln!(
|
||||
"[DEBUG/param-recv] current_block: {:?}",
|
||||
self.current_block
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ pub enum CallTarget {
|
||||
|
||||
/// Method call (box.method)
|
||||
Method {
|
||||
box_type: Option<String>, // None = infer from value
|
||||
box_type: Option<String>, // None = infer from value
|
||||
method: String,
|
||||
receiver: ValueId,
|
||||
},
|
||||
@ -55,4 +55,4 @@ impl CallTarget {
|
||||
CallTarget::Closure { .. } => "<closure>".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,12 +5,16 @@
|
||||
* Replaces 6 different call instructions with a single unified system
|
||||
*/
|
||||
|
||||
use crate::mir::{Callee, EffectMask, ValueId};
|
||||
use crate::mir::definitions::call_unified::{CallFlags, MirCall};
|
||||
use crate::mir::{Callee, EffectMask, ValueId};
|
||||
|
||||
/// Check if unified call system is enabled
|
||||
pub fn is_unified_call_enabled() -> bool {
|
||||
match std::env::var("NYASH_MIR_UNIFIED_CALL").ok().as_deref().map(|s| s.to_ascii_lowercase()) {
|
||||
match std::env::var("NYASH_MIR_UNIFIED_CALL")
|
||||
.ok()
|
||||
.as_deref()
|
||||
.map(|s| s.to_ascii_lowercase())
|
||||
{
|
||||
Some(s) if s == "0" || s == "false" || s == "off" => false,
|
||||
_ => true, // default ON during development; explicit opt-out supported
|
||||
}
|
||||
@ -74,11 +78,7 @@ pub fn create_call_flags(callee: &Callee) -> CallFlags {
|
||||
}
|
||||
|
||||
/// Create a MirCall instruction from components
|
||||
pub fn create_mir_call(
|
||||
dst: Option<ValueId>,
|
||||
callee: Callee,
|
||||
args: Vec<ValueId>,
|
||||
) -> MirCall {
|
||||
pub fn create_mir_call(dst: Option<ValueId>, callee: Callee, args: Vec<ValueId>) -> MirCall {
|
||||
let effects = compute_call_effects(&callee);
|
||||
let flags = create_call_flags(&callee);
|
||||
|
||||
|
||||
@ -12,9 +12,9 @@
|
||||
* - 既知の関数・メソッドのエフェクト知識を集約
|
||||
*/
|
||||
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
use crate::mir::builder::{Effect, EffectMask};
|
||||
use super::extern_calls;
|
||||
use crate::mir::builder::{Effect, EffectMask};
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
|
||||
/// エフェクト解析専用箱
|
||||
///
|
||||
@ -36,16 +36,16 @@ impl EffectsAnalyzerBox {
|
||||
/// - WriteHeap: ヒープ書き込み
|
||||
pub fn compute_call_effects(callee: &Callee) -> EffectMask {
|
||||
match callee {
|
||||
Callee::Global(name) => {
|
||||
match name.as_str() {
|
||||
"print" | "error" => EffectMask::IO,
|
||||
"panic" | "exit" => EffectMask::IO.add(Effect::Control),
|
||||
"gc_collect" => EffectMask::IO.add(Effect::Alloc),
|
||||
_ => EffectMask::IO,
|
||||
}
|
||||
Callee::Global(name) => match name.as_str() {
|
||||
"print" | "error" => EffectMask::IO,
|
||||
"panic" | "exit" => EffectMask::IO.add(Effect::Control),
|
||||
"gc_collect" => EffectMask::IO.add(Effect::Alloc),
|
||||
_ => EffectMask::IO,
|
||||
},
|
||||
|
||||
Callee::Method { method, box_name, .. } => {
|
||||
Callee::Method {
|
||||
method, box_name, ..
|
||||
} => {
|
||||
match method.as_str() {
|
||||
"birth" => EffectMask::PURE.add(Effect::Alloc),
|
||||
"get" | "length" | "size" => EffectMask::READ,
|
||||
@ -59,7 +59,7 @@ impl EffectsAnalyzerBox {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Callee::Constructor { .. } => EffectMask::PURE.add(Effect::Alloc),
|
||||
|
||||
@ -68,7 +68,7 @@ impl EffectsAnalyzerBox {
|
||||
Callee::Extern(name) => {
|
||||
let (iface, method) = extern_calls::parse_extern_name(name);
|
||||
extern_calls::compute_extern_effects(&iface, &method)
|
||||
},
|
||||
}
|
||||
|
||||
Callee::Value(_) => EffectMask::IO, // Conservative for dynamic calls
|
||||
}
|
||||
@ -100,8 +100,8 @@ impl EffectsAnalyzerBox {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mir::ValueId;
|
||||
use crate::mir::definitions::call_unified::{CalleeBoxKind, TypeCertainty};
|
||||
use crate::mir::ValueId;
|
||||
|
||||
#[test]
|
||||
fn test_compute_effects_global() {
|
||||
@ -143,7 +143,10 @@ mod tests {
|
||||
};
|
||||
let effects = EffectsAnalyzerBox::compute_call_effects(&callee);
|
||||
// Constructor should have PURE + Alloc
|
||||
assert_eq!(effects, EffectMask::PURE.add(crate::mir::builder::Effect::Alloc));
|
||||
assert_eq!(
|
||||
effects,
|
||||
EffectMask::PURE.add(crate::mir::builder::Effect::Alloc)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
//! - emit_global_call/emit_method_call/emit_constructor_call: 便利ラッパー
|
||||
|
||||
use super::super::{EffectMask, MirBuilder, MirInstruction, ValueId};
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
use super::CallTarget;
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
|
||||
impl MirBuilder {
|
||||
/// Unified call emission - delegates to UnifiedCallEmitterBox
|
||||
@ -21,7 +21,6 @@ impl MirBuilder {
|
||||
super::unified_emitter::UnifiedCallEmitterBox::emit_unified_call(self, dst, target, args)
|
||||
}
|
||||
|
||||
|
||||
/// Legacy call fallback - preserves existing behavior
|
||||
pub fn emit_legacy_call(
|
||||
&mut self,
|
||||
@ -30,7 +29,11 @@ impl MirBuilder {
|
||||
args: Vec<ValueId>,
|
||||
) -> Result<(), String> {
|
||||
match target {
|
||||
CallTarget::Method { receiver, method, box_type: _ } => {
|
||||
CallTarget::Method {
|
||||
receiver,
|
||||
method,
|
||||
box_type: _,
|
||||
} => {
|
||||
// LEGACY PATH (after unified migration):
|
||||
// Instance→Function rewrite is centralized in unified call path.
|
||||
// Legacy path no longer functionizes; always use Box/Plugin call here.
|
||||
@ -38,10 +41,11 @@ impl MirBuilder {
|
||||
// Set flag to prevent emit_box_or_plugin_call from calling emit_unified_call
|
||||
let prev_flag = self.in_unified_boxcall_fallback;
|
||||
self.in_unified_boxcall_fallback = true;
|
||||
let result = self.emit_box_or_plugin_call(dst, receiver, method, None, args, EffectMask::IO);
|
||||
let result =
|
||||
self.emit_box_or_plugin_call(dst, receiver, method, None, args, EffectMask::IO);
|
||||
self.in_unified_boxcall_fallback = prev_flag;
|
||||
result
|
||||
},
|
||||
}
|
||||
CallTarget::Constructor(box_type) => {
|
||||
// Use existing NewBox
|
||||
let dst = dst.ok_or("Constructor must have destination")?;
|
||||
@ -50,7 +54,7 @@ impl MirBuilder {
|
||||
box_type,
|
||||
args,
|
||||
})
|
||||
},
|
||||
}
|
||||
CallTarget::Extern(name) => {
|
||||
// Use existing ExternCall
|
||||
let mut args = args;
|
||||
@ -69,14 +73,22 @@ impl MirBuilder {
|
||||
args,
|
||||
effects: EffectMask::IO,
|
||||
})
|
||||
},
|
||||
}
|
||||
CallTarget::Global(name) => {
|
||||
super::unified_emitter::UnifiedCallEmitterBox::emit_global_unified(self, dst, name, args)
|
||||
},
|
||||
super::unified_emitter::UnifiedCallEmitterBox::emit_global_unified(
|
||||
self, dst, name, args,
|
||||
)
|
||||
}
|
||||
CallTarget::Value(func_val) => {
|
||||
super::unified_emitter::UnifiedCallEmitterBox::emit_value_unified(self, dst, func_val, args)
|
||||
},
|
||||
CallTarget::Closure { params, captures, me_capture } => {
|
||||
super::unified_emitter::UnifiedCallEmitterBox::emit_value_unified(
|
||||
self, dst, func_val, args,
|
||||
)
|
||||
}
|
||||
CallTarget::Closure {
|
||||
params,
|
||||
captures,
|
||||
me_capture,
|
||||
} => {
|
||||
let dst = dst.ok_or("Closure creation must have destination")?;
|
||||
self.emit_instruction(MirInstruction::NewClosure {
|
||||
dst,
|
||||
@ -85,7 +97,7 @@ impl MirBuilder {
|
||||
captures,
|
||||
me: me_capture,
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,11 +139,7 @@ impl MirBuilder {
|
||||
box_type: String,
|
||||
args: Vec<ValueId>,
|
||||
) -> Result<(), String> {
|
||||
self.emit_unified_call(
|
||||
Some(dst),
|
||||
CallTarget::Constructor(box_type),
|
||||
args,
|
||||
)
|
||||
self.emit_unified_call(Some(dst), CallTarget::Constructor(box_type), args)
|
||||
}
|
||||
|
||||
// ========================================
|
||||
@ -146,7 +154,9 @@ impl MirBuilder {
|
||||
name: &str,
|
||||
args: &[ValueId],
|
||||
) -> Result<Option<()>, String> {
|
||||
super::materializer::CallMaterializerBox::try_global_fallback_handlers(self, dst, name, args)
|
||||
super::materializer::CallMaterializerBox::try_global_fallback_handlers(
|
||||
self, dst, name, args,
|
||||
)
|
||||
}
|
||||
|
||||
/// Ensure receiver is materialized in Callee::Method (delegates to CallMaterializerBox)
|
||||
|
||||
@ -125,18 +125,8 @@ pub fn get_env_method_spec(
|
||||
)),
|
||||
|
||||
// Direct env access
|
||||
("env", "get") => Some((
|
||||
"env".to_string(),
|
||||
"get".to_string(),
|
||||
EffectMask::READ,
|
||||
true,
|
||||
)),
|
||||
("env", "set") => Some((
|
||||
"env".to_string(),
|
||||
"set".to_string(),
|
||||
EffectMask::IO,
|
||||
false,
|
||||
)),
|
||||
("env", "get") => Some(("env".to_string(), "get".to_string(), EffectMask::READ, true)),
|
||||
("env", "set") => Some(("env".to_string(), "set".to_string(), EffectMask::IO, false)),
|
||||
|
||||
// Unknown
|
||||
_ => None,
|
||||
@ -157,9 +147,16 @@ pub fn parse_extern_name(name: &str) -> (String, String) {
|
||||
/// Check if a name refers to an environment interface
|
||||
#[allow(dead_code)]
|
||||
pub fn is_env_interface(name: &str) -> bool {
|
||||
matches!(name,
|
||||
"env" | "env.console" | "env.fs" | "env.net" |
|
||||
"env.canvas" | "env.task" | "env.future" | "env.process"
|
||||
matches!(
|
||||
name,
|
||||
"env"
|
||||
| "env.console"
|
||||
| "env.fs"
|
||||
| "env.net"
|
||||
| "env.canvas"
|
||||
| "env.task"
|
||||
| "env.future"
|
||||
| "env.process"
|
||||
)
|
||||
}
|
||||
|
||||
@ -167,13 +164,9 @@ pub fn is_env_interface(name: &str) -> bool {
|
||||
pub fn compute_extern_effects(iface: &str, method: &str) -> EffectMask {
|
||||
match (iface, method) {
|
||||
// Pure reads
|
||||
(_, m) if m.starts_with("get") || m == "argv" || m == "env" => {
|
||||
EffectMask::READ
|
||||
}
|
||||
(_, m) if m.starts_with("get") || m == "argv" || m == "env" => EffectMask::READ,
|
||||
// Control flow changes
|
||||
(_, "exit") | (_, "panic") | (_, "throw") => {
|
||||
EffectMask::IO.add(Effect::Control)
|
||||
}
|
||||
(_, "exit") | (_, "panic") | (_, "throw") => EffectMask::IO.add(Effect::Control),
|
||||
// Memory allocation
|
||||
(_, m) if m.starts_with("new") || m.starts_with("create") => {
|
||||
EffectMask::IO.add(Effect::Alloc)
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
* Manages the complex state transitions during function lowering
|
||||
*/
|
||||
|
||||
use super::special_handlers::contains_value_return;
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::{Effect, EffectMask, FunctionSignature, MirType};
|
||||
use super::special_handlers::contains_value_return;
|
||||
|
||||
/// Prepare function signature for a box method
|
||||
/// Includes 'me' parameter as first parameter
|
||||
@ -32,7 +32,7 @@ pub fn prepare_method_signature(
|
||||
// Determine return type based on body analysis
|
||||
let returns_value = contains_value_return(body);
|
||||
let ret_ty = if returns_value {
|
||||
MirType::Unknown // Will be inferred later
|
||||
MirType::Unknown // Will be inferred later
|
||||
} else {
|
||||
MirType::Void
|
||||
};
|
||||
@ -62,7 +62,7 @@ pub fn prepare_static_method_signature(
|
||||
// Determine return type based on body analysis
|
||||
let returns_value = contains_value_return(body);
|
||||
let ret_ty = if returns_value {
|
||||
MirType::Unknown // Will be inferred later
|
||||
MirType::Unknown // Will be inferred later
|
||||
} else {
|
||||
MirType::Void
|
||||
};
|
||||
@ -77,11 +77,7 @@ pub fn prepare_static_method_signature(
|
||||
|
||||
/// Generate canonical method name for MIR function
|
||||
/// E.g., "StringBox.upper/0" for StringBox's upper method with 0 args
|
||||
pub fn generate_method_function_name(
|
||||
box_name: &str,
|
||||
method_name: &str,
|
||||
arity: usize,
|
||||
) -> String {
|
||||
pub fn generate_method_function_name(box_name: &str, method_name: &str, arity: usize) -> String {
|
||||
format!("{}.{}/{}", box_name, method_name, arity)
|
||||
}
|
||||
|
||||
@ -120,10 +116,9 @@ pub fn create_method_parameter_mapping(
|
||||
}
|
||||
|
||||
/// Create parameter mapping for static method lowering
|
||||
pub fn create_static_parameter_mapping(
|
||||
params: &[String],
|
||||
) -> Vec<(String, MirType)> {
|
||||
params.iter()
|
||||
pub fn create_static_parameter_mapping(params: &[String]) -> Vec<(String, MirType)> {
|
||||
params
|
||||
.iter()
|
||||
.map(|p| (p.clone(), MirType::Unknown))
|
||||
.collect()
|
||||
}
|
||||
@ -139,14 +134,24 @@ pub fn wrap_in_program(statements: Vec<ASTNode>) -> ASTNode {
|
||||
/// Check if method name suggests it returns a value
|
||||
pub fn method_likely_returns_value(method_name: &str) -> bool {
|
||||
// Heuristic: methods that likely return values
|
||||
method_name.starts_with("get") ||
|
||||
method_name.starts_with("is") ||
|
||||
method_name.starts_with("has") ||
|
||||
method_name.starts_with("to") ||
|
||||
matches!(method_name,
|
||||
"length" | "size" | "count" |
|
||||
"upper" | "lower" | "trim" |
|
||||
"add" | "sub" | "mul" | "div" |
|
||||
"min" | "max" | "abs"
|
||||
)
|
||||
method_name.starts_with("get")
|
||||
|| method_name.starts_with("is")
|
||||
|| method_name.starts_with("has")
|
||||
|| method_name.starts_with("to")
|
||||
|| matches!(
|
||||
method_name,
|
||||
"length"
|
||||
| "size"
|
||||
| "count"
|
||||
| "upper"
|
||||
| "lower"
|
||||
| "trim"
|
||||
| "add"
|
||||
| "sub"
|
||||
| "mul"
|
||||
| "div"
|
||||
| "min"
|
||||
| "max"
|
||||
| "abs"
|
||||
)
|
||||
}
|
||||
|
||||
@ -12,8 +12,8 @@
|
||||
* - receiver実体化の保証
|
||||
*/
|
||||
|
||||
use crate::mir::{Callee, MirType, ValueId};
|
||||
use crate::mir::definitions::call_unified::CalleeBoxKind;
|
||||
use crate::mir::{Callee, MirType, ValueId};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// 構造ガード専用箱
|
||||
@ -49,31 +49,43 @@ impl<'a> CalleeGuardBox<'a> {
|
||||
/// - StageBArgsBox.resolve_src内のargs.get(i)がStage1UsingResolverBox.getに
|
||||
/// 化けるのを防ぐ(args型はMapBox/ArrayBox → 正規化)
|
||||
pub fn apply_static_runtime_guard(&self, callee: Callee) -> Result<Callee, String> {
|
||||
if let Callee::Method { ref box_name, ref method, receiver: Some(recv), certainty, box_kind } = callee {
|
||||
if let Callee::Method {
|
||||
ref box_name,
|
||||
ref method,
|
||||
receiver: Some(recv),
|
||||
certainty,
|
||||
box_kind,
|
||||
} = callee
|
||||
{
|
||||
// Only apply guard if box_kind is StaticCompiler
|
||||
if box_kind == CalleeBoxKind::StaticCompiler {
|
||||
// Check if receiver has a Box type
|
||||
if let Some(MirType::Box(receiver_box)) = self.value_types.get(&recv) {
|
||||
let trace_enabled = std::env::var("NYASH_CALLEE_RESOLVE_TRACE")
|
||||
.ok()
|
||||
.as_deref() == Some("1");
|
||||
let trace_enabled =
|
||||
std::env::var("NYASH_CALLEE_RESOLVE_TRACE").ok().as_deref() == Some("1");
|
||||
|
||||
// If receiver box type matches the static box name, this is a me-call
|
||||
// Let it through for static method lowering (don't normalize)
|
||||
if receiver_box == box_name {
|
||||
if trace_enabled {
|
||||
eprintln!("[static-runtime-guard] ME-CALL detected:");
|
||||
eprintln!(" {}.{} with receiver type: {} (same as box_name)", box_name, method, receiver_box);
|
||||
eprintln!(
|
||||
" {}.{} with receiver type: {} (same as box_name)",
|
||||
box_name, method, receiver_box
|
||||
);
|
||||
eprintln!(" → Allowing for static method lowering");
|
||||
}
|
||||
return Ok(callee); // Pass through unchanged
|
||||
return Ok(callee); // Pass through unchanged
|
||||
}
|
||||
|
||||
// Otherwise, this is a true mix-up: runtime box with static box name
|
||||
// Normalize to the runtime box type
|
||||
if trace_enabled {
|
||||
eprintln!("[static-runtime-guard] CORRECTING mix-up:");
|
||||
eprintln!(" Original: {}.{} (box_kind=StaticCompiler)", box_name, method);
|
||||
eprintln!(
|
||||
" Original: {}.{} (box_kind=StaticCompiler)",
|
||||
box_name, method
|
||||
);
|
||||
eprintln!(" Receiver %{} has runtime type: {}", recv.0, receiver_box);
|
||||
eprintln!(" Normalized: {}.{}", receiver_box, method);
|
||||
}
|
||||
@ -83,7 +95,7 @@ impl<'a> CalleeGuardBox<'a> {
|
||||
method: method.clone(),
|
||||
receiver: Some(recv),
|
||||
certainty,
|
||||
box_kind: CalleeBoxKind::RuntimeData, // Switch to runtime
|
||||
box_kind: CalleeBoxKind::RuntimeData, // Switch to runtime
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -188,7 +200,9 @@ mod tests {
|
||||
// Mix-up → should normalize to MapBox
|
||||
let result = guard.apply_static_runtime_guard(callee).unwrap();
|
||||
match result {
|
||||
Callee::Method { box_name, box_kind, .. } => {
|
||||
Callee::Method {
|
||||
box_name, box_kind, ..
|
||||
} => {
|
||||
assert_eq!(box_name, "MapBox");
|
||||
assert_eq!(box_kind, CalleeBoxKind::RuntimeData);
|
||||
}
|
||||
|
||||
@ -5,11 +5,11 @@
|
||||
//! - BoxCompilationContext による完全独立化
|
||||
//! - パラメータ・型情報の適切な管理
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::builder::{MirBuilder, MirType, MirInstruction};
|
||||
use crate::mir::{MirValueKind, ValueId}; // Phase 26-A-3: ValueId型安全化
|
||||
use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
|
||||
use super::function_lowering;
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::builder::{MirBuilder, MirInstruction, MirType};
|
||||
use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
|
||||
use crate::mir::{MirValueKind, ValueId}; // Phase 26-A-3: ValueId型安全化
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// 🎯 箱理論: Lowering Context(準備と復元)
|
||||
@ -24,10 +24,7 @@ struct LoweringContext {
|
||||
|
||||
impl MirBuilder {
|
||||
/// 🎯 箱理論: Step 1 - Lowering Context準備
|
||||
fn prepare_lowering_context(
|
||||
&mut self,
|
||||
func_name: &str,
|
||||
) -> LoweringContext {
|
||||
fn prepare_lowering_context(&mut self, func_name: &str) -> LoweringContext {
|
||||
// Static box context設定
|
||||
let saved_static_ctx = self.current_static_box.clone();
|
||||
if let Some(pos) = func_name.find('.') {
|
||||
@ -77,11 +74,8 @@ impl MirBuilder {
|
||||
body: &[ASTNode],
|
||||
ctx: &mut LoweringContext,
|
||||
) -> Result<(), String> {
|
||||
let signature = function_lowering::prepare_static_method_signature(
|
||||
func_name.clone(),
|
||||
params,
|
||||
body,
|
||||
);
|
||||
let signature =
|
||||
function_lowering::prepare_static_method_signature(func_name.clone(), params, body);
|
||||
let entry = self.block_gen.next();
|
||||
let function = super::super::MirFunction::new(signature, entry);
|
||||
|
||||
@ -89,7 +83,10 @@ impl MirBuilder {
|
||||
ctx.saved_function = self.current_function.take();
|
||||
ctx.saved_block = self.current_block.take();
|
||||
|
||||
eprintln!("[DEBUG/create_function_skeleton] Creating function: {}", func_name);
|
||||
eprintln!(
|
||||
"[DEBUG/create_function_skeleton] Creating function: {}",
|
||||
func_name
|
||||
);
|
||||
eprintln!("[DEBUG/create_function_skeleton] Entry block: {:?}", entry);
|
||||
|
||||
// 新しい関数に切り替え
|
||||
@ -116,9 +113,15 @@ impl MirBuilder {
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
// 📦 Hotfix 5: Use pre-populated params from MirFunction::new()
|
||||
// Static methods have implicit receiver at params[0], so actual parameters start at offset
|
||||
let receiver_offset = if f.params.is_empty() { 0 } else {
|
||||
let receiver_offset = if f.params.is_empty() {
|
||||
0
|
||||
} else {
|
||||
// If params already populated (by Hotfix 4+5), use them
|
||||
if f.params.len() > params.len() { 1 } else { 0 }
|
||||
if f.params.len() > params.len() {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
let param_types = f.signature.params.clone();
|
||||
@ -186,8 +189,7 @@ impl MirBuilder {
|
||||
|
||||
// 型推論
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
if returns_value
|
||||
&& matches!(f.signature.return_type, MirType::Void | MirType::Unknown)
|
||||
if returns_value && matches!(f.signature.return_type, MirType::Void | MirType::Unknown)
|
||||
{
|
||||
let mut inferred: Option<MirType> = None;
|
||||
'search: for (_bid, bb) in f.blocks.iter() {
|
||||
@ -254,12 +256,8 @@ impl MirBuilder {
|
||||
body: &[ASTNode],
|
||||
ctx: &mut LoweringContext,
|
||||
) -> Result<(), String> {
|
||||
let signature = function_lowering::prepare_method_signature(
|
||||
func_name,
|
||||
box_name,
|
||||
params,
|
||||
body,
|
||||
);
|
||||
let signature =
|
||||
function_lowering::prepare_method_signature(func_name, box_name, params, body);
|
||||
let entry = self.block_gen.next();
|
||||
let function = super::super::MirFunction::new(signature, entry);
|
||||
|
||||
@ -418,8 +416,7 @@ impl MirBuilder {
|
||||
|
||||
// 型推論(Step 5の一部として)
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
if returns_value
|
||||
&& matches!(f.signature.return_type, MirType::Void | MirType::Unknown)
|
||||
if returns_value && matches!(f.signature.return_type, MirType::Void | MirType::Unknown)
|
||||
{
|
||||
let mut inferred: Option<MirType> = None;
|
||||
'search: for (_bid, bb) in f.blocks.iter() {
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
* - Call発行前の準備処理全般
|
||||
*/
|
||||
|
||||
use crate::mir::builder::{MirBuilder, ValueId, MirInstruction, EffectMask};
|
||||
use crate::mir::builder::{EffectMask, MirBuilder, MirInstruction, ValueId};
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
|
||||
/// Call前処理・準備専用箱
|
||||
@ -43,11 +43,17 @@ impl CallMaterializerBox {
|
||||
let one = crate::mir::builder::emission::constant::emit_integer(builder, 1);
|
||||
if dst.is_none() {
|
||||
// If a destination was not provided, copy into the allocated dstv
|
||||
builder.emit_instruction(MirInstruction::Copy { dst: dstv, src: one })?;
|
||||
builder.emit_instruction(MirInstruction::Copy {
|
||||
dst: dstv,
|
||||
src: one,
|
||||
})?;
|
||||
crate::mir::builder::metadata::propagate::propagate(builder, one, dstv);
|
||||
} else {
|
||||
// If caller provided dst, ensure the computed value lands there
|
||||
builder.emit_instruction(MirInstruction::Copy { dst: dstv, src: one })?;
|
||||
builder.emit_instruction(MirInstruction::Copy {
|
||||
dst: dstv,
|
||||
src: one,
|
||||
})?;
|
||||
crate::mir::builder::metadata::propagate::propagate(builder, one, dstv);
|
||||
}
|
||||
return Ok(Some(()));
|
||||
@ -57,7 +63,8 @@ impl CallMaterializerBox {
|
||||
if let Some(ref module) = builder.current_module {
|
||||
if module.functions.contains_key(name) {
|
||||
let dstv = dst.unwrap_or_else(|| builder.next_value_id());
|
||||
let name_const = crate::mir::builder::name_const::make_name_const_result(builder, name)?;
|
||||
let name_const =
|
||||
crate::mir::builder::name_const::make_name_const_result(builder, name)?;
|
||||
builder.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(dstv),
|
||||
func: name_const,
|
||||
@ -82,7 +89,8 @@ impl CallMaterializerBox {
|
||||
let func_name = format!("{}.{}{}", bx, name, format!("/{}", args.len()));
|
||||
// Emit legacy call directly to preserve behavior
|
||||
let dstv = dst.unwrap_or_else(|| builder.next_value_id());
|
||||
let name_const = crate::mir::builder::name_const::make_name_const_result(builder, &func_name)?;
|
||||
let name_const =
|
||||
crate::mir::builder::name_const::make_name_const_result(builder, &func_name)?;
|
||||
builder.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(dstv),
|
||||
func: name_const,
|
||||
|
||||
@ -53,12 +53,17 @@ pub fn resolve_call_target(
|
||||
// or report a clear unresolved error.
|
||||
|
||||
// 6. Resolution failed - prevent runtime string-based resolution
|
||||
Err(format!("Unresolved function: '{}'. {}", name, suggest_resolution(name)))
|
||||
Err(format!(
|
||||
"Unresolved function: '{}'. {}",
|
||||
name,
|
||||
suggest_resolution(name)
|
||||
))
|
||||
}
|
||||
|
||||
/// Check if function name is a built-in global function
|
||||
pub fn is_builtin_function(name: &str) -> bool {
|
||||
matches!(name,
|
||||
matches!(
|
||||
name,
|
||||
"print" | "error" | "panic" | "exit" | "now" |
|
||||
"gc_collect" | "gc_stats" |
|
||||
// Math functions (handled specially)
|
||||
@ -68,9 +73,7 @@ pub fn is_builtin_function(name: &str) -> bool {
|
||||
|
||||
/// Check if function name is an external/host function
|
||||
pub fn is_extern_function(name: &str) -> bool {
|
||||
name.starts_with("nyash.") ||
|
||||
name.starts_with("env.") ||
|
||||
name.starts_with("system.")
|
||||
name.starts_with("nyash.") || name.starts_with("env.") || name.starts_with("system.")
|
||||
}
|
||||
|
||||
/// Check if method is commonly shadowed (for warning generation)
|
||||
@ -90,18 +93,12 @@ pub fn generate_self_recursion_warning(box_name: &str, method: &str) -> String {
|
||||
/// Suggest resolution for unresolved function
|
||||
pub fn suggest_resolution(name: &str) -> String {
|
||||
match name {
|
||||
n if n.starts_with("console") => {
|
||||
"Did you mean 'env.console.log' or 'print'?".to_string()
|
||||
}
|
||||
"log" | "println" => {
|
||||
"Did you mean 'print' or 'env.console.log'?".to_string()
|
||||
}
|
||||
n if n.starts_with("console") => "Did you mean 'env.console.log' or 'print'?".to_string(),
|
||||
"log" | "println" => "Did you mean 'print' or 'env.console.log'?".to_string(),
|
||||
n if n.contains('.') => {
|
||||
"Qualified names should use 'env.' prefix for external calls.".to_string()
|
||||
}
|
||||
_ => {
|
||||
"Check function name or ensure it's in scope.".to_string()
|
||||
}
|
||||
_ => "Check function name or ensure it's in scope.".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -17,24 +17,24 @@ pub mod method_resolution;
|
||||
pub mod special_handlers;
|
||||
|
||||
// New refactored modules (Box Theory Phase 1 & 2 & 25.1d & Phase 3)
|
||||
pub mod lowering;
|
||||
pub mod utils;
|
||||
pub mod emit; // Phase 2: Call emission
|
||||
pub mod build; // Phase 2: Call building
|
||||
pub mod guard; // Phase 25.1d: Structural guard (static/runtime box separation)
|
||||
pub mod resolver; // Phase 25.1d: Callee resolution (CallTarget → Callee)
|
||||
pub mod unified_emitter; // Phase 3-A: Unified call emitter (統一Call発行専用箱)
|
||||
pub mod build; // Phase 2: Call building
|
||||
pub mod effects_analyzer; // Phase 3-B: Effects analyzer (エフェクト解析専用箱)
|
||||
pub mod materializer; // Phase 3-C: Call materializer (Call前処理・準備専用箱)
|
||||
pub mod emit; // Phase 2: Call emission
|
||||
pub mod guard; // Phase 25.1d: Structural guard (static/runtime box separation)
|
||||
pub mod lowering;
|
||||
pub mod materializer;
|
||||
pub mod resolver; // Phase 25.1d: Callee resolution (CallTarget → Callee)
|
||||
pub mod unified_emitter; // Phase 3-A: Unified call emitter (統一Call発行専用箱)
|
||||
pub mod utils; // Phase 3-C: Call materializer (Call前処理・準備専用箱)
|
||||
|
||||
// Re-export public interfaces
|
||||
#[allow(unused_imports)]
|
||||
pub use build::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use call_target::CallTarget;
|
||||
#[allow(unused_imports)]
|
||||
pub use emit::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use lowering::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use utils::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use emit::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use build::*;
|
||||
|
||||
@ -12,11 +12,11 @@
|
||||
* - Call引数検証
|
||||
*/
|
||||
|
||||
use crate::mir::{Callee, MirType, ValueId};
|
||||
use super::method_resolution;
|
||||
use crate::mir::builder::type_registry::TypeRegistry;
|
||||
use crate::mir::builder::CallTarget;
|
||||
use crate::mir::definitions::call_unified::{CalleeBoxKind, TypeCertainty};
|
||||
use crate::mir::builder::type_registry::TypeRegistry;
|
||||
use super::method_resolution;
|
||||
use crate::mir::{Callee, MirType, ValueId};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Callee解決専用箱
|
||||
@ -56,9 +56,7 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
///
|
||||
/// 🎯 TypeRegistry対応: NYASH_USE_TYPE_REGISTRY=1 で registry 優先
|
||||
pub fn resolve(&self, target: CallTarget) -> Result<Callee, String> {
|
||||
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY")
|
||||
.ok()
|
||||
.as_deref() == Some("1");
|
||||
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY").ok().as_deref() == Some("1");
|
||||
|
||||
match target {
|
||||
CallTarget::Global(name) => {
|
||||
@ -71,27 +69,36 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
// Module-local or static lowered function (e.g., "Box.method/N")
|
||||
Ok(Callee::Global(name))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
CallTarget::Method { box_type, method, receiver } => {
|
||||
CallTarget::Method {
|
||||
box_type,
|
||||
method,
|
||||
receiver,
|
||||
} => {
|
||||
// 🔍 Debug: trace box_name resolution
|
||||
let trace_enabled = std::env::var("NYASH_CALLEE_RESOLVE_TRACE")
|
||||
.ok()
|
||||
.as_deref() == Some("1");
|
||||
let trace_enabled =
|
||||
std::env::var("NYASH_CALLEE_RESOLVE_TRACE").ok().as_deref() == Some("1");
|
||||
if trace_enabled {
|
||||
eprintln!("[callee-resolve] receiver=%{} method={}", receiver.0, method);
|
||||
eprintln!(
|
||||
"[callee-resolve] receiver=%{} method={}",
|
||||
receiver.0, method
|
||||
);
|
||||
eprintln!("[callee-resolve] explicit box_type: {:?}", box_type);
|
||||
eprintln!("[callee-resolve] use_registry: {}", use_registry);
|
||||
}
|
||||
|
||||
let inferred_box_type = self.infer_box_type(receiver, box_type, trace_enabled, use_registry);
|
||||
let inferred_box_type =
|
||||
self.infer_box_type(receiver, box_type, trace_enabled, use_registry);
|
||||
|
||||
// Certainty is Known when we have explicit origin or Box型の型情報を持つ場合
|
||||
let has_box_type = self.value_types
|
||||
let has_box_type = self
|
||||
.value_types
|
||||
.get(&receiver)
|
||||
.map(|t| matches!(t, MirType::Box(_)))
|
||||
.unwrap_or(false);
|
||||
let certainty = if self.value_origin_newbox.contains_key(&receiver) || has_box_type {
|
||||
let certainty = if self.value_origin_newbox.contains_key(&receiver) || has_box_type
|
||||
{
|
||||
TypeCertainty::Known
|
||||
} else {
|
||||
TypeCertainty::Union
|
||||
@ -101,7 +108,10 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
let box_kind = self.classify_box_kind(&inferred_box_type);
|
||||
|
||||
if trace_enabled {
|
||||
eprintln!("[callee-resolve] inferred_box_name: {}", inferred_box_type);
|
||||
eprintln!(
|
||||
"[callee-resolve] inferred_box_name: {}",
|
||||
inferred_box_type
|
||||
);
|
||||
eprintln!("[callee-resolve] box_kind: {:?}", box_kind);
|
||||
}
|
||||
|
||||
@ -112,23 +122,23 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
certainty,
|
||||
box_kind,
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
CallTarget::Constructor(box_type) => {
|
||||
Ok(Callee::Constructor { box_type })
|
||||
},
|
||||
CallTarget::Constructor(box_type) => Ok(Callee::Constructor { box_type }),
|
||||
|
||||
CallTarget::Extern(name) => {
|
||||
Ok(Callee::Extern(name))
|
||||
},
|
||||
CallTarget::Extern(name) => Ok(Callee::Extern(name)),
|
||||
|
||||
CallTarget::Value(func_val) => {
|
||||
Ok(Callee::Value(func_val))
|
||||
},
|
||||
CallTarget::Value(func_val) => Ok(Callee::Value(func_val)),
|
||||
|
||||
CallTarget::Closure { params, captures, me_capture } => {
|
||||
Ok(Callee::Closure { params, captures, me_capture })
|
||||
},
|
||||
CallTarget::Closure {
|
||||
params,
|
||||
captures,
|
||||
me_capture,
|
||||
} => Ok(Callee::Closure {
|
||||
params,
|
||||
captures,
|
||||
me_capture,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,14 +193,18 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
}
|
||||
"exit" => {
|
||||
if args.len() != 1 {
|
||||
return Err("exit requires exactly one argument (exit code)".to_string());
|
||||
return Err(
|
||||
"exit requires exactly one argument (exit code)".to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {} // Unknown functions pass through
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Callee::Method { box_name, method, .. } => {
|
||||
Callee::Method {
|
||||
box_name, method, ..
|
||||
} => {
|
||||
// Validate known methods
|
||||
match (box_name.as_str(), method.as_str()) {
|
||||
("ArrayBox", "get") | ("ArrayBox", "set") => {
|
||||
@ -200,7 +214,7 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
}
|
||||
_ => {} // Unknown methods pass through
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_ => {} // Other callee types don't have validation yet
|
||||
}
|
||||
@ -240,12 +254,10 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
}
|
||||
|
||||
// 従来: HashMap から推論(型情報を優先し、origin は補助とする)
|
||||
let from_type = self.value_types
|
||||
.get(&receiver)
|
||||
.and_then(|t| match t {
|
||||
MirType::Box(box_name) => Some(box_name.clone()),
|
||||
_ => None,
|
||||
});
|
||||
let from_type = self.value_types.get(&receiver).and_then(|t| match t {
|
||||
MirType::Box(box_name) => Some(box_name.clone()),
|
||||
_ => None,
|
||||
});
|
||||
let from_origin = self.value_origin_newbox.get(&receiver).cloned();
|
||||
|
||||
if trace_enabled {
|
||||
@ -254,14 +266,12 @@ impl<'a> CalleeResolverBox<'a> {
|
||||
}
|
||||
|
||||
// 型情報(MirType)がある場合はそれを優先し、無い場合のみ origin にフォールバックする。
|
||||
from_type
|
||||
.or(from_origin)
|
||||
.unwrap_or_else(|| {
|
||||
if trace_enabled {
|
||||
eprintln!("[callee-resolve] FALLBACK: UnknownBox");
|
||||
}
|
||||
"UnknownBox".to_string()
|
||||
})
|
||||
from_type.or(from_origin).unwrap_or_else(|| {
|
||||
if trace_enabled {
|
||||
eprintln!("[callee-resolve] FALLBACK: UnknownBox");
|
||||
}
|
||||
"UnknownBox".to_string()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -277,14 +287,26 @@ mod tests {
|
||||
let resolver = CalleeResolverBox::new(&value_origin, &value_types, None);
|
||||
|
||||
// Stage-B boxes
|
||||
assert_eq!(resolver.classify_box_kind("StageBArgsBox"), CalleeBoxKind::StaticCompiler);
|
||||
assert_eq!(resolver.classify_box_kind("StageBDriverBox"), CalleeBoxKind::StaticCompiler);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("StageBArgsBox"),
|
||||
CalleeBoxKind::StaticCompiler
|
||||
);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("StageBDriverBox"),
|
||||
CalleeBoxKind::StaticCompiler
|
||||
);
|
||||
|
||||
// Stage-1 boxes
|
||||
assert_eq!(resolver.classify_box_kind("Stage1UsingResolverBox"), CalleeBoxKind::StaticCompiler);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("Stage1UsingResolverBox"),
|
||||
CalleeBoxKind::StaticCompiler
|
||||
);
|
||||
|
||||
// Parser boxes
|
||||
assert_eq!(resolver.classify_box_kind("ParserBox"), CalleeBoxKind::StaticCompiler);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("ParserBox"),
|
||||
CalleeBoxKind::StaticCompiler
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -293,10 +315,22 @@ mod tests {
|
||||
let value_types = HashMap::new();
|
||||
let resolver = CalleeResolverBox::new(&value_origin, &value_types, None);
|
||||
|
||||
assert_eq!(resolver.classify_box_kind("MapBox"), CalleeBoxKind::RuntimeData);
|
||||
assert_eq!(resolver.classify_box_kind("ArrayBox"), CalleeBoxKind::RuntimeData);
|
||||
assert_eq!(resolver.classify_box_kind("StringBox"), CalleeBoxKind::RuntimeData);
|
||||
assert_eq!(resolver.classify_box_kind("UnknownBox"), CalleeBoxKind::RuntimeData);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("MapBox"),
|
||||
CalleeBoxKind::RuntimeData
|
||||
);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("ArrayBox"),
|
||||
CalleeBoxKind::RuntimeData
|
||||
);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("StringBox"),
|
||||
CalleeBoxKind::RuntimeData
|
||||
);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("UnknownBox"),
|
||||
CalleeBoxKind::RuntimeData
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -305,8 +339,14 @@ mod tests {
|
||||
let value_types = HashMap::new();
|
||||
let resolver = CalleeResolverBox::new(&value_origin, &value_types, None);
|
||||
|
||||
assert_eq!(resolver.classify_box_kind("MyCustomBox"), CalleeBoxKind::UserDefined);
|
||||
assert_eq!(resolver.classify_box_kind("PersonBox"), CalleeBoxKind::UserDefined);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("MyCustomBox"),
|
||||
CalleeBoxKind::UserDefined
|
||||
);
|
||||
assert_eq!(
|
||||
resolver.classify_box_kind("PersonBox"),
|
||||
CalleeBoxKind::UserDefined
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -12,7 +12,10 @@ use crate::mir::MirType;
|
||||
|
||||
/// Check if a function is a math function
|
||||
pub fn is_math_function(name: &str) -> bool {
|
||||
matches!(name, "sin" | "cos" | "abs" | "min" | "max" | "sqrt" | "pow" | "floor" | "ceil")
|
||||
matches!(
|
||||
name,
|
||||
"sin" | "cos" | "abs" | "min" | "max" | "sqrt" | "pow" | "floor" | "ceil"
|
||||
)
|
||||
}
|
||||
|
||||
/// Check if a method is a type operation (.is() or .as())
|
||||
@ -62,11 +65,17 @@ pub fn parse_type_name_to_mir(name: &str) -> MirType {
|
||||
/// Check if a value is a numeric literal or numeric Box constructor
|
||||
pub fn is_numeric_value(node: &ASTNode) -> bool {
|
||||
match node {
|
||||
ASTNode::Literal { value: LiteralValue::Integer(_), .. } => true,
|
||||
ASTNode::Literal { value: LiteralValue::Float(_), .. } => true,
|
||||
ASTNode::New { class, arguments, .. } => {
|
||||
(class == "IntegerBox" || class == "FloatBox") && arguments.len() == 1
|
||||
}
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(_),
|
||||
..
|
||||
} => true,
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Float(_),
|
||||
..
|
||||
} => true,
|
||||
ASTNode::New {
|
||||
class, arguments, ..
|
||||
} => (class == "IntegerBox" || class == "FloatBox") && arguments.len() == 1,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -74,8 +83,14 @@ pub fn is_numeric_value(node: &ASTNode) -> bool {
|
||||
/// Extract numeric type from AST node
|
||||
pub fn extract_numeric_type(node: &ASTNode) -> Option<MirType> {
|
||||
match node {
|
||||
ASTNode::Literal { value: LiteralValue::Integer(_), .. } => Some(MirType::Integer),
|
||||
ASTNode::Literal { value: LiteralValue::Float(_), .. } => Some(MirType::Float),
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(_),
|
||||
..
|
||||
} => Some(MirType::Integer),
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Float(_),
|
||||
..
|
||||
} => Some(MirType::Float),
|
||||
ASTNode::New { class, .. } if class == "IntegerBox" => Some(MirType::Integer),
|
||||
ASTNode::New { class, .. } if class == "FloatBox" => Some(MirType::Float),
|
||||
_ => None,
|
||||
@ -87,7 +102,11 @@ pub fn contains_value_return(nodes: &[ASTNode]) -> bool {
|
||||
fn node_has_value_return(node: &ASTNode) -> bool {
|
||||
match node {
|
||||
ASTNode::Return { value: Some(_), .. } => true,
|
||||
ASTNode::If { then_body, else_body, .. } => {
|
||||
ASTNode::If {
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} => {
|
||||
contains_value_return(then_body)
|
||||
|| else_body
|
||||
.as_ref()
|
||||
@ -125,9 +144,9 @@ pub fn make_function_name_with_arity(base_name: &str, arity: usize) -> String {
|
||||
|
||||
/// Check if a name is a reserved/special function
|
||||
pub fn is_reserved_function(name: &str) -> bool {
|
||||
matches!(name,
|
||||
"birth" | "me" | "this" | "super" | "from" |
|
||||
"new" | "delete" | "typeof" | "instanceof"
|
||||
matches!(
|
||||
name,
|
||||
"birth" | "me" | "this" | "super" | "from" | "new" | "delete" | "typeof" | "instanceof"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -13,10 +13,10 @@
|
||||
* - emit_value_unified: 第一級関数呼び出し
|
||||
*/
|
||||
|
||||
use crate::mir::builder::{MirBuilder, ValueId, MirInstruction, Effect, EffectMask};
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
use super::CallTarget;
|
||||
use super::call_unified;
|
||||
use super::CallTarget;
|
||||
use crate::mir::builder::{Effect, EffectMask, MirBuilder, MirInstruction, ValueId};
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
|
||||
/// 統一Call発行専用箱
|
||||
///
|
||||
@ -39,10 +39,16 @@ impl UnifiedCallEmitterBox {
|
||||
const MAX_EMIT_DEPTH: usize = 100;
|
||||
builder.recursion_depth += 1;
|
||||
if builder.recursion_depth > MAX_EMIT_DEPTH {
|
||||
eprintln!("[FATAL] emit_unified_call recursion depth exceeded {}", MAX_EMIT_DEPTH);
|
||||
eprintln!(
|
||||
"[FATAL] emit_unified_call recursion depth exceeded {}",
|
||||
MAX_EMIT_DEPTH
|
||||
);
|
||||
eprintln!("[FATAL] Current depth: {}", builder.recursion_depth);
|
||||
eprintln!("[FATAL] Target: {:?}", target);
|
||||
return Err(format!("emit_unified_call recursion depth exceeded: {}", builder.recursion_depth));
|
||||
return Err(format!(
|
||||
"emit_unified_call recursion depth exceeded: {}",
|
||||
builder.recursion_depth
|
||||
));
|
||||
}
|
||||
|
||||
// Check environment variable for unified call usage
|
||||
@ -62,11 +68,16 @@ impl UnifiedCallEmitterBox {
|
||||
target: CallTarget,
|
||||
args: Vec<ValueId>,
|
||||
) -> Result<(), String> {
|
||||
|
||||
// Emit resolve.try for method targets (dev-only; default OFF)
|
||||
let arity_for_try = args.len();
|
||||
if let CallTarget::Method { ref box_type, ref method, receiver } = target {
|
||||
let recv_cls = box_type.clone()
|
||||
if let CallTarget::Method {
|
||||
ref box_type,
|
||||
ref method,
|
||||
receiver,
|
||||
} = target
|
||||
{
|
||||
let recv_cls = box_type
|
||||
.clone()
|
||||
.or_else(|| builder.value_origin_newbox.get(&receiver).cloned())
|
||||
.unwrap_or_default();
|
||||
// Use indexed candidate lookup (tail → names)
|
||||
@ -81,22 +92,60 @@ impl UnifiedCallEmitterBox {
|
||||
}
|
||||
|
||||
// Centralized user-box rewrite for method targets (toString/stringify, equals/1, Known→unique)
|
||||
if let CallTarget::Method { ref box_type, ref method, receiver } = target {
|
||||
let class_name_opt = box_type.clone()
|
||||
if let CallTarget::Method {
|
||||
ref box_type,
|
||||
ref method,
|
||||
receiver,
|
||||
} = target
|
||||
{
|
||||
let class_name_opt = box_type
|
||||
.clone()
|
||||
.or_else(|| builder.value_origin_newbox.get(&receiver).cloned())
|
||||
.or_else(|| builder.value_types.get(&receiver).and_then(|t| if let crate::mir::MirType::Box(b) = t { Some(b.clone()) } else { None }));
|
||||
.or_else(|| {
|
||||
builder.value_types.get(&receiver).and_then(|t| {
|
||||
if let crate::mir::MirType::Box(b) = t {
|
||||
Some(b.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
});
|
||||
// Early str-like
|
||||
if let Some(res) = crate::mir::builder::rewrite::special::try_early_str_like_to_dst(
|
||||
builder, dst, receiver, &class_name_opt, method, args.len(),
|
||||
) { res?; return Ok(()); }
|
||||
builder,
|
||||
dst,
|
||||
receiver,
|
||||
&class_name_opt,
|
||||
method,
|
||||
args.len(),
|
||||
) {
|
||||
res?;
|
||||
return Ok(());
|
||||
}
|
||||
// equals/1
|
||||
if let Some(res) = crate::mir::builder::rewrite::special::try_special_equals_to_dst(
|
||||
builder, dst, receiver, &class_name_opt, method, args.clone(),
|
||||
) { res?; return Ok(()); }
|
||||
builder,
|
||||
dst,
|
||||
receiver,
|
||||
&class_name_opt,
|
||||
method,
|
||||
args.clone(),
|
||||
) {
|
||||
res?;
|
||||
return Ok(());
|
||||
}
|
||||
// Known or unique
|
||||
if let Some(res) = crate::mir::builder::rewrite::known::try_known_or_unique_to_dst(
|
||||
builder, dst, receiver, &class_name_opt, method, args.clone(),
|
||||
) { res?; return Ok(()); }
|
||||
builder,
|
||||
dst,
|
||||
receiver,
|
||||
&class_name_opt,
|
||||
method,
|
||||
args.clone(),
|
||||
) {
|
||||
res?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// Convert CallTarget to Callee using CalleeResolverBox
|
||||
@ -105,14 +154,18 @@ impl UnifiedCallEmitterBox {
|
||||
let resolver = super::resolver::CalleeResolverBox::new(
|
||||
&builder.value_origin_newbox,
|
||||
&builder.value_types,
|
||||
Some(&builder.type_registry), // 🎯 TypeRegistry を渡す
|
||||
Some(&builder.type_registry), // 🎯 TypeRegistry を渡す
|
||||
);
|
||||
let mut callee = match resolver.resolve(target.clone()) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
if let CallTarget::Global(ref name) = target {
|
||||
// Try fallback handlers (via CallMaterializerBox)
|
||||
if let Some(result) = super::materializer::CallMaterializerBox::try_global_fallback_handlers(builder, dst, name, &args)? {
|
||||
if let Some(result) =
|
||||
super::materializer::CallMaterializerBox::try_global_fallback_handlers(
|
||||
builder, dst, name, &args,
|
||||
)?
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
@ -121,7 +174,9 @@ impl UnifiedCallEmitterBox {
|
||||
};
|
||||
|
||||
// Safety: ensure receiver is materialized even after callee conversion (via CallMaterializerBox)
|
||||
callee = super::materializer::CallMaterializerBox::materialize_receiver_in_callee(builder, callee)?;
|
||||
callee = super::materializer::CallMaterializerBox::materialize_receiver_in_callee(
|
||||
builder, callee,
|
||||
)?;
|
||||
|
||||
// Structural guard: prevent static compiler boxes from being called with runtime receivers
|
||||
// 箱理論: CalleeGuardBox による構造的分離
|
||||
@ -129,7 +184,13 @@ impl UnifiedCallEmitterBox {
|
||||
callee = guard.apply_static_runtime_guard(callee)?;
|
||||
|
||||
// Emit resolve.choose for method callee (dev-only; default OFF)
|
||||
if let Callee::Method { box_name, method, certainty, .. } = &callee {
|
||||
if let Callee::Method {
|
||||
box_name,
|
||||
method,
|
||||
certainty,
|
||||
..
|
||||
} = &callee
|
||||
{
|
||||
let chosen = format!("{}.{}{}", box_name, method, format!("/{}", arity_for_try));
|
||||
let meta = serde_json::json!({
|
||||
"recv_cls": box_name,
|
||||
@ -152,11 +213,28 @@ impl UnifiedCallEmitterBox {
|
||||
resolver.validate_args(&callee, &args)?;
|
||||
|
||||
// Stability guard: decide route via RouterPolicyBox (behavior-preserving rules)
|
||||
if let Callee::Method { box_name, method, receiver: Some(r), certainty, .. } = &callee {
|
||||
let route = crate::mir::builder::router::policy::choose_route(box_name, method, *certainty, arity_for_try);
|
||||
if let Callee::Method {
|
||||
box_name,
|
||||
method,
|
||||
receiver: Some(r),
|
||||
certainty,
|
||||
..
|
||||
} = &callee
|
||||
{
|
||||
let route = crate::mir::builder::router::policy::choose_route(
|
||||
box_name,
|
||||
method,
|
||||
*certainty,
|
||||
arity_for_try,
|
||||
);
|
||||
if let crate::mir::builder::router::policy::Route::BoxCall = route {
|
||||
if crate::mir::builder::utils::builder_debug_enabled() || std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[router-guard] {}.{} → BoxCall fallback (recv=%{})", box_name, method, r.0);
|
||||
if crate::mir::builder::utils::builder_debug_enabled()
|
||||
|| std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1")
|
||||
{
|
||||
eprintln!(
|
||||
"[router-guard] {}.{} → BoxCall fallback (recv=%{})",
|
||||
box_name, method, r.0
|
||||
);
|
||||
}
|
||||
let effects = EffectMask::READ.add(Effect::ReadHeap);
|
||||
// Prevent BoxCall helper from bouncing back into emit_unified_call
|
||||
@ -166,7 +244,8 @@ impl UnifiedCallEmitterBox {
|
||||
// would otherwise choose Unified.
|
||||
let prev_flag = builder.in_unified_boxcall_fallback;
|
||||
builder.in_unified_boxcall_fallback = true;
|
||||
let res = builder.emit_box_or_plugin_call(dst, *r, method.clone(), None, args, effects);
|
||||
let res =
|
||||
builder.emit_box_or_plugin_call(dst, *r, method.clone(), None, args, effects);
|
||||
builder.in_unified_boxcall_fallback = prev_flag;
|
||||
return res;
|
||||
}
|
||||
@ -175,13 +254,21 @@ impl UnifiedCallEmitterBox {
|
||||
// Finalize operands in current block (EmitGuardBox wrapper)
|
||||
let mut callee = callee;
|
||||
let mut args_local: Vec<ValueId> = args;
|
||||
crate::mir::builder::emit_guard::finalize_call_operands(builder, &mut callee, &mut args_local);
|
||||
crate::mir::builder::emit_guard::finalize_call_operands(
|
||||
builder,
|
||||
&mut callee,
|
||||
&mut args_local,
|
||||
);
|
||||
|
||||
// 📦 Hotfix 7: Include receiver in args for Callee::Method
|
||||
// VM's exec_function_inner expects receiver as the first parameter (ValueId(0))
|
||||
// but finalize_call_operands keeps receiver in Callee, not in args.
|
||||
// We must add it to args_local here so VM can bind it correctly.
|
||||
if let Callee::Method { receiver: Some(recv), .. } = &callee {
|
||||
if let Callee::Method {
|
||||
receiver: Some(recv),
|
||||
..
|
||||
} = &callee
|
||||
{
|
||||
args_local.insert(0, *recv);
|
||||
}
|
||||
|
||||
@ -189,11 +276,21 @@ impl UnifiedCallEmitterBox {
|
||||
let mir_call = call_unified::create_mir_call(dst, callee.clone(), args_local.clone());
|
||||
|
||||
// Dev trace: show final callee/recv right before emission (guarded)
|
||||
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") || crate::mir::builder::utils::builder_debug_enabled() {
|
||||
if let Callee::Method { method, receiver, box_name, .. } = &callee {
|
||||
if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1")
|
||||
|| crate::mir::builder::utils::builder_debug_enabled()
|
||||
{
|
||||
if let Callee::Method {
|
||||
method,
|
||||
receiver,
|
||||
box_name,
|
||||
..
|
||||
} = &callee
|
||||
{
|
||||
if let Some(r) = receiver {
|
||||
eprintln!("[vm-call-final] bb={:?} method={} recv=%{} class={}",
|
||||
builder.current_block, method, r.0, box_name);
|
||||
eprintln!(
|
||||
"[vm-call-final] bb={:?} method={} recv=%{} class={}",
|
||||
builder.current_block, method, r.0, box_name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -223,7 +320,11 @@ impl UnifiedCallEmitterBox {
|
||||
// Create a string constant for the function name via NameConstBox
|
||||
let name_const = crate::mir::builder::name_const::make_name_const_result(builder, &name)?;
|
||||
// Allocate a destination if not provided so we can annotate it
|
||||
let actual_dst = if let Some(d) = dst { d } else { builder.next_value_id() };
|
||||
let actual_dst = if let Some(d) = dst {
|
||||
d
|
||||
} else {
|
||||
builder.next_value_id()
|
||||
};
|
||||
let mut args = args;
|
||||
crate::mir::builder::ssa::local::finalize_args(builder, &mut args);
|
||||
builder.emit_instruction(MirInstruction::Call {
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
})?;
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
@ -2,4 +2,3 @@
|
||||
//! - value_types と value_origin_newbox の伝播を一箇所に集約する。
|
||||
|
||||
pub mod propagate;
|
||||
|
||||
|
||||
@ -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 経由
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -3,6 +3,5 @@
|
||||
//! - ssa: PHI/SSA related debug emissions
|
||||
//! - resolve: method resolution try/choose(既存呼び出しの置換は段階的に)
|
||||
|
||||
pub mod ssa;
|
||||
pub mod resolve;
|
||||
|
||||
pub mod ssa;
|
||||
|
||||
@ -23,14 +23,20 @@ fn sample_every() -> usize {
|
||||
|
||||
/// Dev‑only: emit a resolve.try event(candidates 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);
|
||||
}
|
||||
|
||||
/// Dev‑only: emit a resolve.choose event(decision)。
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,4 +10,3 @@
|
||||
|
||||
pub mod infer;
|
||||
pub mod phi;
|
||||
|
||||
|
||||
@ -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); } }
|
||||
}
|
||||
|
||||
@ -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)?;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,4 +7,3 @@
|
||||
|
||||
pub mod known;
|
||||
pub mod special;
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,2 @@
|
||||
//! Router policy module
|
||||
pub mod policy;
|
||||
|
||||
|
||||
@ -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={:?}",
|
||||
|
||||
@ -1,2 +1 @@
|
||||
pub mod block;
|
||||
|
||||
|
||||
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,2 +1 @@
|
||||
pub mod local;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -3,4 +3,3 @@
|
||||
//! - inference.rs(後段、挙動不変の観測強化と最小推論)。
|
||||
|
||||
pub mod annotation;
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user