chore(gen): 加 [MODSHA1] 临时诊断定位 string.split 模块名解析失败(定位后删除)

在 translate_call 的模块限定分支加两条探针:

- [MODSHA1]:mod_ret_sha1 为 None 时打印 mod_path_ret / func_name / exact / suffix / ctrl_stdint(对照模块,必在 includes)——可区分 store 整体为空 与 store 有内容但缺该模块。

- [MODSHA1-D]:裸名兜底分支里,若 cl.func 仍是 Attribute(模块限定却解析不出模块的异常组合)则打印 mod_path_ret 与 func_name——覆盖 mod_path_ret 为 None 的情形。

同时把 mod_path_ret 的声明从 if 块内移到 if 外(与 mod_ret_sha1 一致),避免下游分支读未声明局部取到栈垃圾。

证据链:报错来自本函数 861 行(全仓唯一 fatal_if_unknown=1 的调用点),条件恒为 _resolve_module_sha1 对 string 返回 None;但 _sha1_map.txt 有 includes/string.vp、其 vpi 有 def split -> int、Phase1Main:390 是唯一写 map 处 ⇒ 静态无法解释,需运行时诊断。
This commit is contained in:
TermiNexus committed 2026-09-27 12:52:03 +08:00
1 parent 87f5b1026c
commit 424dbd7379
1 file changed
+25 -1
+25 -1
View File
@@ -760,13 +760,15 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# _LookupMethodRetTyFromTextLL 的 strlen(0x1) 零页崩溃
# (llvmlite/__init__.vp CreateGlobalInt 翻译时 0xC0000005 根因)。
mod_ret_sha1: str = None
# [MODSHA1] 诊断用:模块名(cl.func 为 Attribute 时赋值,否则保持 None)。
# 声明在 if 外,使下方裸名兜底分支也能安全读取(读未声明的局部 = 栈垃圾)。
mod_path_ret: str = None
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at_ret: ast.Attribute* = ast.Attribute*(cl.func)
# 解析调用模块路径:单层(stdlib.free,value 是 Name)与多层
# (w32.win32file.CreatePipe,value 是嵌套 Attribute)都支持。
# 缺失多层路径 → mod_ret_sha1=None → 误走裸名外部函数路径,
# fail-fast 误杀可收敛的模块限定调用(CreatePipe 根因)。
mod_path_ret: str = None
if at_ret.value is not None and at_ret.value.kind() == ast.ASTKind.Name:
mod_ret_nm: ast.Name* = ast.Name*(at_ret.value)
mod_path_ret = mod_ret_nm.id
@@ -783,6 +785,20 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if trans is not None:
fi_from_rt = trans._from_imports
mod_ret_sha1 = self.Gen.StructGen._resolve_module_sha1(mod_path_ret, fi_from_rt)
# [MODSHA1] 临时诊断(定位后删除):模块限定调用的模块名 → SHA1
# 解析失败时,打印本模块名与对照模块名(stdint,必在 includes)
# 的精确/后缀查询结果,区分「store 整体为空」与「store 有内容但
# 缺该模块」。devfs.vp 的 string.split 报错即经此分支。
if mod_ret_sha1 is None:
dg_ex: str = self.Gen.CallGenBase._lookup_module_sha1(mod_path_ret)
dg_sf: str = self.Gen.CallGenBase._lookup_module_sha1_suffix(mod_path_ret)
dg_ctrl: str = self.Gen.CallGenBase._lookup_module_sha1("stdint")
if dg_ex is None: dg_ex = "-"
if dg_sf is None: dg_sf = "-"
if dg_ctrl is None: dg_ctrl = "-"
stdio.printf("[MODSHA1] mod='%s' fn='%s' exact=%s suffix=%s ctrl_stdint=%s line=%d\n",
mod_path_ret, func_name, dg_ex, dg_sf, dg_ctrl, cl.lineno)
stdio.fflush(None)
if mod_ret_sha1 is not None:
# 函数内懒 import:VpiReader imports HandlesExprCall,
# 顶层 import 会成环(与 _lookup_method_ret_ty_via_vpi 一致)
@@ -837,6 +853,14 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# 本遍仅临时占位。
call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name, node, 0)
else:
# [MODSHA1] 临时诊断(定位后删除):走到裸名兜底 ⇒ mod_ret_sha1 为
# None。仅当 cl.func 仍是模块限定形态(Attribute)时打印——这正是
# "模块限定却解析不出模块"的异常组合(此时 mod_path_ret 为 None
# 说明 value 段不是 Name/Attribute;非 None 说明模块名查不到 SHA1)。
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
stdio.printf("[MODSHA1-D] mod='%s' fn='%s' line=%d\n",
mod_path_ret, func_name, cl.lineno)
stdio.fflush(None)
# 裸名调用(cl.func 是 Name,mod_ret_sha1=None)两类来源:from-import 的函数(后续解析模块归属构造 mangled call_name 并经 text.ll/vpi 精确解析返回类型,可收敛)→ fatal_if_unknown=0 仅临时占位,不 fail-fast;纯外部 C 函数(无登记、无模块归属,白名单未命中)→ fail-fast(fatal_if_unknown=1)。闭包变量与持有函数指针/闭包的局部变量同样按可解析占位——返回类型由 translate_closure_call/lookup_var 路径固定处理,不依赖外部返回类型推断,否则被 fail-fast 误杀
bare_resolvable: int = 0
if self.Gen.CallGenLeafKit.is_closure_name(func_name) != 0: