fix(TPV): 本文件自己声明的 extern 裸名优先于依赖 vpi 注入的同名声明
现场:Test/WebTest 编译 includes/socket.vp:236 报 CastError: 指针隐式转指针已禁止:源 %"56d0ee99ecf0a0b5.SocketAddr"* → 目标 %"99c88d4cbd933ac6.WSASocketAddr"* 源码: result = connect(self.fd, c.Addr(addr), 16) 机制(**链接期无冲突**——两边都不是 export,@connect 是同一个 C 符号,最终只有 ws2_32 里一个定义;问题在"编译 socket.vp 时的同一个模块内"): 1. extern/state/export 声明的符号**不带 SHA1 前缀**(FuncGenDef.vp:172-173 `mangled_name = fd.name`)⇒ socket.vp 与 w32.winsock2 都产出裸名 @connect (前者形参 SocketAddr|t.CPtr,后者 WSASocketAddr|t.CPtr)。 2. Translator 的 prologue 先注入依赖 w32.winsock2 的 vpi (StubMergerBuild.LoadDepVpiIntoModule → VpiReader.ParseVpiIntoModule → _inject_function_declare:create_declare + module_add_function),当前模块里 先出现了 winsock2 版的 @connect。 3. 随后 forward_declare_functions 处理 socket.vp 自己的 `extern def connect` 时, find_func_in_module 命中已存在 ⇒ `continue`(FuncGenDef.vp:178-182) ⇒ **本文件自己的声明被静默丢弃**,调用点改按 winsock2 的形参 coerce (报错里的目标类型 WSASocketAddr* 正是 winsock2 版的指纹)。 (同文件 send/recv/socket/listen 亦被顶掉,只是形参是 i8*/u64,不报错—— 即 socket.vp 的 POSIX 分支本来就在调用 winsock2 的声明。) 改动(注入期跳过"本文件已声明的裸名",让本文件自己的声明优先): - StructGenMixin:新增 FileFuncNames 表 + register_file_funcs(pool, tree) / is_file_func(name),与既有的 FileClassNames / register_file_classes / is_file_class 同规矩(键与表同池、每文件随 reset_visible_structs 重建)。 - GenApi:新增 register_file_funcs / is_file_func 委托包装。 - Translator._translate_module_level:在 register_file_classes 之后调用 register_file_funcs(三种模式共用、早于依赖 vpi 注入)。 - VpiReader._inject_function_declare:算出 mangled name 后,若 `use_bare_name != 0` 且 `is_file_func(fd.name) != 0` 直接返回(不注入)。判据只用**裸名**:带 SHA1 前缀的依赖函数名与本文件同名声明不会互相顶替,避免误跳过跨模块调用。 待验证:重编译 TransPyV(自举两遍)→ WebTest / JsonTest / FileIoTest。
This commit is contained in:
1 parent
d979cc8c1f
commit
dc4e06d88b
4 files changed
+104
-1
No files matched your search
@@ -3028,6 +3028,19 @@ def _inject_function_declare(pool: memhub.MemBuddy*,
|
||||
else:
|
||||
mangled_name = GenApi._mangle_name_with_sha1(src_sha1, fd.name)
|
||||
|
||||
# 本文件自己的裸名声明优先:extern/state/export 声明的符号**不带 SHA1 前缀**
|
||||
# (与 FuncGenDef.vp:172-173 同口径),若本文件自己也声明了同名符号,先注入的
|
||||
# 依赖版本会占用该名字 → forward_declare_functions 见名已存在即 continue
|
||||
# (FuncGenDef.vp:178-182)→ 本文件自己的签名被静默丢弃,调用点改按依赖签名
|
||||
# coerce。现场:includes/socket.vp 的
|
||||
# `extern def connect(fd: u64, addr: SocketAddr | t.CPtr, addrlen: INT)`
|
||||
# 被 w32.winsock2 的 `connect(..., WSASocketAddr | t.CPtr, ...)` 顶掉 →
|
||||
# socket.vp:236 `connect(self.fd, c.Addr(addr), 16)` 报
|
||||
# 「指针隐式转指针已禁止:SocketAddr* → WSASocketAddr*」。
|
||||
# 判据只用**裸名**:带 SHA1 前缀的依赖函数名与本文件同名声明不会相互顶替。
|
||||
if use_bare_name != 0 and GenApi.is_file_func(fd.name) != 0:
|
||||
return
|
||||
|
||||
# 去重:如果函数已存在则跳过
|
||||
existing: llvmlite.Function* = GenApi.find_func_in_module(mod, mangled_name)
|
||||
if existing is not None:
|
||||
|
||||
@@ -358,6 +358,24 @@ def register_file_classes(pool: memhub.MemBuddy*, sha1: str, tree: ast.AST*) ->
|
||||
_g_gen.StructGen.register_file_classes(pool, sha1, tree)
|
||||
|
||||
|
||||
# ---------------- register_file_funcs / is_file_func(委托 StructGenMixin) ----------------
|
||||
def register_file_funcs(pool: memhub.MemBuddy*, tree: ast.AST*) -> None:
|
||||
"""文件级预登记本文件顶层 FunctionDef 的函数名。
|
||||
|
||||
与 register_file_classes 同址调用(Translator 文件入口 prologue,早于依赖 vpi
|
||||
注入):注入侧据此跳过"本文件已声明的裸名",避免依赖的 extern 声明顶掉本文件
|
||||
自己的 extern 声明(现场 includes/socket.vp connect 被 w32.winsock2 顶掉)。"""
|
||||
if _g_gen is not None:
|
||||
_g_gen.StructGen.register_file_funcs(pool, tree)
|
||||
|
||||
|
||||
def is_file_func(name: str) -> int:
|
||||
"""裸名是否为**本文件**顶层声明的函数(1=是 / 0=否)"""
|
||||
if _g_gen is not None:
|
||||
return _g_gen.StructGen.is_file_func(name)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- find_struct_by_type(委托 StructGenMixin.find_struct_by_type) ----------------
|
||||
def find_struct_by_type(struct_ty: llvmlite.LLVMType*) -> GenTypes.StructEntry*:
|
||||
"""委托到 gen.find_struct_by_type(原 Handles 壳)"""
|
||||
|
||||
@@ -139,6 +139,13 @@ novirtual class StructGenMixin:
|
||||
# 误归属(旧 sha1 与"本文件声明了该类"两个正向事实不会同时成立)。
|
||||
FileModuleSha1: str
|
||||
|
||||
# 本文件顶层 FunctionDef 的函数名表:用途见 register_file_funcs ——
|
||||
# 依赖 stub 注入的**裸名**声明(extern/state/export,不带 SHA1 前缀)若与
|
||||
# 本文件自己的声明同名,会先占用该名字,使本文件声明在
|
||||
# forward_declare_functions 里被静默丢弃(调用点改按依赖签名 coerce)。
|
||||
# 注入侧(VpiReader._inject_function_declare)据此跳过这些名字。
|
||||
FileFuncNames: hashtable.HashTable*
|
||||
|
||||
# --- 名 | 类型 下标索引(查找快速路径;索引不可信时回退原线性扫描) ---
|
||||
StructNameIdx: sizet* # 名 → 首注册下标(槽 u64:0=空槽,否则 index+1)
|
||||
StructTypeIdx: sizet* # Ty 指针值 → 首注册下标(同布局)
|
||||
@@ -158,6 +165,7 @@ novirtual class StructGenMixin:
|
||||
self.CurDefClassName = None
|
||||
self.FileClassNames = None
|
||||
self.FileModuleSha1 = None
|
||||
self.FileFuncNames = None
|
||||
self.StructNameIdx = None
|
||||
self.StructTypeIdx = None
|
||||
self.StructIdxCap = 0
|
||||
@@ -200,6 +208,7 @@ novirtual class StructGenMixin:
|
||||
# (同池串)⇒ 必须一并清空,避免下一文件读到已回收内存。
|
||||
self.FileClassNames = None
|
||||
self.FileModuleSha1 = None
|
||||
self.FileFuncNames = None
|
||||
self.ModuleClasses = hashtable.HashTable(pool)
|
||||
if strict != 0:
|
||||
# 严格模式:创建新的 HashTable 记录可见结构体
|
||||
@@ -331,6 +340,62 @@ novirtual class StructGenMixin:
|
||||
return 0
|
||||
return self.FileClassNames.__contains__(name)
|
||||
|
||||
def register_file_funcs(self, pool: memhub.MemBuddy*, tree: ast.AST*) -> None:
|
||||
"""文件级预登记:把**本文件顶层 FunctionDef 的函数名**记下来。
|
||||
|
||||
用途(依赖 stub 注入的裸名去重):编译本文件时,prologue 会**先**把依赖模块的
|
||||
.vpi 注入进来(StubMergerBuild.LoadDepVpiIntoModule);而 extern/state/export
|
||||
声明的符号**不带 SHA1 前缀**(FuncGenDef.vp:172-173 `mangled_name = fd.name`)
|
||||
⇒ 依赖的裸名声明先占用该名字;随后 forward_declare_functions 处理本文件自己的
|
||||
同名声明时见名已存在即 `continue`(FuncGenDef.vp:178-182)⇒ **本文件自己的
|
||||
声明被静默丢弃**,调用点改按依赖的签名 coerce。
|
||||
|
||||
现场:includes/socket.vp 的
|
||||
`extern def connect(fd: u64, addr: SocketAddr | t.CPtr, addrlen: INT)`
|
||||
被 w32.winsock2 的 `connect(..., WSASocketAddr | t.CPtr, ...)` 顶掉 →
|
||||
socket.vp:236 `connect(self.fd, c.Addr(addr), 16)` 报
|
||||
「指针隐式转指针已禁止:%"…SocketAddr"* → %"…WSASocketAddr"*」。
|
||||
|
||||
注入侧据本表跳过"本文件已声明的裸名"(VpiReader._inject_function_declare),
|
||||
使本文件自己的声明优先。表与键同池、每文件重建(与 FileClassNames 同规矩)。
|
||||
"""
|
||||
if pool is None or tree is None:
|
||||
return
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is None:
|
||||
return
|
||||
if self.FileFuncNames is None:
|
||||
self.FileFuncNames = hashtable.HashTable(pool)
|
||||
if self.FileFuncNames is None:
|
||||
return
|
||||
cn: sizet = len(ch)
|
||||
ci: sizet
|
||||
for ci in range(cn):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is None:
|
||||
continue
|
||||
if child.kind() != ast.ASTKind.FunctionDef:
|
||||
continue
|
||||
fd: ast.FunctionDef* = ast.FunctionDef*(child)
|
||||
if fd is None or fd.name is None:
|
||||
continue
|
||||
if self.FileFuncNames.__contains__(fd.name) != 0:
|
||||
continue
|
||||
# 键必须与表同池(AST 的 name 串零拷贝指向 src_buf,跨阶段可能失效):
|
||||
# 复制到 pool 后再登记,避免表 resize/查询时 strcmp 到悬空内存。
|
||||
nm_len: sizet = string.strlen(fd.name)
|
||||
nm_buf: str = pool.alloc(nm_len + 1)
|
||||
if nm_buf is None:
|
||||
continue
|
||||
string.strcpy(nm_buf, fd.name)
|
||||
self.FileFuncNames.set_int(nm_buf, 1)
|
||||
|
||||
def is_file_func(self, name: str) -> int:
|
||||
"""裸名 name 是否为本文件顶层声明的函数(1=是 / 0=否)"""
|
||||
if name is None or self.FileFuncNames is None:
|
||||
return 0
|
||||
return self.FileFuncNames.__contains__(name)
|
||||
|
||||
def get_file_module_sha1(self) -> str:
|
||||
"""本文件(正在翻译的文件)的模块 SHA1(register_file_classes 传入;未登记返回 None)。"""
|
||||
return self.FileModuleSha1
|
||||
|
||||
@@ -555,7 +555,14 @@ novirtual class Translator:
|
||||
# "非基本类型指针不可隐式转换"报出。
|
||||
# 登记只依赖 tree + 本文件模块 SHA1,与导入、类定义窗口、类定义顺序都无关,
|
||||
# 故提到门之前,三种模式(1a=1 / 1a-pre=2 / 全量=0)共用。
|
||||
GenApi.register_file_classes(pool, self.ModuleSha1, tree)
|
||||
GenApi.register_file_classes(pool, self.ModuleSha1, tree)
|
||||
# 本文件顶层函数名预登记(同址、同目的):依赖 vpi 注入的**裸名**声明
|
||||
# (extern/state/export 不带 SHA1 前缀)若与本文件自己的声明同名,会先占用
|
||||
# 该名字,使本文件声明在 forward_declare_functions 被静默丢弃(调用点改按
|
||||
# 依赖签名 coerce)。现场:includes/socket.vp 的 extern def connect 被
|
||||
# w32.winsock2 的 connect 顶掉 → socket.vp:236 报
|
||||
# 「指针隐式转指针已禁止:SocketAddr* → WSASocketAddr*」。
|
||||
GenApi.register_file_funcs(pool, tree)
|
||||
# 全量翻译模式下,先处理导入语句,再创建前向声明,解决前向引用问题
|
||||
if self._declare_only == 0:
|
||||
# 预处理导入语句,确保 _imported_modules 和 _from_imports 已填充
|
||||
|
||||
Reference in new issue
Block a user