fix(mir): PHI検証panic修正 - update_cfg()を検証前に呼び出し

A案実装: debug_verify_phi_inputs呼び出し前にCFG predecessorを更新

修正箇所(7箇所):
- src/mir/builder/phi.rs:50, 73, 132, 143
- src/mir/builder/ops.rs:273, 328, 351

根本原因:
- Branch/Jump命令でsuccessorは即座に更新
- predecessorはupdate_cfg()で遅延再構築
- PHI検証が先に実行されてpredecessor未更新でpanic

解決策:
- 各debug_verify_phi_inputs呼び出し前に
  if let Some(func) = self.current_function.as_mut() {
      func.update_cfg();
  }
  を挿入してCFGを同期

影響: if/else文、論理演算子(&&/||)のPHI生成が正常動作
This commit is contained in:
nyash-codex
2025-11-01 13:28:56 +09:00
parent 149ec61d4d
commit 6a452b2dca
174 changed files with 2432 additions and 1014 deletions

View File

@ -8,10 +8,10 @@ static box BoxHelpers {
// ArrayBox.size/1 の結果unwrap (MapBox-wrapped integer対応)
array_len(arr) {
if arr == null { return 0 }
local size_val = call("ArrayBox.size/1", arr)
local size_val = arr.length()
local repr = "" + size_val
if repr.indexOf("MapBox(") == 0 {
local inner = call("MapBox.get/2", size_val, "value")
local inner = size_val.get("value")
if inner != null { return inner }
}
return size_val
@ -20,19 +20,19 @@ static box BoxHelpers {
// ArrayBox.get/2 の安全呼び出し
array_get(arr, idx) {
if arr == null { return null }
return call("ArrayBox.get/2", arr, idx)
return arr.get(idx)
}
// MapBox.get/2 の安全呼び出し
map_get(obj, key) {
if obj == null { return null }
return call("MapBox.get/2", obj, key)
return obj.get(key)
}
// MapBox.set/3 の安全呼び出し(形だけ統一)
map_set(obj, key, val) {
if obj == null { obj = new MapBox() }
call("MapBox.set/3", obj, key, val)
obj.set(key, val)
return obj
}
@ -41,7 +41,7 @@ static box BoxHelpers {
if val == null { return 0 }
local repr = "" + val
if repr.indexOf("MapBox(") == 0 {
local inner = call("MapBox.get/2", val, "value")
local inner = val.get("value")
if inner != null { return inner }
}
return val
@ -67,53 +67,53 @@ static box BoxHelpers {
expect_map(val, context) {
if val == null {
print("[BoxHelpers] expected MapBox for " + context + " but got null")
call("MapBox.get/2", val, "__box_helpers_expect_map_null")
val.get("__box_helpers_expect_map_null")
return val
}
if me.is_map(val) == 1 { return val }
print("[BoxHelpers] dev assert failed: expected MapBox for " + context)
call("MapBox.get/2", val, "__box_helpers_expect_map")
val.get("__box_helpers_expect_map")
return val
}
expect_array(val, context) {
if val == null {
print("[BoxHelpers] expected ArrayBox for " + context + " but got null")
call("ArrayBox.get/2", val, 0)
val.get(0)
return val
}
if me.is_array(val) == 1 { return val }
print("[BoxHelpers] dev assert failed: expected ArrayBox for " + context)
call("ArrayBox.get/2", val, 0)
val.get(0)
return val
}
expect_i64(val, context) {
if val == null {
print("[BoxHelpers] dev assert failed: expected i64 (non-null) for " + context)
call("MapBox.get/2", val, "__box_helpers_expect_i64_null")
val.get("__box_helpers_expect_i64_null")
return 0
}
local repr = "" + val
if repr.indexOf("MapBox(") == 0 {
local ty = call("MapBox.get/2", val, "type")
local ty = val.get("type")
if ty != null {
local ty_str = "" + ty
if ty_str != "i64" && ty_str != "int" && ty_str != "integer" {
print("[BoxHelpers] dev assert failed: unexpected type " + ty_str + " in " + context)
call("MapBox.get/2", val, "__box_helpers_expect_i64_type")
val.get("__box_helpers_expect_i64_type")
return 0
}
}
local inner = call("MapBox.get/2", val, "value")
local inner = val.get("value")
if inner != null { return StringHelpers.to_i64(inner) }
print("[BoxHelpers] dev assert failed: missing value in " + context)
call("MapBox.get/2", val, "__box_helpers_expect_i64_value")
val.get("__box_helpers_expect_i64_value")
return 0
}
if StringHelpers.is_numeric_str("" + val) == 1 { return StringHelpers.to_i64(val) }
print("[BoxHelpers] dev assert failed: expected numeric value for " + context)
call("MapBox.get/2", val, "__box_helpers_expect_i64_direct")
val.get("__box_helpers_expect_i64_direct")
return 0
}
}

View File

@ -32,17 +32,17 @@ static box MiniVmBinOp {
local k_sval = "\"value\":\""
local lvp = scan.index_of_from(json, k_sval, lhdr)
if lvp > 0 && lvp < obj_end {
local li = lvp + k_sval.size()
local li = lvp + k_sval.length()
local lval = cur.read_quoted_from(json, li)
if lval {
local k_right_lit = "\"right\":{\"kind\":\"Literal\""
local rhdr = scan.index_of_from(json, k_right_lit, li + lval.size())
local rhdr = scan.index_of_from(json, k_right_lit, li + lval.length())
if rhdr > 0 && rhdr < obj_end {
local rvp = scan.index_of_from(json, k_sval, rhdr)
if rvp > 0 && rvp < obj_end {
local ri = rvp + k_sval.size()
local ri = rvp + k_sval.length()
local rval = cur.read_quoted_from(json, ri)
if rval { print(lval + rval) return ri + rval.size() + 1 }
if rval { print(lval + rval) return ri + rval.length() + 1 }
}
}
}
@ -55,7 +55,7 @@ static box MiniVmBinOp {
local k_lint = "\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
local li2 = scan.index_of_from(json, k_lint, opos)
if li2 <= 0 || li2 >= obj_end { return -1 }
local ldigits = scan.read_digits(json, li2 + k_lint.size())
local ldigits = scan.read_digits(json, li2 + k_lint.length())
if ldigits == "" { return -1 }
local k_r = "\"right\":{\"kind\":\"Literal\""
local rpos = scan.index_of_from(json, k_r, lpos)
@ -63,7 +63,7 @@ static box MiniVmBinOp {
local k_rint = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
local ri2 = scan.index_of_from(json, k_rint, lpos)
if ri2 <= 0 || ri2 >= obj_end { return -1 }
local rdigits = scan.read_digits(json, ri2 + k_rint.size())
local rdigits = scan.read_digits(json, ri2 + k_rint.length())
if rdigits == "" { return -1 }
local ai = scan._str_to_int(ldigits)
local bi = scan._str_to_int(rdigits)
@ -92,11 +92,11 @@ static box MiniVmBinOp {
if scan.index_of_from(json, "\"kind\":\"BinaryOp\"", print_pos) > 0 {
local lp = scan.index_of_from(json, k_lint, print_pos)
if lp > 0 { if lp < slice_end {
local ld = scan.read_digits(json, lp + k_lint.size())
local ld = scan.read_digits(json, lp + k_lint.length())
if ld != "" {
local rp = scan.index_of_from(json, k_rint, lp + k_lint.size())
local rp = scan.index_of_from(json, k_rint, lp + k_lint.length())
if rp > 0 { if rp < slice_end {
local rd = scan.read_digits(json, rp + k_rint.size())
local rd = scan.read_digits(json, rp + k_rint.length())
if rd != "" { print(scan._int_to_str(scan._str_to_int(ld) + scan._str_to_int(rd))) return 1 }
}}
}
@ -117,11 +117,11 @@ static box MiniVmBinOp {
local k_rint = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
local lp = scan.index_of_from(json, k_lint, obj_start)
if lp > 0 { if lp < obj_end2 {
local ld = scan.read_digits(json, lp + k_lint.size())
local ld = scan.read_digits(json, lp + k_lint.length())
if ld != "" {
local rp = scan.index_of_from(json, k_rint, lp + k_lint.size())
local rp = scan.index_of_from(json, k_rint, lp + k_lint.length())
if rp > 0 { if rp < obj_end2 {
local rd = scan.read_digits(json, rp + k_rint.size())
local rd = scan.read_digits(json, rp + k_rint.length())
if rd != "" { print(scan._int_to_str(scan._str_to_int(ld) + scan._str_to_int(rd))) return 1 }
}}
}
@ -136,11 +136,11 @@ static box MiniVmBinOp {
local k_rint = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
local lp = scan.index_of_from(json, k_lint, obj_start)
if lp > 0 { if lp < obj_end {
local ld = scan.read_digits(json, lp + k_lint.size())
local ld = scan.read_digits(json, lp + k_lint.length())
if ld != "" {
local rp = scan.index_of_from(json, k_rint, lp + k_lint.size())
local rp = scan.index_of_from(json, k_rint, lp + k_lint.length())
if rp > 0 { if rp < obj_end {
local rd = scan.read_digits(json, rp + k_rint.size())
local rd = scan.read_digits(json, rp + k_rint.length())
if rd != "" { print(scan._int_to_str(scan._str_to_int(ld) + scan._str_to_int(rd))) return 1 }
}}
}
@ -155,11 +155,11 @@ static box MiniVmBinOp {
local k_rint = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
local lp = scan.index_of_from(json, k_lint, obj_start)
if lp > 0 { if lp < obj_end {
local ld = scan.read_digits(json, lp + k_lint.size())
local ld = scan.read_digits(json, lp + k_lint.length())
if ld != "" {
local rp = scan.index_of_from(json, k_rint, lp + k_lint.size())
local rp = scan.index_of_from(json, k_rint, lp + k_lint.length())
if rp > 0 { if rp < obj_end {
local rd = scan.read_digits(json, rp + k_rint.size())
local rd = scan.read_digits(json, rp + k_rint.length())
if rd != "" { print(scan._int_to_str(scan._str_to_int(ld) + scan._str_to_int(rd))) return 1 }
}}
}
@ -169,17 +169,17 @@ static box MiniVmBinOp {
local nums = []
local i = obj_start
loop (i < obj_end) {
if call("String.substring/2", json, i, i+1) == "\"" {
if json.substring(i, i+1) == "\"" {
local j = scan.index_of_from(json, "\"", i+1)
if j < 0 || j >= obj_end { break }
i = j + 1
continue
}
local d = scan.read_digits(json, i)
if d { nums.push(d) i = i + d.size() continue }
if d { nums.push(d) i = i + d.length() continue }
i = i + 1
}
local nsz = nums.size()
local nsz = nums.length()
if nsz < 2 { return -1 }
local a = scan._str_to_int(nums.get(nsz-2))
local b = scan._str_to_int(nums.get(nsz-1))
@ -209,7 +209,7 @@ static box MiniVmBinOp {
local a = 0
local pos = scan.index_of_from(json, k_v, obj_start)
loop (pos > 0 && pos < obj_end) {
local di = cur.read_digits_from(json, pos + k_v.size())
local di = cur.read_digits_from(json, pos + k_v.length())
if di != "" {
if found == 0 { a = scan._str_to_int(di) found = 1 } else {
local b = scan._str_to_int(di)
@ -217,7 +217,7 @@ static box MiniVmBinOp {
return obj_end + 1
}
}
pos = scan.index_of_from(json, k_v, pos + k_v.size())
pos = scan.index_of_from(json, k_v, pos + k_v.length())
if pos <= 0 || pos >= obj_end { break }
}
return -1
@ -236,19 +236,19 @@ static box MiniVmBinOp {
local p = opos
p = scan.index_of_from(json, k_v, p)
if p < 0 { return -1 }
p = scan.index_of_from(json, k_v, p + k_v.size())
p = scan.index_of_from(json, k_v, p + k_v.length())
if p < 0 { return -1 }
local end1 = scan.index_of_from(json, "}", p)
if end1 < 0 { return -1 }
local d1 = scan.read_digits(json, p + k_v.size())
local d1 = scan.read_digits(json, p + k_v.length())
if d1 == "" { return -1 }
p = scan.index_of_from(json, k_v, end1)
if p < 0 { return -1 }
p = scan.index_of_from(json, k_v, p + k_v.size())
p = scan.index_of_from(json, k_v, p + k_v.length())
if p < 0 { return -1 }
local end2 = scan.index_of_from(json, "}", p)
if end2 < 0 { return -1 }
local d2 = scan.read_digits(json, p + k_v.size())
local d2 = scan.read_digits(json, p + k_v.length())
if d2 == "" { return -1 }
local ai = scan._str_to_int(d1)
local bi = scan._str_to_int(d2)
@ -265,11 +265,11 @@ static box MiniVmBinOp {
local k_typed = "\"type\":\"int\",\"value\":"
local p1 = scan.index_of_from(json, k_typed, bpos)
if p1 < 0 { return "" }
local d1 = scan.read_digits(json, p1 + k_typed.size())
local d1 = scan.read_digits(json, p1 + k_typed.length())
if d1 == "" { return "" }
local p2 = scan.index_of_from(json, k_typed, p1 + k_typed.size())
local p2 = scan.index_of_from(json, k_typed, p1 + k_typed.length())
if p2 < 0 { return "" }
local d2 = scan.read_digits(json, p2 + k_typed.size())
local d2 = scan.read_digits(json, p2 + k_typed.length())
if d2 == "" { return "" }
return scan._int_to_str(scan._str_to_int(d1) + scan._str_to_int(d2))
}

View File

@ -11,7 +11,7 @@ static box MiniVmCompare {
local k_op = "\"operation\":\""
local opos = scan.index_of_from(json, k_op, cpos)
if opos <= 0 || opos >= end { return -1 }
local oi = opos + k_op.size()
local oi = opos + k_op.length()
local oj = scan.index_of_from(json, "\"", oi)
if oj <= 0 || oj > end { return -1 }
local op = json.substring(oi, oj)
@ -22,14 +22,14 @@ static box MiniVmCompare {
local k_v = "\"value\":"
local hv = scan.index_of_from(json, k_v, hl)
if hv <= 0 || hv >= end { return -1 }
local a = scan.read_digits(json, hv + k_v.size())
local a = scan.read_digits(json, hv + k_v.length())
// rhs value
local k_rhs = "\"rhs\":{\"kind\":\"Literal\""
local hr = scan.index_of_from(json, k_rhs, hl)
if hr <= 0 || hr >= end { return -1 }
local rv = scan.index_of_from(json, k_v, hr)
if rv <= 0 || rv >= end { return -1 }
local b = scan.read_digits(json, rv + k_v.size())
local b = scan.read_digits(json, rv + k_v.length())
if !a || !b { return -1 }
local ai = scan._str_to_int(a)
local bi = scan._str_to_int(b)

View File

@ -20,10 +20,10 @@ static box MiniVmScan {
// Linear pass: sum all numbers outside of quotes
sum_numbers_no_quotes(json) {
@i = 0
@n = json.size()
@n = json.length()
@total = 0
loop (i < n) {
@ch = call("String.substring/2", json, i, i+1)
@ch = json.substring(i, i+1)
if ch == "\"" {
@j = me.index_of_from(json, "\"", i+1)
if j < 0 { break }
@ -31,7 +31,7 @@ static box MiniVmScan {
continue
}
@d = me.read_digits(json, i)
if d { total = total + me._str_to_int(d) i = i + d.size() continue }
if d { total = total + me._str_to_int(d) i = i + d.length() continue }
i = i + 1
}
return me._int_to_str(total)
@ -40,11 +40,11 @@ static box MiniVmScan {
// Naive: sum all digit runs anywhere
sum_all_digits_naive(json) {
@i = 0
@n = json.size()
@n = json.length()
@total = 0
loop (i < n) {
@d = me.read_digits(json, i)
if d { total = total + me._str_to_int(d) i = i + d.size() continue }
if d { total = total + me._str_to_int(d) i = i + d.length() continue }
i = i + 1
}
return me._int_to_str(total)
@ -53,11 +53,11 @@ static box MiniVmScan {
// Sum first two integers outside quotes; returns string or empty
sum_first_two_numbers(json) {
@i = 0
@n = json.size()
@n = json.length()
@total = 0
local found = 0
loop (i < n) {
@ch = call("String.substring/2", json, i, i+1)
@ch = json.substring(i, i+1)
if ch == "\"" {
@j = me.index_of_from(json, "\"", i+1)
if j < 0 { break }
@ -68,7 +68,7 @@ static box MiniVmScan {
if d {
total = total + me._str_to_int(d)
found = found + 1
i = i + d.size()
i = i + d.length()
if found >= 2 { return me._int_to_str(total) }
continue
}

View File

@ -8,14 +8,14 @@ static box ModulesInspectBox {
if path == null { return "" }
local s = "" + path
// Strip leading ./
if s.size() >= 2 && s.substring(0,2) == "./" { s = s.substring(2, s.size()) }
if s.length() >= 2 && s.substring(0,2) == "./" { s = s.substring(2, s.length()) }
// Remove leading apps/
local pref = "apps/"
if s.size() >= pref.size() && s.substring(0, pref.size()) == pref { s = s.substring(pref.size(), s.size()) }
if s.length() >= pref.length() && s.substring(0, pref.length()) == pref { s = s.substring(pref.length(), s.length()) }
// Replace '-' with '.' in directory parts only
local out = ""
local i = 0
loop(i < s.size()) {
loop(i < s.length()) {
local ch = s.substring(i, i+1)
if ch == "/" { out = out + "." i = i + 1 continue }
if ch == "-" { out = out + "." i = i + 1 continue }
@ -23,11 +23,11 @@ static box ModulesInspectBox {
i = i + 1
}
// Drop .hako and optional _box suffix
if out.size() >= 5 && out.substring(out.size()-5, out.size()) == ".hako" {
out = out.substring(0, out.size()-5)
if out.length() >= 5 && out.substring(out.length()-5, out.length()) == ".hako" {
out = out.substring(0, out.length()-5)
}
if out.size() >= 4 && out.substring(out.size()-4, out.size()) == "_box" {
out = out.substring(0, out.size()-4)
if out.length() >= 4 && out.substring(out.length()-4, out.length()) == "_box" {
out = out.substring(0, out.length()-4)
}
return out
}

View File

@ -25,7 +25,7 @@ static box StringHelpers {
local i = 0
local neg = 0
if s.substring(0,1) == "-" { neg = 1 i = 1 }
local n = s.size()
local n = s.length()
if i >= n { return 0 }
local acc = 0
loop (i < n) {
@ -46,7 +46,7 @@ static box StringHelpers {
if s == null { return "\"\"" }
local out = ""
local i = 0
local n = s.size()
local n = s.length()
loop (i < n) {
local ch = s.substring(i, i+1)
if ch == "\\" { out = out + "\\\\" }
@ -65,7 +65,7 @@ static box StringHelpers {
// Check if string is numeric-like (optional leading '-', then digits).
is_numeric_str(s) {
if s == null { return 0 }
local n = s.size()
local n = s.length()
if n == 0 { return 0 }
local i = 0
if s.substring(0,1) == "-" { if n == 1 { return 0 } i = 1 }
@ -95,8 +95,8 @@ static box StringHelpers {
// Pattern matching
starts_with(src, i, pat) {
local n = src.size()
local m = pat.size()
local n = src.length()
local m = pat.length()
if i + m > n { return 0 }
local k = 0
loop(k < m) {
@ -109,8 +109,8 @@ static box StringHelpers {
// Keyword match with word boundary (next char not [A-Za-z0-9_])
starts_with_kw(src, i, kw) {
if me.starts_with(src, i, kw) == 0 { return 0 }
local n = src.size()
local j = i + kw.size()
local n = src.length()
local j = i + kw.length()
if j >= n { return 1 }
local ch = src.substring(j, j+1)
if me.is_alpha(ch) || me.is_digit(ch) { return 0 }
@ -119,8 +119,8 @@ static box StringHelpers {
// String search
index_of(src, i, pat) {
local n = src.size()
local m = pat.size()
local n = src.length()
local m = pat.length()
if m == 0 { return i }
local j = i
loop(j + m <= n) {
@ -133,7 +133,7 @@ static box StringHelpers {
// Trim spaces and tabs (with optional semicolon at end)
trim(s) {
local i = 0
local n = s.size()
local n = s.length()
loop(i < n && (s.substring(i,i+1) == " " || s.substring(i,i+1) == "\t")) { i = i + 1 }
local j = n
loop(j > i && (s.substring(j-1,j) == " " || s.substring(j-1,j) == "\t" || s.substring(j-1,j) == ";")) { j = j - 1 }
@ -143,7 +143,7 @@ static box StringHelpers {
// Skip whitespace from position i
skip_ws(src, i) {
if src == null { return i }
local n = src.size()
local n = src.length()
local cont = 1
local guard = 0
local max = 100000
@ -160,8 +160,8 @@ static box StringHelpers {
last_index_of(src, pat) {
if src == null { return -1 }
if pat == null { return -1 }
local n = src.size()
local m = pat.size()
local n = src.length()
local m = pat.length()
if m == 0 { return n }
if m > n { return -1 }
local i = n - m

View File

@ -8,9 +8,9 @@ static box StringOps {
index_of_from(text, needle, pos) {
if text == null { return -1 }
if pos < 0 { pos = 0 }
local n = text.size()
local n = text.length()
if pos >= n { return -1 }
local m = needle.size()
local m = needle.length()
if m <= 0 { return pos }
local i = pos
local limit = n - m