diff --git a/lib/core/Handles/HandlesExprAttr.py b/lib/core/Handles/HandlesExprAttr.py index d99906d..39caad6 100644 --- a/lib/core/Handles/HandlesExprAttr.py +++ b/lib/core/Handles/HandlesExprAttr.py @@ -951,25 +951,39 @@ class ExprAttrHandle(BaseHandle): ObjVal = inner_val if ObjVal is None: ObjVal = self.HandleExprLlvm(Node.value) - if not ObjVal: - return None # 联合类型转换 (X | t.CPtr)(arg) 的属性访问(治本修复): # TPC 联合转换对非 CType 类(如 ast.Name / ast.Call)目标类型解析失败, # 结果可能退回 Ptr(i8) 或错误类型,导致下方 find_struct_by_pointee 失败、 # 属性访问被静默丢弃——((ast.Name|t.CPtr)(ov)).id 编译后 store 0 次 # (with 语句 asname 未绑定、pool 未定义根因)。从联合 BinOp 提取真实 # 基础类并 bitcast 到 Ptr(类) 再访问成员。 - if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.BinOp) \ - and isinstance(Node.value.func.op, ast.BitOr): - union_cls: str | None = self._extract_union_base_class(Node.value.func) - if union_cls and union_cls in Gen.structs: - target_ty: Any = ir.PointerType(Gen.structs[union_cls]) + # + # 该形态的每个 return None 都改成 fail-fast(见 _FailFastAttrCallBase): + # `(T | t.CPtr)(x)` 只可能来自指针强转(parser 把 `(T*)x` / `T*(x)` 统一 + # 产成该 Call),返回 None 会让调用方只报"表达式编译期求值为空",看不出是 + # 类名没被当成类、结构体不透明、还是成员偏移取不到(现场:TransPyV + # ExprGenBase.vp:360 `v = (ast.Expr*(node)).value`,而同一函数前三条 + # `(ast.Assign/AnnAssign/AugAssign*(node)).value` 同款写法正常)。 + _IsUnionCastBase: bool = (isinstance(Node.value, ast.Call) + and isinstance(Node.value.func, ast.BinOp) + and isinstance(Node.value.func.op, ast.BitOr)) + _UnionCastCls: str | None = None + if _IsUnionCastBase: + _UnionCastCls = self._extract_union_base_class(Node.value.func) + if ObjVal is not None and _UnionCastCls and _UnionCastCls in Gen.structs: + target_ty: Any = ir.PointerType(Gen.structs[_UnionCastCls]) if isinstance(ObjVal.type, ir.PointerType): if ObjVal.type != target_ty: ObjVal = Gen.builder.bitcast(ObjVal, target_ty, name="cast_union_attr") elif isinstance(ObjVal.type, ir.IntType): ObjVal = Gen.builder.inttoptr(ObjVal, target_ty, name="int2ptr_union_attr") + if not ObjVal: + self._FailFastAttrCallBase("基座强转调用求值为空(HandleExprLlvm 返回 None)", + Node, Gen, _IsUnionCastBase, _UnionCastCls, None) + return None if not isinstance(ObjVal.type, ir.PointerType): + self._FailFastAttrCallBase("基座强转结果不是指针类型", + Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) return None pointee: Any = ObjVal.type.pointee if isinstance(pointee, ir.PointerType): @@ -977,6 +991,8 @@ class ExprAttrHandle(BaseHandle): pointee = ObjVal.type.pointee found: Any = Gen.find_struct_by_pointee(pointee) if not found: + self._FailFastAttrCallBase("被指结构体未在 structs 登记(find_struct_by_pointee 未命中)", + Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) return None CN: str ST: Any @@ -988,15 +1004,78 @@ class ExprAttrHandle(BaseHandle): spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN) if spec_val is not None: return spec_val + self._FailFastAttrCallBase("结构体不透明(stub / .vpi 未加载出字段)", + Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) return None bitfield_offsets: Any = Gen.class_member_bitoffsets.get(CN, {}) if Node.attr in bitfield_offsets: return self._load_bitfield_member(ObjVal, CN, Node.attr) offset: int | None = Gen._get_member_offset(Node.attr, CN) if offset is None: + self._FailFastAttrCallBase("成员偏移取不到(class_members / 继承链均未命中)", + Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) return None return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) + def _FailFastAttrCallBase(self, Stage: str, Node: ast.Attribute, Gen: Any, + IsUnionCastBase: bool, UnionCls: str | None, + ObjVal: Any) -> None: + """`(T | t.CPtr)(x).attr` 属性访问失败时的 fail-fast(非该形态时静默返回,行为不变)。 + + `(T | t.CPtr)(x)` 只可能来自指针强转(parser 把 `(T*)x` / `(T*)(x)` / `T*(x)` + 统一产成该 Call),其属性访问必须有值产出。此前一律 `return None`,调用方只能 + 报"赋值表达式 'value'(Attribute 形态)编译期求值为空",把"类名没被当成类 / + 结构体不透明 / 成员偏移取不到"三种完全不同的根因混成一条报文 + (现场:TransPyV ExprGenBase.vp:360 `v = (ast.Expr*(node)).value`)。 + 此处逐项列出判定依据,直接指认失败环节。 + """ + if not IsUnionCastBase: + return + from lib.core.LLVMCG.FuncGen import FailFastError + _Parts: list[str] = [ + f"属性 '{Node.attr}' 的基座是强转调用 '{self._ExprText(Node.value)}'," + f"失败环节:{Stage}", + f"联合基类名={UnionCls!r}" + f";短名 in structs={'是' if (UnionCls and UnionCls in Gen.structs) else '否'}" + f";structs 同短名键=" + f"{[k for k in Gen.structs if UnionCls and k.split('.')[-1] == UnionCls][:6]}" + f";_struct_sha1_map={getattr(Gen, '_struct_sha1_map', {}).get(UnionCls)!r}" + f";class_sha1_map={getattr(Gen, 'class_sha1_map', {}).get(UnionCls)!r}", + ] + if ObjVal is None: + _Parts.append("基座求值结果=None") + else: + _Parts.append(f"基座求值类型={ObjVal.type!r}") + if isinstance(ObjVal.type, ir.PointerType): + _Pt: Any = ObjVal.type.pointee + _Parts.append(f"被指类型={_Pt!r};elements=" + f"{getattr(_Pt, 'elements', 'n/a')}") + _Found: Any = Gen.find_struct_by_pointee(_Pt) + _Parts.append( + f"find_struct_by_pointee={'命中 ' + str(_Found[0]) if _Found else '未命中'}") + if _Found: + _CN: str = _Found[0] + _Parts.append( + f"class_members[{_CN!r}]=" + f"{[m[0] for m in Gen.class_members.get(_CN, [])][:12]}" + f"({len(Gen.class_members.get(_CN, []))} 项)" + f";成员 '{Node.attr}' 偏移={self._SafeOffset(Gen, Node.attr, _CN)!r}") + if UnionCls: + _Parts.append( + f"class_members[{UnionCls!r}]=" + f"{[m[0] for m in Gen.class_members.get(UnionCls, [])][:12]}" + f"({len(Gen.class_members.get(UnionCls, []))} 项)" + f";class_parent={getattr(Gen, 'class_parent', {}).get(UnionCls)!r}") + raise FailFastError(";".join(_Parts)) + + @staticmethod + def _SafeOffset(Gen: Any, Attr: str, ClassName: str) -> Any: + """取证用成员偏移查询:本身绝不能再抛异常掩盖原始故障。""" + try: + return Gen._get_member_offset(Attr, ClassName) + except Exception as _E: + return f"<查询异常 {type(_E).__name__}: {_E}>" + def _extract_union_base_class(self, NodeFunc: ast.BinOp) -> str | None: """从 (X | t.CPtr) 联合 BinOp 提取第一个非指针/非修饰符的类名 diff --git a/lib/core/Handles/HandlesExprCall.py b/lib/core/Handles/HandlesExprCall.py index c3c5ef0..c8cc21c 100644 --- a/lib/core/Handles/HandlesExprCall.py +++ b/lib/core/Handles/HandlesExprCall.py @@ -2065,14 +2065,88 @@ class ExprCallHandle(BaseHandle): ) target_type_str = self._ExtractTypeStrFromUnionBinOp(Node.func) if not target_type_str: - # Fallback 也无法解析:记录详细诊断信息后返回 None - _vlog().warning( - f"_HandleTypeUnionCastLlvm: fallback 解析失败,无法确定目标类型 " - f"(Node.func={ast.dump(Node.func)})" - ) - return None + # fail-fast(原为 _vlog().warning + return None): + # `(T | t.CPtr)(x)` 只可能来自指针强转——parser 把 `(T*)x` / `(T*)(x)` / + # `T*(x)`(含 `**`)统一产成 func = `T | t.CPtr` 的 Call(见 + # vast/ExprParser._ConsumePtrSuffixCall、vast_c.c ConsumePtrSuffixCall)。 + # 该形态必须有值产出;返回 None 会让调用方把整条表达式当"编译期求值为空" + # 丢掉(现场:TransPyV ExprGenBase.vp:360 `v = (ast.Expr*(node)).value` + # → TPC 报"赋值表达式 'value'(Attribute 形态)编译期求值为空",而同函数 + # 前三条 `(ast.Assign/AnnAssign/AugAssign*(node)).value` 同款写法正常)。 + # 故把"哪一步没解析出目标类型"逐项摊开,而不是静默丢弃整条表达式。 + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError(self._DescribeUnionCastFailure( + Node, Gen, type_info, MergeException, target_type_str)) return self._HandleTypeCastLlvm(Node.args[0], target_type_str) + def _DescribeUnionCastFailure(self, Node: ast.Call, Gen: Any, TypeInfo: Any, + MergeException: Any, TargetStr: Any) -> str: + """联合类型强转目标类型解析失败时的判定依据(逐项列出,供 fail-fast 报文)。 + + 只做取证,不做类型解析:把 MergeTypes / _ExtractTypeStrFromUnionBinOp 的 + 返回值、异常,以及联合中每个操作数在符号表与类登记表(Gen.structs / + _struct_sha1_map / class_sha1_map)里的命中情况全部列出,使"是基座类名 + 没被当成类"还是"CPtr 标记侧解析失败"一眼可分。 + """ + _Flat: list = [] + + def _Collect(n: ast.AST) -> None: + if isinstance(n, ast.BinOp) and isinstance(n.op, ast.BitOr): + _Collect(n.left) + _Collect(n.right) + else: + _Flat.append(n) + + _Collect(Node.func) + _Operands: list[str] = [] + for _op in _Flat: + _short: str = '' + _full: str = '' + if isinstance(_op, ast.Attribute): + _short = _op.attr + _cur: ast.AST = _op.value + while isinstance(_cur, ast.Attribute): + _cur = _cur.value + _full = f"{_cur.id}.{_short}" if isinstance(_cur, ast.Name) else _short + elif isinstance(_op, ast.Name): + _short = _op.id + _full = _short + else: + _Operands.append(f"<{type(_op).__name__}>(非类型名形态)") + continue + _sym: Any = None + try: + _sym = self.Trans.SymbolTable.lookup(_short) + except Exception: + _sym = None + _verdict: str = '符号表未命中' + if _sym is not None: + _verdict = (f"IsStruct={getattr(_sym, 'IsStruct', None)}" + f" IsRenum={getattr(_sym, 'IsRenum', None)}" + f" IsEnum={getattr(_sym, 'IsEnum', None)}" + f" IsEnumMember={getattr(_sym, 'IsEnumMember', None)}" + f" IsTypedef={getattr(_sym, 'IsTypedef', None)}" + f" Name={getattr(_sym, 'Name', None)!r}") + _Operands.append( + f"{_full}({_verdict};in structs={_short in Gen.structs}" + f";structs 同短名键={[k for k in Gen.structs if k.split('.')[-1] == _short][:4]}" + f";_struct_sha1_map={getattr(Gen, '_struct_sha1_map', {}).get(_short)!r}" + f";class_sha1_map={getattr(Gen, 'class_sha1_map', {}).get(_short)!r})") + + _ToStr: str = '<非 CTypeInfo>' + try: + if isinstance(TypeInfo, CTypeInfo): + _ToStr = repr(TypeInfo.ToString()) + elif TypeInfo is None: + _ToStr = 'None' + except Exception as _E: + _ToStr = f"" + return (f"联合类型强转 '({ast.unparse(Node.func)})(...)' 无法确定目标类型," + f"拒绝静默返回 None(会让整条表达式被判为『编译期求值为空』而丢弃):" + f"MergeTypes 返回={type(TypeInfo).__name__}(ToString()={_ToStr});" + f"_ExtractTypeStrFromUnionBinOp={TargetStr!r};" + f"MergeTypes 异常={MergeException!r};操作数判据:" + ";".join(_Operands)) + def _ExtractTypeStrFromUnionBinOp(self, NodeFunc: ast.AST) -> str | None: """Fallback:从类型联合 BinOp 手动提取类型字符串。