diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index 26bbe28..7f45b31 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -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: diff --git a/App/lib/core/Gen/GenApi.vp b/App/lib/core/Gen/GenApi.vp index da9a914..4481e87 100644 --- a/App/lib/core/Gen/GenApi.vp +++ b/App/lib/core/Gen/GenApi.vp @@ -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 壳)""" diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index 469c08d..f0bb818 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -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 diff --git a/App/lib/core/Gen/Translator.vp b/App/lib/core/Gen/Translator.vp index d2d1a72..4502adc 100644 --- a/App/lib/core/Gen/Translator.vp +++ b/App/lib/core/Gen/Translator.vp @@ -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 已填充