diff --git a/lib/Projectrans/DeclarationGenerator.py b/lib/Projectrans/DeclarationGenerator.py index 8d1574d..85a9470 100644 --- a/lib/Projectrans/DeclarationGenerator.py +++ b/lib/Projectrans/DeclarationGenerator.py @@ -8,7 +8,9 @@ import llvmlite.ir as ir from lib.includes import t from lib.includes.t import CTypeRegistry -from lib.core.SymbolUtils import IsListAnnotation, AnnotationContainsName, IsCPtrSubscriptNode +from lib.core.SymbolUtils import ( + IsListAnnotation, AnnotationContainsName, IsCPtrSubscriptNode, IsCPtrNode, + IsRawPtrSubscriptNode, IsPtrSubscriptNode, ResolveGenericTypeArgStr) class DeclarationGenerator: @@ -40,6 +42,13 @@ class DeclarationGenerator: self.typedef_map: dict[str, ast.AST] = typedef_map or {} self._pyi_tree: ast.Module | None = None self._t_alias_map: dict[str, type] = {} + # 泛型模板类名(PEP 695 `class Box[T]`):声明端特化判定用,与定义端 + # HandlesClassDef._generic_class_templates 同源(ClassDef 的 type_params + # 标记),generate() 里从本模块 pyi 与 class_def_map 收集 + self._generic_class_names: set[str] = set() + # t/c 导入名表(from t import CInt as myint → 'myint' → ('t','CInt')), + # 与定义端 Trans._t_c_imported_names 同口径,供泛型实参串解析使用 + self._t_c_imported_names: dict[str, tuple[str, str]] = {} # 跨模块类定义映射:{class_name: ClassDef_node},供 _get_inherited_members # 查找其他模块定义的父类(如 exprs.py 的 Name(AST) 查找 base.py 的 AST) self._cross_module_class_defs: dict[str, ast.ClassDef] = class_def_map or {} @@ -50,6 +59,16 @@ class DeclarationGenerator: self._DefineConstants: dict[str, int | str] = {} self._pyi_tree = tree + # 泛型模板类名(PEP 695 `class Box[T]`):class_def_map 覆盖所有已生成 + # .vpi 的模块(含本模块),本模块 pyi 直接解析的 ClassDef 优先, + # 与定义端 HandlesClassDef 注册 _generic_class_templates 的判据一致 + # (都只看 ClassDef.type_params 是否非空,不看类名白名单)。 + self._generic_class_names = { + node.name for node in ast.iter_child_nodes(tree) + if isinstance(node, ast.ClassDef) and getattr(node, 'type_params', None)} + for _gc_name, _gc_node in self._cross_module_class_defs.items(): + if getattr(_gc_node, 'type_params', None): + self._generic_class_names.add(_gc_name) global_typedef_map: dict[str, ast.AST] = self.typedef_map self.typedef_map: dict[str, ast.AST] = {} if global_typedef_map: @@ -57,12 +76,14 @@ class DeclarationGenerator: # 构建 t/c 类型别名映射(from t import CInt as myint) self._t_alias_map: dict[str, type] = {} + self._t_c_imported_names: dict[str, tuple[str, str]] = {} for node in ast.iter_child_nodes(tree): if isinstance(node, ast.ImportFrom): if node.module in ('t', 'c'): for alias in node.names: name: str = alias.name asname: str = alias.asname or name + self._t_c_imported_names[asname] = (node.module, name) if node.module == 't': t_cls: type | None = getattr(t, name, None) if isinstance(t_cls, type) and issubclass(t_cls, t.CType): @@ -1036,6 +1057,45 @@ class DeclarationGenerator: """ return base_type + '*' * self._ptr_level_of(cptr_sub_type) + def _ptr_subscript_type_str(self, annotation: ast.Subscript) -> str: + """指针下标(rawptr[T] / t.CPtr[T] / 裸名 CPtr[T],判据见 + SymbolUtils.IsPtrSubscriptNode)→ LLVM 类型串。 + + 与定义端同源:SymbolUtils.DesugarPtrSubscriptToUnion 把三者折叠为等价 + 联合写法 `T | t.CPtr` 后交 HandlesTypeMerge.MergeTypes,指针层数由 + _MergeAllComponentTypes 的"struct 基类型吸收一层 CPtr"决定 —— 裸类引用 + 的非 embedded 形式本身即指针('%"sha1.X"*'),再包一层会被吸收 → 结果 + 就是该指针本身;其余类型(标量 / 已是指针的 typedef / 泛型容器特化 / + 联合写法 / 嵌套指针下标)逐层外包。rawptr[...] 与 t.CPtr[...] 共用本条 + 规则(此前 t.CPtr[list[X]] 在此得 list[X]**、rawptr[list[X]] 得 + list[X]*,同一指针语义两种结果)。 + """ + inner: ast.AST = annotation.slice + inner_type: str = self._get_type_str(inner) + if self._is_bare_class_ref_annotation(inner, inner_type): + return inner_type + if inner_type in ('', 'void', 'void*'): + return 'i8*' + return f'{inner_type}*' + + def _is_bare_class_ref_annotation(self, inner: ast.AST, inner_type: str) -> bool: + """节点是否为"裸类引用"(本身即指针 → 吸收一层 t.CPtr)。 + + 与 _get_type_str 的既有约定一致:类引用的**非** embedded 形式本身就是 + 指针('%"sha1.X"*'),所以 `AST | t.CPtr` 合并后仍是该指针(定义端 + _MergeAllComponentTypes 的 struct 吸收)。联合写法(ast.BinOp)与指针 + 下标(rawptr[...] / t.CPtr[...])不是裸类引用——它们已自带指针层数, + 须逐层外包(rawptr[Node] | t.CPtr → Node**、ast.AST | t.CPtr | t.CPtr + → AST**,与定义端整体拉平后只吸收一次 CPtr 一致)。 + """ + if not inner_type: + return False + if isinstance(inner, ast.BinOp): + return False + if isinstance(inner, ast.Subscript): + return not (IsRawPtrSubscriptNode(inner) or IsCPtrSubscriptNode(inner)) + return inner_type.startswith('%') and inner_type.endswith('*') + def _get_type_str(self, annotation: ast.AST | None, embedded: bool = False) -> str: if annotation is None: return 'i8*' @@ -1164,13 +1224,23 @@ class DeclarationGenerator: # t.CPtr (i8*) 与 left 合并:对齐 Phase 2 _MergeAllComponentTypes 的 # "first CPtr absorption" 语义:struct | t.CPtr → struct*(吸收,struct 变量默认是指针)。 # 但对于本身已是指针的类型(如 bytes=str=i8*),bytes | t.CPtr → i8**(不吸收)。 - # 例外:泛型特化类型(含 '[')回退为 i8*,避免引用未定义的特化结构体。 + # 例外:LLVM 聚合数组([N x T],字符串以 '[' 开头)不可再外包指针层 → i8*。 + # 注:具名泛型特化(%"mod.list[X]"*)**不**走此回退——stub 已由 + # _collect_undefined_type_decls 为其补 opaque 声明,保留名字才能与 define 端 + # 的 %"mod.list[ast.ASTptr]"* 逐字一致(原 `'[' in left_type` 会把 + # list[X] | t.CPtr 一并退化成 i8*,与定义端 %"mod.list[X]"* 不一致)。 if right_type == 'i8*': - if '[' in left_type: + if left_type[:1] == '[': return 'i8*' if left_type in ('', 'void'): return 'i8*' - if left_type.startswith('%"') and left_type.endswith('*'): + # 吸收仅对"裸类引用"成立(其非 embedded 形式本身即指针): + # ast.AST | t.CPtr → %"sha1.AST"*。已自带指针层数的左值 + # (rawptr[...] / t.CPtr[...] 下标 / 联合写法)须逐层外包: + # rawptr[Node] | t.CPtr → Node**、ast.AST | t.CPtr | t.CPtr → AST** + # ——与定义端 _CollectAllOperandTypes 整体拉平、只吸收一次 CPtr + # (_MergeAllComponentTypes 的 base_ptr_count>0 分支)一致。 + if self._is_bare_class_ref_annotation(annotation.left, left_type): return left_type if '*' in left_type: return f'{left_type}*' @@ -1185,27 +1255,40 @@ class DeclarationGenerator: return self._merge_cptr_subscript(left_type, right_type) if IsCPtrSubscriptNode(annotation.left) and right_type[:1] not in ('[', '{'): return self._merge_cptr_subscript(right_type, left_type) + # t.CPtr 裸标记在左侧(t.CPtr | X):与右侧写法(X | t.CPtr,见上) + # 完全对称。缺此分支时左值 i8* 会吞掉右侧的指针层数: + # t.CPtr | rawptr[Node] 声明端曾得 i8*,而定义端为 %"sha1.Node"**(差一层)。 + # 仅对裸标记 t.CPtr 生效(left 为 t.CPtr[T] 下标走上一条分支; + # str/bytes 等其它 i8* 左值不受影响)。 + if IsCPtrNode(annotation.left) and right_type[:1] not in ('[', '{'): + if self._is_bare_class_ref_annotation(annotation.right, right_type): + return right_type + return f'{right_type}*' if '*' in left_type: return left_type if '*' in right_type: return right_type return left_type elif isinstance(annotation, ast.Subscript): - base: str = self._get_type_str(annotation.value) - if base == 'i8*' and isinstance(annotation.slice, ast.Constant): - return f'[{self._get_const_int(annotation.slice)} x i8]' - # 处理 t.CPtr[Type] 语法(指向 Type 的指针) - # 例如: t.CPtr[t.CPtr] -> i8** (void**), t.CPtr[t.CInt] -> i32* (int*) - # t.CPtr[t.CPtr[t.CInt]] -> i32** (int**) + # 指针下标(rawptr[T] / t.CPtr[T] / 裸名 CPtr[T] / 导入别名 X[T],以及带 + # 模块前缀书写的 X.CPtr[T])≡ 源码联合写法 `T | t.CPtr`:与定义端 + # **同一判据**(SymbolUtils.IsPtrSubscriptNode,别名经本模块的 + # _t_c_imported_names 表解析 X→CPtr)+ 同一层数规则 + # (_ptr_subscript_type_str:裸类引用吸收一层、其余逐层外包),内层 T + # 照常解析(跨模块类由 struct_sha1_map 补 SHA1 前缀)。此前 rawptr[T] + # 在此退化成 i8*,t.CPtr[T] 走的是"embedded + 追加一层"的独立分支: + # 非同一规则,且对泛型容器特化多出一层(t.CPtr[list[X]] 得 list[X]**, + # 而 rawptr[list[X]] 得 list[X]*)。 ValueIsCPtr: bool = ( (isinstance(annotation.value, ast.Attribute) and annotation.value.attr == 'CPtr') or (isinstance(annotation.value, ast.Name) and annotation.value.id == 'CPtr') ) - if ValueIsCPtr and isinstance(annotation.slice, (ast.Attribute, ast.Name, ast.Subscript)): - slice_type: str = self._get_type_str(annotation.slice, embedded=True) - if slice_type and slice_type != 'void': - return f'{slice_type}*' - return 'i8*' + if (IsPtrSubscriptNode(annotation, TCAliasMap=self._t_c_imported_names) + or (ValueIsCPtr and isinstance(annotation.slice, (ast.Attribute, ast.Name, ast.Subscript)))): + return self._ptr_subscript_type_str(annotation) + base: str = self._get_type_str(annotation.value) + if base == 'i8*' and isinstance(annotation.slice, ast.Constant): + return f'[{self._get_const_int(annotation.slice)} x i8]' if isinstance(annotation.slice, ast.Constant) and isinstance(annotation.slice.value, int): return f'[{annotation.slice.value} x {base}]' # 处理 t.CArray[elem_type, count] / rawarray[elem_type, count] 注解 → [count x elem_type] @@ -1235,49 +1318,50 @@ class DeclarationGenerator: else: elem_types = [self._get_type_str(slice_node, embedded=True)] return '{ ' + ', '.join(elem_types) + ' }' - # 处理泛型类类型注解(如 list[str]) + # 处理泛型类类型注解(如 list[str] / list[rawptr[ast.AST]] / + # 用户泛型类 Box[Node]) + # 实参串走 SymbolUtils.ResolveGenericTypeArgStr(与定义端 + # _mangle_generic_class_name 同一规则源):此前只认 Name / Name-Tuple + # 实参,Attribute(ast.AST)/ Subscript(rawptr[...])/ BinOp + # (X | t.CPtr)/ 混合 Tuple 一律退回未特化的 %"...list"*, + # 与 define 端发射的 %".list[ast.ASTptr]" 不一致 + # (Undefined method / 签名错位)。 + # 内建容器(non_struct_types)之外,**用户泛型类**同样特化:判据 + # _generic_class_names(ClassDef.type_params,与定义端 + # HandlesClassDef._generic_class_templates 同源),此前只有内建容器名 + # 会特化 → Box[Node] 字段在 stub 端是 %".Box"*、定义端是 + # %".Box[<已解析实参>]"*(Holder 字段布局两侧不一致)。 if isinstance(annotation.value, ast.Name): gc_name: str = annotation.value.id - if gc_name in non_struct_types and gc_name != 'tuple': + if (gc_name in non_struct_types or gc_name in self._generic_class_names) \ + and gc_name != 'tuple': gc_slice: ast.AST = annotation.slice - gc_args: list[str] = [] - if isinstance(gc_slice, ast.Name): - gc_args.append(gc_slice.id) - elif isinstance(gc_slice, ast.Tuple): - gc_valid: bool = True - for gc_elt in gc_slice.elts: - if isinstance(gc_elt, ast.Name): - gc_args.append(gc_elt.id) - else: - gc_valid = False - break - if not gc_valid: - gc_args = [] - if gc_args: - gc_mangled: list[str] = [] - for gc_ta in gc_args: - # t.CPtr/CPtr → 'ptr'(与 _resolve_type_arg_str 的 - # 泛型 mangling 一致);NameToLLVM('CPtr') 返回 'i8*' - # 会导致声明端 'list[i8*]' 与特化端 'list[ptr]' 不匹配 - if gc_ta == 'CPtr': - gc_mangled.append('ptr') - continue - gc_llvm: str | None = CTypeRegistry.NameToLLVM(gc_ta) - if gc_llvm: - if gc_llvm in ('float', 'double', 'half', 'fp128'): - gc_cls: type | None = CTypeRegistry.GetClassByName(gc_ta) - gc_sz: int = gc_cls().Size if gc_cls else 0 - gc_mangled.append('f' + str(gc_sz)) - else: - gc_mangled.append(gc_llvm) - else: - gc_mangled.append(gc_ta) - gc_spec: str = gc_name + '[' + ']['.join(gc_mangled) + ']' - gc_sha1: str | None = self.module_sha1 - gc_sname: str = f'%"{gc_sha1}.{gc_spec}"' if gc_sha1 else f'%struct.{gc_spec}' - if embedded: - return gc_sname - return f'{gc_sname}*' + # 多参数泛型(dict[K, V]):'][' 分隔,与 + # _mangle_generic_class_name 一致 + gc_nodes: list[ast.AST] = ( + list(gc_slice.elts) if isinstance(gc_slice, ast.Tuple) else [gc_slice]) + gc_mangled: list[str] = [ + ResolveGenericTypeArgStr( + gc_node, + TCAliasMap=self._t_c_imported_names, + StructSha1Map=self.struct_sha1_map) + for gc_node in gc_nodes] + gc_spec: str = gc_name + '[' + ']['.join(gc_mangled) + ']' + # 特化名前缀取**泛型模板类定义模块**的 SHA1(list → includes/_list.vp、 + # Box → 定义 Box 的模块,查 struct_sha1_map 即"类名 → 定义模块"表), + # 与定义端 HandlesClassDef._specialize_generic_class 的 + # Gen.module_sha1 = template['sha1']、调用点 + # Gen._struct_sha1_map[spec_name] 同一条规则源。 + # 此前用 self.module_sha1(使用方模块):同一特化在 declare(使用方前缀) + # 与 define/call(模板模块前缀)两侧不一致 → 跨模块 undefined symbol。 + gc_sha1: str | None = self.struct_sha1_map.get(gc_name, self.module_sha1) + gc_sname: str = f'%"{gc_sha1}.{gc_spec}"' if gc_sha1 else f'%struct.{gc_spec}' + # 一律以指针引用(不区分 embedded):定义端对嵌入式泛型容器 + # (字段 / t.CArray 元素)用不透明指针(实测字段 i8*,见 + # FieldForms/FieldBox/FieldOrder),嵌入 unsized opaque + # 结构体值会让字段/元素偏移错位;非 embedded(参数/返回) + # 亦与定义端同为指针 + return f'{gc_sname}*' return f'{base}' elif isinstance(annotation, ast.Constant): if annotation.value is None: diff --git a/lib/core/Handles/HandlesAnnAssign.py b/lib/core/Handles/HandlesAnnAssign.py index 88f5824..0a05874 100644 --- a/lib/core/Handles/HandlesAnnAssign.py +++ b/lib/core/Handles/HandlesAnnAssign.py @@ -9,7 +9,7 @@ from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo from lib.core.VLogger import get_logger as _vlog from lib.constants.config import mode as _config_mode from lib.includes import t -from lib.core.SymbolUtils import IsListAnnotation, ParseListAnnotation, FindStructNameInAnnotation, AnnotationContainsName, IsCPtrNode +from lib.core.SymbolUtils import IsListAnnotation, ParseListAnnotation, FindStructNameInAnnotationWithContext, AnnotationContainsName, IsCPtrNode def _IsGenericTpName(name: str) -> bool: @@ -492,7 +492,8 @@ class AnnAssignHandle(BaseHandle): self._HandleGenericListLiteralAssign(VarName, Node, Gen, list_sub) return if TypeInfo.IsVoid and isinstance(Node.annotation, ast.BinOp): - found: str | None = FindStructNameInAnnotation(Node.annotation, Gen.structs) + found: str | None = FindStructNameInAnnotationWithContext( + Node.annotation, self.Trans, Gen) if found: TypeInfo = CTypeInfo() TypeInfo.BaseType = t.CStruct(name=found) @@ -547,7 +548,8 @@ class AnnAssignHandle(BaseHandle): # FindStructNameInAnnotation 以 attr/短名命中,找回真实类名。 if (isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr)): - _union_struct_name2: str | None = FindStructNameInAnnotation(Node.annotation, Gen.structs) + _union_struct_name2: str | None = FindStructNameInAnnotationWithContext( + Node.annotation, self.Trans, Gen) # resolver 对模块限定 struct 联合(llvmlite.Value | t.CPtr)常给出 # 指针占位名(如 '{sha1}.rawptr')且该占位恰好注册在 Gen.structs, # 使上方 TypeInfo.Name in Gen.structs 通过并登记错误类名 → 字段 @@ -876,7 +878,8 @@ class AnnAssignHandle(BaseHandle): Gen.local_var_byteorders[VarName] = 'big' if isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr): - found_struct: str | None = FindStructNameInAnnotation(Node.annotation, Gen.structs) + found_struct: str | None = FindStructNameInAnnotationWithContext( + Node.annotation, self.Trans, Gen) # 若 FindStructNameInAnnotation 仅返回裸模板名(如 'ndarray'),但注解中 # 含泛型特化(如 numpy.ndarray[t.CDouble]),用 LLVM 类型名构造特化名 # (如 'ndarray[double]')。main.py 的 Gen.structs 可能只有 opaque 'ndarray', diff --git a/lib/core/Handles/HandlesBase.py b/lib/core/Handles/HandlesBase.py index 7204231..71ccc73 100644 --- a/lib/core/Handles/HandlesBase.py +++ b/lib/core/Handles/HandlesBase.py @@ -13,6 +13,7 @@ from lib.constants import config as _config from lib.includes import t from lib.includes.t import CTypeRegistry from lib.core.TypeSpec import TypeSpec, SymbolMeta, FIELD_ROUTES +from lib.core.SymbolUtils import IsPtrSubscriptNode, IsListAnnotation from lib.core.VLogger import get_logger as _vlog @@ -754,6 +755,17 @@ class BaseHandle: """解析注解类型,BinOp(BitOr) 优先使用 MergeTypes,其他使用 FromNode。 统一处理函数返回类型和参数类型的 BinOp 联合类型解析。 + 指针下标(rawptr[T] 内置裸指针 / t.CPtr[T] / 裸名 CPtr[T],三者等价 + `T | t.CPtr`)同样交给 TypeMerge 解析:内层 T 走通用类型解析(含跨模块 + 类经 Gen.structs 反查 SHA1 前缀),指针层数与源码 `T | t.CPtr` 一致; + 若交给 CTypeInfo.FromNode(TypeAnnotationResolver)解析, + rawptr[ast.AST] 会退化成 CVoid/1(i8*)、t.CPtr[ast.AST] 亦退化成 + CVoid/1(其 Subscript-CPtr 分支不带 Gen.structs 反查),与 declare 端 + %".AST"* 分裂。 + 命中泛型模板的泛型容器下标(list[X] / rawptr[list[X]] 等)同样交 + TypeMerge:字段(embedded)位置与参数/返回注解**同一解析**,否则 + CTypeInfo.FromNode 把容器折叠成 i8*,字段类型与 declare/stub 端 + 的特化名指针不一致(同一结构体在定义端/声明端布局描述不同)。 """ if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): merger: Any = getattr(self.Trans, 'TypeMergeHandler', None) @@ -761,7 +773,90 @@ class BaseHandle: info: CTypeInfo | None = merger.MergeTypes(annotation) if info is not None and isinstance(info, CTypeInfo): return info - return CTypeInfo.FromNode(annotation, self.Trans.SymbolTable) + if IsPtrSubscriptNode(annotation, self.Trans.SymbolTable): + merger = getattr(self.Trans, 'TypeMergeHandler', None) + if merger: + info = merger.GetCTypeInfo(annotation) + if isinstance(info, CTypeInfo) and info.BaseType is not None: + return info + if (isinstance(annotation, ast.Subscript) + and self._SubscriptHitsGenericTemplate(annotation) + and getattr(self.Trans, 'TypeMergeHandler', None) is not None): + gci: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(annotation) + if isinstance(gci, CTypeInfo) and gci.BaseType is not None: + return gci + if isinstance(annotation, ast.Attribute): + return self._ResolveAttributeAnnotationType(annotation) + Fallback: CTypeInfo | None = CTypeInfo.FromNode(annotation, self.Trans.SymbolTable) + return self._ResolveStackArrayElementFallback(annotation, Fallback) + + def _ResolveStackArrayElementFallback( + self, annotation: ast.expr, Fallback: CTypeInfo | None) -> CTypeInfo | None: + """固定数组注解(`t.CArray[T, N]` / `list[T, N]`)元素类型兜底解析。 + + CTypeInfo.FromNode(TypeAnnotationResolver 的 IsListAnnotation 分支)只 + 会用自身解析元素类型:元素命中泛型模板时(list[X] / list[rawptr[X]])解 + 析为空 → 整个注解 BaseType=None → 调用方(CollectAnnAssignMember)把 + VoidType 换成 i8*,字段即塌成 i8*,与声明端 + DeclarationGenerator._get_type_str 的 ValueIsCArray 分支给出的 + `[N x <元素>]` 布局不一致(规则 4:能不变 i8* 的就不变 i8*)。 + 此处仅在该元素解析为空时兜底:用**与参数注解同一解析** + (ResolveAnnotationType 递归,泛型容器因此走 TypeMerge 特化,跨模块类 + 经 Gen.structs 反查 SHA1)解析元素,再套上数组维度(维度序与 + TypeAnnotationResolver 的 `insert(0, count)` 一致)。 + 元素可解析的形态(t.CArray[t.CInt, 8] / t.CArray[Node, 4] / 嵌套 CArray) + 保持 FromNode 结果原样,避免改动既有布局。 + """ + if isinstance(Fallback, CTypeInfo) and Fallback.BaseType is not None: + return Fallback + if not (isinstance(annotation, ast.Subscript) and IsListAnnotation(annotation)): + return Fallback + SliceNode: ast.AST = annotation.slice + if not (isinstance(SliceNode, ast.Tuple) and len(SliceNode.elts) == 2): + return Fallback + ElemInfo: CTypeInfo | None = self.ResolveAnnotationType(SliceNode.elts[0]) + if not isinstance(ElemInfo, CTypeInfo) or ElemInfo.BaseType is None: + return Fallback + CountVal: int | None = CTypeInfo.TryEvalConstExpr(SliceNode.elts[1], self.Trans.SymbolTable) + if not isinstance(CountVal, int) or CountVal <= 0: + return Fallback + Result: CTypeInfo = ElemInfo.Copy() + Result.ArrayDims = [str(CountVal)] + list(ElemInfo.ArrayDims) + return Result + + def _ResolveAttributeAnnotationType(self, annotation: ast.Attribute) -> CTypeInfo | None: + """Attribute 注解(`t.CInt` / `ast.AST` / `.CLS`)解析。 + + 先走 CTypeInfo.FromNode(TypeAnnotationResolver,覆盖 t/c 内建与已在本模块 + SymbolTable 注册的类):其 BaseType 已解析时语义不变。 + **仅在 FromNode 解析不出来时**(BaseType 为 None,即该类未随本模块 + import/预扫描注册)按类名查 `Gen.structs` / `Gen._struct_sha1_map` 后交 + TypeMerge(HandlesTypeMerge 的 Attribute 分支,与声明端 + DeclarationGenerator._get_type_str 的 `attr_name in struct_names`、名称域 + SymbolUtils._ResolveAttributeArgStr 同一条"类名 → SHA1"规则)。 + + 不修此路时跨模块类 Attribute(`rawptr[.CLS]` 脱糖后的 + `.CLS | t.CPtr`、泛型 T 替换出的裸 `.CLS`、`list[ast.AST]` + 特化的 `append(item: T)`)在定义端判为"无法识别的参数类型注解"抛 + SyntaxError,被上层 broad except 吞掉后退化为 i32/i8*,而声明端给 + `%".CLS"*`,字段/签名两侧不一致。 + """ + fallback: CTypeInfo | None = CTypeInfo.FromNode(annotation, self.Trans.SymbolTable) + if fallback is not None and fallback.BaseType is not None: + return fallback + attr_name: str = getattr(annotation, 'attr', '') or '' + gen: Any = getattr(self.Trans, 'LlvmGen', None) + if not attr_name or gen is None: + return fallback + if (attr_name not in getattr(gen, 'structs', {}) + and attr_name not in getattr(gen, '_struct_sha1_map', {})): + return fallback + merger: Any = getattr(self.Trans, 'TypeMergeHandler', None) + if merger is not None: + info: CTypeInfo | None = merger.GetCTypeInfo(annotation) + if isinstance(info, CTypeInfo) and info.BaseType is not None: + return info + return fallback def ResolveParamAnnotation(self, annotation: ast.expr) -> CTypeInfo | None: """函数参数注解解析(阻塞 B 同根第三处修复)。 @@ -781,6 +876,13 @@ class BaseHandle: info: CTypeInfo | None = merger.MergeTypes(annotation) if info is not None and isinstance(info, CTypeInfo): return info + # 指针下标参数注解(rawptr[T] / t.CPtr[T] / 裸名 CPtr[T],含 + # rawptr[list[X]] 等容器:_SubscriptHitsGenericTemplate 也命中,但统一交 + # ResolveAnnotationType 处理更简单——它已把指针下标路由到 TypeMerge)。 + if IsPtrSubscriptNode(annotation, self.Trans.SymbolTable): + info = self.ResolveAnnotationType(annotation) + if isinstance(info, CTypeInfo) and info.BaseType is not None: + return info if isinstance(annotation, ast.Subscript) and getattr(self.Trans, 'TypeMergeHandler', None): if self._SubscriptHitsGenericTemplate(annotation): try: @@ -789,7 +891,16 @@ class BaseHandle: return _gci except Exception: pass - return CTypeInfo.FromNode(annotation, self.Trans.SymbolTable) + # Attribute 参数注解(`.CLS` 裸引用 / 泛型 T 替换出的 `.CLS`) + # 与 ResolveAnnotationType 共用同一条规则(_ResolveAttributeAnnotationType → + # TypeMergeHandler.GetCTypeInfo 的 Attribute 分支查 Gen.structs / + # Gen._struct_sha1_map),不在此复制第二套查表;否则 list[X] 特化方法 + # `append(item: T)` 的参数 T(= 裸 `.CLS`)解析为空 → VoidType → + # 抛"无法识别的参数类型注解" → 特化模板方法未注册。 + if isinstance(annotation, ast.Attribute): + return self.ResolveAnnotationType(annotation) + Fallback: CTypeInfo | None = CTypeInfo.FromNode(annotation, self.Trans.SymbolTable) + return self._ResolveStackArrayElementFallback(annotation, Fallback) def _SubscriptHitsGenericTemplate(self, annotation: ast.Subscript) -> bool: """判断 Subscript 是否命中泛型模板:裸模板名(list[X]/GSList[X]), diff --git a/lib/core/Handles/HandlesClassDef.py b/lib/core/Handles/HandlesClassDef.py index 417300a..a1eea78 100644 --- a/lib/core/Handles/HandlesClassDef.py +++ b/lib/core/Handles/HandlesClassDef.py @@ -8,7 +8,8 @@ import copy 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 IsTModule, FindStructNameInAnnotation +from lib.core.SymbolUtils import ( + IsTModule, FindStructNameInAnnotationWithContext, MangleTypeArgByRegistry) from lib.core.VLogger import get_logger as _vlog from lib.constants.config import mode as _config_mode from lib.includes import t @@ -66,19 +67,52 @@ class ClassHandle(BaseHandle): return False def _mangle_generic_class_name(self, class_name: str, type_args: list[str]) -> str: - mangled_args: list[str] = [] - for ta in type_args: - llvm_str: str | None = CTypeRegistry.NameToLLVM(ta) - if llvm_str: - if llvm_str in ('float', 'double', 'half', 'fp128'): - mangled: str = 'f' + str(CTypeRegistry.GetClassByName(ta)().Size) - else: - mangled = llvm_str - else: - mangled = ta - mangled_args.append(mangled) + """泛型类特化名:class_name + '[arg1][arg2]'。 + + 单项 mangling 走 SymbolUtils.MangleTypeArgByRegistry(声明端 + DeclarationGenerator 的泛型分支以 ResolveGenericTypeArgStr 调用同一函数, + 两侧拼名规则唯一),'][' 分隔与 HandlesTypeMerge / DeclarationGenerator 一致。 + """ + mangled_args: list[str] = [MangleTypeArgByRegistry(ta) for ta in type_args] return class_name + '[' + ']['.join(mangled_args) + ']' + def _HasClassDefinition(self, Name: str, Gen: LlvmGeneratorMixin) -> bool: + """类型名是否有类定义——泛型"类类型参数"的唯一判据。 + + 数据来源就是"是否存在类定义"这一事实本身:Gen._struct_sha1_map + (类名 → 定义模块 SHA1,Phase2 从各 .vpi 扫描构建)与 Gen.structs + (本模块已发射的 struct 名,键形如 '{sha1}.{名}')。 + 因此内建值类型(int/CInt/…,LLVM 映射 i32 等)与语言内建 `str` + (LLVM 映射 i8*)都不在两张表里 → 自然判为值类型,不需要任何 + 内建名白名单/黑名单参与判定。 + """ + if not Name: + return False + if Name in Gen._struct_sha1_map or Name in Gen.structs: + return True + for Sha1 in Gen._struct_sha1_map.values(): + if f'{Sha1}.{Name}' in Gen.structs: + return True + return False + + @staticmethod + def _BareClassAnnotationName(Node: ast.AST) -> str | None: + """取注解节点的裸类名(供 class_type_names 判定),非类名形态返回 None。 + + T 替换后的两种形态都要认: + - ast.Name:'AST'(未注册类)或 'sha1.AST'(旧式点号名)→ 去前缀; + - ast.Attribute:'sha1.AST' 经 _make_replacement_node/_make_name_node + 产出的 Attribute 链(Name('sha1').AST)→ attr 名。 + 其余(Union `X | t.CPtr`、Subscript `list[T]`、内建)→ None,不追加 + 指针层(已显式写了 `| t.CPtr` 的注解不再重复加层)。 + """ + if isinstance(Node, ast.Name): + Name: str = Node.id + return Name.split('.')[-1] if '.' in Name else Name + if isinstance(Node, ast.Attribute): + return Node.attr + return None + def _specialize_generic_class(self, ClassName: str, type_args: list[str], Gen: LlvmGeneratorMixin, type_names: list[str] | None = None, declare_only: bool = False) -> str | None: if not hasattr(self, '_generic_class_templates') or ClassName not in self._generic_class_templates: return None @@ -125,6 +159,17 @@ class ClassHandle(BaseHandle): 'int', 'double', 'float', 'i8', 'i16', 'i32', 'i64', 'void', 'half', 'fp128', 'char', 'bool', } + # 值类型/类类型不再用"内建名白名单"判定:见下方 class_type_names, + # 唯一判据是"是否有类定义"(_HasClassDefinition:Gen._struct_sha1_map + # / Gen.structs)。内建(int/CInt/…)与语言内建 `str`(LLVM 映射 i8*) + # 都不在类表里 → 天然判为值类型,无需名表。 + # 历史教训(名表判据的代价,保留以便回归时理解):`str` 曾漏在 + # "非类类型名"集合外 → `list[str]` 的类型参数被误判为类类型,特化后 + # 方法签名被额外包一层 `| t.CPtr`(append/set 形参 i8**、get/pop 返回 + # i8**)→ 调用点多解一层 load(读字符串首 8 字节)→ ASan 崩溃。 + # 注意 _BUILTIN_TYPE_NAMES 本身仍保留:它用于 type_map 替换 + # 与 _t_c_imported_names 注册,不能把 'str' 并进去 + # (否则 HandlesTypeMerge 会把注解 `str` 当 `t.str` 解析)。 type_map: dict[str, str] = {} for i, tp_name in enumerate(type_param_names): if type_names and i < len(type_names): @@ -147,15 +192,15 @@ class ClassHandle(BaseHandle): type_map[tp_name] = tn else: type_map[tp_name] = type_args[i] - # 检测哪些类型参数在原始类型实参中带有 | t.CPtr - # 当 type_arg 以 'ptr' 结尾(_resolve_type_arg_str 将 t.CPtr 规范化为 'ptr') - # 且对应的 type_name(去模块前缀后)不是内建类型名时, - # 说明调用方写了 list[AST | t.CPtr]。注意用 tn_bare 判断: + # 检测哪些类型参数在原始类型实参中带有指针语义(| t.CPtr / rawptr[T]) + # 当 type_arg 以 '*' 结尾(mangling 把每个 CPtr 层渲染为一层 '*')且对应的 + # type_name(去模块前缀后)不是内建类型名时,说明调用方写了 + # list[AST | t.CPtr] / list[rawptr[AST]]。注意用 tn_bare 判断: # 't.CPtr' 去前缀后是 'CPtr'(内建),不算类类型参数。 cptr_params: set[str] = set() for i, tp_name in enumerate(type_param_names): type_arg: str = type_args[i] - if type_arg.endswith('ptr'): + if type_arg.endswith('*'): tn: str = type_names[i] if type_names and i < len(type_names) else type_args[i] tn_bare_c: str = tn.split('.')[-1] if '.' in tn else tn if tn_bare_c not in _BUILTIN_TYPE_NAMES: @@ -163,12 +208,15 @@ class ClassHandle(BaseHandle): # Bug 2 修复:识别特化为 class 类型的类型参数 # 这类参数在方法签名中是裸 T(无 | t.CPtr),特化后为裸 ClassName(值类型) # 需要加 | t.CPtr 使其成为指针类型(class 实例应按指针传递) + # 判据 = "是否有类定义"(_HasClassDefinition),存**裸名**:注解侧的 + # 比较也用裸名(T 替换后的 'sha1.Param' 去前缀)——此前存带模块前缀的 + # tn(如 'ast.AST')而比较用裸名 'AST',前缀形态永不命中。 class_type_names: set[str] = set() for i, tp_name in enumerate(type_param_names): tn: str = type_names[i] if type_names and i < len(type_names) else type_args[i] tn_bare_cls: str = tn.split('.')[-1] if '.' in tn else tn - if tn_bare_cls not in _BUILTIN_TYPE_NAMES: - class_type_names.add(tn) + if self._HasClassDefinition(tn_bare_cls, Gen): + class_type_names.add(tn_bare_cls) # 仅对 t 模块内建类型名注册 _t_c_imported_names(如 CInt -> ('t','CInt')) # 类名(如 AST)不应注册为 t.AST,否则会污染名称解析 if type_names: @@ -189,30 +237,27 @@ class ClassHandle(BaseHandle): if arg.annotation: arg.annotation = self.Trans.FunctionHandler._replace_type_in_annotation(arg.annotation, type_map, cptr_params) # class 类型参数加指针语义:T -> GNode -> GNode | t.CPtr - # 去除 SHA1 前缀后检查(如 fc06023a9bcfc93a.AST -> AST) - if isinstance(arg.annotation, ast.Name): - raw_name: str = arg.annotation.id - if '.' in raw_name: - raw_name = raw_name.split('.')[-1] - if raw_name in class_type_names: - arg.annotation = ast.BinOp( - left=ast.Name(id=arg.annotation.id, ctx=ast.Load()), - op=ast.BitOr(), - right=ast.Attribute(value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) - ) - if item.returns: - item.returns = self.Trans.FunctionHandler._replace_type_in_annotation(item.returns, type_map, cptr_params) - # 返回类型也加指针语义 - if isinstance(item.returns, ast.Name): - raw_name: str = item.returns.id - if '.' in raw_name: - raw_name = raw_name.split('.')[-1] - if raw_name in class_type_names: - item.returns = ast.BinOp( - left=ast.Name(id=item.returns.id, ctx=ast.Load()), + # T 替换后的真实形态是 Attribute 链('.GNode' → + # Name('').GNode,见 _make_replacement_node/_make_name_node), + # 不是 ast.Name:此前只认 ast.Name → 方法形参漏加 | t.CPtr, + # 特化后按值结构体传参(被调者栈帧副本地址入链 → 悬垂写)。 + raw_name: str | None = self._BareClassAnnotationName(arg.annotation) + if raw_name is not None and raw_name in class_type_names: + arg.annotation = ast.BinOp( + left=arg.annotation, op=ast.BitOr(), right=ast.Attribute(value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) ) + if item.returns: + item.returns = self.Trans.FunctionHandler._replace_type_in_annotation(item.returns, type_map, cptr_params) + # 返回类型也加指针语义(同一裸名判据,形态同上) + raw_name: str | None = self._BareClassAnnotationName(item.returns) + if raw_name is not None and raw_name in class_type_names: + item.returns = ast.BinOp( + left=item.returns, + op=ast.BitOr(), + right=ast.Attribute(value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) + ) self.Trans.FunctionHandler._apply_type_map_to_body(item.body, type_map, cptr_params) saved_builder: ir.IRBuilder = Gen.builder saved_func: ir.Function = Gen.func @@ -228,7 +273,14 @@ class ClassHandle(BaseHandle): # 切换 module_sha1 为泛型模板定义模块的 SHA1, # 使结构体类型名和方法定义名都用定义模块的 SHA1 前缀, # 与调用端 _struct_sha1_map 查到的 SHA1 一致。 - template_sha1: str | None = template.get('sha1') + template_sha1: str | None = template.get('sha1') + if not template_sha1: + # 模板注册表缺 sha1 时回退到"模板类定义模块"(_struct_sha1_map 即 + # 类名 → 定义模块表,Phase2 从各 .vpi 扫描构建):与声明端 + # DeclarationGenerator._get_type_str 的泛型前缀、调用点 + # Gen._struct_sha1_map[spec_name] 同一条规则源;否则 declare/define + # 两侧前缀分裂(使用方模块 vs 模板模块)→ 跨模块 undefined symbol。 + template_sha1 = Gen._struct_sha1_map.get(ClassName) saved_gen_sha1: str | None = getattr(Gen, 'module_sha1', None) saved_trans_sha1: str | None = getattr(self.Trans, '_module_sha1', None) if template_sha1: @@ -507,6 +559,40 @@ class ClassHandle(BaseHandle): ancestor = Gen.class_parent.get(ancestor) return IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable + def _RegisterGenericClassTemplate(self, Node: ast.ClassDef) -> None: + """注册泛型类模板到 `_generic_class_templates`(判据唯一:`_is_generic_class`)。 + + 调用点两处:类处理阶段(_EmitClassLlvm)与前置声明之前的本模块预扫描 + (PrescanModuleGenericClassTemplates)。注册内容唯一(node/type_params/ + 定义模块 SHA1),两个调用点不得各写一份。 + """ + if not hasattr(self, '_generic_class_templates'): + self._generic_class_templates = {} + self._generic_class_templates[Node.name] = { + 'node': Node, + 'type_params': [tp.name for tp in Node.type_params], + # 记录泛型模板定义模块的 SHA1,用于特化时统一命名空间 + 'sha1': getattr(self.Trans, '_module_sha1', None), + } + + def PrescanModuleGenericClassTemplates(self, ModuleBody: object) -> None: + """前置声明之前预注册**本模块**泛型类模板。 + + 判据与 includes 侧对泛型类的注册(HandlesImports._LoadModuleDeclarationsFromFile/ + _EmitImportFromDeclarations 的 `node.type_params` 非空)**同源**,即 + _is_generic_class,不看类名白名单。 + + 类处理阶段(下方 _EmitClassLlvm)才注册本模块模板,而顶层函数前置声明 + (GenerateLlvmDirect 的 _EmitFunctionForwardDeclLlvm)已需要它:模块级函数 + 参数/返回注解写自身泛型类(`def f(b: Box[Node])`)时 + _SubscriptHitsGenericTemplate 判为不命中 → TypeAnnotationResolver 解析为空 → + VoidType → 抛"无法识别的参数类型注解";而同模块方法参数(类处理阶段之后) + 却可用,二者分裂。预注册后两处走同一 _generic_class_templates。 + """ + for Node in ModuleBody: + if isinstance(Node, ast.ClassDef) and self._is_generic_class(Node): + self._RegisterGenericClassTemplate(Node) + def _EmitClassLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin, declare_only: bool = False) -> None: ClassName: str = Node.name # t.CType 及其子类(CChar/CInt/CVoid/CPtr/CTypeDefault 等)是"类型强转"标记, @@ -515,16 +601,7 @@ class ClassHandle(BaseHandle): if self._is_classname_ctype_marker(ClassName): return if self._is_generic_class(Node): - if not hasattr(self, '_generic_class_templates'): - self._generic_class_templates = {} - type_params: list[str] = [tp.name for tp in Node.type_params] - # 记录泛型模板定义模块的 SHA1,用于特化时统一命名空间 - template_sha1: str | None = getattr(self.Trans, '_module_sha1', None) - self._generic_class_templates[ClassName] = { - 'node': Node, - 'type_params': type_params, - 'sha1': template_sha1, - } + self._RegisterGenericClassTemplate(Node) return if self._is_exception_class(Node): self._RegisterExceptionClass(Node) @@ -797,10 +874,11 @@ class ClassHandle(BaseHandle): 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 = FindStructNameInAnnotation(item.annotation, Gen.structs) + element_class: str | None = FindStructNameInAnnotationWithContext( + item.annotation, self.Trans, Gen) # Fallback: FindStructNameInAnnotation 不处理 Subscript slice 为 BinOp 的情况 # (如 list[AST | t.CPtr] | t.CPtr),使用 TypeInfo.Name 作为回退。 - # TypeInfo.Name 由 _HandleSubscript 设置为特化名(如 'list[sha1.ASTptr]'), + # 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 @@ -816,7 +894,8 @@ class ClassHandle(BaseHandle): and isinstance(MemberType.pointee, ir.IdentifiedStructType) and isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr)): - spec_in_anno: str | None = FindStructNameInAnnotation(item.annotation, Gen.structs) + 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] = {} diff --git a/lib/core/Handles/HandlesExprAttr.py b/lib/core/Handles/HandlesExprAttr.py index dd1fa36..98e3be0 100644 --- a/lib/core/Handles/HandlesExprAttr.py +++ b/lib/core/Handles/HandlesExprAttr.py @@ -547,8 +547,15 @@ class ExprAttrHandle(BaseHandle): if self._is_char_pointer(ObjVal): if ClassName in Gen.structs: ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") - # 二级指针解引用 - if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + # 二级指针解引用。模块级全局指针变量(如 _g_gen: rawptr[Generator.Generator]) + # 的全局元素类型比实际存值多一层:写入端(main.vp `GenApi._g_gen = gen`)生成 + # 为 bitcast T*→T** 后直接存值,未取地址;读取端必须做其逆运算 bitcast,绝不能 + # 再 load——否则把 T* 当 T** 再解引用,接收者被取成对象首字段的位,方法内读 + # self->Gen 即野指针(ASan: CallGenBase.SetTempDir 读悬空地址崩溃)。 + if (isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType) + and isinstance(Node.value, ast.Name) and Node.value.id in Gen.module.globals): + ObjVal = Gen.builder.bitcast(ObjVal, ObjVal.type.pointee, name=f"cast_{VarName}_global") + elif isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") # 检查结构体匹配 diff --git a/lib/core/Handles/HandlesExprCall.py b/lib/core/Handles/HandlesExprCall.py index f9ce022..afa901b 100644 --- a/lib/core/Handles/HandlesExprCall.py +++ b/lib/core/Handles/HandlesExprCall.py @@ -12,7 +12,9 @@ from lib.core.VLogger import get_logger as _vlog from lib.constants.config import mode as _config_mode from lib.includes import t from lib.includes.t import CTypeRegistry -from lib.core.SymbolUtils import IsTModule, ExtractTypeNameFromBinOp +from lib.core.SymbolUtils import ( + IsTModule, ExtractTypeNameFromBinOp, IsPtrSubscriptNode, DesugarPtrSubscriptToUnion, + ResolveTypeArgStr, GenericArgRawName) def _safe_ptr_type(ty: ir.Type) -> ir.Type: @@ -3684,46 +3686,18 @@ class ExprCallHandle(BaseHandle): def _resolve_type_arg_str(self, node: ast.AST) -> str: """将类型参数 AST 节点解析为规范化的类型字符串(用于泛型特化的 mangling)。 - 走和普通类型签名同样的规范化路径(不单独创建规则): - - ast.Name: 若为已注册结构体,加 SHA1 前缀(如 'AST' → '9ada227b160c07a1.AST'), - 与 DeclarationGenerator._get_type_str 对 ast.Name 的处理一致 - - ast.Attribute: t.CPtr → 'ptr'(指针标记,等价于普通签名中 t.CPtr → i8* 的指针语义; - 用 'ptr' 而非 '*' 是因为 _get_or_create_struct 的 clean_name 会移除 '*', - 导致 Phase2 直接编译与 .stub.ll 加载路径的 mangled 名不一致); - 其他 t 模块类型按 CTypeRegistry.NameToLLVM 解析 - - ast.BinOp: 递归处理左右操作数并直接拼接(如 AST | t.CPtr → 'sha1.ASTptr'), - 避免空格/管道符导致 _get_or_create_struct 的 clean_name 清洗不一致 + 规则本体在 SymbolUtils.ResolveTypeArgStr(定义端/声明端唯一规则源, + 声明端 DeclarationGenerator 的泛型分支亦调用同一函数),此处仅注入 + 翻译器上下文: + - t/c 导入别名表(from t import CInt as myint → 't.CInt') + - 跨模块结构体 SHA1 表(ast.Name/Attribute 命中 → '{sha1}.{名}',与普通签名一致) """ - if isinstance(node, ast.Name): - tn: str = node.id - t_c_imported = getattr(self.Trans, '_t_c_imported_names', {}) - if tn in t_c_imported: - src_module, src_name = t_c_imported[tn] - return f'{src_module}.{src_name}' - # 若为已注册结构体,加 SHA1 前缀(和普通签名路径一致) - Gen = self.Trans.LlvmGen - struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) if Gen is not None else {} - if tn in struct_sha1_map: - return f'{struct_sha1_map[tn]}.{tn}' - return tn - if isinstance(node, ast.Attribute): - if isinstance(node.value, ast.Name): - full_name: str = f'{node.value.id}.{node.attr}' - # t.CPtr → 'ptr'(指针标记,和普通签名中 t.CPtr 被解析为指针类型 i8* 语义一致) - if full_name == 't.CPtr': - return 'ptr' - # 其他 t 模块类型按 CTypeRegistry 解析(和普通签名路径一致) - llvm_str: str | None = CTypeRegistry.NameToLLVM(node.attr) - if llvm_str is not None: - return llvm_str - return full_name - return ast.unparse(node) - if isinstance(node, ast.BinOp): - left_str: str = self._resolve_type_arg_str(node.left) - right_str: str = self._resolve_type_arg_str(node.right) - # 直接拼接(如 'sha1.AST' + 'ptr' = 'sha1.ASTptr'),避免空格导致清洗不一致 - return f'{left_str}{right_str}' - return ast.unparse(node) + Gen = self.Trans.LlvmGen + StructSha1Map: dict[str, str] = getattr(Gen, '_struct_sha1_map', {}) if Gen is not None else {} + return ResolveTypeArgStr( + node, + TCAliasMap=getattr(self.Trans, '_t_c_imported_names', {}), + StructSha1Map=StructSha1Map) def _get_raw_type_name(self, node: ast.AST) -> str: """获取类型节点的原始名称(不带模块前缀),用于泛型 T 替换。 @@ -3743,30 +3717,24 @@ class ExprCallHandle(BaseHandle): def _resolve_generic_slice_type(self, sn: ast.AST) -> tuple[str, str] | None: """解析泛型 slice 中的单个类型参数节点,返回 (type_arg, type_name) 或 None。 - type_arg: 用于 mangling,走和普通签名同样的规范化路径 - (如 'AST | t.CPtr' → '9ada227b160c07a1.ASTptr',AST 加 SHA1 前缀,t.CPtr 处理为 ptr) - type_name: 用于替换泛型类型参数 T(如 'AST' 或 'CInt',_specialize_generic_class 会自动加 | t.CPtr 给类名) + type_arg: 用于 mangling,走**唯一规则源** _resolve_type_arg_str + (内层与参数注解同解析:'AST | t.CPtr' / 'rawptr[AST]' → + '{sha1}.AST*'、裸类名 'AST' → '{sha1}.AST'、标量按 + NormalizeGenericArgName 归一) + type_name: 用于替换泛型类型参数 T(原始源码名,如 'AST' / 'CInt', + _specialize_generic_class 会自动给类名加 | t.CPtr) + 指针下标 rawptr[T] 先折叠成等价的 `T | t.CPtr` 再解析, + 与源码联合写法得到同一 (type_arg, type_name):否则 list[rawptr[ast.AST]] + 会拼出 'rawptr[ast.AST]' 实参串(内层未混淆)→ 与 list[ast.AST | t.CPtr] + 的 '.AST*' 分裂成两个特化名(结构体/方法声明/调用点三端不一致)。 """ + if IsPtrSubscriptNode(sn, self.Trans.SymbolTable): + sn = DesugarPtrSubscriptToUnion(sn, self.Trans.SymbolTable) if isinstance(sn, ast.Name): - tn: str = sn.id - ts: str = 'int' - if tn == 'int': - ts, tn = 'int', 'CInt' - elif tn == 'double': - ts, tn = 'double', 'CDouble' - elif tn == 'float': - ts, tn = 'float', 'CFloat' - elif tn == 'char': - ts, tn = 'i8', 'CChar' - elif tn == 'bool': - ts, tn = 'i8', 'CBool' - elif tn in ('CInt', 'CUInt', 'CInt32T', 'CUInt32T', 'CInt64T', 'CUInt64T', 'CInt16T', 'CUInt16T', 'CInt8T', 'CUInt8T', 'CChar', 'CShort', 'CLong'): - ts = 'int' - elif tn in ('CDouble', 'CFloat', 'CFloat64T', 'CFloat32T'): - ts = 'double' - else: - ts = tn - return (ts, tn) + # type_arg 走唯一规则源(命中注册类加 SHA1 前缀,裸名与 + # `list[X]`/`rawptr[X]` 内层收敛到同一名字); + # type_name 保留源码名供 T 替换与内建/类类型判定(两张表收拢在 SymbolUtils) + return (self._resolve_type_arg_str(sn), GenericArgRawName(sn.id)) # BinOp (如 AST | CPtr) 或 Attribute (如 t.CPtr / t.CInt) type_str: str = self._resolve_type_arg_str(sn) # type_name 保留模块前缀(如 't.CUnsignedChar'),避免 T(0) 替换为 diff --git a/lib/core/Handles/HandlesFunctions.py b/lib/core/Handles/HandlesFunctions.py index 98e8701..f7354f5 100644 --- a/lib/core/Handles/HandlesFunctions.py +++ b/lib/core/Handles/HandlesFunctions.py @@ -8,7 +8,9 @@ import copy import traceback import llvmlite.ir as ir from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta -from lib.core.SymbolUtils import IsTModule, FindStructNameInAnnotation, AnnotationContainsName +from lib.core.SymbolUtils import ( + IsTModule, FindStructNameInAnnotationWithContext, AnnotationContainsName, + IsPtrSubscriptNode, DesugarPtrSubscriptToUnion) from lib.core.VLogger import get_logger as _vlog from lib.constants.config import mode as _config_mode from lib.includes import t @@ -520,7 +522,8 @@ class FunctionHandle(BaseHandle): if not isinstance(ReturnTypeInfo, CTypeInfo): ReturnTypeInfo = CTypeInfo() ReturnTypeInfo.BaseType = t.CVoid() - if (ReturnTypeInfo.IsVoid and ReturnTypeInfo.PtrCount == 0 and not ReturnTypeInfo.IsState) or ReturnTypeInfo.PtrCount >= 2: + if ((ReturnTypeInfo.IsVoid and ReturnTypeInfo.PtrCount == 0 and not ReturnTypeInfo.IsState) + or (ReturnTypeInfo.PtrCount >= 2 and ReturnTypeInfo.BaseType is None)): def _FindNonVoidCTypeInfo(node: ast.AST) -> Any: if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): results: list[Any] = [] @@ -557,7 +560,10 @@ class FunctionHandle(BaseHandle): is_str_type: bool = False if isinstance(Node.returns, ast.Name) and Node.returns.id in ('str', 'bytes'): is_str_type = True - ReturnTypeInfo = CTypeInfo.FromNode(Node.returns, self.Trans.SymbolTable) + # 走 ResolveAnnotationType 而非 CTypeInfo.FromNode:指针下标 + # 返回注解(rawptr[T] / t.CPtr[T])需走 TypeMerge 通用解析, + # 否则内层跨模块类解析不出 → CVoid/1(i8*)。 + ReturnTypeInfo = self.ResolveAnnotationType(Node.returns) if ReturnTypeInfo and ReturnTypeInfo.IsDefine: inferred: Any = self._infer_return_type_from_body(Node, Gen, ClassName) if inferred: @@ -637,9 +643,15 @@ class FunctionHandle(BaseHandle): ReturnTypeInfo.PtrCount = 1 if inferred[1] else 0 IsPtr = ReturnTypeInfo.IsPtr if ReturnTypeInfo else False ReturnType: ir.Type = Gen._ctype_to_llvm(ReturnTypeInfo) - if isinstance(ReturnType, ir.PointerType) and isinstance(ReturnType.pointee, ir.PointerType): - if isinstance(ReturnType.pointee.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): - ReturnType = ir.PointerType(ReturnType.pointee.pointee) + # 此处原有 "Ptr(Ptr(Struct)) → Ptr(Struct)" 的压层(历史遗留):会把已解析的 + # 多层指针返回类型(`-> rawptr[rawptr[X]]` / `-> X | t.CPtr | t.CPtr`) + # 与泛型特化方法返回(`list[X**]` 的 get/pop/__getitem__/__next__)少算一层, + # 与同函数的形参(`append(item: T)` 保留 `X**`)、声明端 + # DeclarationGenerator._get_type_str 的 BinOp 分支(同样不压层,见 + # '_is_bare_class_ref_annotation' 为假时逐层外包)以及 _EmitFunctionLlvm + # 的定义体路径全部不一致。违反"多层指针已解析时照原样保留层数"。 + # 实测(36 用例 A/B)删除后仅 `list[X**]` 的 4 个方法 declare 由 `X*` 变 + # `X**`,与 append/set 的形参一致;stub 逐字节不变。 if isinstance(ReturnType, ir.IntType) and ReturnType.width == 8: if RawFuncName == '__str__': ReturnType = ir.PointerType(ir.IntType(8)) @@ -681,7 +693,12 @@ class FunctionHandle(BaseHandle): if Arg.annotation: try: ParamTypeInfo: Any - ParamTypeInfo = self.ResolveAnnotationType(Arg.annotation) + # 走 ResolveParamAnnotation(= ResolveAnnotationType + 泛型模板 + # 识别):参数注解中的泛型容器(list[X] / rawptr[list[X]])与 + # 指针下标(rawptr[X])需走 TypeMerge 通用解析,否则 + # TypeAnnotationResolver 解析不出 → VoidType → 报 + # "无法识别的参数类型注解"。 + ParamTypeInfo = self.ResolveParamAnnotation(Arg.annotation) if ParamTypeInfo is None: ParamTypeInfo = CTypeInfo() ParamTypeInfo.BaseType = t.CInt() @@ -820,20 +837,113 @@ class FunctionHandle(BaseHandle): def _make_replacement_node(self, replacement: str) -> ast.AST: """将 type_map 中的替换字符串转换为 AST 节点。 - mangled 名称如 'sha1.ASTptr' 需要还原为 AST | t.CPtr 结构, + mangled 名称如 '.AST*' 需要还原为 AST | t.CPtr 结构, 否则类型解析会丢失 PtrCount 信息。 - 含点号的名称(如 'sha1.AST')需解析为 ast.Attribute 链。 + 含点号的名称(如 '.AST')需解析为 ast.Attribute 链。 + 下标形态('rawptr[ast.AST]' / 'list[AST]' 等)必须整体解析为 Subscript: + _make_name_node 只按 '.' 切分会把 'rawptr[ast.AST]' 拆成 + Name('rawptr[ast') + Attribute('AST]'),两段都不是有效类型名 → + 参数/返回注解解析为空 → VoidType → "无法识别的参数类型注解"。 + 指针下标(rawptr[T] / t.CPtr[T])再折叠为等价的 `T | t.CPtr`,与源码 + 联合写法同解析路径(对齐 TPV TypeGenResolve._parse_type_string 的 '[' + 分支 + ClassGenGeneric._clone_annotation 的 T 替换)。 + 内层为已混淆名(数字开头,如 'rawptr[7565e081d88517ac.AST]')时 + ast.parse 无法解析(数字开头的令牌),改用手工拆 base/slice(同 TPV + TypeGenResolve._parse_type_string 的数字开头手工拆分分支)。 + 尾部 '*'(泛型实参 '.Node*' / '.Node**',来自 rawptr[T] / + t.CPtr 的逐层 mangling)逐层还原为 `Node | t.CPtr [| t.CPtr]` → 对应 + 层数指针;只剥一层会把 'Node*' 当结构体名建出 phantom opaque + (item 类型与特化名 '.Node**' 的语义不一致)。 + 尾部 'ptr'(旧规则 mangling,如 'Nodeptr' / 'ast.ASTptr')保留兼容: + .vpi/stub 缓存里仍是旧名时按同样方式还原。 """ + StarCount: int = len(replacement) - len(replacement.rstrip('*')) + if StarCount > 0: + BaseName: str = replacement[:len(replacement) - StarCount] + base_node: ast.AST + if BaseName in ('i8', 'void', 'void*'): + # i8*(void*)在源码侧写作 t.CPtr;再叠加 StarCount-1 层 + base_node = ast.Attribute( + value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) + StarCount -= 1 + else: + base_node = self._make_name_node(BaseName) + node: ast.AST = base_node + for _ in range(StarCount): + node = ast.BinOp( + left=node, + op=ast.BitOr(), + right=ast.Attribute( + value=ast.Name(id='t', ctx=ast.Load()), + attr='CPtr', ctx=ast.Load()), + ) + return node if replacement.endswith('ptr'): - # 提取基础名称: 'sha1.ASTptr' -> 'sha1.AST' - base: str = replacement[:-3] - return ast.BinOp( - left=self._make_name_node(base), - op=ast.BitOr(), - right=ast.Attribute(value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) - ) + # 提取基础名称:'sha1.ASTptr' → 'sha1.AST';'Nodeptrptr' → 'Node'(2 层) + base: str = replacement + levels: int = 0 + while base.endswith('ptr') and len(base) > 3: + base = base[:-3] + levels += 1 + node: ast.AST = self._make_name_node(base) + for _ in range(levels): + node = ast.BinOp( + left=node, + op=ast.BitOr(), + right=ast.Attribute( + value=ast.Name(id='t', ctx=ast.Load()), + attr='CPtr', ctx=ast.Load()), + ) + return node + if '[' in replacement: + SubNode: ast.AST | None = None + try: + SubNode = ast.parse(replacement, mode='eval').body + except Exception: + SubNode = None + if not isinstance(SubNode, ast.Subscript): + SubNode = self._parse_subscript_manually(replacement) + if isinstance(SubNode, ast.Subscript): + if IsPtrSubscriptNode(SubNode, self.Trans.SymbolTable): + return DesugarPtrSubscriptToUnion(SubNode, self.Trans.SymbolTable) + return SubNode return self._make_name_node(replacement) + def _parse_subscript_manually(self, replacement: str) -> ast.AST | None: + """手工拆分单实参下标字符串(ast.parse 因数字开头标识符失败时使用)。 + + 处理 `[]`(整体以 ']' 结尾、单实参、无逗号;内层可为嵌套 + 单实参下标):'rawptr[7565e081d88517ac.AST]' → Subscript(Name('rawptr'), + Attribute(Name('7565e081d88517ac'), 'AST')); + 'rawptr[rawptr[7565e081d88517ac.AST]]' → 外层 Subscript 的 slice 递归 + 拆出内层 Subscript(嵌套 rawptr 需两层指针)。带逗号等复杂形态返回 + None(交调用方按普通名称处理),不做字符串猜测。 + """ + open_idx: int = replacement.find('[') + if open_idx <= 0 or not replacement.endswith(']'): + return None + inner: str = replacement[open_idx + 1:-1] + if not inner or ',' in inner: + return None + base_name: str = replacement[:open_idx].strip() + inner_name: str = inner.strip() + if not base_name or not inner_name: + return None + slice_node: ast.AST | None + if '[' in inner_name or inner_name.endswith('*'): + # 嵌套单实参下标 / 指针层数内层(如 rawptr[rawptr[.CLS]]、 + # .list[.X*]):递归按同一还原规则拆解 + slice_node = self._make_replacement_node(inner_name) + else: + try: + slice_node = ast.parse(inner_name, mode='eval').body + except Exception: + slice_node = self._make_name_node(inner_name) + if slice_node is None: + return None + return ast.Subscript( + value=self._make_name_node(base_name), slice=slice_node, ctx=ast.Load()) + @staticmethod def _make_name_node(name: str) -> ast.AST: """将可能含点号的名称转换为 ast.Name 或 ast.Attribute 链。 @@ -1323,7 +1433,8 @@ class FunctionHandle(BaseHandle): if not isinstance(ReturnTypeInfo, CTypeInfo): ReturnTypeInfo = CTypeInfo() ReturnTypeInfo.BaseType = t.CVoid() - if (ReturnTypeInfo.IsVoid and ReturnTypeInfo.PtrCount == 0 and not ReturnTypeInfo.IsState) or ReturnTypeInfo.PtrCount >= 2: + if ((ReturnTypeInfo.IsVoid and ReturnTypeInfo.PtrCount == 0 and not ReturnTypeInfo.IsState) + or (ReturnTypeInfo.PtrCount >= 2 and ReturnTypeInfo.BaseType is None)): def _FindNonVoidCTypeInfo(node): if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): results = [] @@ -1359,7 +1470,10 @@ class FunctionHandle(BaseHandle): is_str_type = False if isinstance(Node.returns, ast.Name) and Node.returns.id in ('str', 'bytes'): is_str_type = True - ReturnTypeInfo = CTypeInfo.FromNode(Node.returns, self.Trans.SymbolTable) + # 与 _GetFunctionSignatureLlvm 同口径:指针下标返回注解 + # (rawptr[T] / t.CPtr[T])走 ResolveAnnotationType 的 + # TypeMerge 通用解析,避免内层跨模块类退化为 i8*。 + ReturnTypeInfo = self.ResolveAnnotationType(Node.returns) if ReturnTypeInfo and ReturnTypeInfo.IsDefine: inferred = self._infer_return_type_from_body(Node, Gen, ClassName) if inferred: @@ -1807,7 +1921,8 @@ class FunctionHandle(BaseHandle): if not (_existing_ps == 'rawptr' or '.rawptr' in _existing_ps or _existing_ps.startswith('rawptr[')): continue - found_struct: str | None = FindStructNameInAnnotation(arg.annotation, Gen.structs) + found_struct: str | None = FindStructNameInAnnotationWithContext( + arg.annotation, self.Trans, Gen) if found_struct: Gen.var_struct_class[param_name] = found_struct # 普通类注解形参(非 BinOp 联合):rawptr[X] / t.CPtr[X] / X / t.X diff --git a/lib/core/Handles/HandlesImports.py b/lib/core/Handles/HandlesImports.py index f305f21..aa3dd2c 100644 --- a/lib/core/Handles/HandlesImports.py +++ b/lib/core/Handles/HandlesImports.py @@ -14,7 +14,7 @@ from lib.core.VLogger import get_logger as _vlog from lib.constants.config import mode as _config_mode from lib.includes import t from lib.includes.t import CTypeRegistry -from lib.core.SymbolUtils import ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName, FindStructNameInAnnotation +from lib.core.SymbolUtils import ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName, FindStructNameInAnnotationWithContext # 注解里的存储类 / 限定符标记:这些名字**不是类名**(联合注解里与指针类混写, @@ -1855,6 +1855,19 @@ class ImportHandle(BaseHandle): Gen.class_requires[ClassName] = values elif field_name == '__require_must__': Gen.class_require_must[ClassName] = values + # 字段收集中保护(与 _EmitClassLlvm 的 _collecting_members_set 同源): + # 本节的成员解析会重入结构体发射 —— CollectAnnAssignMember → + # ResolveAnnotationType → HandlesTypeMerge._HandleSubscript → CollectAll → + # _ResolveGenericSlice → _specialize_generic_class → _EmitClassLlvm → + # _generate_structs(),而此刻 class_members[ClassName] 只含"已扫到的 + # 自有字段"(继承字段展平尚未执行,见下方 _all_inherited 段), + # 且 llvmlite 的 set_body 只允许调用一次 → 残缺布局被永久固化: + # BasicBlock.Lines: rawptr[GSList[Line]] → body 只到 Name 即止 + # Function.Params: rawptr[GSList[Param]] → body 只到 RetTy 即止 + # Line/Param 等则整类丢失继承字段 Next(GEP 索引整体前移一位)。 + # 置入保护后重入的 _generate_structs 会跳过本类(StructGen.py:461), + # 待本节收集+展平完成后由下方显式 _generate_structs() 一次性 set_body。 + Gen._collecting_members_set.add(ClassName) _existing_member_names = {m[0] for m in Gen.class_members[ClassName]} for item in Node.body: # 跳过编译期元数据字段(__provides__/__requires__/__require_must__) @@ -1884,7 +1897,8 @@ class ImportHandle(BaseHandle): # 无法识别字段为 list → len() 误编译为 strlen → stdint 崩溃。 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 = FindStructNameInAnnotation(item.annotation, Gen.structs) + element_class: str | None = FindStructNameInAnnotationWithContext( + item.annotation, self.Trans, Gen) if not element_class and TypeInfo.IsStruct and TypeInfo.Name and TypeInfo.Name in Gen.structs: element_class = TypeInfo.Name if element_class: @@ -1895,7 +1909,8 @@ class ImportHandle(BaseHandle): and isinstance(MemberType.pointee, ir.IdentifiedStructType) and isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr)): - spec_in_anno: str | None = FindStructNameInAnnotation(item.annotation, Gen.structs) + 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] = {} @@ -2255,6 +2270,13 @@ class ImportHandle(BaseHandle): _inherited_ph: list[tuple] = [(f'__inherited_{i}', _st.elements[i]) for i in range(_num_inherited)] # 占位符路径同样整体前置(继承字段在 struct 头部),与主路径保持一致 Gen.class_members[ClassName] = _inherited_ph + Gen.class_members[ClassName] + # 成员收集 + 继承展平完成 → 解除收集中保护,并用完整 class_members 设置 body。 + # 必须在此显式触发一次 _generate_structs():保护期内(上方 add)所有重入的 + # _generate_structs 都跳过了本类,若此处不再触发,本类 struct 将保持 opaque + # (opaque 结构体上的 GEP 是非法 IR,llvm-as 会直接报错),而残缺布局正是 + # 本保护要消除的目标。 + Gen._collecting_members_set.discard(ClassName) + Gen._generate_structs() # ============================================================ # 跨模块 vtable 槽布局与定义模块一致:父类方法在前(子类覆盖放父类槽位), # 子类新增方法在后。定义模块(HandlesClassDef)已按此合并,跨模块若只 @@ -3144,7 +3166,13 @@ class ImportHandle(BaseHandle): return ir.ArrayType(elem_type, size) typedef_names = {'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', 'CStruct', 'enum', 'REnum', 'renum'} - struct_match = re.match(r'%"?((?:[a-f0-9]+\.)?[\w.\[\]]+)"?', type_str) + # 类型名内允许 '*':泛型特化名可带指针层实参(如 + # %"<模sha1>.list[.AST*]"*)。此前字符集缺 '*' 会把名字截断在 + # 内层 '*' 处(丢掉尾随 ']'),随后 gc_base/gc_args 重解析出 + # list[.AST](少一层指针)并 declare_only 特化出无方法体的 + # 假类型 → 调用点引用未定义的 <模sha1>.list[.AST].__len__ + # (链接期 undefined symbol 根因)。 + struct_match = re.match(r'%"?((?:[a-f0-9]+\.)?[\w.\[\]*]+)"?', type_str) if struct_match: raw_name = struct_match.group(1) actual_source_sha1 = source_sha1 diff --git a/lib/core/Handles/HandlesTypeMerge.py b/lib/core/Handles/HandlesTypeMerge.py index 21e8965..f26db16 100644 --- a/lib/core/Handles/HandlesTypeMerge.py +++ b/lib/core/Handles/HandlesTypeMerge.py @@ -4,7 +4,9 @@ if TYPE_CHECKING: from lib.core.translator import Translator from typing import List from lib.core.Handles.HandlesBase import BaseHandle, CTypeHelper, CTypeInfo -from lib.core.SymbolUtils import IsTModule, IsListAnnotation +from lib.core.SymbolUtils import ( + IsTModule, IsListAnnotation, IsPtrSubscriptNode, + DesugarPtrSubscriptToUnion) from lib.includes import t import lib._bootstrap # noqa: F401 设置 sys.path(项目根 + lib/includes) import vast as ast @@ -248,6 +250,24 @@ class HandlesTypeMerge(BaseHandle): Result.PtrCount = 1 return Result + # 跨模块类引用(`.CLS` / `ast.CLS`):按类名查 _struct_sha1_map, + # 与 ast.Name 路径(FromTypeName → _get_or_create_struct 的 + # `clean_name in self._struct_sha1_map` 分支,BaseGen.py:421)、声明端 + # DeclarationGenerator._get_type_str(`attr_name in struct_names`, + # DeclarationGenerator.py:1185)以及名称域 SymbolUtils._ResolveAttributeArgStr + # (SymbolUtils.py:491)**同一条"类名 → SHA1"规则**。 + # 此前此处只查 Gen.structs(仅本模块已创建的结构体)→ 本模块未 import + # 的类(如 rawptr[e4c92b87b98424db.AST])落空 → 返回 BaseType=None → + # 裸引用抛"无法识别的参数类型注解"/脱糖合并后 CVoid/1(i8*), + # 与声明端 %".CLS"* 分裂。 + if _Gen is not None and TypeName in getattr(_Gen, '_struct_sha1_map', {}): + Result = CTypeInfo() + Result.BaseType = t.CStruct(name=TypeName) + Result.IsStruct = True + Result.Name = TypeName + Result.PtrCount = 1 + return Result + Entry = self.Trans.SymbolTable.lookup(TypeName) if Entry and Entry.IsTypedef: if isinstance(Entry, CTypeInfo) and Entry.BaseType: @@ -436,6 +456,15 @@ class HandlesTypeMerge(BaseHandle): t.CArrayPtr[t.CArrayPtr] -> void *(*)[] t.CPtr[t.CArrayPtr[t.CArrayPtr]] -> void *(*(*))[] """ + # 指针下标(rawptr[T] 内置裸指针 / t.CPtr[T] 下标指针,含裸名 CPtr[T]) + # 折叠为等价的联合写法 `T | t.CPtr` 后交给 MergeTypes 统一解析:内层 T + # 因此走通用解析路径(含跨模块类经 Gen.structs 反查 SHA1 前缀),指针 + # 层数与源码 `T | t.CPtr` 完全一致(此前 t.CPtr[ast.AST] 在此得 + # AST**(比 declare/`AST | t.CPtr` 多一层)、rawptr[ast.AST] 退化为 + # CVoid/1 → i8*)。 + if IsPtrSubscriptNode(Node, self.Trans.SymbolTable): + return self.MergeTypes(DesugarPtrSubscriptToUnion(Node, self.Trans.SymbolTable)) + def CollectAll(node: ast.AST, ParentIsArrayPtr: bool = False) -> CTypeInfo: """鏀堕泦绫诲瀷淇℃伅锛岃繑鍥?CTypeInfo""" if isinstance(node, ast.Subscript): @@ -789,6 +818,13 @@ class HandlesTypeMerge(BaseHandle): """递归收集所有操作数的类型(用于一次性合并)""" Types = [] + # 指针下标(rawptr[T] / t.CPtr[T])先折叠成等价的 `T | t.CPtr`:使 + # `rawptr[T] | t.CPtr` 与 `T | t.CPtr | t.CPtr` 拉平成同一组操作数 + # (否则折叠结果作为单个 Subscript 操作数参与合并,指针层数少算一层); + # t.CPtr[T] 与 rawptr[T] 同一条规则(见 SymbolUtils.IsPtrSubscriptNode)。 + if IsPtrSubscriptNode(Node, self.Trans.SymbolTable): + Node = DesugarPtrSubscriptToUnion(Node, self.Trans.SymbolTable) + if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): Types.extend(self._CollectAllOperandTypes(Node.left)) Types.extend(self._CollectAllOperandTypes(Node.right)) diff --git a/lib/core/LLVMCG/BaseGen.py b/lib/core/LLVMCG/BaseGen.py index 584ad50..cc614f0 100644 --- a/lib/core/LLVMCG/BaseGen.py +++ b/lib/core/LLVMCG/BaseGen.py @@ -280,8 +280,27 @@ class BaseGenMixin: return prefix return '' + @staticmethod + def _CleanStructName(name: str) -> str: + """结构体名清洗:去掉指针标记/空格/修饰符(clean_name 既有行为)。 + + 但**保留**泛型特化名 '[...]' 段内的原样文本:特化实参里的 '*' 是类型 + 身份的一部分('list[.AST*]' 表示元素为指向 AST 的指针),若一并 + 移除会让声明端引用名('list[.AST*]')与定义端注册名 + ('list[.AST]')分裂成两个 struct。头部(首个 '[' 之前)行为不变; + '[' 开头(LLVM 数组串 '[4 x i8*]')走原清洗,保持既有语义。 + """ + lb: int = name.find('[') + if lb > 0: + head, tail = name[:lb], name[lb:] + else: + head, tail = name, '' + head = (head.replace(' *', '').replace('*', '').replace(' ', '_') + .replace('const', 'const_').replace('volatile', 'volatile_')) + return head + tail + def _get_or_create_struct(self, name: str, source_sha1: str | None = None, packed: bool = False) -> ir.IdentifiedStructType: - clean_name: str = name.replace(' *', '').replace('*', '').replace(' ', '_').replace('const', 'const_').replace('volatile', 'volatile_') + clean_name: str = self._CleanStructName(name) # 如果名称包含模块前缀(如 "a9fa0f6200c09e65.zbit_writer"), # 先尝试用短名称(如 "zbit_writer")查找已注册的 struct, # 避免为同一 struct 创建多个不同的类型对象导致跨模块引用类型不一致 diff --git a/lib/core/LLVMCG/FuncGen.py b/lib/core/LLVMCG/FuncGen.py index e1c83ec..3dc4867 100644 --- a/lib/core/LLVMCG/FuncGen.py +++ b/lib/core/LLVMCG/FuncGen.py @@ -487,10 +487,56 @@ class FuncGenMixin: _members_final: list = self.class_members.get(ClassName, []) _num_members_final: int = len(_members_final) if _num_elements_final > _num_members_final: - _num_inherited_final: int = _num_elements_final - _num_members_final + # base 是 vtable 占位(struct[0] = i8*),不是继承字段,必须扣除: + # 不扣 base 时,凡"struct 元素数恰好多 1"的类,差值其实只是 vtable + # 本身(class_members 不含 vtable),却会对任意字段名返回 base+0。 + # 实例:ast.ImportFrom 的 level/module → 静默返回偏移 1(=parent, + # %"…AST"*)→ 调用点把 AST* 当 i32 传入 _resolve_relative_module + # → llvmlite "Type of #4 arg mismatch: i32 != %"…AST"*"。 + # 成员表与 struct 不一致(少一个非 vtable 槽)时,先强制按定义模块 + # 的 .vpi 重载一次成员表再按名查:_TryLoadClassMembersFromPyi 的 guard + # 会永久跳过"已存在但不完整"的表,此处即该冻结的解除点。重载后仍缺 + # 才走下方推断/fast-fail。每类只重载一次(避免递归与重复 IO)。 + _ReloadKey: tuple = (id(self), ClassName) + _ReloadTried: set = getattr(self, '_MemberReloadTried', None) + if _ReloadTried is None: + _ReloadTried = set() + self._MemberReloadTried = _ReloadTried + if _ReloadKey not in _ReloadTried: + _ReloadTried.add(_ReloadKey) + _Sha1Final: str | None = getattr(self, 'class_sha1_map', {}).get(ClassName) + if _Sha1Final and self._Trans and hasattr(self._Trans, 'ImportHandler'): + _OldMembers = _members_final + try: + self._Trans.ImportHandler._TryLoadClassMembersFromPyi( + ClassName, f"{_Sha1Final}.stub.ll", self, force=True) + except Exception: + pass + _ReloadedMembers: list = self.class_members.get(ClassName, []) + if len(_ReloadedMembers) < len(_OldMembers): + # 重载反而变少(.vpi 缺该类):回滚,避免丢字段 + self.class_members[ClassName] = _OldMembers + _ReloadedMembers = _OldMembers + _members_final = _ReloadedMembers + _num_members_final = len(_members_final) + for _Ri, (_Rn, _Rt) in enumerate(_members_final): + if _Rn == field_name: + return base + _Ri + _num_inherited_final: int = _num_elements_final - _num_members_final - base if _num_inherited_final == 1: - # 单继承字段,偏移 = base(vtable 之后第一个位置) - return base + 0 + # 推断(缺失槽位 = 位于 base+0 的未展平继承字段)仅在成员表 + # 尚未含展平继承字段时成立:若成员表首项本身就是任一已加载 + # 祖先的字段名(如 ImportFrom 'parent' ← Stmt),说明继承字段 + # 已展平、缺的是类自身字段,推断 base+0 必然读错槽位。 + _ParentNamesFinal: set = set() + for _ParentF in (getattr(self, 'class_parents', {}).get(ClassName) or []): + for _Np, _Tp in self.class_members.get(_ParentF, []) or []: + _ParentNamesFinal.add(_Np) + if _members_final and _members_final[0][0] in _ParentNamesFinal: + pass # 已含展平继承字段 → 落入下方 fast-fail 暴露真实缺失 + else: + # 单继承字段,偏移 = base(vtable 之后第一个位置) + return base + 0 # fast-fail(治本修复 2026-08-10):字段未找到不再静默返回 None。 # 静默 None 会让调用方(_gep_and_Load_member L26-27)直接 return None, # AnnAssign InitValue 变 None → 变量 alloca 未初始化 → 运行期读垃圾指针 diff --git a/lib/core/LLVMCG/TypeConvert.py b/lib/core/LLVMCG/TypeConvert.py index 87a2d19..b6de9d6 100644 --- a/lib/core/LLVMCG/TypeConvert.py +++ b/lib/core/LLVMCG/TypeConvert.py @@ -490,6 +490,25 @@ class TypeConvertMixin: if IsPtr: return ir.PointerType(opaque_struct) return opaque_struct + # 类名与枚举成员/异常类同名时的判据统一:类型注解语境下,只要该名 + # **有类定义**(Gen.structs 已建 / _struct_sha1_map 已登记类→模块), + # 就按结构体解析,而不是被同名枚举成员/异常类降级成 i32(rawptr[Function] + # → i32*:9 个模块 stub 里 LLVMModule.FuncHead/FuncTail、IRBuilder.Func + # 被写成 i32*)。与上方 IsStruct 分支(L479)、HandlesTypeMerge. + # _GetCTypeInfoInternal L241-251、StructGen 的"类名可能与 CEnum 成员名 + # 冲突"守卫同一条规则源 —— 判据是"是否有类定义",不往白名单加名字。 + if (type_str in self.structs + or type_str in getattr(self, '_struct_sha1_map', {})): + if type_str in self.structs: + if IsPtr: + return ir.PointerType(self.structs[type_str]) + return self.structs[type_str] + _cls_sha1: str | None = self._get_source_sha1_from_type_str(type_str) + _cls_struct: ir.IdentifiedStructType = self._get_or_create_struct( + type_str, source_sha1=_cls_sha1) + if IsPtr: + return ir.PointerType(_cls_struct) + return _cls_struct if _Entry and _Entry.IsEnum: return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32)) if _Entry and _Entry.IsExceptionClass: diff --git a/lib/core/LLVMTypeMapper.py b/lib/core/LLVMTypeMapper.py index 90b4939..cab6056 100644 --- a/lib/core/LLVMTypeMapper.py +++ b/lib/core/LLVMTypeMapper.py @@ -18,7 +18,9 @@ if TYPE_CHECKING: from lib.core.SymbolTable import SymbolTable from lib.core.Handles.HandlesBase import CTypeInfo, CTypeHelper, BuiltinTypeMap -from lib.core.SymbolUtils import IsTModule, IsListAnnotation, IsCPtrNode, IsCPtrSubscriptNode +from lib.core.SymbolUtils import ( + IsTModule, IsListAnnotation, IsCPtrNode, IsCPtrSubscriptNode, + IsRawPtrSubscriptNode) from lib.includes import t as _t from lib.includes.t import CTypeRegistry @@ -253,6 +255,11 @@ class LLVMTypeMapper: # t.CPtr[T] → Ptr(resolve(T))(对齐 TPV Subscript 分支 # TypeGenResolve.vp:2769-2790;void 首层归一化为 i8*) return self._add_pointer_level(self.get_llvm_type_str(node.slice)) + if IsRawPtrSubscriptNode(node): + # rawptr[T](内置裸指针下标)≡ `T | t.CPtr`:与定义端 TypeMerge 的 + # 合并语义同口径(裸类引用自身即指针,吸收这一层;其余逐层外包)。 + # 此前只认 IsCPtrSubscriptNode → rawptr[T] 落到下方 warn 并退化成 i32。 + return self._rawptr_subscript_type_str(node) if IsListAnnotation(node): slice_node: ast.AST = node.slice elem_type: str @@ -309,6 +316,23 @@ class LLVMTypeMapper: return ty + '*' return ty + '*' + def _rawptr_subscript_type_str(self, node: ast.Subscript) -> str: + """rawptr[T](内置裸指针下标)→ LLVM 类型串。 + + 与定义端同口径(`T | t.CPtr` 的合并):裸类引用('%struct.X*' / + '%"sha1.X"*' / 泛型特化等以 '%' 开头的结构指针)自身即指针,吸收这一层; + 其余(标量、已是指针的 typedef、联合写法、嵌套 rawptr / t.CPtr 下标) + 逐层外包(void → i8*)。 + """ + inner: ast.AST = node.slice + inner_type: str = self.get_llvm_type_str(inner) + if (not isinstance(inner, ast.BinOp) + and not IsRawPtrSubscriptNode(inner) + and not IsCPtrSubscriptNode(inner) + and inner_type.startswith('%') and inner_type.endswith('*')): + return inner_type + return self._add_pointer_level(inner_type) + def _add_pointer_levels(self, ty: str, count: int) -> str: """给类型字符串叠加 count 层指针(void/void* 首层归一化为 i8*)。""" result: str = ty diff --git a/lib/core/SymbolTypeResolver.py b/lib/core/SymbolTypeResolver.py index 4adeae5..18b43db 100644 --- a/lib/core/SymbolTypeResolver.py +++ b/lib/core/SymbolTypeResolver.py @@ -150,7 +150,7 @@ class TypeResolver: # 会被 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 同样把 + # 与改前 `t.CPtr` 联合分支等价(_InsertTypedefSymbol 同样把 # 'CVoid *' 归一化为 'void *')。 if value_base_type == 'ptr': td.original_type_kind = 'typedef' diff --git a/lib/core/SymbolUtils.py b/lib/core/SymbolUtils.py index 827a4e6..a58487f 100644 --- a/lib/core/SymbolUtils.py +++ b/lib/core/SymbolUtils.py @@ -3,6 +3,7 @@ from __future__ import annotations import vast as ast import re from lib.includes import t +from lib.includes.t import CTypeRegistry _T_MODULE_NAMES: frozenset[str] = frozenset({'t', t.__name__}) @@ -169,11 +170,16 @@ def ExtractTypeNameFromBinOp(annotation: ast.expr) -> str | None: return None -def FindStructNameInAnnotation(node: ast.AST, struct_names: dict | set) -> str | None: +def FindStructNameInAnnotation(node: ast.AST, struct_names: dict | set, + TCAliasMap: dict[str, tuple[str, str]] | None = None, + StructSha1Map: dict[str, str] | None = None) -> str | None: """递归在注解中查找结构体名(支持 BinOp(BitOr) 递归)。 检查 Name.id、Constant.str、Attribute.attr 是否在 struct_names 中。 - 支持泛型特化类型(如 GSList[Param]),构造特化名后检查是否在 struct_names 中。 + 支持泛型特化类型(如 GSList[Param]):特化名按**同一规则源** + (ResolveTypeArgStr:sha1 前缀 + rawptr/CPtr 的 '*' 逐层)构造后在 + struct_names 中查找——查表键与 _mangle_generic_class_name 产物逐字一致, + 否则同一注解在"注解查找"与"特化注册"两处解析出不同名字(opaque 副本)。 """ if isinstance(node, ast.Name) and node.id in struct_names: return node.id @@ -191,33 +197,40 @@ def FindStructNameInAnnotation(node: ast.AST, struct_names: dict | set) -> str | slice_node: ast.AST = node.slice if hasattr(ast, 'Index') and isinstance(slice_node, ast.Index): slice_node = slice_node.value - type_args: list[str] = [] - if isinstance(slice_node, ast.Name): - # 裸 CPtr(i8*)与 t.CPtr 同规范化为 'ptr',与 - # _resolve_type_arg_str/_mangle_generic_class_name 一致 - type_args.append('ptr' if slice_node.id == 'CPtr' else slice_node.id) - elif isinstance(slice_node, ast.Attribute): - type_args.append('ptr' if slice_node.attr == 'CPtr' else slice_node.attr) - elif isinstance(slice_node, ast.Tuple): - for elt in slice_node.elts: - if isinstance(elt, ast.Name): - type_args.append('ptr' if elt.id == 'CPtr' else elt.id) - elif isinstance(elt, ast.Attribute): - type_args.append('ptr' if elt.attr == 'CPtr' else elt.attr) - if type_args: - spec_name: str = base_name + ''.join(f'[{arg}]' for arg in type_args) - if spec_name in struct_names: - return spec_name + elem_nodes: list[ast.AST] = ( + list(slice_node.elts) if isinstance(slice_node, ast.Tuple) else [slice_node]) + type_args: list[str] = [ + ResolveTypeArgStr(E, TCAliasMap, StructSha1Map) for E in elem_nodes] + spec_name: str = base_name + '[' + ']['.join(type_args) + ']' + if spec_name in struct_names: + return spec_name if base_name in struct_names: return base_name if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): - result: str | None = FindStructNameInAnnotation(node.left, struct_names) + result: str | None = FindStructNameInAnnotation( + node.left, struct_names, TCAliasMap, StructSha1Map) if result: return result - return FindStructNameInAnnotation(node.right, struct_names) + return FindStructNameInAnnotation( + node.right, struct_names, TCAliasMap, StructSha1Map) return None +def FindStructNameInAnnotationWithContext(Node: ast.AST, Trans: object, Gen: object, + StructNames: dict | set | None = None) -> str | None: + """按 Gen/Trans 上下文注入规则表后查找结构体名。 + + 规则本体唯一(FindStructNameInAnnotation),此处只做与 + HandlesExprCall._resolve_type_arg_str 同口径的上下文注入,避免各调用点 + 各写一套拼名(查表键必须与 _mangle_generic_class_name 产物逐字一致)。 + """ + Names: dict | set = StructNames if StructNames is not None else Gen.structs + return FindStructNameInAnnotation( + Node, Names, + TCAliasMap=getattr(Trans, '_t_c_imported_names', {}), + StructSha1Map=getattr(Gen, '_struct_sha1_map', {})) + + def AnnotationContainsName(annotation: ast.AST, name: str) -> bool: """检查注解中是否包含指定名称(支持 BinOp(BitOr) 递归)。 @@ -250,3 +263,368 @@ def IsCPtrSubscriptNode(node: ast.AST) -> bool: """ return (isinstance(node, ast.Subscript) and IsCPtrNode(node.value)) + + +# 指针下标的 base 名:rawptr 是编译器内置语法(TPC/TPV 双端保留字,见 +# includes/builtins.vp 语义注释与 TypeAnnotationResolver._BUILTIN_ROWPTR_NAME), +# 单独 rawptr 等价 t.CPtr(void*)。 +_RAWPTR_SUBSCRIPT_NAME: str = 'rawptr' + +# 指针标记类型名(t.CPtr):裸名书写 `CPtr[T]` / 导入别名 `X`(from t import CPtr as X) +# 都归一到本名。 +_CPTR_TYPE_NAME: str = 'CPtr' + + +def ResolveTCImportAliasName(NameId: str, symtable: object | None = None, + TCAliasMap: dict[str, tuple[str, str]] | None = None) -> str: + """t/c 导入别名 → 原始类型名('X' → 'CPtr'、'myint' → 'CInt');未命中原样返回。 + + 两张同源别名表(注册点唯一:HandlesImports._EmitImportFromDeclarationsLlvm / + _RegisterFromImportAliases,与 SymbolTable 序列化),调用方按手头有的表注入: + - TCAliasMap:{别名: (模块, 原名)}(Trans._t_c_imported_names / + DeclarationGenerator._t_c_imported_names 同结构) + - symtable._t_type_symbols:{别名: t.CType 子类}(from t import CPtr as X → t.CPtr) + 非 t/c 模块的 from 导入别名(from ast import AST as A)不参与归一。 + """ + if not NameId: + return NameId + if TCAliasMap: + Entry: tuple[str, str] | None = TCAliasMap.get(NameId) + if Entry: + SrcModule, SrcName = Entry + if IsTModule(SrcModule): + return SrcName + if symtable is not None: + Cls: object | None = getattr(symtable, '_t_type_symbols', {}).get(NameId) + if Cls is not None: + return getattr(Cls, '__name__', NameId) + return NameId + + +def IsRawPtrSubscriptNode(node: ast.AST) -> bool: + """检查节点是否为内置裸指针下标 `rawptr[T]`(非裸标记 rawptr)。 + + `rawptr[T]` 语义 = `t.CPtr[T]` = 源码联合写法 `T | t.CPtr`(Ptr(T)),三者 + 必须走同一解析与拼名路径,否则同一类型的不同书写会分裂出不同特化名 + (结构体/方法声明/调用点三处不一致)。 + 注意:本判据**只**认 `rawptr[T]`;`t.CPtr[T]` / 裸名 `CPtr[T]` 由 + IsPtrSubscriptNode 判定(见下),二者共用同一条脱糖规则 + (DesugarPtrSubscriptToUnion),此处保持 rawptr 专用以便仍需区分书写时使用。 + """ + return (isinstance(node, ast.Subscript) + and isinstance(node.value, ast.Name) + and node.value.id == _RAWPTR_SUBSCRIPT_NAME) + + +def IsPtrSubscriptNode(node: ast.AST, symtable: object | None = None, + TCAliasMap: dict[str, tuple[str, str]] | None = None) -> bool: + """检查节点是否为"指针下标类型":`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]` + / 导入别名 `X[T]`(`from t import CPtr as X`)。 + + 三者语义完全相同(= 源码联合写法 `T | t.CPtr` = Ptr(T),见 + builtins.vp 与 t.py 的 CPtr 语义注释):内层 T 按**参数注解 `a: T` 的规则** + 解析(跨模块类 → '.X'),再包一层指针——T 为类引用时该层被结构体 + 自身吸收('.X*'),其余(标量 / 已是指针的 typedef / 泛型容器特化 / + 嵌套指针下标)逐层外包。 + + - 裸名 `CPtr[T]`(`from t import CPtr` / `from t import *`)与声明端 + DeclarationGenerator._get_type_str 的 ValueIsCPtr 判据一致(那里同样接受 + Attribute 'CPtr' 与 Name 'CPtr' 两种书写)。 + - 传入 symtable 时,`import t as tt` 的 `tt.CPtr[T]` 一并命中(与声明端 + "只看 attr 名"的 ValueIsCPtr 判据对齐,避免同一书写两端分裂); + 不传(名称域 ResolveTypeArgStr 的字面名场景)时只认字面 't'。 + - 导入名别名(`from t import CPtr as X` 的 `X[T]`)经别名表解析 X→CPtr + (TCAliasMap 或 symtable._t_type_symbols,见 ResolveTCImportAliasName), + 定义端与声明端各自注入手头的同一批表,判据只有这一处。 + """ + if IsRawPtrSubscriptNode(node): + return True + if not isinstance(node, ast.Subscript): + return False + value: ast.AST = node.value + if isinstance(value, ast.Name) and value.id == _CPTR_TYPE_NAME: + return True + return IsCPtrMarkerNode(value, symtable, TCAliasMap) + + +def DesugarPtrSubscriptToUnion(node: ast.AST, symtable: object | None = None, + TCAliasMap: dict[str, tuple[str, str]] | None = None) -> ast.AST: + """把指针下标(`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]` / 别名 `X[T]`) + 折叠成等价的联合写法 `T | t.CPtr`。 + + 内层 T 递归折叠(`rawptr[rawptr[X]]` → `(X | t.CPtr) | t.CPtr`、 + `t.CPtr[rawptr[X]]` → `(X | t.CPtr) | t.CPtr`), + 与 TPV TypeGenResolve.resolve_annotation_type 的 Subscript 分支同语义 + (T → Ptr(T),层数逐层外包);折叠后交给 HandlesTypeMerge 既有的 + 联合解析(_CollectAllOperandTypes 会把嵌套 BitOr 拉平成 [T, t.CPtr, ...], + struct 基类型吸收一层、其余逐层累加),从而: + - 内层 T 走通用类型解析(含跨模块类经 Gen.structs 反查 SHA1 前缀); + - `rawptr[X]` / `t.CPtr[X]` / `X[T]` / `X | t.CPtr` 得到同一 CTypeInfo + (PtrCount/BaseType 一致)与同一泛型实参串(逐层 '*')。 + + 非指针下标节点原样返回(调用方无需先判类型);symtable/TCAliasMap 透传给 + 判据(识别 `import t as tt` 的 `tt.CPtr[T]` 与 `from t import CPtr as X` 的 + `X[T]` 内层嵌套)。折叠产物统一用字面 `t.CPtr` 标记,与源码联合写法同形。 + """ + if not IsPtrSubscriptNode(node, symtable, TCAliasMap): + return node + inner: ast.AST = DesugarPtrSubscriptToUnion(node.slice, symtable, TCAliasMap) + cptr_marker: ast.AST = ast.Attribute( + value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) + union_node: ast.AST = ast.BinOp(left=inner, op=ast.BitOr(), right=cptr_marker) + try: + ast.copy_location(union_node, node) + except Exception: + pass + return union_node + + +# --------------------------------------------------------------------------- +# 泛型实参串(mangling):定义端与声明端唯一规则源 +# --------------------------------------------------------------------------- +# 同一泛型特化(如 list[rawptr[ast.AST]])在 define 端发射的结构体名 +# (HandlesClassDef._mangle_generic_class_name → %".list[ast.ASTptr]")、 +# 各端方法名、以及 declare/stub 端引用的类型名必须逐字一致,否则会出现 +# Undefined method / 签名错位。此前 DeclarationGenerator._get_type_str 的泛型 +# 分支自带第二套(只认 Name / Name-Tuple 实参的)规则,其余形态(Attribute / +# Subscript / BinOp / 混合 Tuple)退回未特化的 %".list"*,与 define 端 +# 不一致。故此处收拢唯一规则,两端皆调用本组函数。 + +# 定义端 _resolve_generic_slice_type 的 ast.Name 规范化表: +# 源码里的 C 定长类型名 → 喂给 _mangle_generic_class_name 的 type_arg。 +_GENERIC_ARG_INT_NAMES: frozenset[str] = frozenset({ + 'CInt', 'CUInt', 'CInt32T', 'CUInt32T', 'CInt64T', 'CUInt64T', + 'CInt16T', 'CUInt16T', 'CInt8T', 'CUInt8T', 'CChar', 'CShort', 'CLong', +}) +_GENERIC_ARG_FLOAT_NAMES: frozenset[str] = frozenset({ + 'CDouble', 'CFloat', 'CFloat64T', 'CFloat32T', +}) +# 源码名 → 泛型 T 替换用的原始类型名(_resolve_generic_slice_type 的第二返回值) +_GENERIC_ARG_RAW_NAMES: dict[str, str] = { + 'int': 'CInt', 'double': 'CDouble', 'float': 'CFloat', + 'char': 'CChar', 'bool': 'CBool', +} +# _mangle_generic_class_name 的浮点归一集合(LLVM 浮点串 → 'f' + Size) +_LLVM_FLOAT_STRINGS: frozenset[str] = frozenset({'float', 'double', 'half', 'fp128'}) + + +def NormalizeGenericArgName(NameId: str) -> str: + """ast.Name 泛型实参 → 定义端 type_arg(HandlesExprCall._resolve_generic_slice_type + 的 ast.Name 分支,逐分支同构): + int/double/float 保留源码名('list[int]' 而非 'list[i32]')、 + char/bool → 'i8'、C 整数族 → 'int'、C 浮点族 → 'double'、其余原名。 + """ + if NameId in ('int', 'double', 'float'): + return NameId + if NameId in ('char', 'bool'): + return 'i8' + if NameId in _GENERIC_ARG_INT_NAMES: + return 'int' + if NameId in _GENERIC_ARG_FLOAT_NAMES: + return 'double' + return NameId + + +def GenericArgRawName(NameId: str) -> str: + """ast.Name 泛型实参 → 替换模板 T 用的原始类型名(_resolve_generic_slice_type + 的第二返回值):'int' → 'CInt'(内建类型须用源码名替换 T,见 + HandlesClassDef._specialize_generic_class 的 _BUILTIN_TYPE_NAMES 判定), + 其余原名。仅定义端 T 替换用;mangling 只用 NormalizeGenericArgName。 + """ + return _GENERIC_ARG_RAW_NAMES.get(NameId, NameId) + + +def MangleTypeArgByRegistry(ArgStr: str) -> str: + """实参串 → 最终 mangled 实参(HandlesClassDef._mangle_generic_class_name + 的单项逻辑,逐分支同构):t 类型名经 NameToLLVM 归一(CInt32T → 'i32', + 't.CInt'/'c.CInt' 先去模块前缀再归一),浮点按 Size 归一为 f{n} + (CDouble → 'f8'),未注册名原样保留(含指针层数与已解析的内层名)。 + """ + LookupStr: str = ArgStr + if ArgStr.startswith('t.') or ArgStr.startswith('c.'): + LookupStr = ArgStr[2:] + LlvmStr: str | None = CTypeRegistry.NameToLLVM(LookupStr) + if not LlvmStr: + return ArgStr + if LlvmStr in _LLVM_FLOAT_STRINGS: + Cls: type | None = CTypeRegistry.GetClassByName(LookupStr) + Size: int = Cls().Size if Cls is not None else 0 + return 'f' + str(Size) + return LlvmStr + + +# 指针层数的首层归一化(与 LLVMTypeMapper._add_pointer_level 同口径) +_POINTER_PLACEHOLDER_BASES: frozenset[str] = frozenset({'', 'void', 'void*'}) + + +def AddPointerLayers(TypeStr: str, Layers: int) -> str: + """给已解析的类型串追加 Layers 层指针(空/void/void* 的首层归一化为 i8*)。 + + 与 LLVMTypeMapper._add_pointer_level 同口径;名称域与类型域因此对 + rawptr[t.CVoid] 一致地给出 i8*(唯一允许的 void 兜底)。 + """ + Result: str = TypeStr + for _ in range(Layers): + if Result in _POINTER_PLACEHOLDER_BASES: + Result = 'i8*' + else: + Result = Result + '*' + return Result + + +def IsCPtrMarkerNode(Node: ast.AST, symtable: object | None = None, + TCAliasMap: dict[str, tuple[str, str]] | None = None) -> bool: + """`t.CPtr` 裸指针标记(含 `import t as tt` 与 `from t import CPtr as X` 别名): + 联合写法里贡献一层 '*'。 + + 与 IsCPtrNode 的区别:仅按 attr 名 + t 模块前缀判定,不看切片 + (`t.CPtr[T]` 下标是**基类型**而非层数标记,见 ResolveTypeArgStr 的 + Subscript 分支)。 + - Attribute:`t.CPtr` / `tt.CPtr`(t 模块别名经 symtable.import_aliases 解析)。 + - Name:经别名表归一为 'CPtr' 的书写名(`from t import CPtr as X` 的 `X`); + 字面裸名 `CPtr` 是否算标记取决于该模块是否 `from t import CPtr` + (别名表命中一致:两端都由同一批表判定,不另设白名单)。 + """ + if isinstance(Node, ast.Attribute): + return (Node.attr == _CPTR_TYPE_NAME + and isinstance(Node.value, ast.Name) + and IsTModule(Node.value.id, symtable)) + if isinstance(Node, ast.Name): + return ResolveTCImportAliasName(Node.id, symtable, TCAliasMap) == _CPTR_TYPE_NAME + return False + + +def FlattenUnionOperands(Node: ast.AST) -> list[ast.AST]: + """拉平嵌套 BitOr,按书写顺序返回操作数(`A | t.CPtr | t.CPtr` → [A, CPtr, CPtr])。""" + if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): + return FlattenUnionOperands(Node.left) + FlattenUnionOperands(Node.right) + return [Node] + + +def ResolveTypeArgStr(Node: ast.AST, TCAliasMap: dict[str, tuple[str, str]] | None = None, + StructSha1Map: dict[str, str] | None = None) -> str: + """将类型参数 AST 节点解析为规范化的类型字符串(泛型 mangling 用)。 + + 规则(**与参数注解 `a: T` 完全同一套解析**,定义端/声明端唯一规则源): + - ast.Name: t/c 导入别名 → '{module}.{name}';已注册类 → '{sha1}.{名}' + (与参数注解的 %"{sha1}.X" 同前缀);其余按 NormalizeGenericArgName + 归一('int'/'i32'/'str' 原名、'char'/'bool' → 'i8'、C 整数/浮点族 → + 'int'/'double') + - ast.Attribute: t 模块类型按 CTypeRegistry.NameToLLVM 解析(t.CPtr → i8*、 + t.CInt → i32);跨模块类引用 module.X → '{sha1}.X';其余 '{mod}.{attr}' + - ast.Subscript: 指针下标(`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]` / + 导入别名 `X[T]`)已在入口脱糖为上面的联合写法(走同一规则),故此处只剩泛型容器: + '<容器名>[<已解析实参>]'(多实参 '][' 分隔),容器名的 + 模块前缀由调用方(_mangle_name / '%"{模sha1}.{特化名}"')统一加 + - ast.BinOp(BitOr)(含指针下标脱糖而来): 取非 CPtr 侧为基类型, + 每个 `t.CPtr` 标记追加一层 '*'(`X | t.CPtr` ≡ `rawptr[X]` ≡ + `t.CPtr[X]` → '.X*';`rawptr[rawptr[X]]` → '.X**'; + 标量 `int | t.CPtr` → 'int*') + - 其余形态: ast.unparse + + 注意:名称域**不做**"裸类引用吸收一层 CPtr"(那是注解→LLVM 类型域的规则, + 见 DeclarationGenerator._is_bare_class_ref_annotation):名称域描述的是类型 + 身份('X' = X 本身、'X*' = 指向 X 的指针),逐层累加,否则 + `rawptr[X]` 与 `X | t.CPtr` 会分裂成两个名字。 + """ + if IsPtrSubscriptNode(Node, TCAliasMap=TCAliasMap): + Node = DesugarPtrSubscriptToUnion(Node, TCAliasMap=TCAliasMap) + if isinstance(Node, ast.Name): + tn: str = Node.id + if TCAliasMap and tn in TCAliasMap: + # t 模块别名(from t import CInt as myint)按内建归一表解析到同一名字 + # ('myint' 与 'CInt' 都 → 'int'),避免同义写法分裂特化名 + SrcModule, SrcName = TCAliasMap[tn] + if IsTModule(SrcModule): + return NormalizeGenericArgName(SrcName) + return f'{SrcModule}.{SrcName}' + if StructSha1Map and tn in StructSha1Map: + return f'{StructSha1Map[tn]}.{tn}' + return NormalizeGenericArgName(tn) + if isinstance(Node, ast.Attribute): + return _ResolveAttributeArgStr(Node, TCAliasMap, StructSha1Map) + if isinstance(Node, ast.Subscript): + return _ResolveSubscriptArgStr(Node, TCAliasMap, StructSha1Map) + if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): + return _ResolveUnionArgStr(Node, TCAliasMap, StructSha1Map) + return ast.unparse(Node) + + +def _ResolveAttributeArgStr(Node: ast.Attribute, TCAliasMap: dict[str, tuple[str, str]] | None, + StructSha1Map: dict[str, str] | None) -> str: + """Attribute 形态(t.CInt / ast.AST / .AST)→ 实参串。""" + if not isinstance(Node.value, ast.Name): + return ast.unparse(Node) + ModName: str = Node.value.id + AttrName: str = Node.attr + if IsTModule(ModName) or (TCAliasMap and ModName in TCAliasMap): + LlvmStr: str | None = CTypeRegistry.NameToLLVM(AttrName) + if LlvmStr is not None: + return LlvmStr + return f'{ModName}.{AttrName}' + # 跨模块类引用(ast.AST / .AST):与参数注解同一条 sha1 解析 + if StructSha1Map and AttrName in StructSha1Map: + return f'{StructSha1Map[AttrName]}.{AttrName}' + return f'{ModName}.{AttrName}' + + +def _ResolveSubscriptArgStr(Node: ast.Subscript, TCAliasMap: dict[str, tuple[str, str]] | None, + StructSha1Map: dict[str, str] | None) -> str: + """下标形态(t.CPtr[T] / 泛型容器 list[X] / dict[K,V])→ 实参串。 + + 容器名('list' / 'dict' ...)**不带**模块前缀:它只是 _mangle_generic_class_name + 的基名,特化名的模块前缀由调用方统一加(定义端 _mangle_name / 声明端 + '%"{模sha1}.{特化名}"'),并保证 '_make_replacement_node' 能把实参串 + 重新解析成可用的注解 AST('.list[...]' 会因数字开头而解析失败)。 + """ + if IsCPtrSubscriptNode(Node): + Inner: str = ResolveTypeArgStr(Node.slice, TCAliasMap, StructSha1Map) + return AddPointerLayers(Inner, 1) + BaseName: str = '' + if isinstance(Node.value, ast.Name): + BaseName = Node.value.id + elif isinstance(Node.value, ast.Attribute): + BaseName = Node.value.attr + if not BaseName: + return ast.unparse(Node) + SliceNode: ast.AST = Node.slice + ElemNodes: list[ast.AST] = list(SliceNode.elts) if isinstance(SliceNode, ast.Tuple) else [SliceNode] + Resolved: list[str] = [ResolveTypeArgStr(E, TCAliasMap, StructSha1Map) for E in ElemNodes] + return f'{BaseName}[' + ']['.join(Resolved) + ']' + + +def _ResolveUnionArgStr(Node: ast.BinOp, TCAliasMap: dict[str, tuple[str, str]] | None, + StructSha1Map: dict[str, str] | None) -> str: + """联合形态(X | t.CPtr | ...)实参串:非 CPtr 侧为基类型,CPtr 标记逐层加 '*'。 + + 标记判据是同一处 IsCPtrMarkerNode(含 `from t import CPtr as X` 的裸标记别名), + 别名经 TCAliasMap 解析;没带别名表的 `import t as tt` 的 `tt.CPtr` 在此域 + 不做模块别名解析(两端口径一致的既有缺口)。 + """ + BaseType: str = '' + Layers: int = 0 + for Operand in FlattenUnionOperands(Node): + if IsCPtrMarkerNode(Operand, TCAliasMap=TCAliasMap): + Layers += 1 + elif not BaseType: + BaseType = ResolveTypeArgStr(Operand, TCAliasMap, StructSha1Map) + if not BaseType: + # 只有 CPtr 标记(如 `t.CPtr | t.CPtr`):等价 void* 逐层——首层 i8* + return AddPointerLayers('void', Layers) + return AddPointerLayers(BaseType, Layers) + + +def ResolveGenericTypeArgStr(Node: ast.AST, TCAliasMap: dict[str, tuple[str, str]] | None = None, + StructSha1Map: dict[str, str] | None = None) -> str: + """泛型实参的最终 mangled 串(喂给 _mangle_generic_class_name 的单参数)。 + + 与定义端(HandlesExprCall._resolve_generic_slice_type + _mangle_generic_class_name) + 对单个实参的实际结果同一套规则: + - ast.Name: NormalizeGenericArgName →(命中注册类则 '{sha1}.{名}')→ MangleTypeArgByRegistry + ('int' → 'int'、'i32' → 'i32'、'str' → 'str'、'CInt32T' → 'int'、 + 'CFloat64T' → 'double'、'CChar' → 'i8') + - 其余形态(Attribute / Subscript(含 rawptr[...])/ BinOp / 混合 Tuple 元素): + ResolveTypeArgStr(rawptr[T] 脱糖 → 逐层 '*'、t.CPtr → 'i8*'、跨模块类 → sha1 前缀) + → MangleTypeArgByRegistry + """ + return MangleTypeArgByRegistry(ResolveTypeArgStr(Node, TCAliasMap, StructSha1Map)) diff --git a/lib/core/Translator/LlvmGenerator.py b/lib/core/Translator/LlvmGenerator.py index b0986fa..9eb464f 100644 --- a/lib/core/Translator/LlvmGenerator.py +++ b/lib/core/Translator/LlvmGenerator.py @@ -170,6 +170,14 @@ class LlvmGeneratorMixin: line_info += ":\n %d | %s" % (lineno, source_line) self._ErrorStack.append((type(e).__name__, str(e), line_info)) + # 本模块泛型类模板必须在前置声明之前注册(与 includes 侧注册判据同源, + # 见 HandlesClassDef.PrescanModuleGenericClassTemplates):前置声明要解析 + # 参数/返回注解里的泛型类下标(`def f(b: Box[Node])`),而类处理阶段 + # (下方 _EmitClassLlvm)才注册模板 → 注解解析为空 → VoidType → + # "无法识别的参数类型注解"。放在 import 扫描之后,保证本地类覆盖同名 + # 导入类(与 _EmitClassLlvm 的覆盖顺序一致)。 + self.ClassHandler.PrescanModuleGenericClassTemplates(ast.iter_child_nodes(Tree)) + # 前向声明所有顶层函数,确保全局变量初始化时能引用函数指针 for Node in ast.iter_child_nodes(Tree): if isinstance(Node, ast.FunctionDef):