From f2b33f5e9bdc1fd5a46154a2f5561342b9a670a6 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 15 Aug 2026 00:41:21 +0800 Subject: [PATCH] =?UTF-8?q?fix(core):=20=5Ftranslate=5Fmethod=5Fcall=20?= =?UTF-8?q?=E6=B2=BF=20MRO=20=E7=A5=96=E5=85=88=E9=93=BE=E6=9F=A5=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E7=B1=BB=E5=9E=8B=EF=BC=8C=E4=BF=AE=E5=A4=8D=20Module?= =?UTF-8?q?.append?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mod.append(Module 继承 AST)中 append 定义在 ast/base.vp(sha1=71b2), found_func 未命中时 fallback 按 class_name="Module" 查 text.ll/vpi 找不到 (函数名是 {71b2}.AST.append)→ fail-fast。修复: 1. 新增 ret_anc_name/ret_anc_sha1(方法实际定义类),默认 class_name, MRO fallback 命中祖先类(AST)时更新; 2. text.ll/vpi 均沿 MRO 祖先链用祖先类名 + 祖先类 sha1 重试查询; 3. 调用名也用方法定义类构造({AST_sha1}.AST.append),避免链接 undefined。 --- App/lib/core/Gen/CallGenMixin.vp | 91 +++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 25 deletions(-) diff --git a/App/lib/core/Gen/CallGenMixin.vp b/App/lib/core/Gen/CallGenMixin.vp index b1194fd..5a38a4c 100644 --- a/App/lib/core/Gen/CallGenMixin.vp +++ b/App/lib/core/Gen/CallGenMixin.vp @@ -3729,34 +3729,14 @@ class CallGenMixin: break mi += 1 - # 确定调用名 + # 确定调用名(默认 "ClassName.method";found_func 未命中时沿 MRO 用 + # 方法实际定义类的 SHA1 构造,如 Module.append → AST 定义的 append, + # 函数名为 {AST_sha1}.AST.append) call_name: str = lookup_name if found_func is not None: found_name: str = llvmlite.function_get_name(found_func) if found_name is not None: call_name = found_name - else: - # found_func 为 None(stub 未注入):用类所属模块的 SHA1 构造调用名 - # 沿继承链查找第一个有 SHA1 的类 - cls_sha1_mc: str = None - if mc_entry is not None: - cls_sha1_mc = mc_entry.ModuleSha1 - else: - cls_sha1_mc = HandlesStruct.get_struct_sha1(class_name) - if cls_sha1_mc is None: - 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) - if mangled_mc is not None: - viperlib.snprintf(mangled_mc, mangled_len_mc, "%s.%s", cls_sha1_mc, lookup_name) - call_name = mangled_mc # 确定返回类型 i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) @@ -3771,6 +3751,24 @@ class CallGenMixin: if sp_ty2 is not None: call_ret_ty = sp_ty2 else: + # 方法实际定义类(跨模块调用名/返回类型来源;Module.append 定义在 AST) + # 默认 class_name 自身,MRO fallback 命中祖先类时更新 + ret_anc_name: str = class_name + ret_anc_sha1: str = None + if mc_entry is not None: + ret_anc_sha1 = mc_entry.ModuleSha1 + else: + ret_anc_sha1 = HandlesStruct.get_struct_sha1(class_name) + if ret_anc_sha1 is None: + # 沿 MRO 找第一个有 SHA1 的类 + mro_s0: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, class_name) + mi_s0: int = 1 + while mro_s0 is not None and mro_s0[mi_s0] != 0 and ret_anc_sha1 is None: + anc_s0: str = (str | t.CPtr)(t.CVoid(mro_s0[mi_s0], t.CPtr)) + if anc_s0 is not None: + ret_anc_sha1 = HandlesStruct.get_struct_sha1(anc_s0) + mi_s0 += 1 + # 泛型特化方法调用:特化函数可能尚未注册(如 ast.base 用 # list[AST|t.CPtr].get)。主动触发特化(幂等,MethodsEmitted 防重入), # 特化后重新查找即可命中,返回类型来自特化函数定义(天然精确)。 @@ -3827,18 +3825,61 @@ class CallGenMixin: # 尝试从 text.ll 查找实际返回类型 # 避免指针返回值被默认 i32 导致 inttoptr 截断 looked_up_ty: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL( - pool, cls_sha1_mc, class_name, method_name) + pool, ret_anc_sha1, ret_anc_name, method_name) + # 沿 MRO 祖先链重试:方法可能定义在基类(如 Module.append → + # AST.append,函数名 {ast_sha1}.AST.append,且基类在另一模块文件 + # 如 ast/stmts.vp 的 Module vs ast/base.vp 的 AST),按调用类名 + # 及其 sha1 查不到,需用每个祖先类的类名 + 祖先类所属模块 sha1 + # 查 text.ll(NewAstTest mod.append 根因) + if looked_up_ty is None: + mro_t2: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, class_name) + mi_t2: int = 1 + while mro_t2 is not None and mro_t2[mi_t2] != 0 and looked_up_ty is None: + anc_t2: str = (str | t.CPtr)(t.CVoid(mro_t2[mi_t2], t.CPtr)) + if anc_t2 is not None and string.strcmp(anc_t2, class_name) != 0: + anc_sha1_t2: str = HandlesStruct.get_struct_sha1(anc_t2) + looked_up_ty = self._LookupMethodRetTyFromTextLL( + pool, anc_sha1_t2, anc_t2, method_name) + if looked_up_ty is not None: + ret_anc_name = anc_t2 + ret_anc_sha1 = anc_sha1_t2 + mi_t2 += 1 if looked_up_ty is not None: call_ret_ty = looked_up_ty else: # 精确查找:vpi stub 返回注解缓存(方法定义处,不硬编码白名单) vpi_ret_ty: llvmlite.LLVMType | t.CPtr = self._lookup_method_ret_ty_via_vpi( - pool, cls_sha1_mc, class_name, method_name) + pool, ret_anc_sha1, ret_anc_name, method_name) + # vpi 同样沿 MRO 祖先链重试(基类方法注解,用祖先类自身 sha1) + if vpi_ret_ty is None: + mro_v2: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, class_name) + mi_v2: int = 1 + while mro_v2 is not None and mro_v2[mi_v2] != 0 and vpi_ret_ty is None: + anc_v2: str = (str | t.CPtr)(t.CVoid(mro_v2[mi_v2], t.CPtr)) + if anc_v2 is not None and string.strcmp(anc_v2, class_name) != 0: + anc_sha1_v2: str = HandlesStruct.get_struct_sha1(anc_v2) + vpi_ret_ty = self._lookup_method_ret_ty_via_vpi( + pool, anc_sha1_v2, anc_v2, method_name) + if vpi_ret_ty is not None: + ret_anc_name = anc_v2 + ret_anc_sha1 = anc_sha1_v2 + mi_v2 += 1 if vpi_ret_ty is not None: call_ret_ty = vpi_ret_ty else: call_ret_ty = self._fail_method_ret_ty(method_name, cl) + # found_func 未命中但返回类型已解析:用方法定义类构造跨模块调用名 + # (Module.append → {AST_sha1}.AST.append,避免链接 undefined reference) + if ret_anc_name is not None and ret_anc_sha1 is not None: + ml_anc: t.CSizeT = string.strlen(ret_anc_sha1) + string.strlen(ret_anc_name) \ + + string.strlen(method_name) + 3 + mangled_anc: str = pool.alloc(ml_anc) + if mangled_anc is not None: + viperlib.snprintf(mangled_anc, ml_anc, "%s.%s.%s", + ret_anc_sha1, ret_anc_name, method_name) + call_name = mangled_anc + # 翻译调用参数 cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args can: t.CSizeT = 0