Files
TransPyC/wiki/15-multiple-inheritance.md
T
TermiNexus f6f8c4183c test: 新增 MultiInheritTest 多继承专项测试 + wiki 方案
4 组用例: 字段合并 C(A,B) / 菱形去重最左优先(Diamond) /
虚方法 vtable 多继承(ColoredCircle) / 运算符重载 MRO 链(Vec2Both)。
wiki/15-multiple-inheritance.md: Viper 多继承方案(M1-M6)设计文档。
2026-08-10 00:29:13 +08:00

6.5 KiB
Raw Blame History

Viper 多继承支持方案

背景:Viper 语法层已支持 class X(A, B)(ClassDef.bases 是列表,parser 正常解析), 但语义层只实现单继承(StructEntry.ParentName 单值,字段展平/vtable/方法查找均单父)。 目标:实现多继承(含字段合并、vtable 合并、方法查找、菱形去重),并同步 TPC 参考实现。 状态:方案稿,未实施。

1. 现状(已实测定位)

位置 现状 多继承缺口
ast/stmts.vp:504 ClassDef.bases 列表,parser 支持多 base 无(语法已就绪)
HandlesClassDef.vp:2868 entry.ParentName = parent_name 单值 需父类数组
HandlesClassDef.vp:2618-2655 字段展平 只遍历单个 parent_entry 需 MRO 多父合并
HandlesClassDef.vp:3028+ _collect_vtable_methods 单父 parent_vt_count 需多父 vtable 合并
HandlesStruct.vp:56 ParentName 单 str 字段 需数组 + MRO 辅助
HandlesExprCall.vp try_operator_overload / _translate_method_call get_parent_name 单链 需多父链
HandlesExpr.vp 属性访问回退链 get_parent_name 单链 需多父链

2. MRO 规则(分阶段)

Phase 1(本方案默认):深度优先 + 声明顺序 + 去重

  • 查找顺序:当前类 → bases 声明顺序逐个递归
  • 同名冲突:先声明者优先(最左优先)
  • 菱形(D(A, B),A/B 均继承 C):深度优先去重(已访问集合),C 的字段/方法只合并一次
  • 理由:与 Python C3 相比实现简单,覆盖本仓库实际场景(Generator Mixin 链、无复杂菱形)

Phase 2(可选,后续):C3 线性化

  • 严格 C3 保证单调性,处理复杂菱形歧义
  • 仅在 Phase 1 遇到真实需求后实施

3. 数据结构改造(HandlesStruct.vp)

@t.NoVTable
class StructEntry:
    # 现有字段不变
    ParentNames: list[str] | t.CPtr   # 新增:父类名数组(声明顺序,跳过 marker)
    ParentCount: t.CInt               # 新增

# 辅助函数(新增)
def get_parent_count(entry) -> int
def get_parent_name_at(entry, i) -> str
def get_mro_chain(entry) -> list[str]   # 深度优先去重线性化(含自身)
def get_mro_parent_entries(entry) -> 父类 StructEntry 数组
  • ParentName 保留(兼容现有调用,指向第一个父类),新代码优先用 ParentNames
  • 迁移点:get_parent_name() 调用方逐步替换为 MRO 链遍历

4. 字段布局(HandlesClassDef.vp 字段展平 L2618-2655)

改造前(单父):

[parent_fields..., child_fields...]

改造后(多父):

[vtable?, parent1_fields..., parent2_fields..., child_fields...]

实现:

  1. parent_entries = get_mro_parent_entries(self_entry)(深度优先去重)
  2. 遍历每个父类字段:__vtable__ 跳过;字段名去重(已存在跳过,先声明者优先)
  3. 布局顺序 = MRO 父类顺序 → 子类自身字段

影响面:所有继承类的 __sizeof__/GEP 偏移变化 → 需全量回归

5. vtable 合并(HandlesClassDef.vp _collect_vtable_methods L3028+)

改造前(单父):parent_vt_count 遍历,full_name 用实际定义类(find_func_global 探测,已支持多层单继承)

改造后:

  1. 遍历 MRO 所有父类的 VTableMethods(每个父类的 vtable 方法短名)
  2. 方法短名去重:先声明父类优先;子类覆盖优先(child_overrides 检查不变)
  3. full_name 定位:沿用 find_func_global("类名.方法") 探测(多父链探测)
  4. 槽位顺序:父类方法(MRO 序)→ 子类新增方法

6. 方法查找/运算符重载/属性访问(多父链替换单链)

位置 单链现状 改法
HandlesExprCall.vp try_operator_overload L123-135 while cur_parent is not None 改为 get_mro_chain 遍历
HandlesExprCall.vp try_unary_operator_overload L211-221 同上 同上
HandlesExprCall.vp _translate_method_call 继承方法查找 get_parent_name 单链 MRO 链
HandlesExpr.vp 属性访问 lookup_field 回退 单链 MRO 链
HandlesClassDef.vp _find_method_defining_class(本轮已加) entry.ParentName 单链 MRO 链
HandlesType.vp / 其他 get_parent_name 调用 单链 逐步替换

7. 菱形继承处理

  • 字段:MRO 去重(已访问父类集合),C 字段只合并一次
  • 方法/vtable:方法短名去重,C 的 vtable 槽合并一次
  • 歧义:先声明优先;Phase 1 不报歧义错误(静默最左优先),Phase 2 C3 时再考虑警告

8. TPC 同步清单

TPC(Python,lib/core/)与 TPV 结构一致,按顺序同步:

  1. Handles/HandlesStruct.py:StructEntry 父类数组 + MRO 辅助
  2. Handles/HandlesClassDef.py:字段展平多父、vtable 多父合并、parent 收集
  3. Handles/HandlesExprCall.py:方法查找/运算符重载多父链
  4. Handles/HandlesExpr.py(若 TPC 属性访问在 HandlesExprAttr.py):多父回退

顺序:先 TPV(活跃编译器)实现 + 验证,再 TPC 同步(保持一致,防分叉)。

9. 实施计划

阶段 内容 验证
M1 HandlesStruct:父类数组 + MRO 辅助(新增字段/函数,不改现有逻辑) bootstrap
M2 HandlesClassDef:parent 收集多 base + 字段展平多父 bootstrap + 单继承回归
M3 HandlesClassDef:vtable 多父合并 bootstrap + 多继承 vtable 用例
M4 HandlesExprCall/HandlesExpr:方法/运算符/属性多父链 bootstrap + 全用例
M5 TPC 同步(4 文件) TPC 自测
M6 新增 Test/MultiInheritTest(字段/vtable/菱形去重/运算符重载) 编译 + 运行

10. 验证用例(Test/MultiInheritTest/App/main.vp)

  • 多父字段合并 + 属性访问(d = D(); d.a + d.b)
  • 多父方法调用(非虚 + 虚 vtable 分派)
  • 运算符重载继承链(父类 dunder)
  • 菱形去重(D(A, B),A/B ← C:C 字段/方法只出现一次)
  • 同名冲突最左优先
  • Generator 多继承实例化(class Generator(BaseGenMixin, TypeGenMixin, ...))——本轮对象化 P2+ 的落地验证

11. 风险与对策

风险 对策
字段布局变化破坏现有继承类 全量回归(所有 Test 项目)
vtable 合并槽位错误 多继承 vtable 用例 + obj 反汇编核对
Viper 方法符号不转发(P1 已踩坑) 多继承下子类仍只调自己文件定义的方法;父类方法用字段/模块函数
菱形去重遗漏 MRO 已访问集合严格实现
TPC/TPV 分叉 TPV 先验,TPC 紧跟同步