chore(诊断): len 分派 fail-fast 追加成员槽来源明细(项数/base/下标/名序/布局槽/实例同源判定)

类别:b(机制漏豁免)/诊断增强,不改变任何成功路径。

现场:编译 StmtGenDeclMixin.vp:242 `len(mcc_pre.children)` 报
  [fast-fail] len(mcc_pre.children) 分派 'list[10f3f43ddd447eec.AST*].__len__' 失败:
  类='MatchCase' 偏移=6 接收者类型=i32
静态可查到的**正确**布局(temp/8c9751aec486f41c.stub.ll 实测 10 槽):
  [0]i8*(vtable) [1]AST*(parent) [2]MemManager*(pool) [3..6]i32(lineno/col_offset/
  end_lineno/end_col_offset) [7]list[AST*]*(children) [8]AST*(pattern) [9]AST*(guard)
声明侧(.vpi):`class AST` 7 字段 + `class MatchCase(AST)` 2 字段 ⇒ 表 9 项、
children 表内下标 6 ⇒ 正确 base=1 ⇒ 正确偏移 7。而失败现场是 6 ⇒ **base 被算成 0**
(表 10 项触发 `_Delta==0 ⇒ base=0`,或取值处实例与类键实例不同源)——
只看 `偏移=6` 无法区分,故把两侧读数打进报文。

- `AssignHandle._DescribeMemberSlot` 增可选 `StructHint`:base 与失败现场**同源**
  (不传时行为与改前逐字一致)。
- `HandlesExprBuiltin._HandleLenLlvm` 的 len 分派失败路径(仅该路径)追加上述明细:
  `class_members[类] 项数/base/下标/声明槽类型`、**完整名序**、
  `布局 struct name+元素数+elements[offset]`、`structs[类] 与布局是否同一对象`。
  明细生成失败只追加一行说明,绝不掩盖原始 TypeError。

静态核验:py_compile 通过;全仓 symtable 未定义名扫描 0 条。
This commit is contained in:
TermiNexus committed 2026-09-30 16:16:38 +08:00
1 parent 9afd6a45ed
commit cad70fa8f3
2 files changed
+18 -3

No files matched your search

+7 -2
View File
@@ -1612,7 +1612,8 @@ class AssignHandle(BaseHandle):
f"或把基座显式转为 t.CPtr(无类型指针)后按 8 字节指针语义写入。{SrcInfo}")
def _DescribeMemberSlot(self, Gen: "Translator.LlvmGen", ClassName: str | None,
FieldName: str, Offset: int, SelfPtr: ir.Value) -> str:
FieldName: str, Offset: int, SelfPtr: ir.Value,
StructHint: Any = None) -> str:
"""`self.字段 = 值` 成员槽写入失败的**来源明细**(供 fail-fast 报文引用)。
槽类型有两个**互相独立**的来源,必须自洽:
@@ -1622,11 +1623,15 @@ class AssignHandle(BaseHandle):
现场:Generator.ExprGenConst 声明 `%"...ExprGenConst"*` 却落到 `i8**`。
此处把两侧数据逐项列出,直接判定是声明侧退化还是布局侧退化(含 structs
键空间大小与"是否同一对象",用于识别裸名 / sha1 全名双条目)。
`StructHint`(可选):调用方实际 GEP 用的那个 struct 实例。偏移基址必须与**那份
布局**同源(见 FuncGen._member_base 的 hint 说明)——传入时本报文报出的 base /
下标才与失败现场一致;不传时行为与改前逐字一致(回落到类键实例)。
"""
Lines: list[str] = []
try:
Members: list = Gen.class_members.get(ClassName) or []
Base: int = Gen._member_base(ClassName)
Base: int = Gen._member_base(ClassName, StructHint)
Idx: int = Offset - Base
Declared: Any = Members[Idx][1] if 0 <= Idx < len(Members) else None
Lines.append(
+11 -1
View File
@@ -696,10 +696,20 @@ class ExprBuiltinHandle(BaseHandle):
try:
result = self.Trans.ExprUtils._try_operator_overLoad(field_cls, '__len__', list_val, Gen)
except TypeError as _e_len:
# 来源明细(声明侧 class_members 名序/项数 vs 布局侧 elements)
# 追加进报文:本处失败的本质是"偏移算错",只看 偏移=6 无法区分
# 是"表多一项(→ base 被 delta==0 判 0)"还是"取值处实例与类键实例
# 不同源"。判据/读数见 AssignHandle._DescribeMemberSlot。
_extra_len: str = ''
try:
_extra_len = self.Trans.AssignHandler._DescribeMemberSlot(
Gen, bare_cls, attr_name, offset, self_ptr, _STl2)
except Exception as _ea:
_extra_len = f"\n (来源明细生成失败: {type(_ea).__name__}: {_ea})"
raise TypeError(
f"[fast-fail] len({obj_name}.{attr_name}) 分派 '{field_cls}.__len__' 失败:"
f"类='{bare_cls}' 偏移={offset} 接收者类型={list_val.type};"
f"(成员偏移与 struct 布局不一致)原错误: {_e_len}")
f"(成员偏移与 struct 布局不一致)原错误: {_e_len}{_extra_len}")
if result is not None:
return result
val = self.HandleExprLlvm(arg_node)