From b3170ba8237896b27657789fd2cc7f2331f3587b Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 10 Aug 2026 00:28:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(handles):=20Viper=20=E5=A4=9A=E7=BB=A7?= =?UTF-8?q?=E6=89=BF=E6=94=AF=E6=8C=81=20M1-M4=20-=20=E7=88=B6=E7=B1=BB?= =?UTF-8?q?=E6=95=B0=E7=BB=84+MRO/=E5=AD=97=E6=AE=B5=E5=B1=95=E5=B9=B3/vta?= =?UTF-8?q?ble=E5=90=88=E5=B9=B6/this=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HandlesStruct: StructEntry 增加 ParentNames/ParentCount + get_mro_chain (深度优先+声明顺序+去重, 含自身, mro[0]=自身) HandlesClassDef: 多父字段展平去重 + vtable 合并(方法短名去重+探测实际定义类) + 继承链统一 vtable 判定 + 无字段类(接口/标记类)支持 + 空 struct 跳过 zeroinitializer HandlesExprCall: 方法调用沿 MRO 回退 + 继承方法 this 调整(GEP 定位+bitcast 成父类指针) HandlesExprOps: 运算符重载 MRO 链查找 --- App/lib/core/Handles/HandlesClassDef.vp | 326 ++++++++++++++++-------- App/lib/core/Handles/HandlesExprCall.vp | 86 +++++-- App/lib/core/Handles/HandlesExprOps.vp | 74 +++--- App/lib/core/Handles/HandlesStruct.vp | 145 ++++++++++- 4 files changed, 467 insertions(+), 164 deletions(-) diff --git a/App/lib/core/Handles/HandlesClassDef.vp b/App/lib/core/Handles/HandlesClassDef.vp index d178b36..343d758 100644 --- a/App/lib/core/Handles/HandlesClassDef.vp +++ b/App/lib/core/Handles/HandlesClassDef.vp @@ -506,6 +506,73 @@ def _get_parent_class(cd: ast.ClassDef | t.CPtr, return None +# ============================================================ +# _collect_parents — 收集 ClassDef 的所有真实父类名(多继承) +# +# 遍历 cd.bases,跳过 marker base(Object/CVTable/NoVTable 等), +# 收集全部真实父类名到 parents_buf(t.CSizeT 数组,每元素 str 指针)。 +# 返回收集数量。 +# ============================================================ +def _collect_parents(pool: memhub.MemBuddy | t.CPtr, + cd: ast.ClassDef | t.CPtr, + parents_buf: t.CSizeT | t.CPtr, + max_parents: int) -> int: + """收集所有非 marker 父类名到 parents_buf,返回数量""" + if pool is None or cd is None or cd.bases is None or parents_buf is None: + return 0 + bases: list[ast.AST | t.CPtr] | t.CPtr = cd.bases + bn: t.CSizeT = bases.__len__() + count: int = 0 + for bi in range(bn): + if count >= max_parents: + break + base_node: ast.AST | t.CPtr = bases.get(bi) + if base_node is None: + continue + bname: str = _get_base_name(base_node, pool) + if bname is not None and _is_marker_base(bname) == 0: + parents_buf[count] = t.CSizeT(bname) + count += 1 + return count + + +# ============================================================ +# _field_name_exists — 检查字段名是否已存在于收集数组(去重) +# ============================================================ +def _field_name_exists(names_buf: t.CSizeT | t.CPtr, + count: int, name: str) -> int: + """检查字段名是否已在 names_buf 中(先声明父类优先,重复跳过)""" + if names_buf is None or name is None: + return 0 + for fi in range(count): + addr: t.CSizeT = names_buf[fi] + if addr != 0: + existing: str = (str | t.CPtr)(t.CVoid(addr, t.CPtr)) + if existing is not None and string.strcmp(existing, name) == 0: + return 1 + return 0 + + +# ============================================================ +# _class_body_has_field — 检查类体是否声明了字段(AnnAssign) +# +# 用于区分"无字段类"(接口/标记类,合法)与"注册不完整" +# (类体有字段但 FieldCount==0,漏注册报错)。 +# ============================================================ +def _class_body_has_field(cd: ast.ClassDef | t.CPtr) -> int: + """检查类体是否声明了字段(AnnAssign),0=纯方法/无字段类""" + if cd is None or cd.children is None: + return 0 + cn: t.CSizeT = cd.children.__len__() + for ci in range(cn): + stmt: ast.AST | t.CPtr = cd.children.get(ci) + if stmt is None: + continue + if stmt.kind() == ast.ASTKind.AnnAssign: + return 1 + return 0 + + # ============================================================ # _trigger_base_specialization — 触发 base 中 Subscript 的泛型特化 # @@ -641,7 +708,13 @@ def _should_method_be_virtual(cd: ast.ClassDef | t.CPtr, else: is_nvt = HandlesStruct.is_novtable_by_name(parent_name) if is_nvt != 1: - return 1 # 有继承且父类非 NoVTable + # 继承链统一虚表:父类有虚表 → 方法进虚表;父类无虚表 → 静态方法。 + # 与 _detect_vtable_status 规则一致,避免父类无 vptr 布局/子类有 vptr 的错位。 + if p_entry is None: + return 1 # 父类信息未知,保守进虚表 + if p_entry.HasVTable == 1: + return 1 + return 0 # 5. 默认排除 return 0 @@ -691,7 +764,14 @@ def _detect_vtable_status(cd: ast.ClassDef | t.CPtr, if is_nvt == 1: # 父类是 NoVTable,检查函数级 @t.CVTable return _has_any_func_cvtable(cd) - return 1 # 有父类且非 NoVTable,自动启用虚表 + # 继承链统一虚表:父类有虚表 → 子类启用;父类无虚表 → 子类静态。 + # 若父类无 vtable 而子类有,父类方法按无 vptr 布局(self.field@0)操作 + # 子类实例(有 vptr@0)会字段错位 → 运行时错误(父类方法读 vptr 当字段)。 + if p_entry is None: + return 1 # 父类信息未知(跨模块/未注册),保守启用虚表 + if p_entry.HasVTable == 1: + return 1 + return 0 # 默认(无装饰器无继承),检查函数级 @t.CVTable return _has_any_func_cvtable(cd) @@ -2516,12 +2596,15 @@ def translate_class_def(trans: HT.Translator | t.CPtr, if existing is not None: # BUG 5 方案1修改点2:existing 时字段完整性检查 # 结构体已注册,但需验证字段信息是否完整(防止 i32 回退残留或注册不完整) + # 注意:无字段类(接口/标记类,类体无 AnnAssign)合法(空 struct + 方法), + # 仅当"类体声明了字段但 FieldCount==0"才算注册不完整(Phase A-pre 漏注册)。 if existing.FieldCount == 0 and trans._declare_only == 0: - # Phase B 时字段数为 0 说明注册不完整(Phase A-pre 可能漏注册) - HandlesType.fatal_error( - node, - f"结构体已注册但字段数为0,类: {class_name},可能注册不完整", - "InternalCompilerError") + if _class_body_has_field(cd) != 0: + # Phase B 时字段数为 0 说明注册不完整(Phase A-pre 可能漏注册) + HandlesType.fatal_error( + node, + f"结构体已注册但字段数为0,类: {class_name},可能注册不完整", + "InternalCompilerError") # 结构体已注册(Phase A-pre 多遍扫描或 Phase B 重新翻译),但仍需在当前模块中翻译方法 if existing.Ty is not None: # Phase 1b(declare_only=0):existing.Ty 是 Phase 1a 注册的 struct_ty, @@ -2563,6 +2646,11 @@ def translate_class_def(trans: HT.Translator | t.CPtr, # ============================================================ has_vtable: int = _detect_vtable_status(cd, trans) parent_name: str = _get_parent_class(cd, trans) + # 多继承:收集所有真实父类名(跳过 marker base),供字段展平/vtable/MRO 使用 + parents_buf: t.CSizeT | t.CPtr = pool.alloc(8 * HandlesStruct.PARENT_MAX) + parents_count: int = 0 + if parents_buf is not None: + parents_count = _collect_parents(pool, cd, parents_buf, HandlesStruct.PARENT_MAX) is_novtable_deco: int = 0 if cd.decorator_list is not None: if _has_decorator(cd.decorator_list, "NoVTable") == 1: @@ -2616,16 +2704,25 @@ def translate_class_def(trans: HT.Translator | t.CPtr, # [vtable_ptr?, parent_field1, ..., child_field1, ...] # 跳过父类的 __vtable__ 字段(子类会有自己的 vtable 指针) # ============================================================ - if parent_name is not None: - # 用 SHA1 感知查找父类,规避跨模块同名找错 - parent_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module( - parent_name, trans.ModuleSha1) - if parent_entry is None: - # 父类可能定义在另一个模块(不同 SHA1,如 HandlesBase.Mixin), - # find_struct_by_module 不回退全局查找,此处手动回退到 find_struct, - # 否则子类无法继承父类字段 → field_count==0 → __sizeof__ 失败 - parent_entry = HandlesStruct.find_struct(parent_name) - if parent_entry is not None: + # 多继承字段展平:遍历所有父类(声明顺序),字段名去重(先声明优先) + if parents_count > 0 and parents_buf is not None: + for pi in range(parents_count): + pname_addr: t.CSizeT = parents_buf[pi] + if pname_addr == 0: + continue + pname: str = (str | t.CPtr)(t.CVoid(pname_addr, t.CPtr)) + if pname is None: + continue + # 用 SHA1 感知查找父类,规避跨模块同名找错 + parent_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module( + pname, trans.ModuleSha1) + if parent_entry is None: + # 父类可能定义在另一个模块(不同 SHA1,如 HandlesBase.Mixin), + # find_struct_by_module 不回退全局查找,此处手动回退到 find_struct, + # 否则子类无法继承父类字段 → field_count==0 → __sizeof__ 失败 + parent_entry = HandlesStruct.find_struct(pname) + if parent_entry is None: + continue pfc: int = parent_entry.FieldCount for pfi in range(pfc): pfe: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct._get_field_entry( @@ -2638,6 +2735,9 @@ def translate_class_def(trans: HT.Translator | t.CPtr, # 跳过父类的 vtable 指针字段 if string.strcmp(pfn, "__vtable__") == 0: continue + # 多继承去重:字段名已存在跳过(先声明父类优先) + if _field_name_exists(field_names_buf, field_count, pfn) != 0: + continue if field_count < 48: pft: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_field_type_ptr(pfe) pfd: ast.AST | t.CPtr = HandlesStruct.get_field_default_ptr(pfe) @@ -2750,11 +2850,13 @@ def translate_class_def(trans: HT.Translator | t.CPtr, # 静默返回,Phase2.py 的多遍 PhaseA-pre 会在后续遍中重试。 if trans._declare_only == 1 and parent_name is not None: return 0 + # 无字段类(接口/标记类):不能整体跳过——类可能只有方法 + # (如多继承中的 ShapeM/Vec2Extra),需注册类并翻译方法。 + # 空 struct 在 LLVM 中合法({});@t.CVTable 类由下方补 vtable 指针字段。 fb_cf: t.CChar | t.CPtr = VLogger.fmt_buf() if fb_cf is not None: viperlib.snprintf(fb_cf, 1024, "%s has no fields", class_name) VLogger.info(fb_cf, "CLASS") - return 0 # ============================================================ # 2. 创建 LLVM StructType @@ -2867,6 +2969,9 @@ def translate_class_def(trans: HT.Translator | t.CPtr, entry.IsNoVTable = 1 if parent_name is not None: entry.ParentName = parent_name + # 多继承:设置父类数组(供 MRO/字段/vtable 查询,stdlib 深拷贝跨文件存活) + if parents_count > 0 and parents_buf is not None: + HandlesStruct.set_parent_names(class_name, parents_buf, parents_count) # 添加字段信息 # 如果有虚表,先添加 __vtable__ 字段占位(索引 0),用户字段从索引 1 开始 @@ -3067,95 +3172,101 @@ def _collect_vtable_methods(trans: HT.Translator | t.CPtr, # ============================================================ vt_self_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module( class_name, trans.ModuleSha1) - parent_name: str = None - if vt_self_entry is not None: - parent_name = vt_self_entry.ParentName - if parent_name is not None: - # 查找父类 entry(优先同模块 SHA1 匹配) - vt_parent_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module( - parent_name, trans.ModuleSha1) - if vt_parent_entry is None: - # 跨模块继承: 父类在不同模块定义(SHA1 不同),回退到按类名查找 - vt_parent_entry = HandlesStruct.find_struct(parent_name) - parent_vt_count: int = 0 - if vt_parent_entry is not None: - parent_vt_count = vt_parent_entry.VTableMethodCount - if parent_vt_count > 0: - for pvi in range(parent_vt_count): - if method_count >= VTBL_METHOD_MAX: - break - pmethod_name: str = None - if vt_parent_entry is not None: - pmethod_name = HandlesStruct.get_vtable_method_name(parent_name, pvi) - if pmethod_name is None: - continue - # 检查子类是否覆盖了该方法,以及是否标记 @t.NoVTable - child_overrides: int = 0 - child_nvt: int = 0 - for ci2 in range(cn): - stmt2: ast.AST | t.CPtr = children.get(ci2) - if stmt2 is None: + # 多继承:遍历所有父类(声明顺序),vtable 方法合并(短名去重,先声明父类优先) + parent_count: int = HandlesStruct.get_parent_count(class_name) + if parent_count > 0: + for pi in range(parent_count): + pname: str = HandlesStruct.get_parent_name_at(class_name, pi) + if pname is None: + continue + # 查找父类 entry(优先同模块 SHA1 匹配) + vt_parent_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module( + pname, trans.ModuleSha1) + if vt_parent_entry is None: + # 跨模块继承: 父类在不同模块定义(SHA1 不同),回退到按类名查找 + vt_parent_entry = HandlesStruct.find_struct(pname) + parent_vt_count: int = 0 + if vt_parent_entry is not None: + parent_vt_count = vt_parent_entry.VTableMethodCount + if parent_vt_count > 0: + for pvi in range(parent_vt_count): + if method_count >= VTBL_METHOD_MAX: + break + pmethod_name: str = None + if vt_parent_entry is not None: + pmethod_name = HandlesStruct.get_vtable_method_name(pname, pvi) + if pmethod_name is None: continue - if stmt2.kind() != ast.ASTKind.FunctionDef: + # 多继承 vtable 去重:方法短名已存在跳过(先声明父类优先) + if _field_name_exists(method_names_buf, method_count, pmethod_name) != 0: continue - fd2: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(stmt2) - if fd2 is not None and fd2.name is not None: - if string.strcmp(fd2.name, pmethod_name) == 0: - child_overrides = 1 - # 检查子类覆盖方法是否标记 @t.NoVTable - if fd2.decorator_list is not None: - if _has_decorator(fd2.decorator_list, "NoVTable") == 1: - child_nvt = 1 - break - # 构造 full_name - # 子类覆盖且未标记 NoVTable → 用子类实现 - # 子类未覆盖 或 子类覆盖但标记 NoVTable → 用实际定义类实现(保持 vtable 槽位) - # - # 多层继承修复: 直接父类可能只是继承而非定义该方法 - # (如 ElectricCar 继承 Car 的 is_running 实际定义在 Vehicle), - # 沿父类链向上用 find_func_global("类名.方法名") 探测实际定义类, - # 避免生成 "Car.is_running" 这类找不到函数的 full_name → vtable 槽变 null。 - def_class: str = class_name - if child_overrides == 1 and child_nvt == 0: - def_class = class_name - else: - def_class = parent_name - probe_class: str = parent_name - while probe_class is not None: - probe_buf: t.CChar | t.CPtr = pool.alloc(128) - if probe_buf is not None: - viperlib.snprintf(probe_buf, 128, "%s.%s", probe_class, pmethod_name) - if HandlesExprCall.find_func_global(probe_buf) is not None: - def_class = probe_class + # 检查子类是否覆盖了该方法,以及是否标记 @t.NoVTable + child_overrides: int = 0 + child_nvt: int = 0 + for ci2 in range(cn): + stmt2: ast.AST | t.CPtr = children.get(ci2) + if stmt2 is None: + continue + if stmt2.kind() != ast.ASTKind.FunctionDef: + continue + fd2: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(stmt2) + if fd2 is not None and fd2.name is not None: + if string.strcmp(fd2.name, pmethod_name) == 0: + child_overrides = 1 + # 检查子类覆盖方法是否标记 @t.NoVTable + if fd2.decorator_list is not None: + if _has_decorator(fd2.decorator_list, "NoVTable") == 1: + child_nvt = 1 break - probe_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct( - probe_class) - if probe_entry is None or probe_entry.ParentName is None: - break - probe_class = probe_entry.ParentName - pfname_buf: t.CChar | t.CPtr = pool.alloc(128) - if pfname_buf is not None: - viperlib.snprintf(pfname_buf, 128, "%s.%s", def_class, pmethod_name) - full_names_buf[method_count] = t.CSizeT(pfname_buf) - # 复制方法名(短名) - pmname_len: t.CSizeT = string.strlen(pmethod_name) - pmname_buf: t.CChar | t.CPtr = stdlib.malloc(pmname_len + 1) - if pmname_buf is not None: - string.strcpy(pmname_buf, pmethod_name) - method_names_buf[method_count] = t.CSizeT(pmname_buf) - # 记录 mangling 用的 SHA1(继承未覆盖的方法用实际定义类的模块 SHA1) - if child_overrides == 1 and child_nvt == 0: - # 子类覆盖: 用当前模块 SHA1(保持 0) - method_parent_sha1s[method_count] = 0 - else: - # 继承未覆盖: 用实际定义类的模块 SHA1(跨模块继承时 mangling 正确) - def_entry2: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct( - def_class) - if def_entry2 is not None and def_entry2.ModuleSha1 is not None: - method_parent_sha1s[method_count] = t.CSizeT(def_entry2.ModuleSha1) + # 构造 full_name + # 子类覆盖且未标记 NoVTable → 用子类实现 + # 子类未覆盖 或 子类覆盖但标记 NoVTable → 用实际定义类实现(保持 vtable 槽位) + # + # 多层继承修复: 直接父类可能只是继承而非定义该方法 + # (如 ElectricCar 继承 Car 的 is_running 实际定义在 Vehicle), + # 沿父类链向上用 find_func_global("类名.方法名") 探测实际定义类, + # 避免生成 "Car.is_running" 这类找不到函数的 full_name → vtable 槽变 null。 + def_class: str = class_name + if child_overrides == 1 and child_nvt == 0: + def_class = class_name else: + def_class = pname + # 多继承:沿 pname 的 MRO 祖先链探测实际定义类(跳过自身,先声明父类优先) + mro_ptr: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, pname) + mro_i: int = 1 + while mro_ptr is not None and mro_ptr[mro_i] != 0: + anc: str = (str | t.CPtr)(t.CVoid(mro_ptr[mro_i], t.CPtr)) + if anc is not None: + probe_buf: t.CChar | t.CPtr = pool.alloc(128) + if probe_buf is not None: + viperlib.snprintf(probe_buf, 128, "%s.%s", anc, pmethod_name) + if HandlesExprCall.find_func_global(probe_buf) is not None: + def_class = anc + break + mro_i += 1 + pfname_buf: t.CChar | t.CPtr = pool.alloc(128) + if pfname_buf is not None: + viperlib.snprintf(pfname_buf, 128, "%s.%s", def_class, pmethod_name) + full_names_buf[method_count] = t.CSizeT(pfname_buf) + # 复制方法名(短名) + pmname_len: t.CSizeT = string.strlen(pmethod_name) + pmname_buf: t.CChar | t.CPtr = stdlib.malloc(pmname_len + 1) + if pmname_buf is not None: + string.strcpy(pmname_buf, pmethod_name) + method_names_buf[method_count] = t.CSizeT(pmname_buf) + # 记录 mangling 用的 SHA1(继承未覆盖的方法用实际定义类的模块 SHA1) + if child_overrides == 1 and child_nvt == 0: + # 子类覆盖: 用当前模块 SHA1(保持 0) method_parent_sha1s[method_count] = 0 - method_count += 1 + else: + # 继承未覆盖: 用实际定义类的模块 SHA1(跨模块继承时 mangling 正确) + def_entry2: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct( + def_class) + if def_entry2 is not None and def_entry2.ModuleSha1 is not None: + method_parent_sha1s[method_count] = t.CSizeT(def_entry2.ModuleSha1) + else: + method_parent_sha1s[method_count] = 0 + method_count += 1 # 2. 收集当前类的虚方法(跳过已从父类继承的) for ci in range(cn): @@ -3917,9 +4028,14 @@ def _generate_before_init(trans: HT.Translator | t.CPtr, self_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, self_ptr_ty, "%self") # 1. 零值填充: store zeroinitializer, Ptr(struct_ty)* %self - zero: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, struct_ty) - if zero is not None: - llvmlite.build_store(func_builder, zero, self_val) + # 空 struct({})在 LLVM 中不允许 zeroinitializer(invalid type for null constant), + # 无字段类(接口/标记类,如多继承的 Vec2Extra)跳过零值填充; + # @t.CVTable 类必有 vtable 指针字段(非空),不受影响。 + struct_fcount_zi: int = llvmlite.get_struct_fcount(struct_ty) + if struct_fcount_zi > 0: + zero: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, struct_ty) + if zero is not None: + llvmlite.build_store(func_builder, zero, self_val) # 1.5 如果有虚表,store vtable 全局地址到 __vtable__ 字段(索引 0) # 用 find_struct_by_type 定位 entry,规避跨模块同名 find_struct 找错 diff --git a/App/lib/core/Handles/HandlesExprCall.vp b/App/lib/core/Handles/HandlesExprCall.vp index f4e0e9c..3bb680c 100644 --- a/App/lib/core/Handles/HandlesExprCall.vp +++ b/App/lib/core/Handles/HandlesExprCall.vp @@ -3504,23 +3504,23 @@ def _translate_method_call(pool: memhub.MemBuddy | t.CPtr, viperlib.snprintf(spec_lookup, 160, "%s.%s", mc_entry.Name, method_name) found_func = find_func_in_module(mod, spec_lookup) - # 沿继承链查找父类方法(子类未覆盖时回退到父类) + # 沿 MRO 祖先链查找父类方法(子类未覆盖时回退到父类,先声明父类优先) + mc_anc: str = None # 方法实际定义类(多继承 this 调整用) if found_func is None: - cur_parent: str = None - if mc_entry is not None: - cur_parent = mc_entry.ParentName - else: - cur_parent = HandlesStruct.get_parent_name(class_name) - while cur_parent is not None: - parent_lookup: t.CChar | t.CPtr = pool.alloc(128) - if parent_lookup is not None: - viperlib.snprintf(parent_lookup, 128, "%s.%s", cur_parent, method_name) - parent_func: llvmlite.Function | t.CPtr = find_func_in_module(mod, parent_lookup) - if parent_func is not None: - found_func = parent_func - break - # 后续父类按名查找(无 ParentSha1,只能按名回退) - cur_parent = HandlesStruct.get_parent_name(cur_parent) + mro_ptr: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, class_name) + mi: int = 1 + while mro_ptr is not None and mro_ptr[mi] != 0: + anc: str = (str | t.CPtr)(t.CVoid(mro_ptr[mi], t.CPtr)) + if anc is not None: + parent_lookup: t.CChar | t.CPtr = pool.alloc(128) + if parent_lookup is not None: + viperlib.snprintf(parent_lookup, 128, "%s.%s", anc, method_name) + parent_func: llvmlite.Function | t.CPtr = find_func_in_module(mod, parent_lookup) + if parent_func is not None: + found_func = parent_func + mc_anc = anc + break + mi += 1 # 确定调用名 call_name: str = lookup_name @@ -3537,14 +3537,13 @@ def _translate_method_call(pool: memhub.MemBuddy | t.CPtr, else: cls_sha1_mc = HandlesStruct.get_struct_sha1(class_name) if cls_sha1_mc is None: - cur_p: str = None - if mc_entry is not None: - cur_p = mc_entry.ParentName - else: - cur_p = HandlesStruct.get_parent_name(class_name) - while cur_p is not None and cls_sha1_mc is None: - cls_sha1_mc = HandlesStruct.get_struct_sha1(cur_p) - cur_p = HandlesStruct.get_parent_name(cur_p) + mro_p: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, class_name) + mj: int = 1 + while mro_p is not None and mro_p[mj] != 0 and cls_sha1_mc is None: + anc_p: str = (str | t.CPtr)(t.CVoid(mro_p[mj], t.CPtr)) + if anc_p is not None: + cls_sha1_mc = HandlesStruct.get_struct_sha1(anc_p) + mj += 1 if cls_sha1_mc is not None: mangled_len_mc: t.CSizeT = string.strlen(cls_sha1_mc) + string.strlen(lookup_name) + 2 mangled_mc: str = pool.alloc(mangled_len_mc) @@ -3643,10 +3642,41 @@ def _translate_method_call(pool: memhub.MemBuddy | t.CPtr, if kw_calls is not None: kwc = kw_calls.__len__() - # 构建参数链表: obj_ptr → arg1 → arg2 → ... - llvmlite.value_set_next(obj_ptr, None) - head: llvmlite.Value | t.CPtr = obj_ptr - tail: llvmlite.Value | t.CPtr = obj_ptr + # 多继承 this 调整:继承方法定义在"无 vtable 的父类"且该父类字段区在子类中 + # 偏移 > 0 时(如 C(A,B).get_b,B 的字段区在 C 中从 b 开始),父类方法按自身 + # 布局读 self.field@0 会读到前面父类的字段。调整:GEP 到父类第一个用户字段 + # 在子类中的位置,bitcast 成父类指针作为 self。 + # 有 vtable 的父类布局是子类布局前缀(vptr 共享),self 无需调整。 + self_arg: llvmlite.Value | t.CPtr = obj_ptr + if mc_anc is not None and string.strcmp(mc_anc, class_name) != 0: + anc_entry_th: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(mc_anc) + if anc_entry_th is not None and anc_entry_th.HasVTable == 0 and anc_entry_th.FieldCount > 0: + anc_first_name_th: str = None + afi_th: int = 0 + while afi_th < anc_entry_th.FieldCount: + afe_th: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct._get_field_entry( + anc_entry_th, afi_th) + if afe_th is not None: + afn_th: str = HandlesStruct.get_field_name_ptr(afe_th) + if afn_th is not None and string.strcmp(afn_th, "__vtable__") != 0: + anc_first_name_th = afn_th + break + afi_th += 1 + if anc_first_name_th is not None: + sub_entry_th: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct( + class_name) + if sub_entry_th is not None: + sub_field_th: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field( + sub_entry_th.Ty, anc_first_name_th) + if sub_field_th is not None and sub_field_th.Index > 0: + anc_ptr_ty_th: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, anc_entry_th.Ty) + adj_th: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( + builder, sub_entry_th.Ty, sub_field_th.Ty, obj_ptr, sub_field_th.Index) + self_arg = llvmlite.build_bitcast(builder, adj_th, anc_ptr_ty_th) + # 构建参数链表: self → arg1 → arg2 → ... + llvmlite.value_set_next(self_arg, None) + head: llvmlite.Value | t.CPtr = self_arg + tail: llvmlite.Value | t.CPtr = self_arg arg_count_eff: int = 0 if kwc > 0 and found_func is not None: diff --git a/App/lib/core/Handles/HandlesExprOps.vp b/App/lib/core/Handles/HandlesExprOps.vp index 31e14d8..12e9c20 100644 --- a/App/lib/core/Handles/HandlesExprOps.vp +++ b/App/lib/core/Handles/HandlesExprOps.vp @@ -122,17 +122,21 @@ def try_operator_overload(pool: memhub.MemBuddy | t.CPtr, search_class: str = class_name if found_func is None: - cur_parent: str = HandlesStruct.get_parent_name(class_name) - while cur_parent is not None: - parent_lookup: t.CChar | t.CPtr = pool.alloc(128) - if parent_lookup is not None: - viperlib.snprintf(parent_lookup, 128, "%s.%s", cur_parent, dunder) - parent_func: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, parent_lookup) - if parent_func is not None: - found_func = parent_func - search_class = cur_parent - break - cur_parent = HandlesStruct.get_parent_name(cur_parent) + # 多继承:沿 MRO 祖先链查找(先声明父类优先) + mro_ptr: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, class_name) + mi: int = 1 + while mro_ptr is not None and mro_ptr[mi] != 0: + anc: str = (str | t.CPtr)(t.CVoid(mro_ptr[mi], t.CPtr)) + if anc is not None: + parent_lookup: t.CChar | t.CPtr = pool.alloc(128) + if parent_lookup is not None: + viperlib.snprintf(parent_lookup, 128, "%s.%s", anc, dunder) + parent_func: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, parent_lookup) + if parent_func is not None: + found_func = parent_func + search_class = anc + break + mi += 1 # 方法不在当前模块中时,检查类(含父类)是否有 SHA1(stub 可能未注入) # 优先用类型指针定位 entry 获取 SHA1(规避跨模块同名 find_struct 找错) @@ -143,10 +147,13 @@ def try_operator_overload(pool: memhub.MemBuddy | t.CPtr, if cls_sha1 is None: cls_sha1 = HandlesStruct.get_struct_sha1(search_class) if cls_sha1 is None: - cur_p: str = HandlesStruct.get_parent_name(search_class) - while cur_p is not None and cls_sha1 is None: - cls_sha1 = HandlesStruct.get_struct_sha1(cur_p) - cur_p = HandlesStruct.get_parent_name(cur_p) + mro_p: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, search_class) + mj: int = 1 + while mro_p is not None and mro_p[mj] != 0 and cls_sha1 is None: + anc_p: str = (str | t.CPtr)(t.CVoid(mro_p[mj], t.CPtr)) + if anc_p is not None: + cls_sha1 = HandlesStruct.get_struct_sha1(anc_p) + mj += 1 # 既无函数定义也无 SHA1 → 该类没有此 dunder 方法,返回 None 回退原生运算 if found_func is None and cls_sha1 is None: @@ -208,17 +215,21 @@ def try_unary_operator_overload(pool: memhub.MemBuddy | t.CPtr, search_class: str = class_name if found_func is None: - cur_parent: str = HandlesStruct.get_parent_name(class_name) - while cur_parent is not None: - parent_lookup: t.CChar | t.CPtr = pool.alloc(128) - if parent_lookup is not None: - viperlib.snprintf(parent_lookup, 128, "%s.%s", cur_parent, dunder) - parent_func: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, parent_lookup) - if parent_func is not None: - found_func = parent_func - search_class = cur_parent - break - cur_parent = HandlesStruct.get_parent_name(cur_parent) + # 多继承:沿 MRO 祖先链查找(先声明父类优先) + mro_ptr: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, class_name) + mi: int = 1 + while mro_ptr is not None and mro_ptr[mi] != 0: + anc: str = (str | t.CPtr)(t.CVoid(mro_ptr[mi], t.CPtr)) + if anc is not None: + parent_lookup: t.CChar | t.CPtr = pool.alloc(128) + if parent_lookup is not None: + viperlib.snprintf(parent_lookup, 128, "%s.%s", anc, dunder) + parent_func: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, parent_lookup) + if parent_func is not None: + found_func = parent_func + search_class = anc + break + mi += 1 # 方法不在当前模块中时,检查类(含父类)是否有 SHA1(stub 可能未注入) cls_sha1: str = None @@ -228,10 +239,13 @@ def try_unary_operator_overload(pool: memhub.MemBuddy | t.CPtr, if cls_sha1 is None: cls_sha1 = HandlesStruct.get_struct_sha1(search_class) if cls_sha1 is None: - cur_p: str = HandlesStruct.get_parent_name(search_class) - while cur_p is not None and cls_sha1 is None: - cls_sha1 = HandlesStruct.get_struct_sha1(cur_p) - cur_p = HandlesStruct.get_parent_name(cur_p) + mro_p: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, search_class) + mj: int = 1 + while mro_p is not None and mro_p[mj] != 0 and cls_sha1 is None: + anc_p: str = (str | t.CPtr)(t.CVoid(mro_p[mj], t.CPtr)) + if anc_p is not None: + cls_sha1 = HandlesStruct.get_struct_sha1(anc_p) + mj += 1 # 既无函数定义也无 SHA1 → 该类没有此 dunder 方法,返回 None 回退原生运算 if found_func is None and cls_sha1 is None: diff --git a/App/lib/core/Handles/HandlesStruct.vp b/App/lib/core/Handles/HandlesStruct.vp index e9a4016..852aaf3 100644 --- a/App/lib/core/Handles/HandlesStruct.vp +++ b/App/lib/core/Handles/HandlesStruct.vp @@ -24,6 +24,9 @@ import lib.core.VLogger as VLogger STRUCT_MAX: t.CDefine = 512 FIELD_MAX: t.CDefine = 48 FIELD_NAME_MAX: t.CDefine = 64 +# 多继承(MRO)支持:单类最大父类数 / MRO 链最大长度 +PARENT_MAX: t.CDefine = 8 +MRO_MAX: t.CDefine = 16 # ============================================================ @@ -53,7 +56,9 @@ class StructEntry: HasNew: int # 1=有__new__方法 / 0=无 HasVTable: int # 1=有虚表 / 0=无虚表 IsNoVTable: int # 1=明确标记 @t.NoVTable / 0=未标记 - ParentName: t.CChar | t.CPtr # 父类名(None=无父类) + ParentName: t.CChar | t.CPtr # 父类名(None=无父类,多继承时为第一个父类,兼容旧调用) + ParentNames: t.CSizeT | t.CPtr # 多继承父类名数组(每元素 str 指针,ParentCount 个;stdlib 深拷贝) + ParentCount: t.CInt # 父类数量(0=无父类,1=单继承,>1=多继承) VTableMethodCount: int # 虚表中的方法数量 VTableMethods: t.CChar | t.CPtr # 虚表方法名数组(每个方法名 str,VTableMethodCount 个) ModuleSha1: t.CChar | t.CPtr # 定义该类的模块 SHA1(None=无 SHA1) @@ -928,6 +933,144 @@ def set_parent_name(name: str, parent_name: str) -> int: return 1 +# ============================================================ +# set_parent_names — 设置多继承父类名数组 +# +# names_ptr: t.CSizeT 数组(每元素类名 str 指针),count 个。 +# 传 None/0 清除父类数组。ParentName(第一个父类)由调用方 +# 另行通过 set_parent_name 维护(兼容现有单继承调用方)。 +# ============================================================ +def set_parent_names(name: str, names_ptr: t.CSizeT | t.CPtr, + count: int) -> int: + """设置结构体的父类名数组(stdlib 深拷贝,跨文件永久存活)""" + if name is None: + return 0 + entry: StructEntry | t.CPtr = find_struct(name) + if entry is None: + return 0 + if names_ptr is None or count <= 0: + entry.ParentNames = None + entry.ParentCount = 0 + return 1 + if count > PARENT_MAX: + count = PARENT_MAX + arr: t.CSizeT | t.CPtr = stdlib.malloc(8 * count) + if arr is None: + return 0 + for i in range(count): + addr: t.CSizeT = names_ptr[i] + arr[i] = 0 + if addr != 0: + src: str = (str | t.CPtr)(t.CVoid(addr, t.CPtr)) + if src is not None: + slen: t.CSizeT = string.strlen(src) + dup: t.CChar | t.CPtr = stdlib.malloc(slen + 1) + if dup is not None: + string.strcpy(dup, src) + arr[i] = t.CSizeT(dup) + entry.ParentNames = t.CSizeT(arr) + entry.ParentCount = count + return 1 + + +# ============================================================ +# get_parent_count — 获取父类数量 +# ============================================================ +def get_parent_count(name: str) -> int: + """获取父类数量(0=无父类,1=单继承,>1=多继承)""" + if name is None: + return 0 + entry: StructEntry | t.CPtr = find_struct(name) + if entry is None: + return 0 + return entry.ParentCount + + +# ============================================================ +# get_parent_name_at — 获取第 idx 个父类名(声明顺序) +# ============================================================ +def get_parent_name_at(name: str, idx: int) -> str: + """获取第 idx 个父类名,越界返回 None""" + if name is None or idx < 0: + return None + entry: StructEntry | t.CPtr = find_struct(name) + if entry is None or entry.ParentNames is None: + return None + if idx >= entry.ParentCount: + return None + parr: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(entry.ParentNames, t.CPtr)) + addr: t.CSizeT = parr[idx] + if addr == 0: + return None + return (str | t.CPtr)(t.CVoid(addr, t.CPtr)) + + +# ============================================================ +# get_mro_chain — MRO 线性化类名数组(深度优先 + 声明顺序 + 去重,含自身) +# +# 返回: pool 分配的 t.CSizeT 数组(每元素类名 str 指针),末尾 0 结束。 +# 顺序: 自身 → 父类1 及祖先 → 父类2 及祖先(先声明优先,菱形去重)。 +# 失败返回 None。 +# ============================================================ +def get_mro_chain(pool: memhub.MemBuddy | t.CPtr, + name: str) -> t.CSizeT | t.CPtr: + """MRO 线性化类名数组(深度优先 + 声明顺序 + 去重,含自身)""" + if pool is None or name is None: + return None + arr: t.CSizeT | t.CPtr = pool.alloc(8 * (MRO_MAX + 1)) + if arr is None: + return None + stack: t.CSizeT | t.CPtr = pool.alloc(8 * MRO_MAX) + if stack is None: + return None + visited: t.CSizeT | t.CPtr = pool.alloc(8 * MRO_MAX) + if visited is None: + return None + stack_count: int = 0 + out_count: int = 0 + visit_count: int = 0 + # 初始压入自身 + stack[0] = t.CSizeT(name) + stack_count = 1 + while stack_count > 0: + stack_count -= 1 + cur_addr: t.CSizeT = stack[stack_count] + cur: str = (str | t.CPtr)(t.CVoid(cur_addr, t.CPtr)) + if cur is None: + continue + # 已访问检查(线性搜索) + dup: int = 0 + for vi in range(visit_count): + va: t.CSizeT = visited[vi] + if va != 0: + vn: str = (str | t.CPtr)(t.CVoid(va, t.CPtr)) + if vn is not None and string.strcmp(vn, cur) == 0: + dup = 1 + break + if dup != 0: + continue + # 加入结果与已访问集合 + if out_count < MRO_MAX: + arr[out_count] = t.CSizeT(cur) + out_count += 1 + if visit_count < MRO_MAX: + visited[visit_count] = t.CSizeT(cur) + visit_count += 1 + # 父类逆序压栈(正向 range 计算逆序索引,保证声明顺序弹出) + entry: StructEntry | t.CPtr = find_struct(cur) + if entry is not None and entry.ParentCount > 0 and entry.ParentNames is not None: + pc: int = entry.ParentCount + parr: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(entry.ParentNames, t.CPtr)) + for pi2 in range(pc): + pi: int = pc - 1 - pi2 + if stack_count < MRO_MAX: + stack[stack_count] = parr[pi] + stack_count += 1 + # 末尾 0 结束 + arr[out_count] = 0 + return arr + + # ============================================================ # get_parent_name — 获取父类名 # ============================================================