diff --git a/lib/core/Handles/HandlesBase.py b/lib/core/Handles/HandlesBase.py index 7fea4c7..f43c814 100644 --- a/lib/core/Handles/HandlesBase.py +++ b/lib/core/Handles/HandlesBase.py @@ -15,7 +15,8 @@ from lib.includes.t import CTypeRegistry from lib.core.TypeSpec import TypeSpec, SymbolMeta, FIELD_ROUTES from lib.core.SymbolUtils import ( IsPtrSubscriptNode, IsListAnnotation, IsCPtrMarkerNode, - FlattenUnionOperands, FixedArrayKeywordAnnotationError) + FlattenUnionOperands, IsI8DoublePtrType, IsClassRefPtrAnnotation, + FixedArrayKeywordAnnotationError) from lib.core.VLogger import get_logger as _vlog @@ -1091,6 +1092,46 @@ class BaseHandle: # (llvmlite set_body 只能设一次),后期只修表不修体(现场:Generator 第 3/11 # 槽 i8**)。此处响亮失败并附判据,使降级**当场暴露**而非留到赋值时报"层数不同"。 self._CheckPtrAnnotationKeepsClass(item.annotation, TypeInfo, VarName, AnnotationText) + # 形状判据(更硬的一条):注解形态是「**指针包装的类引用**」而解析结果正是 + # `i8**`(= void 基座两层,T 的类型被丢掉)⇒ 必属降级,直接响亮失败并把两侧 + # 证据打全。放在字段类型的**唯一漏斗**里,覆盖全部"按注解推导成员类型"的写入链 + # (本地类 / 跨模块 .vpi / 嵌套类)。 + if (IsClassRefPtrAnnotation(item.annotation, self.Trans.SymbolTable) + and IsI8DoublePtrType(MemberType)): + _inner_name: str = '' + _inner_node: Any = (item.annotation.slice + if isinstance(item.annotation, ast.Subscript) else None) + if isinstance(_inner_node, ast.Attribute): + _inner_name = _inner_node.attr + elif isinstance(_inner_node, ast.Name): + _inner_name = _inner_node.id + _sym: Any = None + try: + _sym = self.Trans.SymbolTable.lookup(_inner_name) if _inner_name else None + except Exception: + _sym = None + _gen: Any = getattr(self.Trans, 'LlvmGen', None) + _structs_hit: str = ( + '有' if (_gen is not None and _inner_name in getattr(_gen, 'structs', {})) else '无') + _sha1_hit: Any = ( + getattr(_gen, '_struct_sha1_map', {}).get(_inner_name) if _gen is not None else None) + raise ValueError( + f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 是**类引用指针**," + f"却被解析成 {MemberType}(void 基座两层 = 类型信息丢失)—— 拒绝降级:" + f"CTypeInfo(BaseType={getattr(TypeInfo, 'BaseType', None)} " + f"PtrCount={getattr(TypeInfo, 'PtrCount', None)} " + f"IsStruct={getattr(TypeInfo, 'IsStruct', None)} " + f"IsTypedef={getattr(TypeInfo, 'IsTypedef', None)} " + f"IsEnum={getattr(TypeInfo, 'IsEnum', None)} " + f"Name={getattr(TypeInfo, 'Name', None)});内层名='{_inner_name}' " + f"符号表[该名] => IsTypedef={getattr(_sym, 'IsTypedef', None)} " + f"BaseType={getattr(_sym, 'BaseType', None)} " + f"PtrCount={getattr(_sym, 'PtrCount', None)} " + f"IsStruct={getattr(_sym, 'IsStruct', None)} " + f"IsRenum={getattr(_sym, 'IsRenum', None)} " + f"IsModuleAlias={getattr(_sym, 'IsModuleAlias', None)};" + f"Gen.structs 命中={_structs_hit} _struct_sha1_map 命中={_sha1_hit!r}" + ) return VarName, MemberType, TypeInfo def _CheckPtrAnnotationKeepsClass(self, Annotation: ast.AST, Info: CTypeInfo | None, diff --git a/lib/core/LLVMCG/StructGen.py b/lib/core/LLVMCG/StructGen.py index 324f409..6cbde73 100644 --- a/lib/core/LLVMCG/StructGen.py +++ b/lib/core/LLVMCG/StructGen.py @@ -348,81 +348,6 @@ class StructGenMixin: return True return False - @staticmethod - def _IsI8DoublePtr(T: Any) -> bool: - """是否为 i8 系两层指针(`i8**`)—— 即 `T | t.CPtr` 在 T 的类型丢失后**退化**成的形状。""" - return (isinstance(T, ir.PointerType) - and isinstance(T.pointee, ir.PointerType) - and isinstance(T.pointee.pointee, ir.IntType) - and T.pointee.pointee.width == 8) - - def _IsClassRefPtrAnnotation(self, Ann: Any) -> bool: - """注解是否为「指针包装的**类引用**」:`rawptr[Mod.Class]` / `Mod.Class | t.CPtr`。 - - 判据保守(只用于识别"本应是结构体指针"): - - 必须是指针包装(`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]`,或含 `t.CPtr` 标记的联合); - - 内层须是类引用形态:`Attribute` 且根模块不是 `t` / `c`(如 `ExprGenFstr.ExprGenFstr`、 - `llvmlite.LLVMType`),或首字母大写的裸名(类名书写惯例)。 - 模块名、`rawptr`/`CPtr` 标记、泛型特化实参(内层是 Subscript)一律不算。 - """ - from lib.core.SymbolUtils import (IsRawPtrSubscriptNode, IsCPtrSubscriptNode, - IsCPtrMarkerNode) - _tbl: Any = getattr(self, 'SymbolTable', None) - Inner: Any = None - if IsRawPtrSubscriptNode(Ann) or IsCPtrSubscriptNode(Ann): - Inner = Ann.slice - elif isinstance(Ann, ast.BinOp) and isinstance(Ann.op, ast.BitOr): - for _side in (Ann.left, Ann.right): - if IsCPtrMarkerNode(_side, _tbl): - continue - if Inner is not None: - return False - Inner = _side - else: - return False - if isinstance(Inner, ast.Attribute): - _cur: Any = Inner - while isinstance(_cur, ast.Attribute): - _cur = _cur.value - _root: str = _cur.id if isinstance(_cur, ast.Name) else '' - return _root not in ('t', 'c') - if isinstance(Inner, ast.Name): - return bool(Inner.id) and Inner.id[0].isupper() - return False - - def _HasDegradedMemberTypes(self, ClassName: str, MemberTypes: list) -> bool: - """本次即将冻结的布局里,是否存在「注解是类引用、类型却退化成 `i8**`」的成员。 - - 为什么必须在 set_body **之前**拦:llvmlite 的 struct body 只能设一次,且**实测本 - 版本 `_elements = None` 之后 `set_body` 仍以 `" is already defined"` 拒绝 - 重设** —— 一旦按降级类型冻结,后续 pass 把 `class_members` 修好也改不回布局 - (现场:Generator 的槽是 `i8**` 而声明是 `%"…ExprGenFstr"*`,赋值时报「层数不同」)。 - 故此时**不设 body**、保持 opaque,等类型就绪的 pass 再设;若始终不就绪,会在使用点 - 以「opaque / 层数不同」响亮失败,而不是静默产出错布局。 - """ - if self._IsRenumLayoutClass(ClassName): - # REnum 镜像布局:class_members 里是变体而非字段,不参与本判据。 - return False - _anns: Any = (getattr(self, 'class_member_annotations', None) or {}).get(ClassName) - if not _anns: - return False - _members: list = self.class_members.get(ClassName) or [] - if not _members: - return False - _offset: int = len(MemberTypes) - len(_members) - for _i, _item in enumerate(_members): - _j: int = _offset + _i - if _j < 0 or _j >= len(MemberTypes): - continue - if not (isinstance(_item, tuple) and _item): - continue - _ann: Any = _anns.get(_item[0]) - if _ann is None or not self._IsClassRefPtrAnnotation(_ann): - continue - if StructGenMixin._IsI8DoublePtr(MemberTypes[_j]): - return True - return False - def _generate_structs(self) -> None: all_classes: set[str] = set(self.class_methods.keys()) | set(self.class_members.keys()) @@ -766,14 +691,7 @@ class StructGenMixin: # 根因:register_method 在 class_members[ClassName]=[] 时立即调用 # _generate_structs,此时 member_types 为空。保持 opaque 状态, # 让后续 _TryLoadStructFromStub 能正确设置 body。 - # - # 【不冻结降级布局】member_types 里若含「注解是类引用、类型却退化成 - # i8**」的成员,说明本次 pass 的类型尚未就绪 —— 此时**不设 body**: - # llvmlite 的 body 只能设一次(实测本版本 `_elements = None` 后 - # set_body 仍以 "already defined" 拒绝重设),按降级类型冻结后永远 - # 改不回来(后续 pass 修好 class_members 也没用)。保持 opaque,等 - # 类型就绪的 pass 再设;始终不就绪则在使用点响亮失败,而不是静默错布局。 - if member_types and not self._HasDegradedMemberTypes(ClassName, member_types): + if member_types: struct_type.set_body(*member_types) def _create_Vtable_globals(self) -> None: diff --git a/lib/core/SymbolUtils.py b/lib/core/SymbolUtils.py index 54a5bef..c60c0f0 100644 --- a/lib/core/SymbolUtils.py +++ b/lib/core/SymbolUtils.py @@ -2387,6 +2387,54 @@ def IsRawPtrSubscriptNode(node: ast.AST) -> bool: and node.value.id == _RAWPTR_SUBSCRIPT_NAME) +def IsI8DoublePtrType(T: object) -> bool: + """是否为 **i8 系两层指针**(`i8**`)—— 即 `T | t.CPtr` 在 T 的类型丢失后**退化**成的形状。 + + 与 "T 是有类型的上层指针" 无关:只认 `i8**`(`Ptr(Ptr(i8))`)。用于识别 + 「类引用指针被解析成通用两层指针」这类**类型信息丢失**。 + """ + import llvmlite.ir as ir + return (isinstance(T, ir.PointerType) + and isinstance(T.pointee, ir.PointerType) + and isinstance(T.pointee.pointee, ir.IntType) + and T.pointee.pointee.width == 8) + + +def IsClassRefPtrAnnotation(Ann: ast.AST, symtable: object | None = None) -> bool: + """注解是否为「**指针包装的类引用**」:`rawptr[Mod.Class]` / `Mod.Class | t.CPtr`。 + + 只回答"这个注解**本应**解析出结构体指针",不做任何类型解析,判据保守: + - 必须是指针包装:`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]`,或含 `t.CPtr` + 标记的联合写法 `T | t.CPtr`; + - 内层须是类引用形态:`Attribute` 且**根模块不是 `t` / `c`** + (如 `ExprGenFstr.ExprGenFstr`、`llvmlite.LLVMType`),或首字母大写的裸名 + (类名书写惯例)。 + 模块名、`rawptr` / `CPtr` 标记本身、泛型特化实参(内层是 Subscript)、 + `str` / `int` 这类小写内建名一律**不算**。 + """ + Inner: ast.AST | None = None + if IsRawPtrSubscriptNode(Ann) or IsCPtrSubscriptNode(Ann): + Inner = Ann.slice + elif isinstance(Ann, ast.BinOp) and isinstance(Ann.op, ast.BitOr): + for Side in (Ann.left, Ann.right): + if IsCPtrMarkerNode(Side, symtable): + continue + if Inner is not None: + return False + Inner = Side + else: + return False + if isinstance(Inner, ast.Attribute): + Cur: ast.AST = Inner + while isinstance(Cur, ast.Attribute): + Cur = Cur.value + Root: str = Cur.id if isinstance(Cur, ast.Name) else '' + return Root not in ('t', 'c') + if isinstance(Inner, ast.Name): + return bool(Inner.id) and Inner.id[0].isupper() + return False + + 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]`