From 935a5b372bf1a5ffe84f1cadff96b9018991b47a Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 29 Sep 2026 17:46:58 +0800 Subject: [PATCH] =?UTF-8?q?fix(tpc):=20=E5=B1=9E=E6=80=A7=E8=AF=BB?= =?UTF-8?q?=E5=8F=96=E8=BF=94=E5=9B=9E=20None=20=E6=97=B6=E7=99=BB?= =?UTF-8?q?=E8=AE=B0=E3=80=8C=E7=A1=AE=E5=88=87=E5=8E=9F=E5=9B=A0=E3=80=8D?= =?UTF-8?q?=EF=BC=8C5=20=E4=B8=AA=20return=20None=20=E7=82=B9=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E5=90=8C=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类别: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元素数/成员表项数` 三个数字是判定该情况的唯一依据,必须一次报全。 --- lib/core/Handles/HandlesBase.py | 50 +++++++++++++++++++++++++++-- lib/core/Handles/HandlesExprAttr.py | 27 ++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/lib/core/Handles/HandlesBase.py b/lib/core/Handles/HandlesBase.py index 5d48440..cf2a632 100644 --- a/lib/core/Handles/HandlesBase.py +++ b/lib/core/Handles/HandlesBase.py @@ -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 diff --git a/lib/core/Handles/HandlesExprAttr.py b/lib/core/Handles/HandlesExprAttr.py index 39caad6..3c53ea0 100644 --- a/lib/core/Handles/HandlesExprAttr.py +++ b/lib/core/Handles/HandlesExprAttr.py @@ -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