diff --git a/lib/core/Handles/HandlesAssign.py b/lib/core/Handles/HandlesAssign.py index f29f163..357f7c2 100644 --- a/lib/core/Handles/HandlesAssign.py +++ b/lib/core/Handles/HandlesAssign.py @@ -847,7 +847,19 @@ class AssignHandle(BaseHandle): Gen._store(Value, Gen.variables[VarName]) return MemberPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - self._StoreWithCoerce(Value, MemberPtr) + # 成员槽写入失败时补「来源明细」:槽类型的两个来源(class_members + # 声明侧 / struct body 布局侧)是两条独立链,不一致时会静默写进 + # 类型完全不同的槽。报文里逐项列出两侧数据,直接判定谁退化了 + # (现场:Generator.ExprGenConst 声明 %"...ExprGenConst"* 却落到 i8**)。 + try: + self._StoreWithCoerce(Value, MemberPtr) + except Exception as _Me: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if not isinstance(_Me, _FFE): + raise + raise _FFE( + f"{_Me}{self._DescribeMemberSlot(Gen, ClassName, AttrName, offset, SelfPtr)}" + ) from _Me elif isinstance(Target.value, ast.Name): VarName: str = Target.value.id @@ -1533,6 +1545,55 @@ class AssignHandle(BaseHandle): f"(`rawptr[T] | t.CPtr` 或 `rawptr[rawptr[T]]`)后再赋值;" f"或把基座显式转为 t.CPtr(无类型指针)后按 8 字节指针语义写入。{SrcInfo}") + def _DescribeMemberSlot(self, Gen: "Translator.LlvmGen", ClassName: str | None, + FieldName: str, Offset: int, SelfPtr: ir.Value) -> str: + """`self.字段 = 值` 成员槽写入失败的**来源明细**(供 fail-fast 报文引用)。 + + 槽类型有两个**互相独立**的来源,必须自洽: + - 声明侧:`Gen.class_members[类][下标]`(CollectAnnAssignMember / .vpi 加载); + - 布局侧:`SelfPtr` 所指 struct 的 `elements[offset]`(StructGen / stub 解析)。 + 两者不一致时,GEP 会把值写进**类型完全不同**的槽(不是"转换",是越界/错位), + 现场:Generator.ExprGenConst 声明 `%"...ExprGenConst"*` 却落到 `i8**`。 + 此处把两侧数据逐项列出,直接判定是声明侧退化还是布局侧退化(含 structs + 键空间大小与"是否同一对象",用于识别裸名 / sha1 全名双条目)。 + """ + Lines: list[str] = [] + try: + Members: list = Gen.class_members.get(ClassName) or [] + Base: int = Gen._member_base(ClassName) + Idx: int = Offset - Base + Declared: Any = Members[Idx][1] if 0 <= Idx < len(Members) else None + Lines.append( + f" class_members[{ClassName}] 项数={len(Members)} base={Base} " + f"下标={Idx} 声明槽类型={Declared}") + except Exception as _e1: + Lines.append(f" class_members 读取失败: {type(_e1).__name__}: {_e1}") + Layout: Any = None + try: + Layout = getattr(SelfPtr.type, 'pointee', None) + Elems: Any = getattr(Layout, 'elements', None) + try: + LayoutName: Any = getattr(Layout, 'name', None) + except Exception: + LayoutName = '<未命名>' + Elem: Any = None + if Elems is not None and 0 <= Offset < len(Elems): + Elem = Elems[Offset] + Lines.append( + f" 布局 struct name={LayoutName!r} 元素数=" + f"{len(Elems) if Elems is not None else None} elements[{Offset}]={Elem}") + except Exception as _e2: + Lines.append(f" 布局 struct 读取失败: {type(_e2).__name__}: {_e2}") + try: + _Structs: dict = getattr(Gen, 'structs', {}) or {} + _ByName: Any = _Structs.get(ClassName) + Lines.append( + f" structs 键数={len(_Structs)} structs[{ClassName}] 与布局同一对象=" + f"{_ByName is Layout}") + except Exception: + pass + return f"\n [成员槽来源] self.{FieldName} = ...\n" + "\n".join(Lines) + def _StoreWithCoerce(self, Value: ir.Value, Ptr: ir.Value, Node: ast.AST | None = None) -> None: Gen: "Translator.LlvmGen" = self.Trans.LlvmGen if isinstance(Ptr.type, ir.PointerType): diff --git a/lib/core/Handles/HandlesImports.py b/lib/core/Handles/HandlesImports.py index 2d7af94..317dcdc 100644 --- a/lib/core/Handles/HandlesImports.py +++ b/lib/core/Handles/HandlesImports.py @@ -3585,6 +3585,18 @@ class ImportHandle(BaseHandle): pointee_type = self._parse_llvm_type(pointee, Gen, source_sha1=source_sha1, create_structs=create_structs) if pointee_type is not None: return ir.PointerType(pointee_type) + # 被指类型是**具名聚合**(`%...`)却在"允许建结构体"模式下解析不出来: + # 具名类型必然可由 _get_or_create_struct 兜出,取不到即内部不变量已被破坏 + # (成员表/布局两链不同源、名字带多余前缀等)。静默退化成 i8* 会把 + # 「结构体指针」悄悄变成通用指针,外层再套一层即成 i8**(现场: + # Generator.ExprGenConst 声明 %"...ExprGenConst"* 却落到 i8**)。 + # create_structs=False 时未加载不属异常 ⇒ 只在该模式下响亮失败。 + if create_structs and pointee.startswith('%'): + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + raise _FFE( + f"具名类型指针的被指类型解析失败:'{type_str}' 的被指部分 '{pointee}'" + f" 在 create_structs=True 下仍取不到结构体(source_sha1={source_sha1})" + f"—— 拒绝静默退化为 i8*(会使结构体指针变通用指针,再套一层即 i8**)") return ir.PointerType(ir.IntType(8)) if type_str.startswith('[') and ']' in type_str: diff --git a/lib/core/Handles/HandlesTypeMerge.py b/lib/core/Handles/HandlesTypeMerge.py index 8a94d4f..bb99253 100644 --- a/lib/core/Handles/HandlesTypeMerge.py +++ b/lib/core/Handles/HandlesTypeMerge.py @@ -5,7 +5,7 @@ if TYPE_CHECKING: from typing import List from lib.core.Handles.HandlesBase import BaseHandle, CTypeHelper, CTypeInfo from lib.core.SymbolUtils import ( - IsTModule, IsListAnnotation, IsPtrSubscriptNode, + IsTModule, IsListAnnotation, IsPtrSubscriptNode, IsCPtrMarkerNode, DesugarPtrSubscriptToUnion) from lib.includes import t import lib._bootstrap # noqa: F401 设置 sys.path(项目根 + lib/includes) @@ -495,6 +495,21 @@ class HandlesTypeMerge(BaseHandle): if IsPtrSubscriptNode(Node, self.Trans.SymbolTable): return self.MergeTypes(DesugarPtrSubscriptToUnion(Node, self.Trans.SymbolTable)) + # 判据自洽守卫(fail-fast):base 是指针标记(`rawptr` / `t.CPtr` / 裸名 + # `CPtr` / 别名 `X[...]`,见 IsPtrSubscriptNode)却被上面那条判据漏掉 ⇒ + # 两处判据分裂(内部不变量被破坏)。历史行为是落到下方 CollectAll 当成 + # 「数组下标」解析,**静默**产出"基座 void + 多层指针"(如 i8**),与 + # declare 端的结构体指针分裂(现场:Generator.ExprGenConst 声明 + # `%"...ExprGenConst"*` 却落到 i8**)。按「不许静默降级」改为响亮失败, + # 暴露上游根因。 + if (isinstance(Node.value, ast.Name) and Node.value.id == 'rawptr') \ + or IsCPtrMarkerNode(Node.value, self.Trans.SymbolTable): + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"指针下标判据分裂:注解 '{ast.unparse(Node)}' 的 base 是指针标记," + f"但 IsPtrSubscriptNode 未命中 —— 拒绝落到 CollectAll 静默产出" + f"「基座 void 的多层指针」(结构体指针会被悄悄变成 i8**)") + def CollectAll(node: ast.AST, ParentIsArrayPtr: bool = False) -> CTypeInfo: """鏀堕泦绫诲瀷淇℃伅锛岃繑鍥?CTypeInfo""" if isinstance(node, ast.Subscript):