fix(tpc): 降级判据改用「注解形态 + 解析结果形状」,放在字段类型唯一漏斗
上一版拦截失效的原因(实测):`class_member_annotations` 全仓**只被声明与读取、从未被
写入**(Step 2 一直是空转),故依赖它的 `_HasDegradedMemberTypes` 恒为 False。改法:
1) SymbolUtils 新增两个**形状判据**(纯判定,不做类型解析):
- IsI8DoublePtrType(T):是否 i8 系两层指针(`i8**`)= `T | t.CPtr` 在 T 类型丢失后
退化成的形状;
- IsClassRefPtrAnnotation(Ann):注解是否「指针包装的类引用」(rawptr[Mod.Class] /
Mod.Class | t.CPtr;内层为非 t/c 模块的 Attribute 或首字母大写裸名)。
2) HandlesBase.CollectAnnAssignMember(字段类型的**唯一漏斗**,覆盖本地类 /
跨模块 .vpi / 嵌套类三条写入链)新增硬判据:注解是类引用指针 **且** 解析结果正是
`i8**` ⇒ 必属类型信息丢失,当场 fail-fast,并打出 CTypeInfo 全字段(BaseType/
PtrCount/IsStruct/IsTypedef/IsEnum/Name)与内层名的符号表条目标志。此前的
`_CheckPtrAnnotationKeepsClass` 只看"void 基座且无类身份",而现场这条链**带 Name/
IsTypedef**(Attribute 分支的 typedef 出口 → BaseType=CVoid 但 Name=类名),正好漏过。
3) StructGen 删除依赖那张空表的 `_HasDegradedMemberTypes` / `_IsI8DoublePtr` /
`_IsClassRefPtrAnnotation` 及 set_body 前的拦截(改由漏斗处兜住,不留死代码)。
现场:Generator.vp:200 `self.ExprGenFstr = SubExprGenFstr`
→ 布局槽 elements[11]=i8** 而声明 %"a7b68c79f39cb4d3.ExprGenFstr"*
This commit is contained in:
1 parent
66de269ebf
commit
7167703ca7
3 files changed
+91
-84
No files matched your search
@@ -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,
|
||||
|
||||
@@ -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` 仍以 `"<name> 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:
|
||||
|
||||
@@ -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]`
|
||||
|
||||
Reference in new issue
Block a user