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:
1 parent
05f4adb233
commit
74ccd31c19
3 files changed
+119
-39
No files matched your search
@@ -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
@@ -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 同一口径),
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user