From 033c0a7771f4ad988a2d485e9997fdd9563c0954 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 16 Sep 2026 18:16:38 +0800 Subject: [PATCH] =?UTF-8?q?fix(tpc):=20=E4=BF=AE=E5=A4=8D=20t.CVoid=20?= =?UTF-8?q?=E6=B8=85=E7=90=86=E6=9A=B4=E9=9C=B2=E7=9A=84=E4=B8=89=E5=A4=84?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E8=A7=A3=E6=9E=90=E7=BC=BA=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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**) --- lib/core/Handles/HandlesExprAttr.py | 112 ++++++++++++++++------------ lib/core/LLVMCG/TypeConvert.py | 56 ++++++++------ lib/core/SymbolTypeResolver.py | 13 ++++ 3 files changed, 110 insertions(+), 71 deletions(-) diff --git a/lib/core/Handles/HandlesExprAttr.py b/lib/core/Handles/HandlesExprAttr.py index a27783d..dd1fa36 100644 --- a/lib/core/Handles/HandlesExprAttr.py +++ b/lib/core/Handles/HandlesExprAttr.py @@ -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 前缀/泛型特化名。 diff --git a/lib/core/LLVMCG/TypeConvert.py b/lib/core/LLVMCG/TypeConvert.py index 0c92924..87a2d19 100644 --- a/lib/core/LLVMCG/TypeConvert.py +++ b/lib/core/LLVMCG/TypeConvert.py @@ -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: diff --git a/lib/core/SymbolTypeResolver.py b/lib/core/SymbolTypeResolver.py index 6ed87c1..4adeae5 100644 --- a/lib/core/SymbolTypeResolver.py +++ b/lib/core/SymbolTypeResolver.py @@ -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: