fix(tpc): 裸名解析处理同名冲突 + 声明阶段多轮,消除跨模块类型解析残余失败

承接上一条(两阶段加载)后仍有 11 个模块签名解析失败,两类根因:

① 同名冲突:平坦符号表(无前缀)一个名字只能留一个条目,不同模块可用不同类型

   种类声明同名符号,且谁后插入谁占住裸名 —— stdint.vp:9 `INT: t.CTypedef = t.CInt`

   与 argparse.vp:33 `define INT = 1`(sig: `INT: t.CDefine = 1`)撞名 ⇒ posix 的

   `oflag: INT` 解析失败;llvmlite/__function.vp:77 `class Function` 与

   ast/tokens.vp:95 枚举成员 `Function: t.State` 撞名 ⇒ 5 个模块的 `func: rawptr[Function]`

   失败;ast/exprs.vp:18 `class Name` 同型(ast/parser.vp)。

   修法:_resolve_name 在平坦条目不是 typedef/类/枚举时,跨 _namespaces 找回同名

   typedef / 类 / 枚举(注解位置只可能是这三种)。

② 声明阶段的顺序依赖:typedef 右侧可引用其它模块的 typedef(win32base 的

   `HANDLE: t.CTypedef = VOIDPTR`,VOIDPTR 在 stdint)⇒ 单轮仍与顺序有关。

   修法:Phase2Translator 声明阶段多轮(未解析 typedef 的模块下一轮重 stage,

   无进展即停,_CountUnresolvedTypedefs 判定),签名阶段仍是全部声明入表后统一跑。

③ 顺带修:两阶段是两个 TypeResolver 实例,签名阶段也须注入本模块类型名

   (TypeResolver.inject_known_type_names 供两阶段共用)。

验证(TransPyV 全量 127 个 sig 模拟 ①→②):commit 失败数 11 → 0;

posix.open ret=i32 params=[i8*,i32]、read ret=i64 params=[i32,i8*,i64] 解析正确。
This commit is contained in:
TermiNexus committed 2026-09-28 18:25:57 +08:00
1 parent 05f4adb233
commit 74ccd31c19
3 files changed
+119 -39

No files matched your search

+43 -7
View File
@@ -15,6 +15,7 @@ from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_compl
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
from lib.core.Handles.HandlesBase import CTypeInfo
from lib.core.SymbolData import ModuleSymbols
from lib.includes import t
from lib.core.SymbolUtils import AnnotationContainsName, ExpandSoftKeywords
from lib.core.SymbolUtils import ClassHasFuncLevelCVTable, IsVtableSlotMethod
@@ -404,6 +405,23 @@ def _ExtractCDefineConstantsFromPyi(pyi_path: str, all_dc: dict[str, object]) ->
_vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e)
def _CountUnresolvedTypedefs(module_symbols: object) -> int:
"""该模块"有值注解却解析不出类型"的 typedef 个数(供声明阶段判断是否要下一轮)。
typedef 右侧可以引用**其它模块**的 typedef:例 win32base 的
`HANDLE: t.CTypedef = VOIDPTR`(VOIDPTR 在 stdint)——本模块先 stage 时右侧
解析为空(original_type_kind/original_class 均为 None),要等依赖模块入表后
重 stage 才能补全。返回 0 表示本模块声明已完整(不再需要重跑)。
"""
miss: int = 0
for td in getattr(module_symbols, 'typedefs', []):
if getattr(td, 'value', None) is None and getattr(td, 'annotation', None) is None:
continue
if getattr(td, 'original_type_kind', None) is None and getattr(td, 'original_class', None) is None:
miss += 1
return miss
def _parallel_translate_worker(src_path: str, out_path: str, src_root: str, temp_dir: str, output_dir: str,
include_dirs: list[str], triple: str | None, datalayout: str | None,
sha1_map: dict[str, str], sig_files: dict[str, str],
@@ -3990,15 +4008,33 @@ class Phase2Translator:
if short_name != module_name:
base_trans.translator._source_module_sig_files[short_name] = sig_path
# ① 声明阶段(全部模块)
_staged_modules = []
for _stage_path, _stage_mod in staged:
_stage_ms = base_trans.translator.SymbolTable.StageModuleSymbols(_stage_path, _stage_mod)
if _stage_ms is not None:
_staged_modules.append((_stage_ms, _stage_mod, _stage_path))
# ① 声明阶段(全部模块;多轮直到"本轮没有新增可解析的声明")
# 单轮仍会与模块顺序有关:typedef 右侧可以引用**其它模块**的 typedef,
# 例 win32base 的 `HANDLE: t.CTypedef = VOIDPTR`,VOIDPTR 在 stdint
# ⇒ 本模块先 stage 时右侧解析为空;下一轮把它重 stage 一遍(重 stage 覆盖
# 登记,条目随依赖入表而变完整)。无进展即停,最多再跑一轮。
_stage_map: dict[tuple[str, str], ModuleSymbols] = {}
_round_pending = list(staged)
_prev_miss: int | None = None
while _round_pending:
_miss_total: int = 0
_next_round: list[tuple[str, str]] = []
for _stage_path, _stage_mod in _round_pending:
_stage_ms = base_trans.translator.SymbolTable.StageModuleSymbols(_stage_path, _stage_mod)
if _stage_ms is None:
continue
_stage_map[(_stage_path, _stage_mod)] = _stage_ms
_miss: int = _CountUnresolvedTypedefs(_stage_ms)
_miss_total += _miss
if _miss:
_next_round.append((_stage_path, _stage_mod))
if not _next_round or (_prev_miss is not None and _miss_total >= _prev_miss):
break
_prev_miss = _miss_total
_round_pending = _next_round
# ② 签名阶段(全部模块;此时任一模块的签名都能看到全部声明)
for _commit_ms, _commit_mod, _commit_path in _staged_modules:
for (_commit_path, _commit_mod), _commit_ms in _stage_map.items():
base_trans.translator.SymbolTable.CommitModuleSymbols(_commit_ms, _commit_mod, _commit_path)
# 所有 includes 模块符号已加载,现在处理包重导出
+54 -21
View File
@@ -74,7 +74,35 @@ class LLVMTypeMapper:
展开,否则会误进 fail-fast。本地导入避免与 TypeAnnotationResolver 成环。
"""
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR
return _TAR._lookup_builtins_typedef(name, self._symtab)
return _TAR._lookup_builtins_typedef(name, self._symtab)
def _typedef_entry_to_llvm(self, entry: CTypeInfo) -> str:
"""typedef 条目 → LLVM 类型串(解析不出返回 '',由调用方决定后续分支)。
原为 _resolve_name 内联的 typedef 分支,抽出后供"裸名解析"与"同名冲突时
跨命名空间找回 typedef"两处复用(口径必须一致)。
"""
if not entry or not entry.IsTypedef:
return ''
# 优先用 OriginalType 解析
if entry.OriginalType:
if isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.IsFuncPtr:
return 'i8*'
if isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.BaseType:
# 用 CTypeRegistry 生成归一化 LLVM 字符串(void*→i8*),struct/enum 回退 ToString()
resolved: str = self._ctype_to_llvm_str(entry.OriginalType)
return resolved if resolved else entry.OriginalType.ToString()
if isinstance(entry.OriginalType, str):
# 字符串形式的 OriginalType(如 'CUnsignedInt *')需转换为 LLVM 类型字符串
llvm_str: str = self._ctype_str_to_llvm(entry.OriginalType)
if llvm_str:
return llvm_str
# OriginalType 为空或字符串无法转换时,回退到 BaseType
if entry.BaseType and (not isinstance(entry.BaseType, (_t._CTypedef,)) or entry.PtrCount > 0):
resolved_b: str = self._ctype_to_llvm_str(entry)
if resolved_b:
return resolved_b
return ''
# ==================================================================
# 公共 API
@@ -241,26 +269,9 @@ class LLVMTypeMapper:
if node.id in self._symtab:
entry: CTypeInfo = self._symtab[node.id]
if entry and entry.IsTypedef:
# 优先用 OriginalType 解析
if entry.OriginalType:
if isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.IsFuncPtr:
return 'i8*'
elif isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.BaseType:
# 用 CTypeRegistry 生成归一化 LLVM 字符串(void*→i8*),struct/enum 回退 ToString()
resolved: str = self._ctype_to_llvm_str(entry.OriginalType)
if resolved:
return resolved
return entry.OriginalType.ToString()
elif isinstance(entry.OriginalType, str):
# 字符串形式的 OriginalType(如 'CUnsignedInt *')需转换为 LLVM 类型字符串
llvm_str: str = self._ctype_str_to_llvm(entry.OriginalType)
if llvm_str:
return llvm_str
# OriginalType 为空或字符串无法转换时,回退到 BaseType
if entry.BaseType and (not isinstance(entry.BaseType, (_t._CTypedef,)) or entry.PtrCount > 0):
resolved = self._ctype_to_llvm_str(entry)
if resolved:
return resolved
resolved_td: str = self._typedef_entry_to_llvm(entry)
if resolved_td:
return resolved_td
# --- 本模块声明的类型名 -------------------------------------------
# 类名:裸类引用自身即指针 —— 与 _resolve_attribute 的 '%struct.X*' 同口径
# (下游 TypeConvert._type_str_to_llvm 按短名命中已注册 struct ⇒ %"sha1.X"*)。
@@ -278,6 +289,28 @@ class LLVMTypeMapper:
return f'%struct.{node.id}*'
if entry_loaded.IsEnum and not entry_loaded.IsRenum:
return f'i{EnumIntWidth(entry_loaded)}'
# --- 同名冲突:平坦条目被 define / 枚举成员 / 函数占位时找回类型 ----
# 平坦表(无前缀)一个名字只能留一个条目,而不同模块可能用**不同类型种类**
# 声明同名符号(谁后插入谁占住裸名),两个真实例子:
# · stdint.vp:9 `INT: t.CTypedef = t.CInt` vs argparse.vp:33
# `define INT = 1`(sig 里是 `INT: t.CDefine = 1`)—— 注解 `oflag: INT`
# · llvmlite/__function.vp:77 `class Function` vs ast/tokens.vp:95
# 枚举成员 `Function: t.State` —— 注解 `func: rawptr[Function]`
# 注解位置只可能是 typedef / 类 / 枚举 ⇒ 平坦条目不是这三种时,跨命名空间
# (各模块的声明登记在 _namespaces[模块名] 下)再找同名条目。
_cand_sym: CTypeInfo | None
for _ns_syms in getattr(self._symtab, '_namespaces', {}).values():
_cand_sym = _ns_syms.get(node.id)
if _cand_sym is None:
continue
if getattr(_cand_sym, 'IsTypedef', False):
_resolved_ns: str = self._typedef_entry_to_llvm(_cand_sym)
if _resolved_ns:
return _resolved_ns
elif getattr(_cand_sym, 'IsStruct', False):
return f'%struct.{node.id}*'
elif getattr(_cand_sym, 'IsEnum', False) and not getattr(_cand_sym, 'IsRenum', False):
return f'i{EnumIntWidth(_cand_sym)}'
# --- builtins.vp 隐式 CTypedef ----------------------------------
# int64/uint64/size_t 等:用户文件无需 import 即用,符号表必定 miss,
# 必须在此按权威定义展开(与 TypeConvert.L454-468 同一口径),
+22 -11
View File
@@ -40,17 +40,8 @@ class TypeResolver:
# 构建 class_members 查找表(typedef Postdefinition 需要)
class_members: dict[str, dict[str, CTypeInfo]] = {cls.name: cls.members for cls in module_symbols.classes}
# 注入"本模块声明的类型名"(函数/typedef 签名解析需要)。
# 本模块类名在 Pass 3 插入符号表之前**不可见** ⇒ 若不注入,裸类引用
# (rawptr[Constant] / Constant)在 _resolve_name 里解析不出,旧口径退化成
# i32(rawptr[X] → i32*),类信息丢失后经跨模块符号表传到调用点,报错会
# 指向源码(形参 i32* 对不上实参 %"sha1.X"*)而掩盖根因。
# 枚举名按 type_kind == 'enum' 单独登记宽度(IsStruct 口径不含枚举)。
self._type_mapper.set_known_type_names(
{cls.name for cls in module_symbols.classes if cls.type_kind != 'enum'},
{cls.name: (cls.enum_width or 32) for cls in module_symbols.classes
if cls.type_kind == 'enum'},
)
# 注入"本模块声明的类型名"(typedef 签名解析需要)
self.inject_known_type_names(module_symbols)
# 解析 typedef(按顺序记录,供后续 typedef 引用同模块已解析的 typedef)
td: TypedefSymbolData
@@ -59,8 +50,28 @@ class TypeResolver:
self._resolved_typedefs[td.name] = td.original_class
return module_symbols
def inject_known_type_names(self, module_symbols: ModuleSymbols) -> None:
"""把本模块声明的类名 / 枚举宽度注入类型映射器。
本模块类名在 Pass 3 插入符号表之前**不可见** ⇒ 若不注入,裸类引用
(rawptr[Constant] / Constant)在 _resolve_name 里解析不出,旧口径退化成
i32(rawptr[X] → i32*),类信息丢失后经跨模块符号表传到调用点,报错会
指向源码(形参 i32* 对不上实参 %"sha1.X"*)而掩盖根因。
枚举名按 type_kind == 'enum' 单独登记宽度(IsStruct 口径不含枚举)。
声明阶段与签名阶段**各自**都要注入:两阶段加载用的是两个 TypeResolver
实例(映射器随实例走),签名阶段缺了它就没有"本模块裸类名"这条来源。
"""
self._type_mapper.set_known_type_names(
{cls.name for cls in module_symbols.classes if cls.type_kind != 'enum'},
{cls.name: (cls.enum_width or 32) for cls in module_symbols.classes
if cls.type_kind == 'enum'},
)
def resolve_signatures(self, module_symbols: ModuleSymbols) -> ModuleSymbols:
"""阶段 2:解析函数签名与 define 值(依赖其它模块的声明已插入符号表)"""
# 本模块类型名注入(见 inject_known_type_names:两阶段是两个实例)
self.inject_known_type_names(module_symbols)
# 解析函数签名
func: FuncSymbolData
for func in module_symbols.functions: