fix(call): _lookup_reexport_from_vpi 支持 star import 再导出定位

- from X import * 的 re-export(如 fltk/__init__ 再导出 _cfl 全部 extern FFI)此前无法定位定义模块

- fltk.Fl_init_all 被误加 SHA1 前缀走 mangled 精确解析 → extern def 无 define → fail-fast

- 命中后由 is_cexport_func/_is_state_func_from_vpi 二次校验裸名
This commit is contained in:
TermiNexus committed 2026-09-01 20:26:14 +08:00
1 parent 29667672e4
commit 60deee1d17
1 file changed
+90
+90
View File
@@ -231,6 +231,44 @@ class CallGenBase:
mod_end: t.CSizeT = imp_pos
# 名称列表从 "import " 之后(逗号分隔,含 "A as B" 取原名 A)
nm_start: t.CSizeT = imp_pos + 8
# star import 支持:from X import * → 任意函数都属 X 模块。
# fltk/__init__.vp 用 "from fltk._cfl import *" 再导出全部 extern
# FFI,跨模块调用 fltk.Fl_init_all 若无法定位定义模块 → 误加 SHA1
# 前缀走 mangled 精确解析 → extern def 无 define → fail-fast。
# 命中后由调用方 is_cexport_func/_is_state_func_from_vpi 二次校验。
if nm_start < ls + ll and buf[nm_start] == '*':
star_dot: int = 0
if buf[mod_start] == '.':
star_dot = 1
star_buf: str = pool.alloc(mod_end - mod_start + 1)
if star_buf is None:
stdlib.free(buf)
return None
si2: t.CSizeT = 0
while mod_start + si2 < mod_end:
star_buf[si2] = buf[mod_start + si2]
si2 += 1
star_buf[si2] = '\0'
while star_buf[0] == '.':
star_buf = star_buf + 1
if string.strlen(star_buf) == 0:
stdlib.free(buf)
return None
if star_dot != 0:
spkg: str = self._module_package_from_sha1(pool, mod_sha1)
if spkg is None:
stdlib.free(buf)
return None
srl: t.CSizeT = string.strlen(spkg) + string.strlen(star_buf) + 2
sres: str = pool.alloc(srl)
if sres is None:
stdlib.free(buf)
return None
viperlib.snprintf(sres, srl, "%s.%s", spkg, star_buf)
stdlib.free(buf)
return sres
stdlib.free(buf)
return star_buf
scan: t.CSizeT = nm_start
while scan < ls + ll:
while scan < ls + ll and (buf[scan] == ' ' or buf[scan] == ','):
@@ -554,6 +592,19 @@ class CallGenBase:
pi2 += 1
return
existing = existing.Next
# 裸名 C 库函数(如 string.memset → @memset、stdlib.memcpy → @memcpy):
# call_name 无 sha1 前缀 → 无法提取定义侧模块查 vpi 参数注解 →
# 此前用实参类型建 declare(字面量 0=i32 把定义 t.CSizeT(i64) 固化窄,
# VKernel process.vp string.memset 声明 @memset(i8*,i32,i32) vs 定义
# @memset(i8*,i32,i64),跨模块 ABI 宽度不一致,优化路径读高位垃圾
# 写穿内存)。已知 C 库函数直接按标准签名建 declare(与 CallGenLib
# _ensure_c_lib_declare 白名单一致:memset/memcpy/memmove/memcmp/
# strcmp/strncmp/strcpy/strncpy/strcat/strlen/puts/atoi/atol/abs/labs)。
if call_name is not None and string.strchr(call_name, 46) is None:
std_lib_fn: llvmlite.Function | t.CPtr = self.Gen.CallGenLib._ensure_c_lib_declare(
pool, mod, call_name)
if std_lib_fn is not None:
return
# 创建函数声明
func: llvmlite.Function | t.CPtr = llvmlite.new_function(pool, call_name, ret_ty)
if func is None:
@@ -1108,6 +1159,45 @@ class CallGenBase:
stdlib.free(path_buf)
return exists
# ============================================================
# _AppendPhaseAPending - 追加 Phase A MRO/VPI pending 明细到诊断文件
#
# 跨模块方法返回类型解析(mro_ret / mro_anc)在依赖模块 text.ll 未生成
# 时置 _deps_pending。此信息此前只走滚动 DEPDBG(刷屏易被淹没),此处
# 持久化到 {TempDir}/_phaseA_pending.log(tab 分隔),供 Phase2 收敛失败
# 时按调用方 sha1 过滤,输出"MRO/VPI 类似依赖图"的最终诊断。
# 行格式: {owner_sha1}\t{kind}\t{cls}\t{method}\t{dep_sha1}\t{extra}\n
# ============================================================
def _AppendPhaseAPending(self, owner_sha1: str, kind: str,
cls_name: str, method_name: str,
dep_sha1: str, extra: str) -> None:
"""追加一行 MRO/VPI pending 明细到诊断文件(文件不存在则创建)"""
td: str = self.Gen.CallGenBase.TempDir
td_len: t.CSizeT = self.Gen.CallGenBase.TempDirLen
if td is None or td_len == 0 or owner_sha1 is None or kind is None:
return
path_len: t.CSizeT = td_len + 24
path_buf: bytes = stdlib.malloc(path_len)
if path_buf is None:
return
viperlib.snprintf(path_buf, path_len, "%s/_phaseA_pending.log", td)
pf: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.A)
stdlib.free(path_buf)
if pf.closed:
return
line: bytes = stdlib.malloc(512)
if line is not None:
viperlib.snprintf(line, 512, "%s\t%s\t%s\t%s\t%s\t%s\n",
owner_sha1, kind,
cls_name if cls_name is not None else "-",
method_name if method_name is not None else "-",
dep_sha1 if dep_sha1 is not None else "-",
extra if extra is not None else "-")
ln: t.CSizeT = string.strlen(line)
pf.write(line, ln)
stdlib.free(line)
pf.close()
# ============================================================
# 属性方法(@property/@x.setter/@x.deleter)与静态/类方法调用辅助
#