From cad70fa8f39502e375b88d7a90b48b91927d90c3 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 30 Sep 2026 16:16:38 +0800 Subject: [PATCH] =?UTF-8?q?chore(=E8=AF=8A=E6=96=AD):=20len=20=E5=88=86?= =?UTF-8?q?=E6=B4=BE=20fail-fast=20=E8=BF=BD=E5=8A=A0=E6=88=90=E5=91=98?= =?UTF-8?q?=E6=A7=BD=E6=9D=A5=E6=BA=90=E6=98=8E=E7=BB=86=EF=BC=88=E9=A1=B9?= =?UTF-8?q?=E6=95=B0/base/=E4=B8=8B=E6=A0=87/=E5=90=8D=E5=BA=8F/=E5=B8=83?= =?UTF-8?q?=E5=B1=80=E6=A7=BD/=E5=AE=9E=E4=BE=8B=E5=90=8C=E6=BA=90?= =?UTF-8?q?=E5=88=A4=E5=AE=9A=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类别: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 条。 --- lib/core/Handles/HandlesAssign.py | 9 +++++++-- lib/core/Handles/HandlesExprBuiltin.py | 12 +++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/core/Handles/HandlesAssign.py b/lib/core/Handles/HandlesAssign.py index 8eccb34..634d85b 100644 --- a/lib/core/Handles/HandlesAssign.py +++ b/lib/core/Handles/HandlesAssign.py @@ -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( diff --git a/lib/core/Handles/HandlesExprBuiltin.py b/lib/core/Handles/HandlesExprBuiltin.py index b16213e..abfafa5 100644 --- a/lib/core/Handles/HandlesExprBuiltin.py +++ b/lib/core/Handles/HandlesExprBuiltin.py @@ -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)