diff --git a/App/lib/core/Gen/Call/CallGenCall.vp b/App/lib/core/Gen/Call/CallGenCall.vp index 781b974..1bcc789 100644 --- a/App/lib/core/Gen/Call/CallGenCall.vp +++ b/App/lib/core/Gen/Call/CallGenCall.vp @@ -807,16 +807,50 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): break mi += 1 - # 【已删除:兄弟 Mixin 兜底(2026-10-01,用户决定"不兜底、fail-fast")】 - # 原实现:found_func 未命中时遍历全部已注册 struct,取"本模块存在同名方法"的 - # 第一个候选类并绑定(this 再按组合布局前移)。该扫描的候选判据里**没有** - # "接收类自身是否声明了该方法"这一条(TPC 侧 HandlesExprCall.py:3877-3914 有), - # 因而会把同类的方法误绑到无关类:includes/w32/fileio.vp 的 FileW.__init__ - # 调用 self.seek(...) 时(FileW.seek 尚未发射)被绑成 File.seek——旧 IR 靠 - # `bitcast %"…FileW"* to %"…File"*` 静默掩盖,指针规则收紧后以 CastError 报出。 - # 根因已由"先声明后定义"消除(ClassGenVTable._translate_oop_methods 在翻译 - # 方法体前对本类全部方法跑 sig_only=1 建前向声明):方法查找不再依赖类体 - # 书写顺序,未命中即为真未定义,落下方 fail-fast,不再猜候选类。 + # 兄弟 Mixin 方法解析(判据化,**不做"同名即绑"的猜测**)。 + # NoVTable 多继承下,Mixin 内的 self.<兄弟方法>() 接收者是**本 Mixin 类型**, + # 方法却定义在兄弟 Mixin 里(现场 Test/NoVTableCrossMixinTest: + # Combined(BaseA, BaseB) 中 BaseB.caller 调 self.helper(),helper 定义在 BaseA) + # ——本类 MRO 查不到、跨模块 stub 也没有,但组合类布局里确有该字段区, + # 须绑定到**定义类**并由下方 this-adjust(mc_anc 分支)前移指针。 + # 两条正向判据,缺一不可: + # ① 接收类必须是某个已注册类的直接父类(它真的被组合使用)——挡掉 + # includes/w32/fileio.vp 的 FileW.__init__ → self.seek() 误绑 File.seek + # (FileW 无人继承,不是任何 Mixin); + # ② 候选类与接收类必须同处某个已注册组合类的 MRO(真·兄弟)。 + # 仅取**本模块内已有函数**的候选(跨模块定义类需经其 text.ll 取签名,未启用); + # 接收类为跨模块类时不启用(其方法必须经该类自身模块解析)。 + if found_func is None: + sib_recv_local: int = 1 + if trans is not None and trans.ModuleSha1 is not None: + sib_recv_sha1: str = None + if mc_entry is not None and mc_entry.ModuleSha1 is not None: + sib_recv_sha1 = mc_entry.ModuleSha1 + else: + sib_recv_sha1 = self.Gen.StructGen.get_struct_sha1(class_name) + if sib_recv_sha1 is not None: + if string.strcmp(sib_recv_sha1, trans.ModuleSha1) != 0: + sib_recv_local = 0 + if sib_recv_local != 0: + if self._recv_is_mixin_base(class_name) != 0: + sc_i: int = 0 + sc_n: int = self.Gen.StructGen.get_struct_count() + while sc_i < sc_n and found_func is None: + cand_entry: GenTypes.StructEntry* = self.Gen.StructGen._get_struct_entry(sc_i) + if cand_entry is not None and cand_entry.Name is not None: + cand_name: str = cand_entry.Name + if string.strcmp(cand_name, class_name) != 0: + # 泛型特化类名含 '[':不是 Mixin 候选 + if string.strchr(cand_name, '[') is None: + if self._mixin_sibling_same_composite(pool, class_name, cand_name) != 0: + cand_lookup: str = pool.alloc(128) + if cand_lookup is not None: + viperlib.snprintf(cand_lookup, 128, "%s.%s", cand_name, method_name) + cand_func: llvmlite.Function* = self.find_func_in_module(mod, cand_lookup) + if cand_func is not None: + found_func = cand_func + mc_anc = cand_name + sc_i += 1 # 确定调用名(默认 "ClassName.method";found_func 未命中时沿 MRO 用 # 方法实际定义类的 SHA1 构造,如 Module.append → AST 定义的 append, @@ -1933,14 +1967,78 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): self._ensure_method_declare(pool, mod, call_name, ret_ty_sp, head, arg_count_sp) is_variadic_sp: int = self.Gen.CallGenVa._check_and_promote_variadic_call(pool, builder, mod, call_name, head, arg_count_sp, cargs_sp, 1, trans) return llvmlite.build_call(builder, call_name, head, arg_count_sp, ret_ty_sp, is_variadic_sp) + # ============================================================ + # _recv_is_mixin_base - 判据①:接收类是否"被当作父类组合使用"(真 Mixin) + # + # 用于兄弟 Mixin 方法解析:只有当接收类确实是某个已注册类的**直接父类**时, + # 才可能存在"组合类的布局里有该字段区"的情形。反例:FileW 无人继承 ⇒ + # FileW.__init__ 里的 self.seek() 不得绑到同名方法 File.seek。 + # ============================================================ + def _recv_is_mixin_base(self, class_name: str) -> int: + """接收类是否为某个已注册类的直接父类(1=是 / 0=否)""" + if class_name is None: + return 0 + if self.Gen is None: + return 0 + j: int = 0 + n: int = self.Gen.StructGen.get_struct_count() + while j < n: + e: GenTypes.StructEntry* = self.Gen.StructGen._get_struct_entry(j) + if e is not None and e.Name is not None: + pc: int = self.Gen.StructGen.get_parent_count(e.Name) + pj: int = 0 + while pj < pc: + pn: str = self.Gen.StructGen.get_parent_name_at(e.Name, pj) + if pn is not None: + if string.strcmp(pn, class_name) == 0: + return 1 + pj += 1 + j += 1 + return 0 + + # ============================================================ + # _mixin_sibling_same_composite - 判据②:两类是否同处某个组合类的 MRO + # + # 兄弟 Mixin 的充要条件:存在一个已注册类 K,其 MRO 同时含 class_name 与 + # cand_name(如 Combined(BaseA, BaseB):MRO 含 BaseA 与 BaseB ⇒ 二者是兄弟)。 + # 只有满足它才允许把 self.() 绑到同名的兄弟类方法上。 + # ============================================================ + def _mixin_sibling_same_composite(self, pool: memhub.MemBuddy*, + class_name: str, cand_name: str) -> int: + """两类是否同处某个已注册组合类的 MRO(1=是 / 0=否)""" + if class_name is None or cand_name is None or pool is None: + return 0 + if self.Gen is None: + return 0 + j: int = 0 + n: int = self.Gen.StructGen.get_struct_count() + while j < n: + k_entry: GenTypes.StructEntry* = self.Gen.StructGen._get_struct_entry(j) + if k_entry is not None and k_entry.Name is not None: + mro: sizet* = self.Gen.StructGen.get_mro_chain(pool, k_entry.Name) + if mro is not None: + has_self: int = 0 + has_cand: int = 0 + mi: int = 0 + while mro[mi] != 0: + nm: str = rawptr(mro[mi]) + if nm is not None: + if string.strcmp(nm, class_name) == 0: + has_self = 1 + elif string.strcmp(nm, cand_name) == 0: + has_cand = 1 + if has_self != 0 and has_cand != 0: + return 1 + mi += 1 + j += 1 + return 0 + # ============================================================ # _field_byte_offset — 计算字段在结构体布局中的字节偏移 # # 按 LLVM 布局遍历 struct elements 带对齐累加(与 ClassGenMixin._get_type_size # 的 padding 规则一致)。field_index 是 FieldEntry.Index(OOP 类 __vtable__ 占 0)。 # 供多继承 this 调整(字节偏移法)计算 delta。 - # ============================================================ - # ============================================================ def _field_byte_offset(self, pool: memhub.MemBuddy*, entry: GenTypes.StructEntry*,