From 87b9dec1efaffa8ccf64b75d0688393588bc15e9 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 10 Aug 2026 00:29:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(core):=20TPC=20=E5=A4=9A=E7=BB=A7=E6=89=BF?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=20M5=20-=20MRO=20=E6=9F=A5=E6=89=BE/?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E5=B1=95=E5=B9=B3/vtable=E5=90=88=E5=B9=B6/t?= =?UTF-8?q?his=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BaseGen: 新增 class_parents + _get_mro_chain(与 TPV 语义一致: 深度优先 +声明顺序+去重) 与 _get_parent_classes 兼容旧单链 HandlesClassDef: 停用 is_base_of_any 自动 vtable; 多父字段展平去重; 方法合并用定义类名; vtable 槽沿 MRO 取定义类 HandlesExprCall: 方法调用沿 MRO 回退 + stub(无函数体)判定修复 undefined reference; 继承方法 this 调整(GEP+bitcast 成父类指针) HandlesExprUtils/HandlesImports/HandlesWith/StructGen: MRO 查找同步 --- lib/core/Handles/HandlesClassDef.py | 179 +++++++++++++++++---------- lib/core/Handles/HandlesExprCall.py | 158 ++++++++++++++++++----- lib/core/Handles/HandlesExprUtils.py | 22 +++- lib/core/Handles/HandlesImports.py | 50 +++++--- lib/core/Handles/HandlesWith.py | 10 +- lib/core/LLVMCG/BaseGen.py | 36 ++++++ lib/core/LLVMCG/StructGen.py | 17 +-- 7 files changed, 344 insertions(+), 128 deletions(-) diff --git a/lib/core/Handles/HandlesClassDef.py b/lib/core/Handles/HandlesClassDef.py index a9a2031..d7d7723 100644 --- a/lib/core/Handles/HandlesClassDef.py +++ b/lib/core/Handles/HandlesClassDef.py @@ -406,17 +406,14 @@ class ClassHandle(BaseHandle): if class_graph.get(ClassName, {}).get('has_methods', False) and not IsNoVTable: if not IsCpythonObject: IsCpythonObject = True - # 仅当该类作为其他类的基类时,才启用 CVTable - # 检查 _class_graph 中是否有任何类将此类作为基类 - is_base_of_any: bool = False - for other_cls, info in class_graph.items(): - if ClassName in info.get('bases', []): - is_base_of_any = True - break - if is_base_of_any: - IsCVTable = True + # 多继承支持(与 TPV _detect_vtable_status 对齐):不再因"作为其他类的基类" + # 自动启用 vtable。vtable 状态统一由继承链决定(见下方 HasParentClass 判定): + # 类自身无 @t.CVTable 时,父类有 vtable → 子类有;父类无 vtable → 子类静态。 + # 否则基类(如 C(A,B) 中的 A/B)被自动加 vptr 后,其字段区在子类中的偏移 + # 与自身布局不一致,继承方法按自身布局读 self 会字段错位。 HasParentClass: bool = False ParentClassName: str | None = None + ParentClasses: list[str] = [] ParentIsNoVTable: bool = False if Node.bases: for base in Node.bases: @@ -429,18 +426,29 @@ class ClassHandle(BaseHandle): kind: str | None = self._resolve_base_kind(base_name, Gen) if kind is not None or base_name in t._MARKER_BASES: continue # 标记基类,跳过 + # 多继承:收集所有真实父类(声明顺序),不再 break + ParentClasses.append(base_name) + if ParentClassName is None: + ParentClassName = base_name if base_name in class_graph or base_name in Gen.class_members or base_name in Gen.structs: HasParentClass = True - ParentClassName = base_name - # 检测父类是否有 @t.NoVTable 标记(非多态继承) - if class_graph.get(base_name, {}).get('has_novtable', False): - ParentIsNoVTable = True - # 跨模块父类:检查 _cross_module_novtable 集合 - if base_name in Gen._cross_module_novtable: - ParentIsNoVTable = True - break + # 检测父类是否有 @t.NoVTable 标记(非多态继承) + if class_graph.get(base_name, {}).get('has_novtable', False): + ParentIsNoVTable = True + # 跨模块父类:检查 _cross_module_novtable 集合 + if base_name in Gen._cross_module_novtable: + ParentIsNoVTable = True if HasParentClass and not IsCVTable and not IsNoVTable and not ParentIsNoVTable: - IsCVTable = True + # 继承链统一 vtable(与 TPV _detect_vtable_status 一致): + # 父类有 vtable → 子类启用;父类无 vtable → 子类不启用(静态); + # 父类 vtable 状态未知(跨模块/未注册)→ 保守启用。 + parent_has_vtable: bool = (ParentClassName in Gen.class_vtable + or ParentClassName in Gen._cross_module_vtable_classes) + parent_known: bool = (ParentClassName in class_graph + or ParentClassName in Gen.class_members + or ParentClassName in Gen.structs) + if parent_has_vtable or not parent_known: + IsCVTable = True if IsCVTable: Gen.class_vtable.add(ClassName) # 沿继承链向上传播,将所有祖先类加入 vtable @@ -572,6 +580,7 @@ class ClassHandle(BaseHandle): Gen.class_member_byteorders[ClassName] = {} Gen.class_member_bitoffsets[ClassName] = {} ParentClass: str | None = None + ParentClasses: list[str] = [] if Node.bases: class_graph: dict = Gen._class_graph for base in Node.bases: @@ -593,49 +602,83 @@ class ClassHandle(BaseHandle): continue if base_name in ('Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'): continue + # 多继承:收集所有真实父类(声明顺序),不再 break。 # 总是记录父类名,即使父类 class_members 未加载。 # _get_member_offset 会沿 class_parent 链回退查找继承字段。 - ParentClass = base_name - Gen.class_parent[ClassName] = ParentClass - break - if ParentClass and (ParentClass in class_graph or ParentClass in Gen.class_members): - parent_members: list = list(Gen.class_members[ParentClass]) - parent_defaults: dict = dict(Gen.class_member_defaults.get(ParentClass, {})) - existing_names: set = {m[0] for m in Gen.class_members[ClassName]} - inherited: list[tuple] = [] - for pm_name, pm_type in parent_members: - if pm_name not in existing_names: - inherited.append((pm_name, pm_type)) - if pm_name in parent_defaults: - Gen.class_member_defaults[ClassName][pm_name] = parent_defaults[pm_name] - Gen.class_members[ClassName] = inherited + Gen.class_members[ClassName] - if ParentClass in Gen.class_methods: - parent_methods: list = list(Gen.class_methods[ParentClass]) - # 构建子类已有方法映射(方法短名 -> 方法全名) + if ParentClass is None: + ParentClass = base_name + Gen.class_parent[ClassName] = ParentClass + ParentClasses.append(base_name) + if ParentClasses: + Gen.class_parents[ClassName] = ParentClasses + if ParentClasses: + # ============================================================ + # 多继承字段展平:遍历所有父类(声明顺序),字段名去重(先声明父类优先), + # __vtable__ 跳过(vtable 指针由 _generate_structs 按 class_vtable 统一添加)。 + # 布局: [父类1字段..., 父类2字段..., 子类字段...] + # ============================================================ + for ParentClass in ParentClasses: + if ParentClass not in class_graph and ParentClass not in Gen.class_members: + continue + parent_members: list = list(Gen.class_members.get(ParentClass, [])) + parent_defaults: dict = dict(Gen.class_member_defaults.get(ParentClass, {})) + existing_names: set = {m[0] for m in Gen.class_members[ClassName]} + inherited: list[tuple] = [] + for pm_name, pm_type in parent_members: + if pm_name == '__vtable__': + continue + if pm_name not in existing_names: + inherited.append((pm_name, pm_type)) + if pm_name in parent_defaults: + Gen.class_member_defaults[ClassName][pm_name] = parent_defaults[pm_name] + if inherited: + Gen.class_members[ClassName] = inherited + Gen.class_members[ClassName] + # ============================================================ + # 多继承方法合并:遍历所有父类(声明顺序)收集方法(短名去重,先声明父类优先), + # vtable 槽/方法名用"实际定义类.方法"(如 B.get_b,与 TPV 一致), + # 不再生成子类包装器 "ClassName.method"(旧逻辑注册了包装器但未生成函数体, + # 导致链接 undefined reference,如 C.get_b / ColoredCircle.color_code / Vec2Both.extra_val)。 + # ============================================================ + all_parent_methods: list[str] = [] + parent_method_names_seen: set[str] = set() + for ParentClass in ParentClasses: + for pm in list(Gen.class_methods.get(ParentClass, [])): + method_name: str = pm.split('.')[-1] if '.' in pm else pm + if method_name in ('__new__', '__init__', '__before_init__'): + continue + if method_name in parent_method_names_seen: + continue # 先声明父类优先,菱形去重 + parent_method_names_seen.add(method_name) + all_parent_methods.append(pm) + if all_parent_methods: + # 构建子类已有方法映射(方法短名 -> 方法全名)。 + # 子类覆盖判定:既查 class_methods(prescan/导入阶段已注册), + # 也直接扫描 ClassDef 自身的方法(与 TPV _collect_vtable_methods 一致)。 child_method_map: dict[str, str] = {} for m in Gen.class_methods[ClassName]: method_name: str = m.split('.')[-1] if '.' in m else m child_method_map[method_name] = m + for _item_self in Node.body: + if isinstance(_item_self, ast.FunctionDef): + _mn_self: str = _item_self.name + if _mn_self in ('__new__', '__init__', '__before_init__'): + continue + child_method_map[_mn_self] = f"{ClassName}.{_mn_self}" # 父类方法在前(保证 vtable 布局一致性,子类多态分派索引正确), # 子类新增方法在后。子类覆盖的方法放在父类方法的位置(用子类实现)。 new_methods: list = [] covered_names: set[str] = set() - for pm in parent_methods: + for pm in all_parent_methods: method_name: str = pm.split('.')[-1] if '.' in pm else pm if method_name in child_method_map: # 子类覆盖了父类方法 — 用子类的实现,放在父类的位置 new_methods.append(child_method_map[method_name]) covered_names.add(method_name) else: - # 父类独有方法 — 子类未覆盖,注册子类包装器 - child_method: str = f"{ClassName}.{method_name}" - new_methods.append(child_method) - Gen.register_method(ClassName, child_method) + # 父类独有方法 — 直接用父类方法原名(定义类名,如 B.get_b) + new_methods.append(pm) covered_names.add(method_name) # 添加子类新增方法(不在父类中的) - # 用快照遍历:register_method 会原地修改 class_methods, - # 若遍历活列表会把父类独有方法(已由 register_method 追加)再次追加, - # 导致 vtable 槽数与跨模块调用方推断不一致(如 MemBuddy 36 槽 vs 28 槽)。 for m in list(Gen.class_methods[ClassName]): method_name: str = m.split('.')[-1] if '.' in m else m if method_name not in covered_names: @@ -766,9 +809,11 @@ class ClassHandle(BaseHandle): self.Trans.FunctionHandler._EmitFunctionLlvm(item, Gen, ClassName=ClassName) if IsCVTable and ClassName in Gen.Vtables: self._fill_vtable_with_methods(ClassName, Gen) - # Generate wrapper functions for inherited methods that are not overridden - if ParentClass: - self._generate_inherited_method_wrappers(ClassName, Gen) + # 多继承支持:不再生成子类包装器(_generate_inherited_method_wrappers)。 + # 包装器只对 self 做 bitcast 基址转换,不调整字段偏移——对多继承中 + # 第二个及之后的父类(字段区偏移 > 0)会错位。继承方法统一走 + # MRO 祖先链查找 + this 调整(见 HandlesExprCall._adjust_this_for_inherited_method), + # 与 TPV 一致。 Gen._classes_in_progress.discard(ClassName) def _generate_inherited_method_wrappers(self, ClassName: str, Gen: LlvmGeneratorMixin) -> None: @@ -777,19 +822,20 @@ class ClassHandle(BaseHandle): 例如 Label 继承 Widget.place,生成 Label.place 函数, 内部将 self 从 Label* bitcast 为 Widget*,然后调用 Widget.place。 """ - # 收集本类方法 + 沿继承链收集父类方法名 + # 收集本类方法 + 沿 MRO 继承链收集父类方法名 # 原代码只遍历 class_methods[ClassName],遗漏父类独有方法(如 __enter__), # 导致子类调用继承的方法时链接失败(undefined reference) methods: list = list(Gen.class_methods.get(ClassName, [])) - p: str | None = Gen.class_parent.get(ClassName) if hasattr(Gen, 'class_parent') else None - while p: - parent_methods: list = Gen.class_methods.get(p, []) + seen_shorts: set[str] = {m.split('.')[-1] if '.' in m else m for m in methods} + mro_list: list[str] = Gen._get_mro_chain(ClassName) + for anc in mro_list[1:]: # 跳过自身,先声明父类优先(MRO 已去重) + parent_methods: list = Gen.class_methods.get(anc, []) for pm in parent_methods: pm_short: str = pm.split('.')[-1] if '.' in pm else pm # 避免重复:如果子类已覆写同名方法,不添加 - if not any(m.split('.')[-1] == pm_short for m in methods): + if pm_short not in seen_shorts: + seen_shorts.add(pm_short) methods.append(pm) - p = Gen.class_parent.get(p) if not methods: return if ClassName not in Gen.structs: @@ -803,17 +849,15 @@ class ClassHandle(BaseHandle): if Gen._find_function(child_full): continue - # 沿继承链查找父类实现 + # 沿 MRO 继承链查找父类实现(先声明父类优先) parent_func: ir.Function | None = None parent_class: str | None = None - p: str | None = Gen.class_parent.get(ClassName) - while p: - parent_full: str = f"{p}.{method_short}" + for anc in Gen._get_mro_chain(ClassName)[1:]: + parent_full: str = f"{anc}.{method_short}" parent_func = Gen._find_function(parent_full) if parent_func: - parent_class = p + parent_class = anc break - p = Gen.class_parent.get(p) if not parent_func or not parent_class: continue @@ -902,19 +946,24 @@ class ClassHandle(BaseHandle): null_i8ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None) initializers: list[ir.Constant] = [] for mi, method_name in enumerate(methods): - method_short: str = method_name.split('.')[-1] if '.' in method_name else method_name - func: ir.Function | None = Gen._find_function(f"{ClassName}.{method_short}") + # 多继承:vtable 槽直接用完整方法名(实际定义类.方法,如 B.get_b), + # 与 TPV 一致,避免"子类名.方法"包装器导致链接 undefined reference。 + func: ir.Function | None = Gen._find_function(method_name) + if not func: + func = Gen._find_function(f"{method_name}__") + if not func: + method_short: str = method_name.split('.')[-1] if '.' in method_name else method_name + func = Gen._find_function(f"{ClassName}.{method_short}") if not func: func = Gen._find_function(f"{ClassName}.{method_short}__") if not func: - p: str | None = Gen.class_parent.get(ClassName) - while p: - func = Gen._find_function(f"{p}.{method_short}") + # 沿 MRO 祖先链查找定义类(先声明父类优先) + for anc in Gen._get_mro_chain(ClassName)[1:]: + func = Gen._find_function(f"{anc}.{method_short}") if not func: - func = Gen._find_function(f"{p}.{method_short}__") + func = Gen._find_function(f"{anc}.{method_short}__") if func: break - p = Gen.class_parent.get(p) if func: initializers.append(func.bitcast(ir.PointerType(ir.IntType(8)))) else: diff --git a/lib/core/Handles/HandlesExprCall.py b/lib/core/Handles/HandlesExprCall.py index 79dd27b..a4991c5 100644 --- a/lib/core/Handles/HandlesExprCall.py +++ b/lib/core/Handles/HandlesExprCall.py @@ -885,19 +885,23 @@ class ExprCallHandle(BaseHandle): func_type = ir.FunctionType(ret_type, llvm_param_types) func = ir.Function(Gen.module, func_type, name=MangledName) Gen.functions[MangledName] = func - if not func: - p = Gen.class_parent.get(ClassName) - while p: - parent_mangled = Gen._mangle_name(f'{p}.{MethodName}') + # 方法实际定义类(多继承 this 调整用) + method_anc_st: str | None = None + # 若 MangledName 只有 stub 声明(无函数体),也走 MRO 回退找定义类方法 + if not func or len(list(func.blocks)) == 0: + # 多继承:沿 MRO 祖先链查找(先声明父类优先) + for anc in Gen._get_mro_chain(ClassName)[1:]: + parent_mangled = Gen._mangle_name(f'{anc}.{MethodName}') parent_func = Gen.functions.get(parent_mangled) if parent_func: func = parent_func + method_anc_st = anc break - parent_func = Gen._find_function(f'{p}.{MethodName}') + parent_func = Gen._find_function(f'{anc}.{MethodName}') if parent_func: func = parent_func + method_anc_st = anc break - p = Gen.class_parent.get(p) if not func: return None CallArgs = [] @@ -932,7 +936,10 @@ class ExprCallHandle(BaseHandle): if self._is_char_pointer(ObjVal): if ClassName in Gen.structs: ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}_static") - CallArgs[0] = ObjVal + # 多继承 this 调整(仅调整 self 参数;vtable 槽位读取用原始 ObjVal, + # 因为 vptr 总在对象首字段,与 TPV _do_virtual_call 一致) + self_arg_st = self._adjust_this_for_inherited_method(Gen, ObjVal, ClassName, method_anc_st) + CallArgs[0] = self_arg_st VtableSlotPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"vtable_slot_{ClassName}_static") VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}_static") VtableArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods)) @@ -2194,9 +2201,13 @@ class ExprCallHandle(BaseHandle): else: self_methods.append(f"{class_name}.{method_name}") - # 收集基类方法 + # 收集基类方法(多继承:遍历所有父类,声明顺序,短名去重,先声明父类优先) parent_methods: list[str] = [] parent_name: str | None = None + parent_names: list[str] = [] + parent_method_names_seen: set[str] = set() + # 第一个真实父类的 vtable 状态(None=无父类/未知) + first_parent_vtable: bool | None = None for base in target_node.bases: base_name: str | None = None if hasattr(base, 'id'): @@ -2205,6 +2216,7 @@ class ExprCallHandle(BaseHandle): base_name = base.attr if not base_name or base_name in ('Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'): continue + parent_names.append(base_name) if parent_name is None: parent_name = base_name @@ -2212,28 +2224,43 @@ class ExprCallHandle(BaseHandle): base_node = class_map.get(base_name) if base_node is not None: base_methods, base_cvtable, base_novtable, _ = collect_class_methods(base_name, sha1, visited) - if base_novtable: - pass # 父类是 NoVTable,不继承 vtable - elif base_cvtable: - is_cvtable = True - parent_methods.extend(base_methods) + # 仅第一个真实父类决定 vtable 继承状态(与 _resolve_class_flags 一致) + if parent_name == base_name: + first_parent_vtable = (base_cvtable and not base_novtable) + for bm in base_methods: + bmn = bm.split('.')[-1] if '.' in bm else bm + if bmn not in parent_method_names_seen: + parent_method_names_seen.add(bmn) + parent_methods.append(bm) else: # 跨模块基类 — 查找基类的 sha1 base_sha1 = struct_sha1_map.get(base_name) if base_sha1: base_methods, base_cvtable, base_novtable, _ = collect_class_methods(base_name, base_sha1, visited) - if base_novtable: - pass - elif base_cvtable: - is_cvtable = True - parent_methods.extend(base_methods) + if parent_name == base_name: + first_parent_vtable = (base_cvtable and not base_novtable) + for bm in base_methods: + bmn = bm.split('.')[-1] if '.' in bm else bm + if bmn not in parent_method_names_seen: + parent_method_names_seen.add(bmn) + parent_methods.append(bm) else: - # 无法找到基类的 sha1 + # 无法找到基类的 sha1 → 父类信息未知,保守启用 vtable + if parent_name == base_name and first_parent_vtable is None: + first_parent_vtable = True if base_name in Gen._cross_module_novtable: pass # 父类是 NoVTable,不继承 vtable + # 继承链统一 vtable(与 _resolve_class_flags 一致): + # 类自身无 @t.CVTable 时,第一个父类有 vtable → 子类启用; + # 父类无 vtable → 子类静态;父类信息未知 → 保守启用。 + if not is_cvtable and not is_novtable and first_parent_vtable is not None: + if first_parent_vtable: + is_cvtable = True + # 合并方法列表:基类方法在前,子类方法在后 - # 子类覆盖的方法放在父类的位置 + # 子类覆盖的方法放在父类的位置; + # 父类独有方法直接用父类方法原名(实际定义类.方法,与 TPV 一致) child_method_map: dict[str, str] = {} for m in self_methods: mn = m.split('.')[-1] if '.' in m else m @@ -2247,8 +2274,8 @@ class ExprCallHandle(BaseHandle): merged.append(child_method_map[mn]) covered_names.add(mn) else: - # 父类独有方法 — 用子类名(虚分派时函数查找会沿继承链回退) - merged.append(f"{class_name}.{mn}") + # 父类独有方法 — 用父类方法原名(虚分派时函数查找沿 MRO 回退) + merged.append(pm) for m in self_methods: mn = m.split('.')[-1] if '.' in m else m @@ -2269,9 +2296,15 @@ class ExprCallHandle(BaseHandle): Gen.class_vtable.add(ClassName) # 设置方法列表(与 prescan 逻辑一致:基类方法在前) Gen.class_methods[ClassName] = methods - # 设置 class_parent(用于虚分派时的函数查找回退) + # 设置 class_parent / class_parents(用于虚分派时的函数查找回退与 MRO) if parent_name and ClassName not in Gen.class_parent: Gen.class_parent[ClassName] = parent_name + parent_names_map = getattr(Gen, 'class_parents', None) + if parent_names_map is None: + parent_names_map = {} + Gen.class_parents = parent_names_map + if parent_name and ClassName not in parent_names_map: + parent_names_map[ClassName] = [parent_name] except Exception: pass @@ -2332,6 +2365,45 @@ class ExprCallHandle(BaseHandle): # 失败时恢复 declare_only 标记,避免后续调用跳过补发 Gen._generic_class_specializations[spec_key] = (cached_name, True) + def _adjust_this_for_inherited_method(self, Gen: LlvmGeneratorMixin, ObjVal: ir.Value, + ClassName: str, method_anc: str | None) -> ir.Value: + """多继承 this 调整(与 TPV _translate_method_call 一致): + + 继承方法定义在"无 vtable 的父类"且该父类字段区在子类中偏移 > 0 时 + (如 C(A,B).get_b,B 的字段区从 b 开始),父类方法按自身布局读 + self.field@0 会读到前面父类的字段。调整:GEP 到父类第一个用户字段 + 在子类中的位置(索引 > 0),bitcast 成父类指针作为 self。 + 有 vtable 的父类布局是子类前缀(vptr 共享),self 无需调整。 + """ + if method_anc is None or method_anc == ClassName: + return ObjVal + anc_has_vtable: bool = (method_anc in Gen.class_vtable + or method_anc in Gen._cross_module_vtable_classes) + if anc_has_vtable: + return ObjVal + anc_members: list = Gen.class_members.get(method_anc, []) + if not anc_members: + return ObjVal + first_field_name: str | None = None + for _fn, _ft in anc_members: + if _fn == '__vtable__': + continue + first_field_name = _fn + break + if first_field_name is None: + return ObjVal + field_idx: int | None = Gen._get_member_offset(first_field_name, ClassName) + if field_idx is None or field_idx <= 0: + return ObjVal + if method_anc not in Gen.structs: + return ObjVal + adj: ir.Value = Gen.builder.gep( + ObjVal, + [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), field_idx)], + name=f"this_adjust_{method_anc}") + return Gen.builder.bitcast(adj, ir.PointerType(Gen.structs[method_anc]), + name=f"this_adjust_cast_{method_anc}") + def _HandleMethodCallLlvm(self, Node: ast.Call) -> ir.Value: Gen = self.Trans.LlvmGen MethodName = Node.func.attr @@ -2800,18 +2872,22 @@ class ExprCallHandle(BaseHandle): break if method_idx >= 0: func = Gen._find_function(FullMethodName) - if not func: + # 方法实际定义类(多继承 this 调整用,None=本类或未知) + method_anc: str | None = None + # 仅当无函数或只有 stub 声明(无函数体)时,沿 MRO 祖先链回退, + # 否则 stub 会挡住回退导致 this 调整失效(与直接调用路径一致) + if not func or len(list(func.blocks)) == 0: func = Gen._find_function(f'{FullMethodName}__') - if not func: - p = Gen.class_parent.get(ClassName) - while p: - parent_full = f'{p}.{MethodName}' + if not func or len(list(func.blocks)) == 0: + # 多继承:沿 MRO 祖先链查找定义类方法(先声明父类优先) + for anc in Gen._get_mro_chain(ClassName)[1:]: + parent_full = f'{anc}.{MethodName}' func = Gen._find_function(parent_full) if not func: func = Gen._find_function(f'{parent_full}__') if func: + method_anc = anc break - p = Gen.class_parent.get(p) # 修复:vtable 分派需要 func 的函数类型。当 func=None 时(如 __new__ # 编译较早,MemManager.alloc 的 declare 尚未加入 Gen.functions), # 从 stub 获取函数类型并创建 declare,确保 vtable 分派不被跳过 @@ -2829,6 +2905,9 @@ class ExprCallHandle(BaseHandle): plain_name = f'{ClassName}.{MethodName}' func = Gen._find_function(plain_name) if func: + # 多继承 this 调整:继承方法定义在无 vtable 父类且字段区偏移 > 0 时, + # 把 self 定位到父类第一个用户字段在子类中的位置并 bitcast 成父类指针。 + self_arg = self._adjust_this_for_inherited_method(Gen, ObjVal, ClassName, method_anc) VtableSlotPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"vtable_slot_{ClassName}") VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}") VtableArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods)) @@ -2837,7 +2916,7 @@ class ExprCallHandle(BaseHandle): MethodPtrI8 = Gen._load(MethodPtrAddr, name=f"vmethod_ptr_{MethodName}") FuncPtrType = ir.PointerType(func.function_type) MethodPtrTyped = Gen.builder.bitcast(MethodPtrI8, FuncPtrType, name=f"vmethod_typed_{MethodName}") - call_args = [ObjVal] + CallArgs + call_args = [self_arg] + CallArgs self._fill_default_args(call_args, func, FullMethodName) self._append_eh_msg_out_arg(call_args, func, Gen) adjusted = Gen._adjust_args(call_args, func) @@ -2845,6 +2924,8 @@ class ExprCallHandle(BaseHandle): self._check_eh_return(result, FullMethodName, Gen, called_func=func) return result func = Gen._find_function(FullMethodName) + # 方法实际定义类(多继承 this 调整用,None=本类或未知) + method_anc: str | None = None if not func: func = Gen._find_function(f'{FullMethodName}__') if not func and class_sha1: @@ -2860,8 +2941,23 @@ class ExprCallHandle(BaseHandle): if not func: plain_name = f'{ClassName}.{MethodName}' func = Gen._find_function(plain_name) + # 若 FullMethodName 只有 stub 声明(无函数体),也走 MRO 回退找定义类方法 + if not func or len(list(func.blocks)) == 0: + # 多继承:沿 MRO 祖先链查找定义类方法(先声明父类优先), + # 继承方法用"定义类.方法"(如 B.get_b)直接调用,不再依赖子类包装器。 + for anc in Gen._get_mro_chain(ClassName)[1:]: + anc_full = f'{anc}.{MethodName}' + func = Gen._find_function(anc_full) + if not func: + func = Gen._find_function(f'{anc_full}__') + if func: + method_anc = anc + break if func: - call_args = [ObjVal] + CallArgs + # 多继承 this 调整:继承方法定义在无 vtable 父类且字段区偏移 > 0 时, + # 把 self 定位到父类第一个用户字段在子类中的位置并 bitcast 成父类指针。 + self_arg = self._adjust_this_for_inherited_method(Gen, ObjVal, ClassName, method_anc) + call_args = [self_arg] + CallArgs self._fill_default_args(call_args, func, FullMethodName) self._append_eh_msg_out_arg(call_args, func, Gen) call_args = Gen._apply_auto_addr(FullMethodName, call_args) diff --git a/lib/core/Handles/HandlesExprUtils.py b/lib/core/Handles/HandlesExprUtils.py index de3deb8..0464849 100644 --- a/lib/core/Handles/HandlesExprUtils.py +++ b/lib/core/Handles/HandlesExprUtils.py @@ -71,6 +71,17 @@ class ExprUtils: FullMethodName: str = f'{ClassName}.{op_name}' if not Gen._has_function(FullMethodName): FullMethodName = f'{ClassName}.{op_name}__' + if not Gen._has_function(FullMethodName): + # 多继承:沿 MRO 祖先链查找 dunder 方法(先声明父类优先,与 TPV 一致) + for anc in Gen._get_mro_chain(ClassName)[1:]: + anc_method: str = f'{anc}.{op_name}' + if Gen._has_function(anc_method): + FullMethodName = anc_method + break + anc_method_ss = f'{anc}.{op_name}__' + if Gen._has_function(anc_method_ss): + FullMethodName = anc_method_ss + break if not Gen._has_function(FullMethodName): # 泛型特化 fallback:当 ClassName 是 opaque 基础名(如 'ndarray')且 # 无直接匹配方法时,遍历 Gen.structs 查找以 'ClassName[' 开头的特化版本 @@ -92,7 +103,16 @@ class ExprUtils: FullMethodName = f'{SpecClass}.{op_name}__' if Gen._has_function(FullMethodName): func: Any = Gen._get_function(FullMethodName) - call_args: list[Any] = [obj_val] + # 多继承 this 调整:运算符重载方法定义在"无 vtable 父类"且字段区偏移 > 0 时, + # 把 self 定位到父类第一个用户字段在子类中的位置并 bitcast 成父类指针。 + method_anc: str | None = None + _fm_short: str = FullMethodName.split('.')[-1] if '.' in FullMethodName else FullMethodName + if '.' in FullMethodName: + _fm_cls: str = FullMethodName.split('.')[0] + if _fm_cls != ClassName: + method_anc = _fm_cls + _call_self: Any = self.Trans.ExprCallHandle._adjust_this_for_inherited_method(Gen, obj_val, ClassName, method_anc) + call_args: list[Any] = [_call_self] if other_val is not None: call_args.append(other_val) if len(func.ftype.args) == len(call_args): diff --git a/lib/core/Handles/HandlesImports.py b/lib/core/Handles/HandlesImports.py index 6679833..6058d6e 100644 --- a/lib/core/Handles/HandlesImports.py +++ b/lib/core/Handles/HandlesImports.py @@ -1783,6 +1783,7 @@ class ImportHandle(BaseHandle): # 修复:先尝试从已加载的父类获取真实字段名;若父类未加载, # 则用 struct body 元素数推断继承字段数并补充占位条目 __inherited_N。 _ParentClass: str | None = None + _ParentClasses: list[str] = [] if Node.bases: for _base in Node.bases: _base_name: str | None = None @@ -1833,18 +1834,27 @@ class ImportHandle(BaseHandle): continue if _base_name in ('CVTable', 'Object', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'): continue + # 多继承:收集所有真实父类(声明顺序),不再 break。 # 总是记录父类名,即使父类 class_members 未加载。 # _get_member_offset 会沿 class_parent 链回退查找继承字段。 - _ParentClass = _base_name - if not hasattr(Gen, 'class_parent'): - Gen.class_parent = {} - Gen.class_parent[ClassName] = _ParentClass - break + _ParentClasses.append(_base_name) + if _ParentClass is None: + _ParentClass = _base_name + if not hasattr(Gen, 'class_parent'): + Gen.class_parent = {} + Gen.class_parent[ClassName] = _ParentClass + if _ParentClasses: + if not hasattr(Gen, 'class_parents'): + Gen.class_parents = {} + Gen.class_parents[ClassName] = _ParentClasses # 治本修复:在继承展开前,尝试从 stub/.pyi 加载父类的 class_members。 # 确保子类 class_members 能正确展平父类真实字段名(而非 __inherited_N 占位符), # 避免 struct body 从不完整 class_members 生成导致 GEP 越界。 # 同时处理父类 class_members 全为占位符的情况(清除后重新加载)。 - if _ParentClass: + _any_parent_loaded: bool = False + for _ParentClass in _ParentClasses: + if not _ParentClass: + continue _parent_members_check: list = Gen.class_members.get(_ParentClass, []) _parent_all_placeholders: bool = bool(_parent_members_check) and all( n.startswith('__inherited_') for n, _ in _parent_members_check @@ -1869,18 +1879,22 @@ class ImportHandle(BaseHandle): if not _parent_loaded and _old_parent_members: # 加载失败,回滚到旧占位条目(有总比没有好) Gen.class_members[_ParentClass] = _old_parent_members - if _ParentClass and _ParentClass in Gen.class_members: - _parent_members: list = list(Gen.class_members[_ParentClass]) - _parent_defaults: dict = dict(Gen.class_member_defaults.get(_ParentClass, {})) - _existing_names: set = {m[0] for m in Gen.class_members[ClassName]} - _inherited: list[tuple] = [] - for _pm_name, _pm_type in _parent_members: - if _pm_name not in _existing_names: - _inherited.append((_pm_name, _pm_type)) - if _pm_name in _parent_defaults: - Gen.class_member_defaults[ClassName][_pm_name] = _parent_defaults[_pm_name] - Gen.class_members[ClassName] = _inherited + Gen.class_members[ClassName] - else: + if _ParentClass in Gen.class_members: + _parent_members: list = list(Gen.class_members[_ParentClass]) + _parent_defaults: dict = dict(Gen.class_member_defaults.get(_ParentClass, {})) + _existing_names: set = {m[0] for m in Gen.class_members[ClassName]} + _inherited: list[tuple] = [] + for _pm_name, _pm_type in _parent_members: + if _pm_name == '__vtable__': + continue + if _pm_name not in _existing_names: + _inherited.append((_pm_name, _pm_type)) + if _pm_name in _parent_defaults: + Gen.class_member_defaults[ClassName][_pm_name] = _parent_defaults[_pm_name] + if _inherited: + Gen.class_members[ClassName] = _inherited + Gen.class_members[ClassName] + _any_parent_loaded = True + if not _any_parent_loaded and _ParentClasses: # 父类未加载到 class_members(常见于泛型特化基类如 GSListNode[Value], # 其特化不在 .pyi 中)。用 struct body 元素数推断继承字段数。 _st = Gen.structs.get(ClassName) diff --git a/lib/core/Handles/HandlesWith.py b/lib/core/Handles/HandlesWith.py index 6f5d06d..1dc1ddf 100644 --- a/lib/core/Handles/HandlesWith.py +++ b/lib/core/Handles/HandlesWith.py @@ -20,14 +20,12 @@ class WithHandle(BaseHandle): func = Gen._find_function(FullName) if hasattr(Gen, '_find_function') else Gen.functions.get(FullName) if func: return (ClassName, func) - # 沿继承链查找 - p: str | None = Gen.class_parent.get(ClassName) if hasattr(Gen, 'class_parent') else None - while p: - parent_full: str = f'{p}.{MethodName}' + # 沿 MRO 继承链查找(多继承:先声明父类优先) + for anc in Gen._get_mro_chain(ClassName)[1:]: + parent_full: str = f'{anc}.{MethodName}' parent_func = Gen._find_function(parent_full) if hasattr(Gen, '_find_function') else Gen.functions.get(parent_full) if parent_func: - return (p, parent_func) - p = Gen.class_parent.get(p) + return (anc, parent_func) return (None, None) def _HandleWithLlvm(self, Node: ast.With) -> None: diff --git a/lib/core/LLVMCG/BaseGen.py b/lib/core/LLVMCG/BaseGen.py index f409a91..c012f3f 100644 --- a/lib/core/LLVMCG/BaseGen.py +++ b/lib/core/LLVMCG/BaseGen.py @@ -95,6 +95,8 @@ class BaseGenMixin: self.class_vtable: set[str] = set() self._class_graph: dict[str, Any] = {} self.class_parent: dict[str, str] = {} + # 多继承:父类名数组(声明顺序)。class_parent 兼容保留为第一个父类。 + self.class_parents: dict[str, list[str]] = {} # Store member annotations for deferred type resolution (cross-module class references) self.class_member_annotations: dict[str, dict[str, Any]] = {} self.loop_break_targets: list[ir.Block] = [] @@ -594,6 +596,40 @@ class BaseGenMixin: def _has_function(self, name: str) -> bool: return self._find_function(name) is not None + def _get_mro_chain(self, ClassName: str) -> list[str]: + """MRO 线性化类名列表(深度优先 + 声明顺序 + 去重,含自身)。 + + 顺序: 自身 → 父类1 及祖先 → 父类2 及祖先(先声明父类优先,菱形去重)。 + 与 TPV HandlesStruct.get_mro_chain 的语义一致。 + """ + chain: list[str] = [] + visited: set[str] = set() + stack: list[str] = [ClassName] + parents_map: dict[str, list[str]] = getattr(self, 'class_parents', {}) + while stack: + cur: str = stack.pop() + if not cur or cur in visited: + continue + visited.add(cur) + chain.append(cur) + parents: list[str] = parents_map.get(cur, []) + # 父类逆序压栈,保证声明顺序弹出(先访问父类1 的整条链) + for p in reversed(parents): + if p and p not in visited: + stack.append(p) + return chain + + def _get_parent_classes(self, ClassName: str) -> list[str]: + """获取类的所有父类名(声明顺序),兼容旧 class_parent 单链。""" + parents_map: dict[str, list[str]] = getattr(self, 'class_parents', {}) + parents: list[str] = parents_map.get(ClassName) + if parents: + return parents + p: str | None = self.class_parent.get(ClassName) + if p: + return [p] + return [] + def _get_or_declare_function(self, mangled_name: str, func_type: ir.FunctionType) -> ir.Function: # DDEBUG: 检测 mangled_name 含双点(不应到达这里) if '..' in mangled_name: diff --git a/lib/core/LLVMCG/StructGen.py b/lib/core/LLVMCG/StructGen.py index c9454cc..4a3c7eb 100644 --- a/lib/core/LLVMCG/StructGen.py +++ b/lib/core/LLVMCG/StructGen.py @@ -626,18 +626,21 @@ class StructGenMixin: Vtable_entries: list[ir.Constant] = [] for MethodName in methods: short_name: str = MethodName.split('.')[-1] if '.' in MethodName else MethodName - full_MethodName: str = f"{ClassName}.{short_name}" + # 多继承:vtable 槽直接用完整方法名(实际定义类.方法,如 B.get_b) func: ir.Function | None = None - if full_MethodName in self.functions: - func = self.functions[full_MethodName] + if MethodName in self.functions: + func = self.functions[MethodName] if func is None: - parent_cls: str | None = self.class_parent.get(ClassName) - while parent_cls: - parent_full: str = f"{parent_cls}.{short_name}" + full_MethodName: str = f"{ClassName}.{short_name}" + if full_MethodName in self.functions: + func = self.functions[full_MethodName] + if func is None: + # 沿 MRO 祖先链查找定义类(先声明父类优先) + for anc in self._get_mro_chain(ClassName)[1:]: + parent_full: str = f"{anc}.{short_name}" if parent_full in self.functions: func = self.functions[parent_full] break - parent_cls = self.class_parent.get(parent_cls) if func is None: Vtable_entries.append(ir.Constant(ir.PointerType(ir.IntType(8)), None)) continue