fix(tpc): fail-fast 报文补「属性基址取证事实」,属性读返回 None 不再靠反推

类别:a(编译器缺陷 —— fail-fast 报文缺取证事实,无法一眼定位根因)

现象:HandlesBase._RaiseUnresolvedExpr 与 HandlesExprCall._RequireArgValue 在
「属性读取值失败」时只报 Node 形态(Attribute),报错点与真因相隔数层调用,
需要人工从 Gen.var_struct_class / Gen.structs / Gen.class_members 反推。

改动:
1. HandlesBase 抽出公开方法 _DescribeAttrBase(Gen, Node) -> str(原 _RaiseUnresolvedExpr
   内联代码抽出,行为不变),对两类基座给事实:
   - Name 基座:var_struct_class / global_struct_class / alloca 类型 / 是否命中
     Gen.structs / class_members 字段数与字段名单(前 12)。
   - Call(func=BinOp(BitOr)) 强转基座:联合基类名 + 短名是否命中 Gen.structs +
     字段数,并显式标注「强转基座本应走 _FailFastAttrCallBase 详细报文,此处仍是本条
     ⇒ 那条 fail-fast 未触发」,把「另一处缺口」这一事实直接写进报文。
2. _RaiseUnresolvedExpr 改调 _DescribeAttrBase(删除内联重复代码)。
3. HandlesExprCall._RequireArgValue 在实参是 Attribute 时追加同一份基址诊断
   (与 HandlesBase 共用,保证两处口径逐字一致)。

不改变任何判定逻辑,仅补取证事实。
This commit is contained in:
TermiNexus committed 2026-09-29 17:40:23 +08:00
1 parent b3bc94f99f
commit 0392ccdea0
2 files changed
+62 -41

No files matched your search

+52 -41
View File
@@ -555,6 +555,55 @@ class BaseHandle:
return f"Subscript({BaseHandle._ExprShape(Node.value, Depth + 1)})"
return type(Node).__name__
def _DescribeAttrBase(self, Gen: Any, Node: ast.Attribute) -> str:
"""属性读返回 None 时的**基址事实**(供 fail-fast 报文取证,不改变任何判定)。
两类根因:① 类名登记退化(var_struct_class 存 'rawptr' / 'rawptr[X]' / 折叠态,
或压根没登记);② 登记名与 Gen.structs / class_members 的键不同源(短名 vs
sha1 全名)。另有一条**必须验伪**的可能:基座是强转调用 `(T*(x)).attr`
(parser 统一产成 Call(func=BinOp(BitOr)))时,HandlesExprAttr 的
`_handle_attr_call_result` 自带 `_FailFastAttrCallBase` 详细 fail-fast
(FailFastError 全仓无 except,必上抛)——若仍走到本诊断,即说明那条
fail-fast 未被走到,属**另一处缺口**,故显式标注该事实。
"""
_base: ast.AST = Node.value
_parts: list[str] = [f"基座形态={BaseHandle._ExprShape(_base)}"]
if isinstance(_base, ast.Name):
_bn: str = _base.id
_vc: str | None = None
_alloc: Any = None
try:
_vc = (getattr(Gen, 'var_struct_class', None) or {}).get(_bn)
if not _vc:
_vc = (getattr(Gen, 'global_struct_class', None) or {}).get(_bn)
_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"alloca 类型={getattr(_alloc, 'type', None)}")
_parts.append("该名在 Gen.structs="
+ ('是' if _vc and _vc in Gen.structs else ('否' if _vc else '—')))
_parts.append("class_members="
+ (f"{len(Gen.class_members.get(_vc) or [])} 个字段"
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]}")
elif (isinstance(_base, ast.Call) and isinstance(_base.func, ast.BinOp)
and isinstance(_base.func.op, ast.BitOr)):
_cls: str | None = None
try:
_cls = self.Trans.ExprAttrHandle._extract_union_base_class(_base.func)
except Exception: # noqa: BLE001
_cls = None
_parts.append(f"联合基类名={_cls!r}")
_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 未触发")
return ";".join(_parts)
@staticmethod
def _ExprText(Node: ast.AST) -> str:
"""把表达式节点渲染成源码文本(如 func.Blocks.Head、list[X*] * uint64(v)),供错误信息使用"""
@@ -684,48 +733,10 @@ class BaseHandle:
f"{(',注解 ' + ann_text) if ann_text else ''}。位置 {loc}"
f"{(',源码: ' + code_line) if code_line else ''}")
# 【基址诊断】属性读返回 None 时报出**基座真实形态 + 类名登记事实**,不再从
# "求值为空"反推。两类根因:① 类名登记退化(var_struct_class 存 'rawptr' /
# 'rawptr[X]' / 折叠态,或压根没登记);② 登记名与 Gen.structs / class_members
# 的键不同源(短名 vs sha1 全名)。另有一条**必须验伪**的可能:基座是强转调用
# `(T*(x)).attr`(parser 统一产成 Call(func=BinOp(BitOr)))时,HandlesExprAttr
# 的 `_handle_attr_call_result` 自带 `_FailFastAttrCallBase` 详细 fail-fast
# (FailFastError 全仓无 except,必上抛)——若这里仍是通用报文,即说明那条
# fail-fast 未被走到,属**另一处缺口**,故显式标注该事实。
# "求值为空"反推(同一份事实也供 _RequireArgValue 的实参求值失败路径使用,
# 见 HandlesExprCall._RequireArgValue)。
if isinstance(Node, ast.Attribute):
_base: ast.AST = Node.value
_parts: list[str] = [f"基座形态={BaseHandle._ExprShape(_base)}"]
if isinstance(_base, ast.Name):
_bn: str = _base.id
_vc: str | None = None
_alloc: Any = None
try:
_vc = (getattr(Gen, 'var_struct_class', None) or {}).get(_bn)
if not _vc:
_vc = (getattr(Gen, 'global_struct_class', None) or {}).get(_bn)
_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"alloca 类型={getattr(_alloc, 'type', None)}")
_parts.append(f"该名在 Gen.structs={'是' if _vc and _vc in Gen.structs else ('否' if _vc else '—')}")
_parts.append("class_members="
+ (f"{len(Gen.class_members.get(_vc) or [])} 个字段"
if _vc and _vc in Gen.class_members else '无该键'))
elif (isinstance(_base, ast.Call) and isinstance(_base.func, ast.BinOp)
and isinstance(_base.func.op, ast.BitOr)):
_cls: str | None = None
try:
_cls = self.Trans.ExprAttrHandle._extract_union_base_class(_base.func)
except Exception: # noqa: BLE001
_cls = None
_parts.append(f"联合基类名={_cls!r}")
_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 未触发")
msg += "\n│ 基址诊断: " + ";".join(_parts)
msg += "\n│ 基址诊断: " + self._DescribeAttrBase(Gen, Node)
_vlog().compile_error(msg, src_file, lineno, code_line)
sys.exit(1)
+10
View File
@@ -443,6 +443,15 @@ class ExprCallHandle(BaseHandle):
loc: str = f"{src_file}:{lineno}"
if col >= 0:
loc += f":{col}"
# 实参是属性读时补同一份基址事实(与 HandlesBase._RaiseUnresolvedExpr 共用
# `_DescribeAttrBase`):属性读取值失败的两类根因(类名登记退化 / 键不同源)
# 必须一眼可见,不再从"求值失败"反推。
_base_facts: str = ''
if isinstance(ArgNode, ast.Attribute):
try:
_base_facts = self._DescribeAttrBase(Gen, ArgNode)
except Exception: # noqa: BLE001
_base_facts = ''
raise Exception(
f"[TransPyC] 调用实参编译期求值为空: 被调者 '{Callee}' 的第 {ArgIndex} 个实参 "
f"'{expr_text}'({ArgKind} 形态)求值失败;已终止编译,不丢弃该实参"
@@ -450,6 +459,7 @@ class ExprCallHandle(BaseHandle):
f"常见根因:枚举成员/名称未登记、跨模块符号查找失败、类定义在声明期结构体不完整。"
f"位置 {loc}"
f"{(',源码: ' + code_line) if code_line else ''}"
f"{chr(10) + '│ 基址诊断: ' + _base_facts if _base_facts else ''}"
)
def _HandleCallLlvm(self, Node: ast.Call) -> ir.Value: