debug(mir): add comprehensive receiver tracing and block overwrite protection
This commit investigates ValueId(21) undefined error in Stage-B compilation. Changes: - src/mir/builder/builder_calls.rs: - Add NYASH_DEBUG_PARAM_RECEIVER=1 trace for method call receivers - Track variable_map lookups and ValueId mismatches - Log receiver origin and current_block context - src/mir/builder/utils.rs: - Fix start_new_block() to avoid overwriting existing blocks - Check if block exists before calling add_block() - Prevents Copy instructions from being lost - src/mir/function.rs: - Add warning log when replacing existing block - Helps detect block overwrite issues - lang/src/mir/builder/ (Hako files): - Add debug prints for method lowering paths - These were not used (Stage-B uses Rust MIR Builder) - Kept for future Hako MIR Builder debugging Key Discovery: - Stage-B compilation uses Rust MIR Builder, not Hako MIR Builder - ValueId(21) is undefined receiver in StageBArgsBox.resolve_src/1 - MIR shows: call_method ParserBox.length() [recv: %21] but %21 has no definition - Likely caused by LocalSSA Copy emission failure or block overwrite Next: Fix LocalSSA to ensure receiver Copy is properly emitted and preserved 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -37,8 +37,12 @@ static box FuncBodyBasicLowerBox {
|
|||||||
|
|
||||||
// 2) Try simple Return(Method ...) pattern(params ベースの receiver のみ)
|
// 2) Try simple Return(Method ...) pattern(params ベースの receiver のみ)
|
||||||
{
|
{
|
||||||
|
print("[builder/funcs:return_method] trying func=" + func_name + " box=" + box_name)
|
||||||
local mret = me._try_lower_return_method(func_name, box_name, params_arr, s)
|
local mret = me._try_lower_return_method(func_name, box_name, params_arr, s)
|
||||||
if mret != null { return mret }
|
if mret != null {
|
||||||
|
print("[builder/funcs:return_method] SUCCESS via _try_lower_return_method")
|
||||||
|
return mret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) Non-Loop processing: Local/If/Return パターンのみ
|
// 3) Non-Loop processing: Local/If/Return パターンのみ
|
||||||
@ -48,7 +52,14 @@ static box FuncBodyBasicLowerBox {
|
|||||||
s = me._inline_local_ints(s)
|
s = me._inline_local_ints(s)
|
||||||
|
|
||||||
// Try minimal lowers (same order as MirBuilderMinBox, restricted set)
|
// Try minimal lowers (same order as MirBuilderMinBox, restricted set)
|
||||||
{ local out = LowerReturnMethodArrayMapBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:return.method.arraymap]") } }
|
{
|
||||||
|
print("[builder/funcs] trying LowerReturnMethodArrayMapBox (LEGACY, const 0 receiver)")
|
||||||
|
local out = LowerReturnMethodArrayMapBox.try_lower(s)
|
||||||
|
if out != null {
|
||||||
|
print("[builder/funcs] SUCCESS via LowerReturnMethodArrayMapBox (WARNING: uses const 0 receiver!)")
|
||||||
|
return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:return.method.arraymap]")
|
||||||
|
}
|
||||||
|
}
|
||||||
{ local out = LowerReturnBinOpVarIntBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:return.binop.varint]") } }
|
{ local out = LowerReturnBinOpVarIntBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:return.binop.varint]") } }
|
||||||
{ local out = LowerReturnBinOpVarVarBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:return.binop.varvar]") } }
|
{ local out = LowerReturnBinOpVarVarBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:return.binop.varvar]") } }
|
||||||
{ local out = LowerReturnBinOpBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:return.binop.intint]") } }
|
{ local out = LowerReturnBinOpBox.try_lower(s); if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:return.binop.intint]") } }
|
||||||
|
|||||||
@ -12,16 +12,33 @@ static box LowerReturnMethodArrayMapBox {
|
|||||||
local k_m = JsonFragBox.index_of_from(s, "\"type\":\"Method\"", k_ret); if k_m < 0 { return null }
|
local k_m = JsonFragBox.index_of_from(s, "\"type\":\"Method\"", k_ret); if k_m < 0 { return null }
|
||||||
// receiver must be Var(name)
|
// receiver must be Var(name)
|
||||||
local k_recv = JsonFragBox.index_of_from(s, "\"recv\":{\"type\":\"Var\"", k_m); if k_recv < 0 { return null }
|
local k_recv = JsonFragBox.index_of_from(s, "\"recv\":{\"type\":\"Var\"", k_m); if k_recv < 0 { return null }
|
||||||
|
|
||||||
|
// Extract receiver variable name
|
||||||
|
local recv_name = null
|
||||||
|
{
|
||||||
|
local kn = JsonFragBox.index_of_from(s, "\"name\":\"", k_recv)
|
||||||
|
if kn >= 0 {
|
||||||
|
recv_name = JsonFragBox.read_string_after(s, kn + 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
local method = null
|
local method = null
|
||||||
{ local km = JsonFragBox.index_of_from(s, "\"method\":\"", k_m); if km < 0 { return null } method = JsonFragBox.read_string_after(s, km + 9) }
|
{ local km = JsonFragBox.index_of_from(s, "\"method\":\"", k_m); if km < 0 { return null } method = JsonFragBox.read_string_after(s, km + 9) }
|
||||||
// Standardize: generate 'size' (len/length are accepted aliases at receiver)
|
// Standardize: generate 'size' (len/length are accepted aliases at receiver)
|
||||||
method = MethodAliasPolicy.normalize_size(method)
|
method = MethodAliasPolicy.normalize_size(method)
|
||||||
// Allow basic methods
|
// Allow basic methods
|
||||||
if !(method == "size" || method == "length" || method == "len" || method == "get" || method == "set" || method == "push") { return null }
|
if !(method == "size" || method == "length" || method == "len" || method == "get" || method == "set" || method == "push") { return null }
|
||||||
|
|
||||||
|
// Dev trace: show what we matched (always print for now)
|
||||||
|
print("[builder/arraymap] matched return method pattern")
|
||||||
|
print("[builder/arraymap] method=" + method)
|
||||||
|
print("[builder/arraymap] recv_name=" + recv_name)
|
||||||
|
|
||||||
// Parse up to two Int args with actual values
|
// Parse up to two Int args with actual values
|
||||||
// Prepare instruction JSON text (avoid Box allocations in VM)
|
// Prepare instruction JSON text (avoid Box allocations in VM)
|
||||||
local insts = ""
|
local insts = ""
|
||||||
// Receiver placeholder: const 0 -> r1(Var 解決は未実装なので 0 を受信者に置く)
|
// FIXME: Receiver placeholder: const 0 -> r1(Var 解決は未実装なので 0 を受信者に置く)
|
||||||
|
// TODO: Use recv_name to find actual parameter slot instead of hardcoded 0
|
||||||
insts = insts + "{\\\"op\\\":\\\"const\\\",\\\"dst\\\":1,\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":0}}"
|
insts = insts + "{\\\"op\\\":\\\"const\\\",\\\"dst\\\":1,\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":0}}"
|
||||||
local k_args = JsonFragBox.index_of_from(s, "\"args\":", k_m)
|
local k_args = JsonFragBox.index_of_from(s, "\"args\":", k_m)
|
||||||
local next_id = 2
|
local next_id = 2
|
||||||
|
|||||||
@ -699,7 +699,28 @@ impl super::MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. Build object value for remaining cases
|
// 4. Build object value for remaining cases
|
||||||
let object_value = self.build_expression(object)?;
|
let object_value = self.build_expression(object.clone())?;
|
||||||
|
|
||||||
|
// CRITICAL DEBUG: Track receiver ValueId for parameter variables
|
||||||
|
if std::env::var("NYASH_DEBUG_PARAM_RECEIVER").ok().as_deref() == Some("1") {
|
||||||
|
use crate::ast::ASTNode;
|
||||||
|
if let ASTNode::Variable { name, .. } = &object {
|
||||||
|
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);
|
||||||
|
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] current_block: {:?}", self.current_block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 5. Handle TypeOp methods: value.is("Type") / value.as("Type")
|
// 5. Handle TypeOp methods: value.is("Type") / value.as("Type")
|
||||||
// Note: This was duplicated in original code - now unified!
|
// Note: This was duplicated in original code - now unified!
|
||||||
|
|||||||
@ -70,7 +70,9 @@ impl super::MirBuilder {
|
|||||||
/// Start a new basic block and set as current
|
/// Start a new basic block and set as current
|
||||||
pub(crate) fn start_new_block(&mut self, block_id: BasicBlockId) -> Result<(), String> {
|
pub(crate) fn start_new_block(&mut self, block_id: BasicBlockId) -> Result<(), String> {
|
||||||
if let Some(ref mut function) = self.current_function {
|
if let Some(ref mut function) = self.current_function {
|
||||||
function.add_block(BasicBlock::new(block_id));
|
if !function.blocks.contains_key(&block_id) {
|
||||||
|
function.add_block(BasicBlock::new(block_id));
|
||||||
|
}
|
||||||
self.current_block = Some(block_id);
|
self.current_block = Some(block_id);
|
||||||
// Local SSA cache is per-block; clear on block switch
|
// Local SSA cache is per-block; clear on block switch
|
||||||
self.local_ssa_map.clear();
|
self.local_ssa_map.clear();
|
||||||
|
|||||||
@ -98,6 +98,9 @@ impl MirFunction {
|
|||||||
/// Add a new basic block
|
/// Add a new basic block
|
||||||
pub fn add_block(&mut self, block: BasicBlock) -> BasicBlockId {
|
pub fn add_block(&mut self, block: BasicBlock) -> BasicBlockId {
|
||||||
let id = block.id;
|
let id = block.id;
|
||||||
|
if self.blocks.contains_key(&id) && std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
|
||||||
|
eprintln!("[mir-function] replacing existing block {:?}", id);
|
||||||
|
}
|
||||||
self.blocks.insert(id, block);
|
self.blocks.insert(id, block);
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user