fix(tpc): 联合强转 (T|t.CPtr)(x) 及其属性访问失败改为 fail-fast(附判定依据)
类别:b 机制漏豁免(把静默 return None 改成响亮失败并带证据)。 现场:TransPyV lib/core/Gen/Expr/ExprGenBase.vp:360 的 v = (ast.Expr*(node)).value 报“赋值表达式 'value'(Attribute 形态)编译期 求值为空”,而同一函数前三条 (ast.Assign/AnnAssign/AugAssign*(node)).value 同款写法正常 ⇒ 无法从报错看出是哪一步丢了类型。 两处静默返回改为 fail-fast(均只对该强转形态生效,其余路径行为不变): 1. HandlesExprCall._HandleTypeUnionCastLlvm:目标类型串解析失败(原为 _vlog().warning + return None)。新增 _DescribeUnionCastFailure 取证: MergeTypes 返回值/ToString()/异常、_ExtractTypeStrFromUnionBinOp 结果, 以及联合中每个操作数在符号表与类登记表(Gen.structs / _struct_sha1_map / class_sha1_map)的命中情况 —— 直接指认“基座类名没被当成类”还是 “CPtr 标记侧解析失败”。 2. HandlesExprAttr._handle_attr_call_result:(T|t.CPtr)(x).attr 的 5 个 return None(基座求值为空 / 非指针 / find_struct_by_pointee 未命中 / 结构体不透明 / 成员偏移取不到)改为 _FailFastAttrCallBase,报文含联合 基类名与三家登记表命中、被指类型 elements、class_members 项数与目标成员偏移。 原因:(T|t.CPtr)(x) 只可能来自指针强转(parser 把 (T*)x / (T*)(x) / T*(x) 统一产成该 Call),必须有值产出;静默丢弃会让整条表达式被判为空,把 “类名解析失败 / 结构体不透明 / 成员偏移取不到”三种不同根因混成同一条报文。
This commit is contained in:
1 parent
8263f6ec47
commit
fd212b3ff8
2 files changed
+166
-13
No files matched your search
@@ -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 提取第一个非指针/非修饰符的类名
|
||||
|
||||
|
||||
@@ -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"<ToString 异常 {type(_E).__name__}: {_E}>"
|
||||
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 手动提取类型字符串。
|
||||
|
||||
|
||||
Reference in new issue
Block a user