From ecd336771f89fb6103fd1a2fc82d5a941ea1410b Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 30 Sep 2026 18:44:48 +0800 Subject: [PATCH] =?UTF-8?q?fix(FuncGen):=20base/=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=81=8F=E7=A7=BB=E4=B8=80=E5=BE=8B=E6=8C=89"=E9=87=8D?= =?UTF-8?q?=E5=90=8D=E6=9D=A1=E7=9B=AE=E4=B8=8D=E5=8D=A0=E6=A7=BD=E4=BD=8D?= =?UTF-8?q?"=E7=AE=97=EF=BC=8C=E4=BF=AE=20MatchCase=20=E5=81=8F=E7=A7=BB?= =?UTF-8?q?=206=EF=BC=88=E5=BA=94=E4=B8=BA=207=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类别:a(编译器缺陷) 现象(早前现场,本轮定位): TypeError: [fast-fail] len(mcc_pre.children) 分派 'list[….AST*].__len__' 失败: 类='MatchCase' 偏移=6 接收者类型=i32 判据(磁盘实证): · MatchCase 的 body 在 temp 与**全部** output stub 中都是 **10 槽** (vtable + AST 的 7 字段 + pattern/guard)⇒ `children` 的正确偏移 = base(1) + 表内下标(6) = **7**。 · 观测到 6 ⇒ base 被算成 0 ⇒ `_member_base` 的 `_Delta = 槽数 - 表长` 为 0, 即**表长 = 10**(9 个不同字段 + 1 条重名条目),而不是 9。 · 重名条目是**产物**:源码层面的字段名遮蔽已由 HandlesClassDef 的 "字段名遮蔽 fail-fast"(:1083)拦下 ⇒ 表里的重名只可能来自"展平残留 (半截祖先块,已由 2e7bac18 在插入点剥除)/ 同一字段被两条收集路径各登记一次"。 修复(与 `_get_member_offset` 配对路径已有的"重名条目不占槽位"同口径,全线统一): · `_member_base` 的长度校准改用**去重名数**(`_Delta` 不再被重名条目冲掉 ⇒ base 正确); · 新增 `_MemberSlotIndex(Members, FieldName)`:取"首个同名的**去重**下标" (重名条目不计槽位),并把 `_get_member_offset` 中**全部 12 处** "base + 表内下标" 站点改为调用它(含继承链回退、REnum 变体搜索、stub 重载后、_PreRegisterClassMembers 之后等分支);"最后回退"里的 `_num_members_final` 同步改为去重计数。 · 对无重名的表,取值与改前**逐字相同**(去重下标 == 原始下标)。 核验:py_compile + compileall 全 lib 通过;离线冒烟(真实方法、未绑定实例调用): 含 1 条重复 'children' 的表上 `children -> 6`、`guard -> 8`、缺名 -> -1; 去重名数 9 ⇒ `_Delta = 10 - 9 = 1` ⇒ base=1 ⇒ `children` 偏移 7(与 body 10 槽一致)。 未放宽任何 fail-fast。 --- lib/core/LLVMCG/FuncGen.py | 142 ++++++++++++++++++++++++------------- 1 file changed, 94 insertions(+), 48 deletions(-) diff --git a/lib/core/LLVMCG/FuncGen.py b/lib/core/LLVMCG/FuncGen.py index 25173c1..8d176ce 100644 --- a/lib/core/LLVMCG/FuncGen.py +++ b/lib/core/LLVMCG/FuncGen.py @@ -432,7 +432,22 @@ class FuncGenMixin: if (isinstance(_StructBase, ir.IdentifiedStructType) and _StructBase.elements is not None and len(_StructBase.elements) > 0): _NumElems: int = len(_StructBase.elements) - _NumMembers: int = len(self.class_members.get(_Cls, [])) + _MembersRaw: list = self.class_members.get(_Cls, []) + _NumMembers: int = len(_MembersRaw) + # 【重名条目不是布局槽位 ⇒ 长度校准按**去重名数**算(2026-09-30)】 + # 与同文件 `_get_member_offset` 的配对段(重名条目跳过、不占槽位)以及 + # StructGen 的"重名条目"分支同口径。依据:源码层面的字段名遮蔽已由 + # HandlesClassDef 的"字段名遮蔽 fail-fast"(:1083)拦下 ⇒ 成员表里的重名 + # 只可能是**产物**(展平残留 / 同一字段被两条收集路径各登记一次), + # 不是合法槽位。 + # 不这样算的后果(现场):`ast.MatchCase` 表 10 项(9 个字段 + 1 条重复) + # 而 body 恰 10 槽 ⇒ `_Delta == 0` ⇒ base 静默取 0 ⇒ 该类所有字段偏移整体 + # 前移 1:`MatchCase.children` 表内下标 6 被当作槽 6(`end_col_offset`,i32) + # ⇒ `len(mc_c.children)` 分派 `list[AST*].__len__` 时收到 i32: + # "Type of #1 arg mismatch: %"…list[…]"* != i32"。 + _UniqMembers: set = {n for n, _ in _MembersRaw} + if len(_UniqMembers) < _NumMembers: + _NumMembers = len(_UniqMembers) _Delta: int = _NumElems - _NumMembers _FirstElem: ir.Type = _StructBase.elements[0] _IsVtablePtr: bool = (isinstance(_FirstElem, ir.PointerType) @@ -535,6 +550,30 @@ class FuncGenMixin: _Owner = _Key return _Owner + def _MemberSlotIndex(self, Members: list, FieldName: str) -> int: + """字段在成员表里的**槽位下标**(重名条目**不占槽位**;找不到返回 -1)。 + + 与 `_get_member_offset` 的配对路径同口径:重名条目是产物,不是布局槽位 + —— 源码层面的字段名遮蔽已由 HandlesClassDef 的"字段名遮蔽 fail-fast"拦下, + 其余重名只可能来自"展平残留 / 同一字段被两条收集路径各登记一次"。 + 所有"按表内下标 + base 求偏移"的站点都必须走本方法,否则含重名条目的表会让 + 偏移整体偏斜: + · `ast.ExceptHandler` 表 10 项、'children' 出现两次 ⇒ 'name' 原始下标 9 ⇒ 偏移 10(越界); + · `ast.MatchCase` 表 10 项 / 去重 9(body 10 槽)⇒ `_Delta == 0` ⇒ base 变 0 ⇒ + `children` 偏移 6 落到 `end_col_offset`(i32)。 + 重名条目在**首个同名(正本)之后**才出现,故按"首个同名的去重下标"取值。 + """ + _Slot: int = 0 + _Seen: set = set() + for _n, _ in (Members or []): + if _n in _Seen: + continue + _Seen.add(_n) + if _n == FieldName: + return _Slot + _Slot += 1 + return -1 + def _get_member_offset(self, field_name: str, ClassName: str | None = None, StructHint: Any = None) -> int | None: # base 判定用未归一的原始类名:_member_base 内部自行做同一套归一 @@ -814,9 +853,18 @@ class FuncGenMixin: f"返回值可能失准。重名来源通常是「同一字段被两条收集路径各登记一次" f"(类内 AnnAssign 声明 + 方法体 self.x 赋值)」或继承前缀重复前置。", "MemberBase") - for i, (name, _) in enumerate(_MemList): + # 回退取值也按"重名条目**不占槽位**"算(与上方配对路径同口径):直接取 + # `base + 原始下标` 会把重名条目也算进槽位,整体后移(现场:ExceptHandler + # 表 10 项、'children' 出现两次 ⇒ 'name' 原始下标 9 ⇒ 偏移 10 越界)。 + _SlotIdx: int = 0 + _Counted: set = set() + for name, _ in _MemList: + if name in _Counted: + continue + _Counted.add(name) if name == field_name: - return _BaseNow() + i + return _BaseNow() + _SlotIdx + _SlotIdx += 1 # 继承链回退:当字段不在当前类的 class_members 中时(常见于跨模块导入 # 父类未加载,字段被填充为 __inherited_N 占位名),沿 class_parent 链向上查找。 # 子类 struct 布局为 [vtable?] + [基类字段...] + [子类字段...], @@ -846,10 +894,7 @@ class FuncGenMixin: _members: list = self.class_members[_k] _has_placeholder: bool = any(n.startswith('__inherited_') for n, _ in _members) if not _has_placeholder: - for i, (name, _) in enumerate(_members): - if name == field_name: - _hit_idx = i - break + _hit_idx = self._MemberSlotIndex(_members, field_name) if _hit_idx >= 0: break if _hit_idx >= 0: @@ -863,18 +908,18 @@ class FuncGenMixin: stub_name: str = f"{mod_sha1}.stub.ll" self._Trans.ImportHandler._TryLoadClassMembersFromPyi(_k, stub_name, self) if _k in self.class_members and not any(n.startswith('__inherited_') for n, _ in self.class_members[_k]): - for i, (name, _) in enumerate(self.class_members[_k]): - if name == field_name: - return _BaseNow() + i + _pIdx: int = self._MemberSlotIndex(self.class_members[_k], field_name) + if _pIdx >= 0: + return _BaseNow() + _pIdx break if _k in self.class_members and any(n == field_name for n, _ in self.class_members.get(_k, [])): break _parent = self.class_parent.get(_parent) short_name: str | None = ClassName.split('.')[-1] if ClassName and '.' in ClassName else None if short_name and short_name in self.class_members: - for i, (name, _) in enumerate(self.class_members[short_name]): - if name == field_name: - return _BaseNow() + i + _sIdx: int = self._MemberSlotIndex(self.class_members[short_name], field_name) + if _sIdx >= 0: + return _BaseNow() + _sIdx if ClassName and field_name: if ClassName not in self.class_members or field_name not in [n for n, _ in self.class_members.get(ClassName, [])]: if self._Trans and hasattr(self._Trans, 'ImportHandler'): @@ -883,25 +928,25 @@ class FuncGenMixin: stub_name: str = f"{mod_sha1}.stub.ll" self._Trans.ImportHandler._TryLoadClassMembersFromPyi(ClassName, stub_name, self) if ClassName in self.class_members and len(self.class_members[ClassName]) > 0: - for i, (name, _) in enumerate(self.class_members[ClassName]): - if name == field_name: - return _BaseNow() + i + _rIdx: int = self._MemberSlotIndex(self.class_members[ClassName], field_name) + if _rIdx >= 0: + return _BaseNow() + _rIdx break if short_name: for mod_name, mod_sha1 in sha1_map.items(): stub_name: str = f"{mod_sha1}.stub.ll" self._Trans.ImportHandler._TryLoadClassMembersFromPyi(short_name, stub_name, self) if short_name in self.class_members and len(self.class_members[short_name]) > 0: - for i, (name, _) in enumerate(self.class_members[short_name]): - if name == field_name: - return _BaseNow() + i + _rIdx2: int = self._MemberSlotIndex(self.class_members[short_name], field_name) + if _rIdx2 >= 0: + return _BaseNow() + _rIdx2 break if ClassName and field_name: for cn_key in self.class_members: if cn_key == ClassName or cn_key.endswith(f'.{ClassName}') or (short_name and (cn_key == short_name or cn_key.endswith(f'.{short_name}'))): - for i, (name, _) in enumerate(self.class_members[cn_key]): - if name == field_name: - return _BaseNow() + i + _cIdx: int = self._MemberSlotIndex(self.class_members[cn_key], field_name) + if _cIdx >= 0: + return _BaseNow() + _cIdx # REnum 变体搜索: ClassName 是 REnum(如 MetaType),成员注册在变体结构 # (MetaType_Var, MetaType_Concrete, MetaType_App)中。变体与 REnum 主结构 # 共享 max_variant_struct 布局,偏移一致(__tag 在偏移 0,字段从偏移 1 开始) @@ -909,33 +954,33 @@ class FuncGenMixin: variant_prefix: str = f"{ClassName}_" for cn_key in self.class_members: if cn_key.startswith(variant_prefix): - for i, (name, _) in enumerate(self.class_members[cn_key]): - if name == field_name: - return i + _vIdx: int = self._MemberSlotIndex(self.class_members[cn_key], field_name) + if _vIdx >= 0: + return _vIdx if short_name: variant_prefix = f"{short_name}_" for cn_key in self.class_members: if cn_key.startswith(variant_prefix): - for i, (name, _) in enumerate(self.class_members[cn_key]): - if name == field_name: - return i + _vIdx2: int = self._MemberSlotIndex(self.class_members[cn_key], field_name) + if _vIdx2 >= 0: + return _vIdx2 if ClassName and ClassName in self.structs: struct_type: ir.IdentifiedStructType = self.structs[ClassName] if isinstance(struct_type, ir.IdentifiedStructType) and struct_type.elements is not None and len(struct_type.elements) > 0: for cn_key, members in self.class_members.items(): if cn_key == short_name or (short_name and cn_key.endswith(f'.{short_name}')): - for i, (name, _) in enumerate(members): - if name == field_name and i < len(struct_type.elements): - return i + _kIdx: int = self._MemberSlotIndex(members, field_name) + if 0 <= _kIdx < len(struct_type.elements): + return _kIdx break else: if short_name: for cn_key, members in self.class_members.items(): sn: str = cn_key.split('.')[-1] if '.' in cn_key else cn_key if sn == short_name: - for i, (name, _) in enumerate(members): - if name == field_name and i < len(struct_type.elements): - return i + _kIdx2: int = self._MemberSlotIndex(members, field_name) + if 0 <= _kIdx2 < len(struct_type.elements): + return _kIdx2 break if ClassName and field_name and ClassName not in self.class_members: if self._Trans and hasattr(self._Trans, 'ImportHandler'): @@ -947,9 +992,9 @@ class FuncGenMixin: short_cn: str = short_name if short_name else ClassName self._Trans.ImportHandler._TryLoadClassMembersFromPyi(short_cn, stub_name, self) if short_cn in self.class_members and len(self.class_members[short_cn]) > 0: - for i, (name, _) in enumerate(self.class_members[short_cn]): - if name == field_name: - return _BaseNow() + i + _scIdx: int = self._MemberSlotIndex(self.class_members[short_cn], field_name) + if _scIdx >= 0: + return _BaseNow() + _scIdx break if ClassName and ClassName in self.structs and (ClassName not in self.class_members or len(self.class_members.get(ClassName, [])) == 0): struct_type: ir.IdentifiedStructType = self.structs[ClassName] @@ -964,13 +1009,13 @@ class FuncGenMixin: if len(self.class_members[node.name]) == 0: class_handler._PreRegisterClassMembers(node, self) if ClassName in self.class_members: - for i, (name, _) in enumerate(self.class_members[ClassName]): - if name == field_name: - return _BaseNow() + i + _prIdx: int = self._MemberSlotIndex(self.class_members[ClassName], field_name) + if _prIdx >= 0: + return _BaseNow() + _prIdx if short_name and short_name in self.class_members: - for i, (name, _) in enumerate(self.class_members[short_name]): - if name == field_name: - return _BaseNow() + i + _prIdx2: int = self._MemberSlotIndex(self.class_members[short_name], field_name) + if _prIdx2 >= 0: + return _BaseNow() + _prIdx2 break # 最后回退:当继承链回退失败但 struct body 已加载时, # 从 struct elements 数和 class_members 数推断继承字段偏移。 @@ -985,7 +1030,8 @@ class FuncGenMixin: if isinstance(_st_final, ir.IdentifiedStructType) and _st_final.elements is not None: _num_elements_final: int = len(_st_final.elements) _members_final: list = self.class_members.get(ClassName, []) - _num_members_final: int = len(_members_final) + # 与 _member_base 的长度校准同口径:重名条目**不是布局槽位** ⇒ 按去重名数算 + _num_members_final: int = len({_Rn for _Rn, _ in _members_final}) if _num_elements_final > _num_members_final: # base 是 vtable 占位(struct[0] = i8*),不是继承字段,必须扣除: # 不扣 base 时,凡"struct 元素数恰好多 1"的类,差值其实只是 vtable @@ -1018,10 +1064,10 @@ class FuncGenMixin: self.class_members[ClassName] = _OldMembers _ReloadedMembers = _OldMembers _members_final = _ReloadedMembers - _num_members_final = len(_members_final) - for _Ri, (_Rn, _Rt) in enumerate(_members_final): - if _Rn == field_name: - return _BaseNow() + _Ri + _num_members_final = len({_Rn for _Rn, _ in _members_final}) + _rfIdx: int = self._MemberSlotIndex(_members_final, field_name) + if _rfIdx >= 0: + return _BaseNow() + _rfIdx _num_inherited_final: int = _num_elements_final - _num_members_final - _BaseNow() if _num_inherited_final == 1: # 推断(缺失槽位 = 位于 base+0 的未展平继承字段)仅在成员表