# 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) ```python @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 紧跟同步 |