fix(FuncGen): base/字段偏移一律按"重名条目不占槽位"算,修 MatchCase 偏移 6(应为 7)
类别: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。
This commit is contained in:
1 parent
2e7bac1816
commit
ecd336771f
1 file changed
+94
-48
+94
-48
@@ -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 的未展平继承字段)仅在成员表
|
||||
|
||||
Reference in new issue
Block a user