fix(tpc): 属性读取返回 None 时登记「确切原因」,5 个 return None 点不再同义
类别:a(编译器缺陷 —— fail-fast 报文缺根因区分,5 个不同故障同报"求值为空")
现象:`self.Gen.ExprGen.translate_value(builder, pool, mod, ie.orelse, ...)`
(ExprGenOps.vp:97)报「调用实参编译期求值为空」,而同函数同对象的 `ie.test`
(:43)、`ie.body`(:78)求值正常。三个字段在 `IfExp` 布局里是**连续三槽**
(stub: %"…IfExp" = { i8*, AST*, MemBuddy*, i32×4, list*, AST*(test),
AST*(body), AST*(orelse) },共 11 元素),只有最后那个失败 ⇒ 必然是"偏移越出
body 元素数"这一类,但报文只说"求值为空",无法与"类名未解析 / 基座求值为空 /
结构体未匹配 / stub 无 body"区分。
改动(纯取证,不改变任何判定与 IR):
1. HandlesBase 新增 `_NoteAttrNone(Gen, ClassName, AttrName, Reason)`:把
`(类名, 字段名, 原因)` 记到 `Gen._LastAttrNone`。
2. HandlesExprAttr 的 5 个 return None 点全部登记:
- `_gep_and_Load_member`:偏移为 None;**偏移越界**(附 offset / body 元素数 /
st 版元素数 / 成员表项数)。
- `_handle_attr_name`:类名未解析;基座自身求值为空;结构体未匹配(附 ObjVal
类型);stub 无 body;槽位越界(附 offset / pointee 元素数 / st 元素数)。
3. `_DescribeAttrBase` 追加三项事实:`struct body 元素数`、`字段 X 计算偏移`
(`_get_member_offset`,异常则报异常类型)、以及与被报节点 (类名, 字段名)
一致时回显 `读取侧原因=...`(不一致不回显,避免嵌套读取的旧记录冒充)。
依据:字段名在成员表里能找到却没读到值,只可能是"偏移 ≥ body 元素数"
(`_get_member_offset` 找不到字段时已 fail-fast,不会返回 None);因此
`偏移/body元素数/成员表项数` 三个数字是判定该情况的唯一依据,必须一次报全。
This commit is contained in:
1 parent
0392ccdea0
commit
935a5b372b
2 files changed
+75
-2
No files matched your search
@@ -555,6 +555,21 @@ class BaseHandle:
|
||||
return f"Subscript({BaseHandle._ExprShape(Node.value, Depth + 1)})"
|
||||
return type(Node).__name__
|
||||
|
||||
def _NoteAttrNone(self, Gen: Any, ClassName: str | None, AttrName: str, Reason: str) -> None:
|
||||
"""记录「属性读取返回 None」的**确切原因**(供 fail-fast 报文取证,纯记账)。
|
||||
|
||||
`x.attr` 求值为 None 在 `ExprAttrHandle` 里有 5 个不同来源(类名未解析 /
|
||||
基座求值为空 / 结构体未匹配 / stub 无 body / 槽位越界),各自根因完全不同;
|
||||
下游(_RequireArgValue / AnnAssign)只看到"求值为空",无法区分 ⇒ 报错点与
|
||||
真因相隔数层。此处把 `(类名, 字段名, 原因)` 记到 Gen,由 `_DescribeAttrBase`
|
||||
在报文里回显;回显时要求 `(类名, 字段名)` 与被报节点一致,避免嵌套读取留下的
|
||||
旧记录误导(不改变任何判定与 IR)。
|
||||
"""
|
||||
try:
|
||||
Gen._LastAttrNone = (ClassName or '', AttrName or '', Reason)
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
|
||||
def _DescribeAttrBase(self, Gen: Any, Node: ast.Attribute) -> str:
|
||||
"""属性读返回 None 时的**基址事实**(供 fail-fast 报文取证,不改变任何判定)。
|
||||
|
||||
@@ -568,6 +583,7 @@ class BaseHandle:
|
||||
"""
|
||||
_base: ast.AST = Node.value
|
||||
_parts: list[str] = [f"基座形态={BaseHandle._ExprShape(_base)}"]
|
||||
_vc_head: Any = None
|
||||
if isinstance(_base, ast.Name):
|
||||
_bn: str = _base.id
|
||||
_vc: str | None = None
|
||||
@@ -579,7 +595,8 @@ class BaseHandle:
|
||||
_alloc = (getattr(Gen, 'variables', None) or {}).get(_bn)
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
_parts.append(f"'{_bn}' 的 var_struct_class={_vc!r}")
|
||||
_parts.append(f"'{_bn}' 的 var_struct_class={_vc!r}")
|
||||
_vc_head = _vc
|
||||
_parts.append(f"alloca 类型={getattr(_alloc, 'type', None)}")
|
||||
_parts.append("该名在 Gen.structs="
|
||||
+ ('是' if _vc and _vc in Gen.structs else ('否' if _vc else '—')))
|
||||
@@ -588,6 +605,20 @@ class BaseHandle:
|
||||
if _vc and _vc in Gen.class_members else '无该键'))
|
||||
if _vc and _vc in Gen.class_members:
|
||||
_parts.append(f"字段名单={[m[0] for m in Gen.class_members[_vc]][:12]}")
|
||||
# struct body 元素数 与 该字段的**计算偏移**:字段名在成员表里能找到、
|
||||
# 却没读到值,只可能是"偏移 ≥ body 元素数"(`_gep_and_Load_member` 的
|
||||
# 越界 return None)。这两个数字是判定该情况的唯一依据,必须一并报出。
|
||||
_st_attr: Any = Gen.structs.get(_vc) if _vc else None
|
||||
if _st_attr is not None:
|
||||
_nelem: Any = (len(_st_attr.elements)
|
||||
if getattr(_st_attr, 'elements', None) is not None else 'opaque')
|
||||
_parts.append(f"struct body 元素数={_nelem}")
|
||||
if _vc:
|
||||
try:
|
||||
_off: Any = Gen._get_member_offset(Node.attr, _vc)
|
||||
except Exception as _e: # noqa: BLE001
|
||||
_off = f"<取偏移抛 {type(_e).__name__}>"
|
||||
_parts.append(f"字段 '{Node.attr}' 计算偏移={_off}")
|
||||
elif (isinstance(_base, ast.Call) and isinstance(_base.func, ast.BinOp)
|
||||
and isinstance(_base.func.op, ast.BitOr)):
|
||||
_cls: str | None = None
|
||||
@@ -595,13 +626,28 @@ class BaseHandle:
|
||||
_cls = self.Trans.ExprAttrHandle._extract_union_base_class(_base.func)
|
||||
except Exception: # noqa: BLE001
|
||||
_cls = None
|
||||
_parts.append(f"联合基类名={_cls!r}")
|
||||
_parts.append(f"联合基类名={_cls!r}")
|
||||
_vc_head = _cls
|
||||
_parts.append(f"短名 in structs={'是' if _cls and _cls in Gen.structs else '否'}")
|
||||
_parts.append("class_members="
|
||||
+ (f"{len(Gen.class_members.get(_cls) or [])} 个字段"
|
||||
if _cls in Gen.class_members else '无该键'))
|
||||
_parts.append("注: 强转基座形态本应走 _FailFastAttrCallBase 详细报文,"
|
||||
"此处仍是本条 ⇒ 那条 fail-fast 未触发")
|
||||
# 回显读取侧的**确切失败原因**(`ExprAttrHandle` 各 return None 点登记)。
|
||||
# 仅在 (类名, 字段名) 与被报节点一致时输出:嵌套读取留下的旧记录不得冒充本节点。
|
||||
try:
|
||||
_rec: Any = getattr(Gen, '_LastAttrNone', None)
|
||||
except Exception: # noqa: BLE001
|
||||
_rec = None
|
||||
if isinstance(_rec, tuple) and len(_rec) >= 3:
|
||||
_rec_cls, _rec_attr, _rec_reason = _rec[0], _rec[1], _rec[2]
|
||||
_short = lambda s: (s.split('.')[-1] if s else s) # noqa: E731
|
||||
_same: bool = bool(_rec_attr) and _rec_attr == Node.attr and (
|
||||
not _rec_cls or not _vc_head or _rec_cls == _vc_head
|
||||
or _short(_rec_cls) == _short(_vc_head))
|
||||
if _same:
|
||||
_parts.append(f"读取侧原因={_rec_reason}")
|
||||
return ";".join(_parts)
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -24,6 +24,8 @@ class ExprAttrHandle(BaseHandle):
|
||||
def _gep_and_Load_member(self, Gen: LlvmCodeGenerator, ObjVal: ir.Value, offset: int | None, AttrName: str, ClassName: str, ST: ir.Type | None = None) -> ir.Value | None:
|
||||
"""从结构体指针通过 GEP 取成员,处理 array/struct pointer/class_members 特殊情况"""
|
||||
if offset is None:
|
||||
self._NoteAttrNone(Gen, ClassName, AttrName,
|
||||
f"偏移为 None(字段 '{AttrName}' 未进成员表)")
|
||||
return None
|
||||
if isinstance(ObjVal.type, ir.PointerType):
|
||||
pointee: Any = ObjVal.type.pointee
|
||||
@@ -32,6 +34,15 @@ class ExprAttrHandle(BaseHandle):
|
||||
if ST is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None and offset < len(ST.elements):
|
||||
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_gep_{ClassName}")
|
||||
else:
|
||||
_st_len: Any = (len(ST.elements)
|
||||
if isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None
|
||||
else 'None/opaque')
|
||||
self._NoteAttrNone(
|
||||
Gen, ClassName, AttrName,
|
||||
f"偏移越界:字段 '{AttrName}' 需槽位 {offset},"
|
||||
f"但 body 只有 {len(pointee.elements)} 个元素,"
|
||||
f"stub 版结构体(st)也仅 {_st_len} 个 ⇒ 布局与成员表不同源"
|
||||
f"(成员表 {len(Gen.class_members.get(ClassName, []))} 项)")
|
||||
return None
|
||||
MemberPtr: Any = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
|
||||
if isinstance(MemberPtr.type, ir.PointerType) and isinstance(MemberPtr.type.pointee, ir.ArrayType):
|
||||
@@ -505,6 +516,9 @@ class ExprAttrHandle(BaseHandle):
|
||||
if getattr(Gen, 'global_struct_class', None) is not None:
|
||||
Gen.global_struct_class[VarName] = _recovered
|
||||
if not ClassName:
|
||||
self._NoteAttrNone(Gen, None, AttrName,
|
||||
f"类名未解析('{VarName}' 的 var_struct_class 为空且无法由 "
|
||||
f"alloca/值类型恢复)")
|
||||
return None
|
||||
|
||||
# 检查 property getter
|
||||
@@ -569,6 +583,8 @@ class ExprAttrHandle(BaseHandle):
|
||||
# 普通结构体成员访问
|
||||
ObjVal = self.HandleExprLlvm(Node.value)
|
||||
if not ObjVal:
|
||||
self._NoteAttrNone(Gen, ClassName, AttrName,
|
||||
f"基座 '{VarName}' 自身求值为空(类名={ClassName!r})")
|
||||
return None
|
||||
# char* → struct* cast
|
||||
if self._is_char_pointer(ObjVal):
|
||||
@@ -661,12 +677,18 @@ class ExprAttrHandle(BaseHandle):
|
||||
name=f"cast_slot_{ClassName}")
|
||||
struct_match = True
|
||||
if not struct_match:
|
||||
self._NoteAttrNone(
|
||||
Gen, ClassName, AttrName,
|
||||
f"结构体未匹配(ObjVal 类型={ObjVal.type},类名={ClassName!r};"
|
||||
f"pointee 既非该类 struct,静态类名也未能 bitcast 兜底)")
|
||||
return None
|
||||
|
||||
st: Any
|
||||
ok: bool
|
||||
st, ok = self._try_Load_struct_from_stub(Gen, ClassName)
|
||||
if not ok:
|
||||
self._NoteAttrNone(Gen, ClassName, AttrName,
|
||||
f"stub 无 body(Gen.structs[{ClassName!r}] 存在但 elements 为空/opaque)")
|
||||
return None
|
||||
|
||||
# bitfield 处理
|
||||
@@ -702,6 +724,11 @@ class ExprAttrHandle(BaseHandle):
|
||||
if st is not None and isinstance(st, ir.IdentifiedStructType) and st.elements is not None and offset < len(st.elements):
|
||||
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(st), name=f"cast_load_{ClassName}")
|
||||
else:
|
||||
self._NoteAttrNone(
|
||||
Gen, ClassName, AttrName,
|
||||
f"槽位越界(_handle_attr_name 侧):字段 '{AttrName}' 需槽位 "
|
||||
f"{offset},但 ObjVal 的 pointee 只有 {len(actual_elems)} 个元素,"
|
||||
f"st 版结构体也不够({len(st.elements) if isinstance(st, ir.IdentifiedStructType) and st.elements is not None else 'None/opaque'})")
|
||||
return None
|
||||
result = self._gep_and_Load_member(Gen, ObjVal, offset, AttrName, ClassName, st)
|
||||
return result
|
||||
|
||||
Reference in new issue
Block a user