fix(HandlesClassDef/HandlesImports): 展平块幂等——块已整段在表头/尾时不再重复插入(修 41→73 的 body 分叉)

类别:a(编译器缺陷,且已泄漏进 emit 的 IR)。

on-disk 实证:Phase 1 声明(temp/<sha1>.stub.ll)与 Phase 2 emit(output/<sha1>.stub.ll)
对同一类的 body 槽数不一致,共 7 个类,全在 Generator/ClassGen/TypeGen 继承链上:

  TypeGenCType 41 → 73(+32)   TypeGenCDef 41 → 73   TypeGenErr 41 → 73
  ClassGenMixin 42 → 50(+8)   ClassGenVTable 42 → 50
  ClassGenEnum 42 → 50          ClassGenDef 42 → 50

逐槽比对:output[0:41] == temp,且 **output[41:73] == temp[9:41]** —— 多出的 32 槽
正是"追加前那张表**尾部** 32 项的原样副本"。下游即 Repl.vp:802
`GenApi._g_gen.TypeGenCType.CTypedefNames` 因两份 body(41/73)歧义而
"基座 pointee 未匹配 Gen.structs 中任何 struct"。

成因:两条展平路径判据**不对称**——
- 导入侧 `PrependInheritedBlock`:前置,且自带"表头是否等于块"的幂等判据;
- 定义侧 HandlesClassDef 展平:直接 `class_members + _flat` **追加**,**无任何判据**。
一旦块已整段位于表尾(或表中段),就会被再插一次,表里多出 len(块) 条重复条目,
body 随之多出同名槽。

修法:新增唯一判据源 `HandlesImports.InheritedBlockAlreadyPresent(Table, Block)`——
"块是否整段位于 `Table` 的**头部或尾部**(名字 + IR 类型逐项相同)",
两个插入点都接它(导入侧前置 ⇒ 头部;定义侧追加 ⇒ 尾部)。
刻意**不判表中段**:中段的同序列与"合法的同名并存"(子类在自有段重复声明父类字段,
body 本来就有两个槽)不可区分,判成"已存在"会误删本该有的槽;真出现中段重复时
仍由 StructGen 的重名条目告警兜住。跳过重复时打印一行 WARN(类名 / 块长 / 表长),
只跳过重复、不改其余布局。

冒烟(内联 python,无临时文件):现场形状(块 = 表[9:41])→ True;中段 → False;
仅名字相同而类型不同 → False;空块 / 块比表长 / 非 tuple 条目 → False。
静态核验:compileall 通过;全仓 symtable 未定义名扫描 0 条。
This commit is contained in:
TermiNexus committed 2026-09-30 15:44:13 +08:00
1 parent 83453c68db
commit 9afd6a45ed
1 file changed
+18 -1
+18 -1
View File
@@ -823,7 +823,24 @@ class ClassHandle(BaseHandle):
continue
_FlattenInherited(ParentClass, _seen_cls, _flat, Gen)
if _flat:
Gen.class_members[ClassName] = Gen.class_members[ClassName] + _flat
# 【幂等(2026-09-30)】该祖先块已作为**连续整段**存在于本表时不再追加:
# 本处是**追加**(`class_members + _flat`),与导入侧的
# `PrependInheritedBlock`(前置,自带表头判据)判据不对称,"块已在表中段/
# 尾部"的情形谁都不判 ⇒ 整块再插一次,表里多出 len(_flat) 条重复条目,
# body 随之多出同名槽(现场:output/<sha1>.stub.ll 的 TypeGenCType 73 槽
# = temp 声明 41 槽 + 其 [9:41] 的原样副本;同族 7 个类,
# 下游即 Repl.vp:802 的"基座 pointee 未匹配任何 struct")。
# 判据(名字 + IR 类型逐项连续匹配)与成因见
# HandlesImports.InheritedBlockAlreadyPresent;只跳过重复、不改其余布局。
from lib.core.Handles.HandlesImports import InheritedBlockAlreadyPresent as _BlkPresent
if _BlkPresent(list(Gen.class_members[ClassName]), _flat):
_vlog().warning(
f"[成员展平] 类 '{ClassName}' 的祖先块({len(_flat)} 项)已存在于成员表"
f"(表 {len(Gen.class_members[ClassName])} 项)⇒ 跳过重复追加"
f"(重复会让 body 多出同名槽、与 Phase 1 声明布局分叉)",
"成员展平")
else:
Gen.class_members[ClassName] = Gen.class_members[ClassName] + _flat
# ============================================================
# 多继承方法合并:遍历所有父类(声明顺序)收集方法(短名去重,先声明父类优先),
# vtable 槽/方法名用"实际定义类.方法"(如 B.get_b,与 TPV 一致),