fix(tpc): 修复 t.CVoid 清理暴露的三处类型解析缺口

SymbolTypeResolver: 裸 t.CPtr 作 typedef 值(无联合、无下标)时按 void* 展开。_find_base_type 把 attr==CPtr 归一为小写 ptr,而守卫 _PTR_MODIFIERS 写的是 CPtr 命不中,导致 ptr 被当成类型名存进 OriginalType,最终建成 %"<模块sha>.ptr" 幻影结构体(HANDLE/VOIDPTR/DIR 报 cannot store i8* to ...ptr*)。改前 t.CVoid | t.CPtr 走 BinOp 分支恰好绕过

HandlesExprAttr: 属性访问的成员查找补继承链回退(对齐 HandlesExprUtils._get_var_class 的口径)。字段声明在基类时元素类表的键是声明类,而收件人是子类,原实现只查收件人类自身;方法调用侧早已做 MRO 查询,故 150 处 self.Gen.X.method() 未暴露。修后 self.Gen.TypeGenCType.ImportAliasBuf 可解析(该字段在基类 TypeGenBase),且此前被静默丢弃的 _register_import_alias malloc 分支恢复生效

TypeConvert._base_ctype_to_llvm: 注册表给出完整类型串(CPtr/CArrayPtr/CNeedPtr/CAutoPtr -> i8*)时不再返回 None(_llvm_str_to_ir 只认标量名),改走 _type_str_to_llvm——与同文件 _basic_type_to_llvm 既有口径一致;否则 PtrCount 循环会构造 ir.PointerType(None)(llvmlite opaque 指针,无 .pointee),在 HandlesFunctions._GetFunctionSignatureLlvm 抛 AttributeError(opendir 的 FATAL)。触发集合经全注册表扫描恰好 4 个类

等价性验证: 31614 个符号 + 37527 个真实注解点位逐条对比改前/改后差异 0 条(崩溃输入除外,由 opaque ptr 变为 typed i8**)
This commit is contained in:
TermiNexus committed 2026-09-16 18:16:38 +08:00
1 parent 8d950f5d43
commit 033c0a7771
3 files changed
+110 -71

No files matched your search

+65 -47
View File
@@ -465,8 +465,8 @@ class ExprAttrHandle(BaseHandle):
if not ClassName:
# 单级 Name 字段取值的类名恢复(跨模块 stub 下 var_struct_class 缺失的
# 兜底):本地变量(如 `tp: rowptr[Tuple] = (rowptr[Tuple])(node)`)可能
# 因 rowptr[X] 被折叠为 i8* 而漏登记。若变量 alloca / 值本身可辨识为
# 兜底):本地变量(如 `tp: rawptr[Tuple] = (rawptr[Tuple])(node)`)可能
# 因 rawptr[X] 被折叠为 i8* 而漏登记。若变量 alloca / 值本身可辨识为
# 结构体指针,按实际 pointee 恢复真实 Gen.structs 键并回填,避免属性
# 访问求值 None 被 fail-fast 拦截(parser.vp `tp_elts = tp.elts` 根因)。
if (VarName in Gen.variables or VarName in Gen._reg_values
@@ -614,7 +614,7 @@ class ExprAttrHandle(BaseHandle):
else:
Gen.var_struct_class[VarName] = ClassName
# 通用指针槽兜底:静态类名(var_struct_class/键归一后)有效且 ObjVal 是指针,
# 但 pointee 非 struct(如 cast 局部变量 tp: rowptr[Tuple] 的槽被建为 i32**,
# 但 pointee 非 struct(如 cast 局部变量 tp: rawptr[Tuple] 的槽被建为 i32**,
# Name 求值 load 一次得到 i32*,上述 struct 匹配/回退均不命中 → return None)。
# 此时按静态类名信任 bitcast 到 struct* 再走成员 GEP(tp.elts 根因:
# var_struct_class='Tuple'、structs 有 'Tuple',仅槽位为通用指针形态)。
@@ -652,7 +652,7 @@ class ExprAttrHandle(BaseHandle):
Gen.class_member_bitoffsets[ClassName][AttrName] = bo
return self._load_bitfield_member(ObjVal, ClassName, AttrName)
offset = Gen._get_member_offset(AttrName, ClassName)
offset = Gen._get_member_offset(AttrName, ClassName)
# 检查 offset 是否超出实际结构体
if isinstance(ObjVal.type, ir.PointerType):
actual_pointee: Any = ObjVal.type.pointee
@@ -672,45 +672,45 @@ class ExprAttrHandle(BaseHandle):
result = self._gep_and_Load_member(Gen, ObjVal, offset, AttrName, ClassName, st)
return result
def _recover_struct_class(self, Gen: LlvmCodeGenerator, VarName: str) -> str | None:
"""由变量的 LLVM 存储类型恢复结构体类键(var_struct_class 缺失时兜底)。
单级 Name 字段取值(如 tp.elts)的本地变量,若其注解 rowptr[X] 被 TypeMerge
builtins 分支折叠成 i8*、且 var_struct_class 未登记 X,属性访问层无法解析
类名。此处直接检查 Gen.variables / _reg_values / _direct_values /
global_vars 中该变量的 alloca(或值)指针 pointee:能辨识出
IdentifiedStruct 就按 pointee 恢复真实 Gen.structs 键(精确匹配失败时按
短名找元素最多的完整版本),供调用方回填并继续 GEP 成员访问。不发射任何
额外 IR(纯查表)。
"""
if not VarName:
return None
probe: ir.Type | None = None
vp: Any = Gen.variables.get(VarName)
if vp is not None and isinstance(vp.type, ir.PointerType):
probe = vp.type.pointee
if isinstance(probe, ir.PointerType):
probe = probe.pointee
if probe is None:
for reg in (Gen._reg_values.get(VarName), Gen._direct_values.get(VarName)):
if reg is not None and isinstance(reg.type, ir.PointerType):
probe = reg.type.pointee
if isinstance(probe, ir.PointerType):
probe = probe.pointee
break
if probe is None and VarName in Gen.global_vars and hasattr(Gen, 'module') and VarName in Gen.module.globals:
gv: Any = Gen.module.globals[VarName]
if isinstance(gv, ir.GlobalVariable) and isinstance(gv.type, ir.PointerType):
probe = gv.type.pointee
if isinstance(probe, ir.PointerType):
probe = probe.pointee
if probe is None or not isinstance(probe, ir.IdentifiedStructType):
return None
found: Any = Gen.find_struct_by_pointee(probe)
if found:
return found[0]
return self._pointee_llvm_to_struct_key(Gen, probe)
def _recover_struct_class(self, Gen: LlvmCodeGenerator, VarName: str) -> str | None:
"""由变量的 LLVM 存储类型恢复结构体类键(var_struct_class 缺失时兜底)。
单级 Name 字段取值(如 tp.elts)的本地变量,若其注解 rawptr[X] 被 TypeMerge
builtins 分支折叠成 i8*、且 var_struct_class 未登记 X,属性访问层无法解析
类名。此处直接检查 Gen.variables / _reg_values / _direct_values /
global_vars 中该变量的 alloca(或值)指针 pointee:能辨识出
IdentifiedStruct 就按 pointee 恢复真实 Gen.structs 键(精确匹配失败时按
短名找元素最多的完整版本),供调用方回填并继续 GEP 成员访问。不发射任何
额外 IR(纯查表)。
"""
if not VarName:
return None
probe: ir.Type | None = None
vp: Any = Gen.variables.get(VarName)
if vp is not None and isinstance(vp.type, ir.PointerType):
probe = vp.type.pointee
if isinstance(probe, ir.PointerType):
probe = probe.pointee
if probe is None:
for reg in (Gen._reg_values.get(VarName), Gen._direct_values.get(VarName)):
if reg is not None and isinstance(reg.type, ir.PointerType):
probe = reg.type.pointee
if isinstance(probe, ir.PointerType):
probe = probe.pointee
break
if probe is None and VarName in Gen.global_vars and hasattr(Gen, 'module') and VarName in Gen.module.globals:
gv: Any = Gen.module.globals[VarName]
if isinstance(gv, ir.GlobalVariable) and isinstance(gv.type, ir.PointerType):
probe = gv.type.pointee
if isinstance(probe, ir.PointerType):
probe = probe.pointee
if probe is None or not isinstance(probe, ir.IdentifiedStructType):
return None
found: Any = Gen.find_struct_by_pointee(probe)
if found:
return found[0]
return self._pointee_llvm_to_struct_key(Gen, probe)
def _handle_attr_nested(self, Node: ast.Attribute) -> ir.Value | None:
"""处理 a.b.c 嵌套属性访问"""
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
@@ -769,7 +769,7 @@ class ExprAttrHandle(BaseHandle):
# char* → struct* cast + 成员访问
if isinstance(pointee, ir.IntType) and pointee.width == 8:
# 两级自动解引用链(变量 = 指针.字段.成员,如 blk = func.Blocks.Head)
# 根因修复:跨模块 stub 会把容器/类内指针字段(rowptr[GSList[BasicBlock]]
# 根因修复:跨模块 stub 会把容器/类内指针字段(rawptr[GSList[BasicBlock]]
# 等)退化为 i8*,此处 ObjVal 是刚从结构体字段 load 出的 i8*。必须恢复
# 该字段声明的元素/特化类(如 'GSList[BasicBlock]')才能
# bitcast + GEP + load 出成员。否则本函数返回 None → AnnAssign/Assign
@@ -1718,7 +1718,7 @@ class ExprAttrHandle(BaseHandle):
_CurrentCpythonObjectClass 等)可能存在"裸名 vs 带 sha1 前缀"的不一致
(如 key='Function',而持有者是 '732a8c24abb9b516.Function')。先精确
匹配,再按短名(剥离 16 位 hex 前缀)遍历所有 key 匹配,确保能恢复
字段声明的特化类(如 rowptr[GSList[BasicBlock]] → 'GSList[BasicBlock]')。
字段声明的特化类(如 rawptr[GSList[BasicBlock]] → 'GSList[BasicBlock]')。
"""
if not OwnerClass or not FieldName:
return None
@@ -1733,6 +1733,24 @@ class ExprAttrHandle(BaseHandle):
continue
if FieldName in sub_map:
return sub_map[FieldName]
# 继承链回退(对齐 ExprUtils._get_var_class 的同一张表查询):字段声明在
# 基类时 elem_map 的键是**声明类**,而收件人类可能是子类
# (TypeGenBase.Gen: rawptr[Generator.Generator] → 收件人 TypeGenResolve /
# TypeGenCType…)→ 上面两路全 miss。rawptr[X] 字段的 LLVM 成员类型被折叠
# 为 i8*(struct 身份丢失,class_members / struct elements 都推不出目标类),
# elem_map 是唯一能恢复目标类的来源;miss 即任意级链解析
# (_field_element_key)返回 None → 带注解赋值 fail-fast
# (TypeGenResolve.vp:807 self.Gen.TypeGenCType.ImportAliasBuf 根因)。
try:
_mro_chain: list[str] = Gen._get_mro_chain(OwnerClass)
except Exception:
_mro_chain = [OwnerClass]
for _mro_cls in _mro_chain:
_sub_mro: Any = elem_map.get(_mro_cls)
if _sub_mro is None:
_sub_mro = elem_map.get(Gen._extract_short_name(_mro_cls))
if _sub_mro and FieldName in _sub_mro:
return _sub_mro[FieldName]
return None
def _resolve_attr_parent_hint(self, Node: ast.Attribute, Gen: LlvmCodeGenerator) -> str | None:
@@ -1740,7 +1758,7 @@ class ExprAttrHandle(BaseHandle):
例:func.Blocks.Head —— Node.value 是 func.Blocks,父变量 func 的
var_struct_class/global_struct_class 是 'Function',Blocks 字段声明为
rowptr[GSList[BasicBlock]],class_member_element_class 记录元素提示
rawptr[GSList[BasicBlock]],class_member_element_class 记录元素提示
'GSList[BasicBlock]'。该提示用于把退化为 i8* 的字段指针恢复成目标
特化类(char* 分支),或在所有 GSList[T] 都有 Head/Tail/Count 时
选择正确特化版本(struct 分支)。兼容裸名/带 sha1 前缀键。
@@ -1888,7 +1906,7 @@ class ExprAttrHandle(BaseHandle):
"""解析 OwnerClass 上 AttrName 字段的"元素 struct 键"(可在 Gen.structs 定位)。
供任意级链(ve.Alloca.Ty.Pointee)逐段恢复目标类:跨模块 stub 会把
rowptr[X] 指针字段退化为 i8*,load 出的值丢失 struct 身份;此处综合
rawptr[X] 指针字段退化为 i8*,load 出的值丢失 struct 身份;此处综合
class_member_element_class(登记的元素/特化类)→ class_members LLVM
成员类型 → struct elements 字段类型 推断该字段指向的 struct 键,
兼容裸名/sha1 前缀/泛型特化名。
+32 -24
View File
@@ -176,6 +176,14 @@ class TypeConvertMixin:
return ir.VoidType()
llvm_str: str = CTypeRegistry.CTypeToLLVM(type(BaseType))
# 注册表给出的是"完整类型串"(CPtr 家族 → 'i8*'):_llvm_str_to_ir 只认标量名
# (void/i1..i128/float/double/half/fp128),'i8*' 取不到 ⇒ 返回 None ⇒ 调用方
# _ctype_to_llvm 的 PtrCount 循环构造 ir.PointerType(None)(llvmlite opaque 指针,
# 无 .pointee)⇒ HandlesFunctions._GetFunctionSignatureLlvm 访问 .pointee 抛
# AttributeError。含 '*' 的串与 _basic_type_to_llvm 同口径,交给字符串→IR 解析
# ('i8*' → IntType(8).as_pointer(),typed 指针)。
if '*' in llvm_str:
return self._type_str_to_llvm(llvm_str)
return self._llvm_str_to_ir(llvm_str)
def _get_source_sha1_from_type_str(self, type_str: str) -> str | None:
@@ -322,30 +330,30 @@ class TypeConvertMixin:
while type_str.endswith('*'):
ptr_depth += 1
type_str = type_str[:-1].strip()
if ptr_depth > 0:
IsPtr = True
# [builtins 最后匹配] typedef 下标字符串(rowptr[AST] / t.CPtr[AST]):
# base 为 builtins 指针 typedef(rowptr → t.CPtr)时等价"指向切片类型",
# 直接转 arg*,防止 stub/ToString 字符串化产物被误建为 'rowptr[AST]'
# phantom 结构体(class_members 为空 → 属性访问 fail-fast KeyError)。
_sub_m: re.Match | None = re.fullmatch(r'([A-Za-z_][A-Za-z0-9_.]*)\[([^\]]+)\]', type_str)
if _sub_m:
_sub_base: str = _sub_m.group(1)
_sub_arg: str = _sub_m.group(2).strip()
_is_ptr_td: bool = False
try:
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR
if _TAR.builtins_typedef_value(_sub_base):
_is_ptr_td = True
except Exception:
_is_ptr_td = False
if not _is_ptr_td and _sub_base in ('CPtr', 't.CPtr', 'c.CPtr'):
_is_ptr_td = True
if _is_ptr_td and _sub_arg:
_sub_inner: ir.Type = self._type_str_to_llvm(_sub_arg, True)
if IsPtr:
return ir.PointerType(_sub_inner)
return _sub_inner
if ptr_depth > 0:
IsPtr = True
# [builtins 最后匹配] typedef 下标字符串(rawptr[AST] / t.CPtr[AST]):
# base 为 builtins 指针 typedef(rawptr → t.CPtr)时等价"指向切片类型",
# 直接转 arg*,防止 stub/ToString 字符串化产物被误建为 'rawptr[AST]'
# phantom 结构体(class_members 为空 → 属性访问 fail-fast KeyError)。
_sub_m: re.Match | None = re.fullmatch(r'([A-Za-z_][A-Za-z0-9_.]*)\[([^\]]+)\]', type_str)
if _sub_m:
_sub_base: str = _sub_m.group(1)
_sub_arg: str = _sub_m.group(2).strip()
_is_ptr_td: bool = False
try:
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR
if _TAR.builtins_typedef_value(_sub_base):
_is_ptr_td = True
except Exception:
_is_ptr_td = False
if not _is_ptr_td and _sub_base in ('CPtr', 't.CPtr', 'c.CPtr'):
_is_ptr_td = True
if _is_ptr_td and _sub_arg:
_sub_inner: ir.Type = self._type_str_to_llvm(_sub_arg, True)
if IsPtr:
return ir.PointerType(_sub_inner)
return _sub_inner
if type_str in {'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', 'CStruct', 'enum', 'REnum', 'renum'}:
result: ir.Type
if IsPtr:
+13
View File
@@ -144,6 +144,19 @@ class TypeResolver:
value_base_type: str | None = self._find_base_type(td.value)
_PTR_MODIFIERS: frozenset[str] = frozenset({'CPtr', 'CArrayPtr', 'CConst', 'CVolatile', 'CInline', 'CStatic', 'CExtern'})
# 裸 t.CPtr(无 `|` 联合、无 `[T]` 下标):_find_base_type 把 node.attr == 'CPtr'
# 归一化为指针标记 'ptr'(拼写与 _PTR_MODIFIERS 的 'CPtr' 不一致 ⇒ 下方守卫命不中),
# 若让 'ptr' 落进 original_class,经 _InsertTypedefSymbol 存入 symtab.OriginalType 后
# 会被 CTypeInfo.FromTypeName 当成 CStruct('ptr'),最终 _get_or_create_struct
# 建出 %"<模块sha>.ptr" 不透明结构体(VLogger._console_handle: win32base.HANDLE
# 报 "cannot store i8* to %"...ptr"*" 的根因)。按指针语义展开为 'void *',
# 与改前 `t.CVoid | t.CPtr` 联合分支等价(_InsertTypedefSymbol 同样把
# 'CVoid *' 归一化为 'void *')。
if value_base_type == 'ptr':
td.original_type_kind = 'typedef'
td.original_class = 'void *'
return
if value_base_type and value_base_type not in _PTR_MODIFIERS:
cname: str | None = CTypeHelper.GetCName(value_base_type)
if cname: