fix(HandlesImports): 继承前缀前置改为"按表头判幂等",不再按名字过滤整块(重名继承项不再被删)
类别:a 编译器缺陷(静默删掉布局条目,使继承段下标整体前移、写到类型不同的槽)。
问题(旧判据):`_EmitExternalClassDeclLlvm` 的前置段用
`nm2 not in {class_members[ClassName] 整表所有名字}` 过滤祖先块。该判据把
**与子类自有字段重名的继承项整条删掉**,于是继承段与 body 的继承前缀失去一一对应:
被删项**之后**的继承字段表内下标整体前移 k(k = 被删项数),而查询侧回退路径用
`base + 表内下标`(FuncGen._get_member_offset)⇒ 落到类型完全不同的槽。
现场(本轮):TypeGenCType 重复声明 5 个父类已有字段后,继承字段
LastBuiltinsTypedefResult 的下标 32→27,`= "t.CPtr"` 写进 i32 槽 ⇒
"指针隐式转整数已禁止:不能把指针类型 i8* 赋给整数类型 i32"。
新判据(抽成模块级 PrependInheritedBlock,可独立验证):
① 不删块内条目——块内每项都对应 body 的一个继承槽,删任一项都会整体前移;
② 幂等——只问"表头前 len(块) 项的名字序列是否等于祖先块的名字序列",相等即整块
已前置 → 不再前置(保住 2026-08-10 修的重复字段问题:BasicBlock [Next, Next, …]);
③ 同名并存——子类自有字段与继承项同名时两者都留(定义端 body 同样是"继承前缀 +
自有段",两侧槽数一致);源码层面的这种重名已由 HandlesClassDef 的"字段名遮蔽
fail-fast"拦下(1903ed14)。
冒烟(纯逻辑,跑完删除):首次前置=块+自有;重复调用不再前置;**同名项仍全保留**
(对照旧实现会删掉块首项 ⇒ 下标前移);空块 no-op。py_compile 通过。
已知残留(写进函数文档):若此前只前置过部分祖先块(父类表当时较短),本次会整块再前置
一次 ⇒ 该前缀出现重复条目;调用点上方刚对父类 force 重载(块内容稳定),该情形仅在
"父类表被中途改写"时出现,且重复条目会在成员槽失败报文的"名序"里直接可见。
This commit is contained in:
1 parent
a43513c9a7
commit
55cbf1d261
1 file changed
+53
-8
@@ -144,6 +144,46 @@ def _ReportDeclarationParseFailure(FilePath: str, Exc: BaseException, Consequenc
|
||||
_vlog().warning(Message, "Exception")
|
||||
|
||||
|
||||
def PrependInheritedBlock(Gen: object, ClassName: str, Block: list) -> bool:
|
||||
"""把祖先字段块 `Block` 前置到 `Gen.class_members[ClassName]`(幂等,且**不删块内条目**)。
|
||||
|
||||
为什么单独成函数:这段判据是"继承段与 body 继承前缀一一对应"的唯一保障,必须可
|
||||
独立验证(见本仓"按'表头是否已等于块'判幂等"的三条性质)。
|
||||
|
||||
判据与性质:
|
||||
① **不删块内条目**。块里每一项都对应 body 的一个继承槽;只要块内某项被删,
|
||||
被删项之后的继承字段的表内下标就会整体前移 k,而查询侧回退路径用
|
||||
`base + 表内下标`(FuncGen._get_member_offset)⇒ 落到类型不同的槽。
|
||||
旧实现按"名字已在整表内"过滤整块,正是这样删掉了**与子类自有字段重名**的
|
||||
继承项(现场:TypeGenCType 重复声明 5 个父类已有字段后,继承字段
|
||||
LastBuiltinsTypedefResult 下标 32→27,写进 i32 槽 → "指针隐式转整数已禁止")。
|
||||
② **幂等**。本函数会被多个加载路径重复调用(_LoadModuleDeclarationsFromFile /
|
||||
_LoadDeclarationsFromStubLlvm / _TryLoadClassMembersFromPyi 懒加载);判据是
|
||||
"表头前 len(Block) 项的名字序列是否等于 Block 的名字序列"——相等即整块已前置,
|
||||
直接返回 False(不再前置),避免 2026-08-10 那类重复字段
|
||||
(BasicBlock: [Next, Next, Name, ...] → 与 struct elements 数量不匹配 → 偏移错乱)。
|
||||
③ **同名并存**。子类自有字段与继承项同名时,两者都保留(定义端
|
||||
HandlesClassDef 的 body 同样是"继承前缀 + 自有段",两侧槽数一致);
|
||||
源码层面的这种重名已由 HandlesClassDef 的"字段名遮蔽 fail-fast"拦下,
|
||||
此处只是不让它把布局判据带偏。
|
||||
|
||||
已知残留:若此前只前置过**部分**祖先块(父类表当时较短),本次会整块再前置一次,
|
||||
该前缀出现重复条目。调用点上方刚对父类做过 force 重载(块内容稳定),故该情形仅在
|
||||
"父类表被中途改写"时出现,且重复条目会在成员槽失败报文的"名序"里直接可见。
|
||||
|
||||
返回 True 表示本次真的前置了。
|
||||
"""
|
||||
_Members: list = getattr(Gen, 'class_members', {}).get(ClassName) or []
|
||||
_BlockNames: list = [nm for nm, _ in Block]
|
||||
if not _BlockNames:
|
||||
return False
|
||||
_HeadNames: list = [nm for nm, _ in _Members[:len(_BlockNames)]]
|
||||
if _HeadNames == _BlockNames:
|
||||
return False
|
||||
getattr(Gen, 'class_members')[ClassName] = list(Block) + list(_Members)
|
||||
return True
|
||||
|
||||
|
||||
class ImportHandle(BaseHandle):
|
||||
_struct_Load_cache = {}
|
||||
_pyi_cache = {}
|
||||
@@ -2511,14 +2551,19 @@ class ImportHandle(BaseHandle):
|
||||
# 加载路径重复调用(_LoadModuleDeclarationsFromFile / _LoadDeclarationsFromStubLlvm
|
||||
# / _TryLoadClassMembersFromPyi 懒加载),此前调用已前置的父类字段会被再次
|
||||
# 前置 → class_members 出现重复字段(如 BasicBlock: [Next, Next, Name, ...])
|
||||
# → 与 struct elements 数量不匹配 → 属性偏移错乱。按字段名过滤已存在的
|
||||
# 父类字段;跨父类重名字段(不同父类各自声明)仍全部保留。
|
||||
_cur_member_names_pre: set = {n for n, _ in Gen.class_members.get(ClassName, [])}
|
||||
_filtered_inherited: list[tuple] = [
|
||||
(nm2, tp2) for nm2, tp2 in _all_inherited if nm2 not in _cur_member_names_pre
|
||||
]
|
||||
if _filtered_inherited:
|
||||
Gen.class_members[ClassName] = _filtered_inherited + Gen.class_members[ClassName]
|
||||
# → 与 struct elements 数量不匹配 → 属性偏移错乱。
|
||||
# 【判据改为"前置段是否已等于祖先块"(2026-09-30)】旧判据是
|
||||
# `nm2 not in {整表所有名字}`——它会把**与子类自有字段重名的继承项**删掉,
|
||||
# 于是继承段与 body 的继承前缀失去一一对应:被删项**之后**的继承字段表内
|
||||
# 下标整体前移 k(k = 被删项数),而查询侧回退路径用 `base + 表内下标`
|
||||
# (FuncGen._get_member_offset)⇒ 落到**类型完全不同的槽**。现场:
|
||||
# TypeGenCType 重复声明 5 个父类已有字段后,继承字段 LastBuiltinsTypedefResult
|
||||
# 下标 32→27,写到 i32 槽 → "指针隐式转整数已禁止"。
|
||||
# 新判据(幂等且不删块内条目):只问"表头前 len(块) 项的名字序列是否等于
|
||||
# 祖先块的名字序列"——相等 ⇒ 整块已前置(本次是重复调用)⇒ 不再前置;
|
||||
# 否则整块前置。**任何情况下都不删块内条目**,因此"同名自有字段 + 同名继承项"
|
||||
# 会并存(body 里本来就是两个槽,定义端 HandlesClassDef 同样两者都在)。
|
||||
PrependInheritedBlock(Gen, ClassName, _all_inherited)
|
||||
# 父类字段已处理(可能此前已前置),跳过占位符路径避免叠加
|
||||
_any_parent_loaded = True
|
||||
if not _any_parent_loaded and _ParentClasses:
|
||||
|
||||
Reference in new issue
Block a user