refactor(type): 全面清理旧联合与 t/c 标记判断

审计并修复 23 个文件:类型注解中的 T1 | T2 一律 fail-fast;typedef/define/export/extern/static/const/volatile/attribute 优先读取 Markers/Attributes/ByteOrder/BitWidth;指针统一 CPtr/rawptr/CRef/CTempRef/CArrayPtr/CAutoPtr Subscript;stub 输出统一新语法。
This commit is contained in:
TermiNexus committed 2026-10-18 18:01:20 +08:00
1 parent a410de2cf7
commit 0bded58999
23 files changed
+887 -694

No files matched your search

+37 -50
View File
@@ -10,7 +10,7 @@ import llvmlite.ir as ir
from lib.core.Exportable import EnumMember as ExportEnumMember
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
from lib.core.SymbolUtils import (
FindStructNameInAnnotationWithContext, MangleTypeArgByRegistry,
MangleTypeArgByRegistry,
EnumClassIntWidth,
EnumBodyMemberNames, EnumMemberCountMismatchError,
ClassHasFuncLevelCVTable, IsVtableSlotMethod)
@@ -27,6 +27,12 @@ _ENUM_PTR_ANNOTATION_NAMES: frozenset[str] = frozenset({
'CPtr', 'CAutoPtr', 'CRef', 'CTempRef', 'rawptr', 'str', 'bytes',
})
# 指针/引用下标包装名(`CPtr[T]` / `rawptr[T]` / `CRef[T]` ...):
# 新 AST 表示下指针是 Subscript,不再是 `T | CPtr` 联合的一翼。
_PTR_SUBSCRIPT_NAMES: frozenset[str] = frozenset({
'CPtr', 'rawptr', 'CArrayPtr', 'CRef', 'CTempRef', 'CAutoPtr',
})
class ClassHandle(BaseHandle):
def _is_exception_class(self, Node: ast.ClassDef) -> bool:
@@ -177,7 +183,7 @@ class ClassHandle(BaseHandle):
# 都不在类表里 → 天然判为值类型,无需名表。
# 历史教训(名表判据的代价,保留以便回归时理解):`str` 曾漏在
# "非类类型名"集合外 → `list[str]` 的类型参数被误判为类类型,特化后
# 方法签名被额外包一层 `| CPtr`(append/set 形参 i8**、get/pop 返回
# 方法签名被额外包一层 `CPtr[...]`(append/set 形参 i8**、get/pop 返回
# i8**)→ 调用点多解一层 load(读字符串首 8 字节)→ ASan 崩溃。
# 注意 _BUILTIN_TYPE_NAMES 本身仍保留:它用于 type_map 替换
# 与 _from_import_aliases 注册,不能把 'str' 并进去
@@ -194,7 +200,7 @@ class ClassHandle(BaseHandle):
# (带 sha1 前缀 + ptr 后缀),避免模板内部嵌套注解被替换成裸短名
# 后重新特化出只有 declare、无方法体的错误特化(list[AST].__len__
# undefined symbol 根因)。_make_replacement_node 会把 mangled 名
# 还原为 'sha1.AST | CPtr',重新解析时拼回一致的 mangled 名。
# 还原为 'CPtr[sha1.AST]',重新解析时拼回一致的 mangled 名。
tn_bare: str = tn.split('.')[-1] if '.' in tn else tn
if tn_bare in _BUILTIN_TYPE_NAMES:
type_map[tp_name] = tn
@@ -204,10 +210,10 @@ class ClassHandle(BaseHandle):
type_map[tp_name] = tn
else:
type_map[tp_name] = type_args[i]
# 检测哪些类型参数在原始类型实参中带有指针语义(| CPtr / rawptr[T])
# 检测哪些类型参数在原始类型实参中带有指针语义(CPtr[T] / rawptr[T] 等)
# 当 type_arg 以 '*' 结尾(mangling 把每个 CPtr 层渲染为一层 '*')且对应的
# type_name(去模块前缀后)不是内建类型名时,说明调用方写了
# list[AST | CPtr] / list[rawptr[AST]]。注意用 tn_bare 判断:
# list[CPtr[AST]] / list[rawptr[AST]]。注意用 tn_bare 判断:
# 'CPtr' 去前缀后是 'CPtr'(内建),不算类类型参数。
cptr_params: set[str] = set()
for i, tp_name in enumerate(type_param_names):
@@ -218,8 +224,8 @@ class ClassHandle(BaseHandle):
if tn_bare_c not in _BUILTIN_TYPE_NAMES:
cptr_params.add(tp_name)
# Bug 2 修复:识别特化为 class 类型的类型参数
# 这类参数在方法签名中是裸 T(无 | CPtr),特化后为裸 ClassName(值类型)
# 需要加 | CPtr 使其成为指针类型(class 实例应按指针传递)
# 这类参数在方法签名中是裸 T,特化后为裸 ClassName(值类型)
# 需要加 CPtr[...] 使其成为指针类型(class 实例应按指针传递)
# 判据 = "是否有类定义"(_HasClassDefinition),存**裸名**:注解侧的
# 比较也用裸名(T 替换后的 'sha1.Param' 去前缀)——此前存带模块前缀的
# tn(如 'ast.AST')而比较用裸名 'AST',前缀形态永不命中。
@@ -886,7 +892,7 @@ class ClassHandle(BaseHandle):
# 由 HandlesImports.py:2647/2654 与 FuncGen.py:1057 触发;该分支不受
# "字段收集中"保护,见 HandlesImports.py:3299 的 `not force` 豁免)。
# 此时本循环后面的 `append` 会把**嵌套帧已经登记过的同一字段**再写一条 ——
# 现场:AST 的最后一个字段 `children: list[rawptr[AST]] | CPtr` 是全类唯一
# 现场:AST 的最后一个字段 `children: CPtr[list[rawptr[AST]]]` 是全类唯一
# 触发泛型特化的注解,处理它时嵌套帧重填了 AST 表(7 项),本循环恢复后
# 又追加一次 `children` ⇒ 表 8 项 / 去重 7 项,重名条目 `children@7`;
# 该表又被祖先块展平**原样拷贝**给 AST 的每个子类(_FlattenInherited 只按
@@ -901,6 +907,11 @@ class ClassHandle(BaseHandle):
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
if item.target.id in ('__provides__', '__requires__', '__require_must__'):
continue
if isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr):
Line: int = getattr(item.annotation, 'lineno', 0) or 0
raise TypeError(
f"[TPC] 不支持类型联合 'T1 | T2'(第 {Line} 行);"
f"类型注解只能是单一类型:指针请写 CPtr[T],修饰符请用关键字")
parsed = self.CollectAnnAssignMember(item, Gen)
if parsed:
VarName, MemberType, TypeInfo = parsed
@@ -913,39 +924,13 @@ class ClassHandle(BaseHandle):
# FieldEntry.IsConst/IsVolatile/PointeeVolatile 同口径。
Gen.RegisterFieldQualifiers(ClassName, VarName, TypeInfo, item.annotation)
if TypeInfo:
if isinstance(MemberType, ir.PointerType) and isinstance(MemberType.pointee, ir.IntType) and MemberType.pointee.width == 8:
if isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr):
element_class: str | None = FindStructNameInAnnotationWithContext(
item.annotation, self.Trans, Gen)
# Fallback: FindStructNameInAnnotation 不处理 Subscript slice 为 BinOp 的情况
# (如 list[AST | CPtr] | CPtr),使用 TypeInfo.Name 作为回退。
# TypeInfo.Name 由 _HandleSubscript 设置为特化名(如 'list[sha1.AST*]'),
# 经 _MergeCTypeInfoInto 传播后保留。
if not element_class and TypeInfo.IsStruct and TypeInfo.Name and TypeInfo.Name in Gen.structs:
element_class = TypeInfo.Name
if element_class:
if ClassName not in Gen.class_member_element_class:
Gen.class_member_element_class[ClassName] = {}
Gen.class_member_element_class[ClassName][VarName] = element_class
# 泛型特化结构体指针字段(如 GSList[BasicBlock] | CPtr):
# 记录特化名,供后续 _try_access_opaque_generic_member 在所有候选特化版本
# 都有目标字段(如 Head/Tail/Count)时选择正确特化版本。
# 现有 i8* 分支不覆盖结构体指针字段,这里补充。
elif (isinstance(MemberType, ir.PointerType)
and isinstance(MemberType.pointee, ir.IdentifiedStructType)
and isinstance(item.annotation, ast.BinOp)
and isinstance(item.annotation.op, ast.BitOr)):
spec_in_anno: str | None = FindStructNameInAnnotationWithContext(
item.annotation, self.Trans, Gen)
if spec_in_anno and '[' in spec_in_anno:
if ClassName not in Gen.class_member_element_class:
Gen.class_member_element_class[ClassName] = {}
Gen.class_member_element_class[ClassName][VarName] = spec_in_anno
# Subscript 容器字段(list[X] / rawptr[list[X]]):非 BinOp 联合,
# 上面的 i8*/BinOp 分支不命中 → 容器身份丢失 → 字段方法调用
# (get/len…)分派失败 "Undefined method 'get' in class 'rawptr'"
# (Converter.vp L190)。与 HandlesImports 对称,用与特化生成端
# 同源的 _resolve_type_arg_str 记录 element_class。
# Subscript 容器/指针字段(list[X] / CPtr[list[X]] /
# rawptr[GSList[X]]):记录容器/特化名,供字段方法分派与
# 泛型特化结构体指针字段解析使用;身份丢失会导致
# "Undefined method 'get' in class 'rawptr'"(Converter.vp L190)。
# 旧联合写法(如 `GSList[BasicBlock] | CPtr`)已由循环入口
# fail-fast 拦截,不再走合并路径。与 HandlesImports 对称,
# 用与特化生成端同源的 _resolve_type_arg_str 记录 element_class。
if isinstance(item.annotation, ast.Subscript):
_ann_sub_c: ast.Subscript = item.annotation
_subv_c: ast.AST = _ann_sub_c.value
@@ -953,7 +938,7 @@ class ClassHandle(BaseHandle):
_elem_slice_c: ast.AST | None = None
if _subname_c == 'list':
_elem_slice_c = _ann_sub_c.slice
elif _subname_c == 'rawptr':
elif _subname_c in _PTR_SUBSCRIPT_NAMES:
_slice_c: ast.AST = _ann_sub_c.slice
if isinstance(_slice_c, ast.Subscript):
# rawptr[泛型容器](rawptr[list[X]] / rawptr[GSList[Param]]):
@@ -980,8 +965,8 @@ class ClassHandle(BaseHandle):
else:
# rawptr[裸结构](如 rawptr[Token]):跨模块 stub 会把自引用/类内
# 指针字段退化为 i8*,链式成员访问(a.b.c)需 element_class 恢复
# struct 身份走 char*→struct cast。HEAD 态 `Token | CPtr` 联合
# 由上方 BinOp 分支登记;rawptr 重构后此分支补齐(阻塞 A 根因)。
# struct 身份走 char*→struct cast。指针统一为 Subscript
# (`rawptr[Token]` / `CPtr[Token]`),由本分支登记。
# 与 HandlesImports 的 rawptr 分支对称。
_slc_name_c: str | None = None
if isinstance(_slice_c, ast.Name):
@@ -1549,8 +1534,8 @@ class ClassHandle(BaseHandle):
variant_names: list[str] = []
# 预注册 REnum opaque 结构体到 Gen.structs 和 SymbolTable,
# 使自引用成员(如 LLVMType | CPtr)在收集成员时能正确解析为 %LLVMType*。
# 若不预注册,GetCTypeInfo 找不到 LLVMType → 回退到 IsEnum → i32 → | CPtr → i32*(错误)。
# 使自引用成员(如 CPtr[LLVMType] / rawptr[LLVMType])在收集成员时能正确解析为 %LLVMType*。
# 若不预注册,GetCTypeInfo 找不到 LLVMType → 回退到 IsEnum → i32 → 再加一层指针 → i32*(错误)。
PreRenumType: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, ClassName)
Gen.structs[ClassName] = PreRenumType
PreTypeInfo: CTypeInfo = CTypeInfo()
@@ -1560,7 +1545,7 @@ class ClassHandle(BaseHandle):
PreTypeInfo.IsEnum = True
# REnum 在内存布局上是 struct({ i32 __tag, <max_payload> }),
# 必须设置 IsStruct=True 才能让 HandlesTypeMerge 的 CPtr 吸收逻辑生效:
# `REnumType | CPtr` 应得到 REnumType*(PtrCount=1)而非 REnumType**(PtrCount=2)。
# `CPtr[REnumType]` 应得到 REnumType*(PtrCount=1)而非 REnumType**(PtrCount=2)。
# 否则 include-imported 函数返回类型会出现双指针,与声明单指针不匹配 → 运行时崩溃。
PreTypeInfo.IsStruct = True
PreTypeInfo.RenumVariants = []
@@ -1695,7 +1680,7 @@ class ClassHandle(BaseHandle):
PreTypeInfo.RenumVariants = variant_names
def _EnumMemberAnnotationIsPtr(self, annotation: ast.AST | None) -> bool:
"""枚举成员注解是否含指针形态(逐层下标 + 联合类型逐边),True=含指针
"""枚举成员注解是否含指针形态(逐层下标),True=含指针
与 VPC 侧 ClassGenEnum._enum_member_annotation_is_ptr 同口径。枚举值是
整数常量,指针成员语义不成立,必须明确报错(fail-fast)而非静默接受:
@@ -1706,8 +1691,10 @@ class ClassHandle(BaseHandle):
if annotation is None:
return False
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
return (self._EnumMemberAnnotationIsPtr(annotation.left)
or self._EnumMemberAnnotationIsPtr(annotation.right))
Line: int = getattr(annotation, 'lineno', 0) or 0
raise TypeError(
f"[TPC] 不支持类型联合 'T1 | T2'(第 {Line} 行);"
f"类型注解只能是单一类型:指针请写 CPtr[T],修饰符请用关键字")
if isinstance(annotation, ast.Subscript):
return self._EnumMemberAnnotationIsPtr(annotation.value)
name: str | None = None