From 916c1284c9899a638b121a9c61c77edd98030e77 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 25 Aug 2026 13:53:56 +0800 Subject: [PATCH] =?UTF-8?q?fix(tpc):=20=E8=99=9A=E8=B0=83=E7=94=A8=20vtabl?= =?UTF-8?q?e=20=E5=B8=83=E5=B1=80=E6=8C=89=E6=8E=A5=E6=94=B6=E8=80=85?= =?UTF-8?q?=E9=9D=99=E6=80=81=E7=B1=BB=E5=9E=8B=E8=A7=A3=E6=9E=90=E8=80=8C?= =?UTF-8?q?=E9=9D=9E=20this=20=E8=B0=83=E6=95=B4=E5=90=8E=E7=88=B6?= =?UTF-8?q?=E7=B1=BB=E6=8C=87=E9=92=88=20-=20=5Fdo=5Fvirtual=5Fcall=20?= =?UTF-8?q?=E5=8E=9F=E7=94=A8=20self=5Fptr.Ty=20=E8=A7=A3=E6=9E=90=20vc=5F?= =?UTF-8?q?entry=EF=BC=8C=E5=A4=9A=E7=BB=A7=E6=89=BF=E4=B8=AD=20self=20?= =?UTF-8?q?=E8=A2=AB=20bitcast=20=E6=88=90=E7=88=B6=E7=B1=BB=E6=8C=87?= =?UTF-8?q?=E9=92=88(=E5=A6=82=20ColorM*)=E5=90=8E=E5=BE=97=E5=88=B0?= =?UTF-8?q?=E7=88=B6=E7=B1=BB=E5=8D=95=E6=A7=BD=20vtable=20=E5=B8=83?= =?UTF-8?q?=E5=B1=80(method=5Findex/method=5Fcount=20=E6=8C=89=E7=88=B6?= =?UTF-8?q?=E7=B1=BB=E7=AE=97)=E2=86=92=20GEP=20=E6=A7=BD=E4=BD=8D=200=20?= =?UTF-8?q?=E8=AF=BB=E5=88=B0=20area=20=E8=80=8C=E9=9D=9E=20color=5Fcode(M?= =?UTF-8?q?ultiInheritTest=20color=5Fcode=20=E8=99=9A=E8=B0=83=E7=94=A8=20?= =?UTF-8?q?FAILED=20=E6=A0=B9=E5=9B=A0)=EF=BC=9B=E6=94=B9=E4=BC=98?= =?UTF-8?q?=E5=85=88=E7=94=A8=20vtable=5Fbase(=E5=AF=B9=E8=B1=A1=E5=A4=B4)?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E8=A7=A3=E6=9E=90=EF=BC=8C=E6=9C=AA=E4=BC=A0?= =?UTF-8?q?=20vtable=5Fbase=20=E6=97=B6=E5=9B=9E=E9=80=80=20self=5Fptr=20?= =?UTF-8?q?=E4=BF=9D=E6=8C=81=E5=8E=9F=E8=A1=8C=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TransPyV/App/lib/core/Gen/CallGenCall.vp | 1843 ++++++++++++++++++++++ 1 file changed, 1843 insertions(+) create mode 100644 TransPyV/App/lib/core/Gen/CallGenCall.vp diff --git a/TransPyV/App/lib/core/Gen/CallGenCall.vp b/TransPyV/App/lib/core/Gen/CallGenCall.vp new file mode 100644 index 0000000..7f73d4a --- /dev/null +++ b/TransPyV/App/lib/core/Gen/CallGenCall.vp @@ -0,0 +1,1843 @@ +import lib.core.Gen.GenTypes as GenTypes +import t, c +from stdint import * +import ast +import llvmlite +import memhub +import string +import stdio +import stdlib +import sys +import viperlib +import w32.fileio as fileio +import lib.core.VLogger as VLogger +import lib.core.Handles.HandlesBase as HandlesBase +import lib.core.Handles.HandlesTranslator as HT +import lib.core.Handles.HandlesExpr as HandlesExpr +import lib.Stub.StubMerger as StubMerger +import lib.core.Handles.HandlesExprCall as HandlesExprCall +import lib.core.CompilerFactory as CompilerFactory + +import lib.core.Gen.CallGenBase as CallGenBase +import lib.core.Handles.ExprCallTable as ExprCallTable + + +# ============================================================ +# CallGenCall - CallGenMixin 拆分(阶段 B):方法调用分派(含 super / 虚调用 / kwargs 名匹配) +# +# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。 +# 跨 Mixin 调用统一走 HandlesExprCall 委托(经 _g_gen)。 +# @t.NoVTable:静态绑定。 +# ============================================================ +@t.NoVTable +class CallGenCall(CallGenBase.CallGenBase): + """方法调用分派(含 super / 虚调用 / kwargs 名匹配)""" + + # ============================================================ + # _do_virtual_call — 通过虚表间接调用虚方法 + # + # IR 模式: + # %1 = getelementptr StructTy, StructTy* %self, i32 0, i32 0 ; i8** + # %2 = load i8*, i8** %1 ; i8* (vtable ptr) + # %3 = bitcast i8* %2 to {i8*, ...}* ; vtable typed ptr + # %4 = getelementptr {i8*, ...}, {i8*, ...}* %3, i32 0, i32 N ; i8** + # %5 = load i8*, i8** %4 ; i8* (func ptr) + # %6 = bitcast i8* %5 to RetTy(Params)* ; func typed ptr + # call RetTy %6(Params %self, ...) + # + # 参数: + # args_head — 已构建好的参数链表头(self_ptr 为首节点) + # args_count — 参数总数(含 self) + # found_func — 方法对应的 Function 对象(用于推断函数签名) + # ret_ty — 返回类型 + # ============================================================ + + # ============================================================ + def _do_virtual_call(self, pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + class_name: str, + method_name: str, + self_ptr: llvmlite.Value | t.CPtr, + args_head: llvmlite.Value | t.CPtr, + args_count: int, + found_func: llvmlite.Function | t.CPtr, + ret_ty: llvmlite.LLVMType | t.CPtr, + vtable_base: llvmlite.Value | t.CPtr = None) -> llvmlite.Value | t.CPtr: + """通过虚表间接调用虚方法,返回 call 结果(void 方法返回 None) + + vtable_base: 对象头指针(vtable 所在位置,即未调整的 obj_ptr)。 + 多继承 this 调整后 self_ptr 可能前移(如 obj+4 指向父类字段区), + 从调整后位置读 vtable 会读到字段值 → 垃圾指针崩溃。默认 None 时 + vtable_base = self_ptr(未调整场景,读对象头)。 + """ + if pool is None or builder is None or class_name is None or method_name is None: + return None + if self_ptr is None or args_head is None: + return None + if vtable_base is None: + vtable_base = self_ptr + + # 1. 获取结构体类型(用 find_struct_by_type 规避跨模块同名 find_struct 找错) + # 必须用 vtable_base(对象头)的类型而非 self_ptr:多继承 this 调整后 + # self_ptr 已 bitcast 成父类指针(如 ColorM*),用其解析会得到父类 + # vtable 布局(单槽 {i8*})→ method_index/method_count 按父类算, + # GEP 槽位错位读到别的函数(MultiInheritTest color_code 虚调用读到 + # area FAILED 根因)。vtable_base 是接收者静态类型指针(vtable 所在 + # 位置),其 VTableMethods 才是最终对象实际存储的完整 vtable 布局。 + vc_entry: GenTypes.StructEntry | t.CPtr = None + struct_ty: llvmlite.LLVMType | t.CPtr = None + if vtable_base is not None and vtable_base.Ty is not None: + vc_entry = self.Gen.StructGen.get_struct_type_from_value(vtable_base) + if vc_entry is not None: + # vc_entry 是 struct_ty(LLVMType),需要找 StructEntry + vc_entry = self.Gen.StructGen.find_struct_by_type(vc_entry) + if vc_entry is not None: + struct_ty = vc_entry.Ty + if struct_ty is None and self_ptr is not None and self_ptr.Ty is not None: + # fallback:vtable_base 无类型信息时退回 self_ptr(未调整场景两者一致) + vc_entry = self.Gen.StructGen.get_struct_type_from_value(self_ptr) + if vc_entry is not None: + vc_entry = self.Gen.StructGen.find_struct_by_type(vc_entry) + if vc_entry is not None: + struct_ty = vc_entry.Ty + if struct_ty is None: + struct_ty = self.Gen.StructGen.get_struct_type(class_name) + if struct_ty is None: + return None + + # 2. 获取方法在虚表中的索引(用 entry 直接查找) + method_index: int = -1 + if vc_entry is not None and vc_entry.VTableMethods is not None: + methods_arr: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(vc_entry.VTableMethods, t.CPtr)) + for mi in range(vc_entry.VTableMethodCount): + mname_addr: t.CSizeT = methods_arr[mi] + if mname_addr == 0: + continue + mname: str = (str | t.CPtr)(t.CVoid(mname_addr, t.CPtr)) + if mname is not None and string.strcmp(mname, method_name) == 0: + method_index = mi + break + else: + method_index = self.Gen.StructGen.get_vtable_method_index(class_name, method_name) + if method_index < 0: + return None + + # 3. 基础类型 + i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) + i8_ptr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty) + + # 4. bitcast 到 i8** 访问 __vtable__ 字段(索引 0) + # vtable 指针总是在对象第一个字段,无需 GEP,直接 bitcast 到 i8** 即可。 + # 这样即使 struct_ty 是 opaque(跨模块未加载完整定义)也能正常工作。 + # 注意用 vtable_base(对象头)而非 self_ptr:多继承 this 调整后 + # self_ptr 已前移,其头部不是 vtable 而是父类字段值。 + vtable_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, vtable_base, i8_ptr_ptr_ty) + if vtable_slot is None: + return None + + # 5. load vtable 指针 (i8*) + vtable_ptr: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, vtable_slot) + if vtable_ptr is None: + return None + + # 6. 构造 vtable 类型 {i8*, i8*, ...} + method_count: int = 0 + if vc_entry is not None: + method_count = vc_entry.VTableMethodCount + else: + method_count = self.Gen.StructGen.get_vtable_method_count(class_name) + if method_count <= 0: + return None + + vt_first: llvmlite.ParamNode | t.CPtr = None + vt_prev: llvmlite.ParamNode | t.CPtr = None + for vi in range(method_count): + pn: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, i8_ptr_ty) + if pn is None: + continue + if vt_first is None: + vt_first = pn + if vt_prev is not None: + llvmlite.paramnode_set_next(vt_prev, pn) + vt_prev = pn + + vtable_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Struct(pool, vt_first, method_count, None) + if vtable_ty is None: + return None + vtable_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, vtable_ty) + + # 7. bitcast i8* 到 vtable 类型指针 + vtable_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, vtable_ptr, vtable_ptr_ty) + if vtable_typed is None: + return None + + # 8. GEP 到方法槽位 + func_slot: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( + builder, vtable_ty, i8_ptr_ty, vtable_typed, method_index) + if func_slot is None: + return None + + # 9. load 函数指针 (i8*) + func_i8: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, func_slot) + if func_i8 is None: + return None + + # 10. 从 args_head 构造函数类型 RetTy(Param1, Param2, ...) + # 直接遍历 Value 链表提取参数类型,避免跨模块访问 found_func(stub 字段偏移问题) + fp_first: llvmlite.ParamNode | t.CPtr = None + fp_prev: llvmlite.ParamNode | t.CPtr = None + fp_count: int = 0 + cur_val: llvmlite.Value | t.CPtr = args_head + for vi in range(args_count): + if cur_val is None: + break + cur_ty: llvmlite.LLVMType | t.CPtr = llvmlite.value_get_ty(cur_val) + fpn: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, cur_ty) + if fpn is not None: + if fp_first is None: + fp_first = fpn + if fp_prev is not None: + llvmlite.paramnode_set_next(fp_prev, fpn) + fp_prev = fpn + cur_val = llvmlite.value_get_next(cur_val) + fp_count += 1 + + func_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Func(pool, ret_ty, fp_first, fp_count) + if func_ty is None: + return None + func_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, func_ty) + + # 11. bitcast i8* 到函数指针类型 + func_ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, func_i8, func_ptr_ty) + if func_ptr is None: + return None + + # 12. 间接调用 + return llvmlite.build_call_indirect(builder, func_ptr, args_head, args_count, ret_ty) + # ============================================================ + # _call_method_on_ptr — 在结构体指针上调用方法 + # + # 辅助函数:构造 ClassName.method_name 调用,self 指针为首个参数 + # 如果该方法在虚表中,则走虚方法分发路径(间接调用) + # ============================================================ + + # ============================================================ + def _call_method_on_ptr(self, pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + class_name: str, + method_name: str, + self_ptr: llvmlite.Value | t.CPtr, + extra_args: t.CSizeT | t.CPtr, + extra_count: int, + trans: HT.Translator | t.CPtr, + node: ast.AST | t.CPtr = None) -> llvmlite.Value | t.CPtr: + """在结构体指针上调用方法,返回 call 结果(void 方法返回 None) + + node: 触发本次调用的 AST 节点(如 Subscript 下标节点),用于 fatal_error + 准确报告出错行号(传 None 时行号显示 0,不利于定位)。""" + if pool is None or builder is None or class_name is None or method_name is None: + return None + if self_ptr is None: + return None + + # 提前获取结构体 entry(用 find_struct_by_type 规避跨模块同名 find_struct 找错) + # 用于 SHA1 查找和虚表分派 + cmop_entry: GenTypes.StructEntry | t.CPtr = None + cmop_struct_ty: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type_from_value(self_ptr) + if cmop_struct_ty is not None: + cmop_entry = self.Gen.StructGen.find_struct_by_type(cmop_struct_ty) + if cmop_entry is None: + cmop_entry = self.Gen.StructGen.find_struct(class_name) + # fallback: 若 cmop_struct_ty 为 None 但 entry 存在,从 entry 获取类型 + # 用于跨模块调用时 self_ptr 类型信息丢失的情况 + if cmop_struct_ty is None and cmop_entry is not None: + cmop_struct_ty = cmop_entry.Ty + + # 构造方法查找名: ClassName.method_name + lookup_name: str = pool.alloc(128) + if lookup_name is None: + return None + viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, method_name) + + # 查找函数(支持 SHA1 后缀匹配) + found_func: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, lookup_name) + + # 确定调用名 + 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 构造调用名 + # 优先从 entry 获取 SHA1(规避跨模块同名 find_struct 找错) + cls_sha1: str = None + if cmop_entry is not None: + cls_sha1 = cmop_entry.ModuleSha1 + else: + cls_sha1 = self.Gen.StructGen.get_struct_sha1(class_name) + if cls_sha1 is None and trans is not None: + # fallback: 从 from_imports 查找类所属模块的 SHA1(含截断处理) + cls_sha1 = self._lookup_mod_sha1(pool, trans._from_imports, class_name) + if cls_sha1 is None: + err_msg: str = pool.alloc(256) + if err_msg is not None: + viperlib.snprintf(err_msg, 256, "类 '%s' 方法 '%s' 找不到模块 SHA1", class_name, method_name) + self.Gen.TypeGenErr.fatal_error(node, err_msg) + self.Gen.TypeGenErr.fatal_error(node, "找不到模块 SHA1") + # 生成调用名: {cls_sha1}.{lookup_name}(Hash Namespace 单前缀) + mangled2: str = pool.alloc(string.strlen(cls_sha1) + string.strlen(lookup_name) + 2) + if mangled2 is not None: + viperlib.snprintf(mangled2, string.strlen(cls_sha1) + string.strlen(lookup_name) + 2, + "%s.%s", cls_sha1, lookup_name) + call_name = mangled2 + + # 确定返回类型(__before_init__ 和 __init__ 返回 void) + void_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Void(pool) + call_ret_ty: llvmlite.LLVMType | t.CPtr = void_ty + if found_func is not None: + frt: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(found_func) + if frt is not None: + call_ret_ty = frt + elif string.strcmp(method_name, "__getitem__") == 0 or string.strcmp(method_name, "__new__") == 0: + # fail-fast:__getitem__/__new__ 必返回指针,返回类型缺失意味着 + # stub declare 损坏,静默推断会截断指针(32 位)崩溃,立即终止。 + err_cm1: str = pool.alloc(256) + if err_cm1 is not None: + viperlib.snprintf(err_cm1, 256, + "方法 '%s' 返回类型无法确定(stub declare 返回类型缺失)", method_name) + self.Gen.TypeGenErr.fatal_error(node, err_cm1) + self.Gen.TypeGenErr.fatal_error(node, "方法返回类型无法确定") + else: + # found_func 为 None(跨模块调用 stub 未注入):无法从 declare 获取返回类型。 + # __before_init__/__init__ 是编译器生成/约定方法(vpi stub 无条目): + # 恒返回 void(对齐 _special_method_ret_ty L3196-3198),直接固定, + # 否则走 text.ll/vpi 解析失败 → fail-fast 误报 + # (CPythonTest PyLongObj() 构造:类未定义 __init__/__before_init__ + # 但实例化仍生成调用 → "PyLongObj.__before_init__ 返回类型无法确定"根因)。 + if string.strcmp(method_name, "__before_init__") == 0 or \ + string.strcmp(method_name, "__init__") == 0: + call_ret_ty = void_ty + # __new__ 返回 Ptr(struct_ty)(构造函数返回自身指针,可推断)。 + # __getitem__ 返回类型无法从 self 推断(可返回 i8* 如 HashTable.__getitem__ + # -> t.CPtr,或 Ptr(JsonValue) 如 JsonValue.__getitem__)——推断 Ptr(cmop_struct_ty) + # 会与 define 返回类型不匹配,调用侧按错误类型读取返回值导致指针截断崩溃。 + # fail-fast:__getitem__ 无 stub declare 立即报错,暴露依赖模块 vpi 缺失或 + # 拓扑顺序错误(依赖模块未先翻译生成 vpi/text.ll)。 + elif string.strcmp(method_name, "__new__") == 0: + if cmop_struct_ty is not None: + call_ret_ty = llvmlite.Ptr(pool, cmop_struct_ty) + else: + err_cm2: str = pool.alloc(256) + if err_cm2 is not None: + viperlib.snprintf(err_cm2, 256, + "方法 '%s' 的 self 结构体类型未知(字段类型退化,依赖模块未预注册?)", method_name) + self.Gen.TypeGenErr.fatal_error(node, err_cm2) + self.Gen.TypeGenErr.fatal_error(node, "self 结构体类型未知") + elif string.strcmp(method_name, "__getitem__") == 0: + err_cm3: str = pool.alloc(256) + if err_cm3 is not None: + viperlib.snprintf(err_cm3, 256, + "方法 '%s.__getitem__' 的 stub declare 未注入(依赖模块 vpi 缺失或拓扑顺序错误),无法确定返回类型", + class_name) + self.Gen.TypeGenErr.fatal_error(node, err_cm3) + self.Gen.TypeGenErr.fatal_error(node, "__getitem__ stub declare 未注入") + else: + # 其它跨模块方法(泛型特化方法必然无 stub declare):旧实现静默 + # 保持 void → 幽灵 "call void @...__add__" + 调用方(如运算符 + # 重载 try_operator_overload)回退原生运算双重发射 → llc invalid + # operand type for instruction(NumpyTest a+b 根因)。用 text.ll + # → vpi 精确解析返回类型(对齐 _translate_method_call L4641); + # 都失败则 fail-fast,绝不静默 void。 + ret_cm: llvmlite.LLVMType | t.CPtr = None + if cls_sha1 is not None: + ret_cm = self._LookupMethodRetTyFromTextLL( + pool, cls_sha1, class_name, method_name) + if ret_cm is None and cls_sha1 is not None: + ret_cm = self._lookup_method_ret_ty_via_vpi( + pool, cls_sha1, class_name, method_name) + if ret_cm is not None: + call_ret_ty = ret_cm + else: + # Phase A 依赖 text.ll 未生成(方法定义模块尚未翻译)→ 标记重试, + # 保持默认类型继续(对齐 _translate_method_call L4760-4773 与跨模块 + # 函数路径 L8167-8195 的 _deps_pending 机制;本函数同样服务 Mixin + # 跨模块方法调用,如 BuildPipeline gen.extract_module_docstrings) + # cls_sha1 为 None 同样视为依赖未就绪(pass1 定义类/祖先未注册 + # 连 sha1 都取不到,对齐 _translate_method_call L4778-4785) + if cls_sha1 is None \ + or (cls_sha1 is not None + and self._dep_text_ll_exists(pool, cls_sha1) == 0): + if trans is not None: + trans._deps_pending = 1 + # [DEPDBG] 临时诊断:方法返回类型解析依赖未就绪即 pending 的根因(用后移除) + dbg_ms1: str = trans.ModuleSha1 if (trans is not None and trans.ModuleSha1 is not None) else "?" + stdio.printf("[DEPDBG] pending(method_ret): mod=%s cls=%s method=%s sha1=%s\n", + dbg_ms1, class_name, method_name, + cls_sha1 if cls_sha1 is not None else "none") + # 依赖未就绪:保持默认 i32(build_call(Void) 返回 None 会 + # 破坏表达式翻译;pending 文件本遍不写盘,下遍重译) + call_ret_ty = llvmlite.Int32(pool) + else: + err_cm4: str = pool.alloc(256) + if err_cm4 is not None: + viperlib.snprintf(err_cm4, 256, + "方法 '%s.%s' 返回类型无法确定(text.ll/vpi 均未命中,跨模块 stub 未注入)", + class_name, method_name) + self.Gen.TypeGenErr.fatal_error(node, err_cm4) + self.Gen.TypeGenErr.fatal_error(node, "方法返回类型无法确定") + + # 构建参数链表: self_ptr → extra_args... + llvmlite.value_set_next(self_ptr, None) + head: llvmlite.Value | t.CPtr = self_ptr + tail: llvmlite.Value | t.CPtr = self_ptr + + if extra_args is not None and extra_count > 0: + for ei in range(extra_count): + arg_addr: t.CSizeT = extra_args[ei] + if arg_addr == 0: + continue + av: llvmlite.Value | t.CPtr = (llvmlite.Value | t.CPtr)(t.CVoid(arg_addr, t.CPtr)) + if av is not None: + llvmlite.value_set_next(av, None) + llvmlite.value_set_next(tail, av) + tail = av + + total_count: int = 1 + extra_count + + # 默认参数填充:如果调用点参数少于函数定义参数,用默认值填充缺失参数 + # 优先用函数定义的默认值,无默认值时用零值兜底 + # 查找函数的 defaults 信息 + mdf_defaults: t.CVoid | t.CPtr = None + mdf_default_count: int = 0 + mdf_param_count: int = 0 + if trans is not None and trans._funcs is not None: + mdf_entry: GenTypes.FuncEntry | t.CPtr = ExprCallTable.find_func_entry_in_table( + trans._funcs, trans._func_count, lookup_name) + if mdf_entry is not None: + mdf_defaults = mdf_entry.Defaults + mdf_default_count = mdf_entry.DefaultCount + mdf_param_count = mdf_entry.ParamCount + # 全局默认参数表查询无条件执行:mdf_entry 命中时(泛型特化方法注册进 + # 调用方函数表)Defaults 非 None 会短路跳过,但下方"用快照重建默认值" + # 分支依赖 mdf_gd(唯一持有 stdlib 常量快照处)——缺它必 fatal_error + # (ListTest list[int].__new__ 根因) + mdf_gd: GenTypes.FuncDefaultsEntry | t.CPtr = self.Gen.CallGenLeafKit.find_func_defaults(lookup_name) + if mdf_defaults is None and mdf_gd is not None: + mdf_defaults = mdf_gd.Defaults + mdf_default_count = mdf_gd.DefaultCount + mdf_param_count = mdf_gd.ParamCount + + # 跨模块 stub 未注入兜底(同 _translate_method_call):found_func 为 + # None 且函数表无 ParamCount 时,用 vpi 文本缓存补全参数数,默认 + # 参数填充(含零值兜底)随即生效,declare/call 与 define 对齐。 + vpi_pa: str = None + if mdf_param_count == 0 and found_func is None: + vpi_sha1_p: str = None + if cmop_entry is not None and cmop_entry.ModuleSha1 is not None: + vpi_sha1_p = cmop_entry.ModuleSha1 + else: + vpi_sha1_p = self.Gen.StructGen.get_struct_sha1(class_name) + if vpi_sha1_p is not None: + vpi_pa = self.Gen.CallGenBase._vpi_method_param_annot( + pool, vpi_sha1_p, class_name, method_name) + if vpi_pa is not None and vpi_pa[0] != '\0': + vpi_pc2: int = 1 + vi2: t.CSizeT = 0 + while vpi_pa[vi2] != '\0': + if vpi_pa[vi2] == '@': + vpi_pc2 += 1 + vi2 += 1 + mdf_param_count = vpi_pc2 - 1 # 非 self 参数数 + + # [修复 2026-08-25] found_func 参数数回退(同 _translate_method_call): + # 函数表/默认表无 ParamCount 但 found_func(declare)已找到时用其参数数 + # 作补全基准,否则省略默认参数(如 translate_value 的 target_ty=None) + # 的调用补全不触发 → call 实参 < define 形参 → ABI 错位读栈残留垃圾。 + if mdf_param_count == 0 and found_func is not None: + ppcnt_f2: int = 0 + ppn_f2: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) + while ppn_f2 is not None: + ppcnt_f2 += 1 + ppn_f2 = llvmlite.param_get_next(ppn_f2) + if ppcnt_f2 > 1: + mdf_param_count = ppcnt_f2 - 1 # 非 self 参数数 + + # 计算需要填充的参数(不含 self) + # extra_count = 已提供的非 self 参数数量 + # mdf_param_count = 函数定义的非 self 参数数量 + if mdf_param_count > extra_count: + mdf_offset: int = mdf_param_count - mdf_default_count + mdf_pi: int = extra_count + while mdf_pi < mdf_param_count: + # 优先用默认值 + if mdf_defaults is not None and mdf_default_count > 0 and mdf_pi >= mdf_offset: + mdf_di: int = mdf_pi - mdf_offset + if mdf_di < mdf_default_count: + # 用 stdlib 常量快照重建(mdf_gd.Defaults 的 pool 内 AST 可能已被 reset 回收, + # 直接回退读 AST 会 use-after-free——快照失败时 fail-fast 暴露,绝不读悬空 AST) + mdf_val: llvmlite.Value | t.CPtr = None + if mdf_gd is not None: + mdf_val = self.Gen.CallGenLeafKit._build_const_from_snapshot( + pool, mod, mdf_gd, mdf_di, trans, builder) + if mdf_val is None: + # 用 node(本函数报错行号节点,如构造实参/源码 Call)而非 cl—— + # cl 在本函数未定义(无该参数),读取垃圾指针导致 fatal_error + # 显示 "line 0"(FileIoTest File.__init__ 报错行号丢失根因) + self.Gen.TypeGenErr.fatal_error( + node, + f"跨模块默认参数无法重建(快照缺失,默认值需为简单常量): {lookup_name}", + "TypeError") + continue + llvmlite.value_set_next(mdf_val, None) + llvmlite.value_set_next(tail, mdf_val) + tail = mdf_val + total_count += 1 + mdf_pi += 1 + continue + # 无默认值时用零值兜底(需要 found_func 获取参数类型)。 + # 默认参数表按裸名注册(如 "__init__"),跨模块 "ClassName.method" + # 查找常查不到——查不到 ≠ 无默认值(如 Buf.__init__(data, capacity) + # 缺的 length=0/owned=False 恰为零值),零值兜底语义一致。 + if found_func is not None: + param_node_z: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) + skip_z: int = 0 + while skip_z <= mdf_pi and param_node_z is not None: + param_node_z = llvmlite.param_get_next(param_node_z) + skip_z += 1 + if param_node_z is not None: + zero_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(param_node_z) + zero_val: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, zero_ty) + if zero_val is not None: + llvmlite.value_set_next(zero_val, None) + llvmlite.value_set_next(tail, zero_val) + tail = zero_val + total_count += 1 + elif vpi_pa is not None: + # found_func 为 None(stub 未注入):用 vpi 参数注解解析 + # 缺失参数类型(段索引 mdf_pi+1,跳过首参 self 空段), + # 值取类型零值(默认参数值恰为零值;查不到 ≠ 无默认值)。 + seg_idx_v: int = mdf_pi + 1 + seg_no_v: int = 0 + seg_cur_v: t.CSizeT = 0 + while seg_no_v < seg_idx_v and vpi_pa[seg_cur_v] != '\0': + if vpi_pa[seg_cur_v] == '@': + seg_no_v += 1 + seg_cur_v += 1 + seg_start_v: t.CSizeT = seg_cur_v + seg_len_v: t.CSizeT = 0 + while vpi_pa[seg_start_v + seg_len_v] != '\0' \ + and vpi_pa[seg_start_v + seg_len_v] != '@': + seg_len_v += 1 + if seg_no_v == seg_idx_v: + pty_v: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) + if seg_len_v > 0: + seg_buf_v: bytes = stdlib.malloc(seg_len_v + 1) + if seg_buf_v is not None: + string.strncpy(seg_buf_v, vpi_pa + seg_start_v, seg_len_v) + seg_buf_v[seg_len_v] = '\0' + rty_v: llvmlite.LLVMType | t.CPtr = self.Gen.CallGenBase._vpi_resolve_param_annot_ty( + pool, seg_buf_v) + if rty_v is not None: + pty_v = rty_v + stdlib.free(seg_buf_v) + zero_val_v: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, pty_v) + if zero_val_v is not None: + llvmlite.value_set_next(zero_val_v, None) + llvmlite.value_set_next(tail, zero_val_v) + tail = zero_val_v + total_count += 1 + mdf_pi += 1 + elif found_func is not None: + # 无默认参数信息但有 found_func:用零值兜底填充缺失参数 + # (原逻辑;禁止 fail-fast 误杀——同上,查不到默认参数表 ≠ 无默认值) + param_node: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) + skip_idx: int = 0 + while skip_idx < total_count and param_node is not None: + param_node = llvmlite.param_get_next(param_node) + skip_idx += 1 + while param_node is not None: + missing_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(param_node) + zero_val2: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, missing_ty) + if zero_val2 is not None: + llvmlite.value_set_next(zero_val2, None) + llvmlite.value_set_next(tail, zero_val2) + tail = zero_val2 + total_count += 1 + param_node = llvmlite.param_get_next(param_node) + + # 参数类型强制转换:按 found_func 的形参类型 coerce 每个实参(含 self)。 + # 修复: 构造器调用 __init__(ptr, base, 4096) 时 size 形参是 i64 (t.CSizeT), + # 但 4096 字面量按 i32 传递,未提升导致运行时 size 高 32 位垃圾, + # 破坏伙伴分配器的池大小元数据(如 total=2048)引发 calloc 失败/OOM。 + # self 在运算符重载路径 2(lhs 为 X|t.CPtr 联合简化的 Vec3** 对象指针)时 + # 与形参 Vec3* 不匹配,需 bitcast(coerce 指针→指针生成 bitcast), + # 否则 call 参数类型与声明不一致 llc 报错。 + if found_func is not None and head is not None: + pc_param: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) + pc_node: llvmlite.Value | t.CPtr = head + new_head_pc: llvmlite.Value | t.CPtr = head + prev_pc: llvmlite.Value | t.CPtr = None + while pc_node is not None: + pc_next_save: llvmlite.Value | t.CPtr = pc_node.Next + coerced_pc: llvmlite.Value | t.CPtr = pc_node + if pc_param is not None: + pty_pc: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(pc_param) + if pty_pc is not None: + c_pc: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type( + builder, pc_node, pty_pc, None) + if c_pc is not None: + coerced_pc = c_pc + pc_param = llvmlite.param_get_next(pc_param) + coerced_pc.Next = None + if prev_pc is None: + new_head_pc = coerced_pc + else: + prev_pc.Next = coerced_pc + prev_pc = coerced_pc + pc_node = pc_next_save + head = new_head_pc + + # 虚方法分发:如果类有虚表且该方法在虚表中,走间接调用 + # 复用前面已获取的 cmop_entry(find_struct_by_type 规避跨模块同名找错) + if found_func is not None: + cmop_has_vt: int = 0 + cmop_method_idx: int = -1 + if cmop_entry is not None: + cmop_has_vt = cmop_entry.HasVTable + if cmop_has_vt == 1 and cmop_entry.VTableMethods is not None: + cmop_methods_arr: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(cmop_entry.VTableMethods, t.CPtr)) + for cmop_mi in range(cmop_entry.VTableMethodCount): + cmop_mname_addr: t.CSizeT = cmop_methods_arr[cmop_mi] + if cmop_mname_addr == 0: + continue + cmop_mname: str = (str | t.CPtr)(t.CVoid(cmop_mname_addr, t.CPtr)) + if cmop_mname is not None and string.strcmp(cmop_mname, method_name) == 0: + cmop_method_idx = cmop_mi + break + if cmop_has_vt == 1 and cmop_method_idx >= 0: + return self._do_virtual_call(pool, builder, mod, class_name, method_name, + self_ptr, head, total_count, found_func, call_ret_ty) + # 防御:返回类型为 i0(0 位整数)时 fail-fast,绝不输出非法 IR。 + # 根因已修(gen_done 成功后不再读未初始化的 looked_up_ty,见 L3970); + # 此兜底防止未来任何路径产出零值 LLVMType 导致 llc + # "bitwidth for integer type out of range"。 + if call_ret_ty is not None: + cd_i0: int = 0 + match call_ret_ty: + case llvmlite.LLVMType.Int(bits): + if bits == 0: + cd_i0 = 1 + case _: + pass + if cd_i0 != 0: + call_ret_ty = self._fail_method_ret_ty(method_name, node) + # 跨模块调用(found_func 为 None):创建 declare 声明,避免 llc 报 undefined value + if found_func is None: + self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, total_count) + + return llvmlite.build_call(builder, call_name, head, total_count, call_ret_ty, 0) + # ============================================================ + # _translate_method_call - 翻译方法调用 obj.method(args) + # + # 生成: call @SHA1.ClassName.method_name(Ptr(StructTy) %obj, args...) + # + # obj_ptr 是对象变量的 alloca(Ptr(StructTy)),作为 self 参数传入 + # ============================================================ + + # ============================================================ + def _translate_method_call(self, pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + class_name: str, + method_name: str, + obj_ptr: llvmlite.Value | t.CPtr, + cl: ast.Call | t.CPtr, + trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr: + """翻译方法调用,返回 call 结果 Value""" + if pool is None or builder is None or class_name is None or method_name is None: + return None + if obj_ptr is None or cl is None: + return None + + # 提前获取结构体 entry(用 find_struct_by_type 规避跨模块同名 find_struct 找错) + # 用于继承链遍历、SHA1 查找和虚表分派 + mc_entry: GenTypes.StructEntry | t.CPtr = None + mc_struct_ty: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type_from_value(obj_ptr) + if mc_struct_ty is not None: + mc_entry = self.Gen.StructGen.find_struct_by_type(mc_struct_ty) + if mc_entry is None: + mc_entry = self.Gen.StructGen.find_struct(class_name) + + # 构造方法查找名: ClassName.method_name + lookup_name: str = pool.alloc(128) + if lookup_name is None: + return None + viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, method_name) + + # 查找函数(支持 SHA1 后缀匹配) + found_func: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, lookup_name) + + # 泛型模板类方法:特化函数名带类型参数({sha1}.list[int].get), + # "ClassName.method" 短名匹配不到。特化由触发方(通常即调用方)模块完成, + # 用对象实际特化名(mc_entry.Name)构造后缀匹配,获得精确返回类型。 + if found_func is None and mc_entry is not None and mc_entry.Name is not None: + if string.strcmp(mc_entry.Name, class_name) != 0: + spec_lookup: str = pool.alloc(160) + if spec_lookup is not None: + viperlib.snprintf(spec_lookup, 160, "%s.%s", mc_entry.Name, method_name) + found_func = self.find_func_in_module(mod, spec_lookup) + + # 沿 MRO 祖先链查找父类方法(子类未覆盖时回退到父类,先声明父类优先) + mc_anc: str = None # 方法实际定义类(多继承 this 调整用) + if found_func is None: + mro_ptr: t.CSizeT | t.CPtr = self.Gen.StructGen.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: str = 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 = self.find_func_in_module(mod, parent_lookup) + if parent_func is not None: + found_func = parent_func + mc_anc = anc + break + mi += 1 + + # 兄弟 Mixin 兜底(与 TPC HandlesExprCall 一致):方法不在本类 MRO 链 + # (如 BaseB 内 self.helper() 调兄弟 BaseA.helper,BaseB 无 BaseA 的 + # 继承信息)。遍历已注册 struct 表找定义类方法,this 由下方 + # _adjust_this_for_inherited_method 按组合布局前移 sizeof(定义类)。 + # 注意:仅对普通类名兜底——泛型特化类名(list[X] / dict[K,V])是 + # 模板生成的特殊容器,其特化函数(如 list[ASTptr].__getitem__)虽 + # 在模块中存在,但绝非"兄弟 Mixin"(NoVTableCrossMixinTest 的 + # BaseA/BaseB/Combined 场景)。若不过滤,JsonValue.__getitem__ 等 + # 普通方法调用在 found_func 未命中时会误绑定到 list 特化函数, + # this 按 list 布局前移 sizeof(list) → 运行期读 -1 崩溃 + # (Config.vp Load_project_config root.__getitem__ 根因)。 + # 跨模块兄弟 Mixin 兜底命中记录:方法定义类在另一文件(如 + # FuncGenBase._mangle_name——调用类 ClassGenVTable 不继承它), + # 本地 IR 无 define,需经候选类模块 sha1 查 text.ll 返回类型。 + mixin_hit: int = 0 + mixin_ret_ty: llvmlite.LLVMType | t.CPtr = None + mixin_anc_name: str = None + mixin_anc_sha1: str = None + if found_func is None: + # 兄弟 Mixin 兜底仅对"接收者类属于当前模块"的场景适用:self 调用兄弟 + # Mixin 方法时接收者是本类(与候选兄弟类同模块)。接收者类是跨模块类 + # (如 self.Gen.TypeGenResolve 的 TypeGenResolve,定义在另一模块)时, + # 其方法必须经该类自身模块(text.ll/vpi)解析——若本模块恰好定义了 + # 同名方法 {Cand}.{method},本兜底会误绑定:最坏情况绑定到正在编译的 + # 函数自身 → ImportsGenMixin.set_current_file 内 self.Gen.TypeGenResolve. + # set_current_file(...) 被编译成自调用 → 无限递归 stack-overflow + # (二代编 FuncTest 崩溃根因)。接收者类模块无法确定时保持原行为。 + 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: + sc_i: int = 0 + sc_n: int = self.Gen.StructGen.get_struct_count() + while sc_i < sc_n and found_func is None and mixin_hit == 0: + cand_entry: GenTypes.StructEntry | t.CPtr = 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: + # 泛型特化类名含 '['(list[...]/dict[...]):跳过, + # 不是兄弟 Mixin 候选(见上方注释) + if string.strchr(cand_name, '[') is not None: + sc_i += 1 + continue + 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 | t.CPtr = self.find_func_in_module( + mod, cand_lookup) + if cand_func is not None: + found_func = cand_func + mc_anc = cand_name + else: + # 跨模块 Mixin(定义类在另一文件,本地 IR 无 define): + # 用候选类模块 sha1 查 text.ll 返回类型,命中即锁定定义类, + # 后续 ret_anc_* 据此构造 {sha1}.{class}.{method} 调用名。 + # 否则 ret_anc_sha1 停在调用类自身 sha1,_dep_text_ll_exists + # 查自身 text.ll(Phase A 翻译中未生成)→ 永久 pending 死锁 + # (ClassGenVTable._mangle_name → 7871cc1aabebfc33 根因)。 + if mixin_hit == 0: + cand_sha1_cx: str = self.Gen.StructGen.get_struct_sha1(cand_name) + if cand_sha1_cx is not None \ + and self._dep_text_ll_exists(pool, cand_sha1_cx) != 0: + cand_ret_cx: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL( + pool, cand_sha1_cx, cand_name, method_name) + if cand_ret_cx is not None: + mixin_hit = 1 + mixin_ret_ty = cand_ret_cx + mixin_anc_name = cand_name + mixin_anc_sha1 = cand_sha1_cx + sc_i += 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 + + # 确定返回类型 + i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + call_ret_ty: llvmlite.LLVMType | t.CPtr = i32_ty + if found_func is not None: + frt: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(found_func) + if frt is not None: + call_ret_ty = frt + else: + # found_func 为 None(stub 未注入):先处理编译器内置特殊方法 + sp_ty2: llvmlite.LLVMType | t.CPtr = self._special_method_ret_ty(pool, class_name, method_name) + if sp_ty2 is not None: + call_ret_ty = sp_ty2 + # 特殊方法(__before_init__ 等)跨模块调用同样需带 sha1 前缀: + # memhub.alloc_buf 显式调 buf.__before_init__(),buf 是 viperio.Buf*, + # 若用裸名 @Buf.__before_init__ 链接 undefined symbol。用 + # mc_entry.ModuleSha1 / get_struct_sha1 / 类型名提取 sha1 构造 + # {sha1}.Buf.__before_init__。 + sp_anc_name: str = class_name + sp_anc_sha1: str = None + if mc_entry is not None: + sp_anc_sha1 = mc_entry.ModuleSha1 + else: + sp_anc_sha1 = self.Gen.StructGen.get_struct_sha1(class_name) + if sp_anc_sha1 is None and mc_struct_ty is not None: + match mc_struct_ty: + case llvmlite.LLVMType.Struct(_fs_sp, _fc_sp, type_name_sp): + if type_name_sp is not None and type_name_sp[0] != '\0': + tl_sp: t.CSizeT = string.strlen(type_name_sp) + if tl_sp >= 16: + sb_sp: str = pool.alloc(17) + if sb_sp is not None: + string.strncpy(sb_sp, type_name_sp, 16) + sb_sp[16] = '\0' + sp_anc_sha1 = sb_sp + case _: + pass + if sp_anc_sha1 is not None: + ml_sp: t.CSizeT = string.strlen(sp_anc_sha1) + string.strlen(sp_anc_name) \ + + string.strlen(method_name) + 3 + mangled_sp: str = pool.alloc(ml_sp) + if mangled_sp is not None: + viperlib.snprintf(mangled_sp, ml_sp, "%s.%s.%s", + sp_anc_sha1, sp_anc_name, method_name) + call_name = mangled_sp + 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 = self.Gen.StructGen.get_struct_sha1(class_name) + # 跨模块兄弟 Mixin 兜底命中:定义类锁定为候选类(如 FuncGenBase), + # 返回类型与调用名前缀以其模块 sha1 为准,避免 ret_anc_sha1 停在 + # 调用类自身 sha1 → 查自身 text.ll(未生成)→ 永久 pending 死锁 + if mixin_hit != 0: + ret_anc_name = mixin_anc_name + ret_anc_sha1 = mixin_anc_sha1 + call_ret_ty = mixin_ret_ty + if ret_anc_sha1 is None: + # 沿 MRO 找第一个有 SHA1 的类 + mro_s0: t.CSizeT | t.CPtr = self.Gen.StructGen.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 = self.Gen.StructGen.get_struct_sha1(anc_s0) + mi_s0 += 1 + + # 泛型特化方法调用:特化函数可能尚未注册(如 ast.base 用 + # list[AST|t.CPtr].get)。主动触发特化(幂等,MethodsEmitted 防重入), + # 特化后重新查找即可命中,返回类型来自特化函数定义(天然精确)。 + gen_done: int = 0 + # text.ll 查找结果提升为函数级并显式初始化:gen_done==1 成功时 + # 下方回退段不执行,若在此声明则读到未初始化的池内存垃圾(非 NULL), + # 把已正确解析的 call_ret_ty(特化 define 的 i32)覆盖成垃圾零值 + # LLVMType → build_call 输出 call i0(llc bitwidth out of range)。 + looked_up_ty: llvmlite.LLVMType | t.CPtr = None + if class_name is not None: + lbkt_g: str = string.strchr(class_name, '[') + if lbkt_g is not None: + rbkt_g: str = string.strrchr(class_name, ']') + if rbkt_g is not None and rbkt_g > lbkt_g: + base_len_g: t.CSizeT = t.CSizeT(t.CUInt64T(lbkt_g) - t.CUInt64T(class_name)) + arg_len_g: t.CSizeT = t.CSizeT(t.CUInt64T(rbkt_g) - t.CUInt64T(lbkt_g) - 1) + if base_len_g > 0 and arg_len_g > 0: + base_buf_g: str = pool.alloc(base_len_g + 1) + arg_buf_g: str = pool.alloc(arg_len_g + 1) + if base_buf_g is not None and arg_buf_g is not None: + string.strncpy(base_buf_g, class_name, base_len_g) + base_buf_g[base_len_g] = '\0' + string.strncpy(arg_buf_g, lbkt_g + 1, arg_len_g) + arg_buf_g[arg_len_g] = '\0' + ta_list_g: list[str] | t.CPtr = list[str](pool, 8) + if ta_list_g is not None: + # 逗号分隔多类型实参(list[X] / dict[K, V]) + cur_arg_g: str = arg_buf_g + while cur_arg_g is not None: + comma_g: str = string.strchr(cur_arg_g, ',') + if comma_g is None: + ta_list_g.append(cur_arg_g) + break + seg_len_g: t.CSizeT = t.CSizeT(t.CUInt64T(comma_g) - t.CUInt64T(cur_arg_g)) + seg_buf_g: str = pool.alloc(seg_len_g + 1) + if seg_buf_g is not None: + string.strncpy(seg_buf_g, cur_arg_g, seg_len_g) + seg_buf_g[seg_len_g] = '\0' + ta_list_g.append(seg_buf_g) + cur_arg_g = comma_g + 1 + # base_buf_g 可能已带模板模块 SHA1 前缀({tpl}.GSList): + # 剥离后再查模板/特化,保证 _find_generic_template 命中裸模板名。 + base_clean_g: str = base_buf_g + if base_len_g > 17: + is_sha1_pfx_g: int = 1 + si_g: int + for si_g in range(16): + cg_g: t.CChar = base_buf_g[si_g] + if not (('0' <= cg_g <= '9') or ('a' <= cg_g <= 'f')): + is_sha1_pfx_g = 0 + break + if is_sha1_pfx_g != 0 and base_buf_g[16] == '.': + base_clean_g = base_buf_g + 17 + spec_name_g: str = self.Gen.ClassGenGeneric._specialize_generic_class( + trans, base_clean_g, ta_list_g) + if spec_name_g is not None: + # 特化后重新查找特化函数(特化名 = 调用点类名)。 + # find_func_in_module 后缀匹配:定义符号名 + # {模板}.spec.method(Hash Namespace 单前缀), + # 同时以裸名(spec.method)和带模板前缀名 + # ({tpl}.spec.method)结尾,两种 class_name 形式 + # 都能命中本地 define。 + spec_lk_g: str = pool.alloc( + string.strlen(class_name) + string.strlen(method_name) + 2) + if spec_lk_g is not None: + viperlib.snprintf(spec_lk_g, + string.strlen(class_name) + string.strlen(method_name) + 2, + "%s.%s", class_name, method_name) + spec_found_g: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, spec_lk_g) + if spec_found_g is not None: + frt_g: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(spec_found_g) + if frt_g is not None: + call_ret_ty = frt_g + found_func = spec_found_g + gen_done = 1 + # mixin_hit:跨模块 Mixin 兜底已锁定定义类与返回类型,跳过 + # text.ll/vpi/pending 重查段,直接构造 {sha1}.{class}.{method} 调用名 + if gen_done == 0 and mixin_hit == 0: + # 尝试从 text.ll 查找实际返回类型 + # 避免指针返回值被默认 i32 导致 inttoptr 截断 + looked_up_ty = self._LookupMethodRetTyFromTextLL( + 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 = self.Gen.StructGen.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 = self.Gen.StructGen.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 gen_done == 0 and looked_up_ty is not None: + call_ret_ty = looked_up_ty + elif gen_done == 0: + # 精确查找:vpi stub 返回注解缓存(方法定义处,不硬编码白名单) + vpi_ret_ty: llvmlite.LLVMType | t.CPtr = self._lookup_method_ret_ty_via_vpi( + pool, ret_anc_sha1, ret_anc_name, method_name) + # vpi 同样沿 MRO 祖先链重试(基类方法注解,用祖先类自身 sha1) + if vpi_ret_ty is None: + mro_v2: t.CSizeT | t.CPtr = self.Gen.StructGen.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 = self.Gen.StructGen.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: + # Phase A 依赖 text.ll 未生成(方法定义模块尚未翻译)→ 标记 + # 重试,保持默认类型继续(下遍依赖生成后可精确解析),对齐跨模块 + # 函数路径 L8167-8195 的 _deps_pending 机制。BuildPipeline.vp + # L216 gen.extract_module_docstrings 根因:方法定义在 + # ExprGenMixin(Mixin 模块),Phase A 顺序中 ExprGenMixin 的 + # text.ll 尚未生成 → MRO 链查找全部未命中 → fail-fast 误报。 + # ret_anc_sha1 为 None 同样视为依赖未就绪:pass1 中 Generator + # 及其 Mixin 全部 defer(依赖 Handles/* 未翻译)→ 结构体未注册 + # → get_struct_sha1/MRO 查找全空 → 连 sha1 都取不到 + # (g._register_cdefine_from_value 根因)。标记 _deps_pending + # 下遍重译,绝不 fail-fast 中断多遍收敛;真实错误由收敛检查兜底。 + if ret_anc_sha1 is None \ + or (ret_anc_sha1 is not None + and self._dep_text_ll_exists(pool, ret_anc_sha1) == 0): + if trans is not None: + trans._deps_pending = 1 + # [DEPDBG] 临时诊断:方法祖先模块依赖未就绪即 pending 的根因(用后移除) + dbg_ms2: str = trans.ModuleSha1 if (trans is not None and trans.ModuleSha1 is not None) else "?" + stdio.printf("[DEPDBG] pending(mro_ret): mod=%s cls=%s method=%s ret_sha1=%s text=%d\n", + dbg_ms2, class_name, method_name, + ret_anc_sha1 if ret_anc_sha1 is not None else "none", + self._dep_text_ll_exists(pool, ret_anc_sha1) if ret_anc_sha1 is not None else -1) + # 依赖未就绪:保持默认 i32 继续而非 Void——build_call(Void) + # 返回 None,方法调用结果喂进表达式(if 条件/比较)时 fatal + # "if 条件表达式翻译失败"(ExprGenAttr._get_cderef_attr_base + # 根因)。本遍不写盘、下遍重译精确解析,错误类型不会进入 + # 产物(对齐跨模块函数路径 L8167-8195"保持默认类型继续")。 + call_ret_ty = llvmlite.Int32(pool) + else: + # 追加80e 断点 A:方法可能定义在 MRO 祖先(Mixin)模块。 + # 此时 ret_anc_sha1 仍是调用类自身的 sha1(MRO 双路重试 + # 失败未更新),且调用类(如 Generator)已注册、自身 text.ll + # 存在 → 上方 guard 不成立 → 直接 fail-fast。但 MRO 重试 + # 落空可能只是"祖先定义模块未翻译"(AssignGenMixin.vp 的 + # text.ll 未生成),属依赖未就绪而非方法不存在。遍历祖先链: + # 任一祖先的 sha1 取不到或其 text.ll 未生成 → 结果不确定, + # 标记 pending 下遍重试(届时祖先 text.ll 生成,MRO 重试 + # 可精确解析);全部祖先就绪仍查不到才是真错误。 + mro_p2: t.CSizeT | t.CPtr = self.Gen.StructGen.get_mro_chain(pool, class_name) + mi_p2: int = 1 + anc_missing_p2: int = 0 + while mro_p2 is not None and mro_p2[mi_p2] != 0: + anc_p2: str = (str | t.CPtr)(t.CVoid(mro_p2[mi_p2], t.CPtr)) + if anc_p2 is not None: + anc_sha1_p2: str = self.Gen.StructGen.get_struct_sha1(anc_p2) + if anc_sha1_p2 is None \ + or self._dep_text_ll_exists(pool, anc_sha1_p2) == 0: + anc_missing_p2 = 1 + break + mi_p2 += 1 + if anc_missing_p2 != 0: + if trans is not None: + trans._deps_pending = 1 + # [DEPDBG] 临时诊断:MRO 祖先模块依赖未就绪即 pending 的根因(用后移除) + dbg_ms3: str = trans.ModuleSha1 if (trans is not None and trans.ModuleSha1 is not None) else "?" + stdio.printf("[DEPDBG] pending(mro_anc): mod=%s cls=%s method=%s anc=%s anc_sha1=%s\n", + dbg_ms3, class_name, method_name, + anc_p2 if anc_p2 is not None else "none", + anc_sha1_p2 if anc_sha1_p2 is not None else "none") + # 依赖未就绪:保持默认 i32(同上,build_call(Void) 返回 + # None 会破坏表达式翻译,pending 文件本遍不写盘) + call_ret_ty = llvmlite.Int32(pool) + 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 None: + # ret_anc_sha1 缺失(如 find_struct 未命中跨模块结构体)时, + # 从对象实际 LLVM 类型名提取 sha1 前缀:类型名形如 + # "2c175afd85852faf.Buf"({sha1}.{ClassName}),前 16 字符即模块 sha1。 + # 例:memhub.alloc_buf 里 buf.__before_init__(),buf 是 viperio.Buf*, + # 其类型名含 viperio 模块 sha1,借此构造 {sha1}.Buf.__before_init__, + # 避免静默生成裸名 @Buf.__before_init__(链接 undefined symbol)。 + if mc_struct_ty is not None: + match mc_struct_ty: + case llvmlite.LLVMType.Struct(_fields_st, _fcount_st, type_name_st): + if type_name_st is not None and type_name_st[0] != '\0': + type_len_st: t.CSizeT = string.strlen(type_name_st) + if type_len_st >= 16: + sha1_from_ty: str = pool.alloc(17) + if sha1_from_ty is not None: + string.strncpy(sha1_from_ty, type_name_st, 16) + sha1_from_ty[16] = '\0' + ret_anc_sha1 = sha1_from_ty + case _: + pass + 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 + # 类型名提取的 sha1 无法保证为真实模块 sha1(跨模块 stub 类型名 + # 带前缀但 find_struct 未注册时仍可用),此处不校验格式 + elif ret_anc_name is not None and ret_anc_sha1 is None: + # fail-fast:方法所属类既查不到 SHA1 又从类型名提取失败, + # 禁止静默裸名(否则链接期 undefined symbol 难以定位) + self.Gen.TypeGenErr.fatal_error( + cl, + f"方法 '{method_name}' 所属类 '{ret_anc_name}' 无法解析模块 SHA1" + f"(text.ll/vpi/类型名均未命中),跨模块调用名构造失败", + "TypeError") + + # 翻译调用参数 + cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args + can: t.CSizeT = 0 + if cargs is not None: + can = len(cargs) + kw_calls: list[ast.AST | t.CPtr] | t.CPtr = cl.keywords + kwc: t.CSizeT = 0 + if kw_calls is not None: + kwc = len(kw_calls) + + # 多继承 this 调整(字节偏移法,2026-08-10,与 TPC HandlesExprCall._adjust_this_for_inherited_method 一致): + # 继承方法体按"定义类自身布局"读 self 字段(如 B.get_b 内 GEP(self_B, [0,1]) + # 的字节偏移是 B 布局中 b 的 offset 12)。父类字段区在子类中的字节偏移可能 + # 不同(如 AB={vtable,a@8,b@12} 中 b 偏移 16),self 需按字节差前移,使父类 + # 方法内二次 GEP 恰好落到子类字段。 + # delta = 字段在子类中字节偏移 − 字段在父类自身布局中字节偏移 + # delta==0(第一父类,字段区前缀对齐)无需调整;delta>0 时按字节 GEP 到 + # 父类字段区在子类中的等效基址,再 bitcast 成父类指针。 + # 原实现仅覆盖"无 vtable 的父类"(其首字段偏移 0,GEP 到首字段即父类起点), + # 且用 GEP 字段法——对 vtable 父类(首字段偏移非 0,如 B)被直接跳过不调整 + # → AB 场景 B.get_b 按 B 布局读 self.b@12 落到 a → AB 字段/方法 FAILED。 + self_arg: llvmlite.Value | t.CPtr = obj_ptr + if mc_anc is not None and string.strcmp(mc_anc, class_name) != 0: + # 判断 mc_anc 是否为 class_name 的第一父类:第一父类字段区前缀对齐 + # (delta==0)无需调整;非第一父类必须调整 self,任何一步失败都 + # fail-fast——绝不静默用未调整的 obj_ptr 当 self,否则方法按自身 + # 布局读字段读到垃圾(lookup_reexport strcmp 概率崩溃根因候选)。 + is_first_parent_th: int = 0 + mro_fp_th: t.CSizeT | t.CPtr = self.Gen.StructGen.get_mro_chain(pool, class_name) + if mro_fp_th is not None and mro_fp_th[1] != 0: + first_parent_th: str = (str | t.CPtr)(t.CVoid(mro_fp_th[1], t.CPtr)) + if first_parent_th is not None and string.strcmp(first_parent_th, mc_anc) == 0: + is_first_parent_th = 1 + anc_entry_th: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(mc_anc) + # 无字段父类(FieldCount==0,如多继承中的纯方法类 Vec2Extra): + # 继承方法体按"定义类字段"读 self,定义类无字段则方法体不读 + # self 布局 → this 无需字节调整,原样传入即可(对齐 TPC + # _adjust_this_for_inherited_method:anc_members 为空直接返回 ObjVal)。 + if anc_entry_th is not None 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: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen._get_field_entry( + anc_entry_th, afi_th) + if afe_th is not None: + afn_th: str = self.Gen.StructGen.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: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct( + class_name) + if sub_entry_th is not None: + anc_field_th: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field( + anc_entry_th.Ty, anc_first_name_th) + sub_field_th: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field( + sub_entry_th.Ty, anc_first_name_th) + if anc_field_th is not None and sub_field_th is not None: + anc_off_th: int = self._field_byte_offset( + pool, anc_entry_th, anc_field_th.Index) + sub_off_th: int = self._field_byte_offset( + pool, sub_entry_th, sub_field_th.Index) + delta_th: int = sub_off_th - anc_off_th + if delta_th > 0: + i8_ty_th: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_th: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_th) + obj_i8_th: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, obj_ptr, i8_ptr_th) + delta_val_th: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, delta_th) + adj_th: llvmlite.Value | t.CPtr = llvmlite.build_gep( + builder, i8_ty_th, obj_i8_th, delta_val_th) + if adj_th is not None: + anc_ptr_ty_th: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, anc_entry_th.Ty) + # 跨模块基类(如 Generator → ClassGenBase)的完整定义不在本模块, + # bitcast 引用 %"sha1.ClassGenBase" 前必须确保 opaque 声明存在, + # 否则 llc 报 "use of undefined type"(PrintModule 只扫函数签名, + # 函数体内的 bitcast 类型不会被自动声明)。 + llvmlite.module_ensure_opaque_for_type(mod, pool, anc_ptr_ty_th) + self_arg = llvmlite.build_bitcast(builder, adj_th, anc_ptr_ty_th) + elif sub_field_th is None and anc_field_th is not None: + # 兄弟 Mixin 场景(对齐 TPC _adjust_this_for_inherited_method): + # 调用类(class_name=BaseB)不含定义类(mc_anc=BaseA)字段—— + # BaseA 在组合类 MRO 中位于 BaseB 之前(如 Combined(BaseA, BaseB), + # BaseB 内 self.helper() 调 BaseA.helper)。BaseB.this 指向组合类中 + # BaseB 字段区起点,BaseA 区在它前面,this 前移 sizeof(定义类)。 + # 若无此分支,sub_field_th 查找失败 → 下方 fail-fast,兄弟 Mixin + # 调用永远编译不过(NoVTableCrossMixinTest BaseB.caller 根因)。 + anc_size_th: int = self.Gen.ClassGenEnum._get_type_size(anc_entry_th.Ty) + if anc_size_th > 0: + i8_ty_th2: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_th2: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_th2) + obj_i8_th2: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, obj_ptr, i8_ptr_th2) + sib_delta_th: llvmlite.Value | t.CPtr = llvmlite.const_int32( + pool, 0 - anc_size_th) + adj_th2: llvmlite.Value | t.CPtr = llvmlite.build_gep( + builder, i8_ty_th2, obj_i8_th2, sib_delta_th) + if adj_th2 is not None: + anc_ptr_ty_th2: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr( + pool, anc_entry_th.Ty) + llvmlite.module_ensure_opaque_for_type( + mod, pool, anc_ptr_ty_th2) + self_arg = llvmlite.build_bitcast( + builder, adj_th2, anc_ptr_ty_th2) + else: + # 定义类 struct 大小不可得(opaque/未加载):fail-fast + self.Gen.TypeGenErr.fatal_error( + cl, + f"this-adjust 兄弟 Mixin 定义类大小未知: 方法'{method_name}'" + f"定义类'{mc_anc}'的 struct 大小计算失败", + "InternalCompilerError") + elif is_first_parent_th == 0: + self.Gen.TypeGenErr.fatal_error( + cl, + f"this-adjust 字段查找失败: 方法'{method_name}'定义类'{mc_anc}'" + f"首字段'{anc_first_name_th}'在类'{class_name}'/'{mc_anc}'布局中未找到", + "InternalCompilerError") + elif is_first_parent_th == 0: + self.Gen.TypeGenErr.fatal_error( + cl, + f"this-adjust 子类结构体未注册: 方法'{method_name}'定义类'{mc_anc}'," + f"接收类'{class_name}'的 struct 未找到", + "InternalCompilerError") + else: + # 父类仅 __vtable__ 字段(无字段 CVTable 类):方法体不读 + # 布局字段,无需调整(对齐 TPC:first_field_name None → ObjVal) + pass + elif anc_entry_th is not None and anc_entry_th.FieldCount == 0: + # 无字段父类:this 无需字节调整(见上方注释) + pass + elif is_first_parent_th == 0: + # 父类 struct 未注册:fail-fast,绝不静默用未调整的 obj_ptr + # 当 self(否则方法按自身布局读字段读到垃圾) + self.Gen.TypeGenErr.fatal_error( + cl, + f"this-adjust 父类结构体未注册: 方法'{method_name}'定义类'{mc_anc}'的 struct 未找到", + "InternalCompilerError") + # self 参数按声明首参类型 coerce:跨模块 declare self 是类指针 + # (%"sha1.Class"*,见 VpiReader._inject_method_declare),而对象指针 + # 可能是 i8*(X|t.CPtr 联合简化)或 this-adjust 后的祖先指针,需 + # bitcast 匹配声明签名(对齐 _call_method_on_ptr 的 coerce 块), + # 否则 llc 报 call 参数类型与 declare 不一致。 + if found_func is not None: + pp_self: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) + if pp_self is not None: + pty_self: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(pp_self) + if pty_self is not None: + cself_tm: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type( + builder, self_arg, pty_self, None) + if cself_tm is not None: + self_arg = cself_tm + # 构建参数链表: 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: + # 关键字参数:遍历函数定义参数(跳过 self),位置参数优先,其余按名匹配 + # 缺失参数用零值兜底,保持 call 参数数量与 define 一致 + cur_param_k: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) + if cur_param_k is not None: + cur_param_k = llvmlite.param_get_next(cur_param_k) # 跳过 self + pi_k: int = 0 + while cur_param_k is not None: + arg_node_k: ast.AST | t.CPtr = None + if pi_k < can: + arg_node_k = cargs.get(pi_k) + else: + pname_k: str = llvmlite.param_get_name(cur_param_k) + if pname_k is not None: + ki_k: int + for ki_k in range(kwc): + kw_k: ast.Keyword | t.CPtr = (ast.Keyword | t.CPtr)(kw_calls.get(ki_k)) + if kw_k is None or kw_k.arg is None or kw_k.value is None: + continue + if self._kwarg_name_matches(kw_k.arg, pname_k) == 1: + arg_node_k = kw_k.value + break + arg_val_k: llvmlite.Value | t.CPtr = None + if arg_node_k is not None: + # 先取形参类型(char 字面量 → str 形参需物化字符串,见 translate_constant) + pty_k0: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cur_param_k) + arg_val_k = self.Gen.ExprGen.translate_value( + builder, pool, mod, arg_node_k, None, 0, trans, pty_k0) + if arg_val_k is None: + # 缺失参数:零值兜底(保持参数数量匹配)。 + # kw 省略的形参可能有默认值(查不到默认表 ≠ 无默认值), + # 零值兜底与原逻辑一致,禁止 fail-fast 误杀。 + pty_k: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cur_param_k) + if pty_k is not None: + arg_val_k = llvmlite.ConstZero(pool, pty_k) + if arg_val_k is not None: + pty_k2: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cur_param_k) + if pty_k2 is not None: + arg_val_k = self.Gen.ExprGenBase.coerce_to_type( + builder, arg_val_k, pty_k2, arg_node_k) + llvmlite.value_set_next(arg_val_k, None) + llvmlite.value_set_next(tail, arg_val_k) + tail = arg_val_k + arg_count_eff += 1 + pi_k += 1 + cur_param_k = llvmlite.param_get_next(cur_param_k) + else: + # 位置参数(原逻辑) + for ai in range(can): + arg_node: ast.AST | t.CPtr = cargs.get(ai) + if arg_node is None: + continue + # 先取形参类型:单引号单字符字面量(CONST_CHAR)传给 str/指针 + # 形参时需物化 1 字符字符串(translate_constant 依据 target_ty 判定), + # 否则 coerce inttoptr 把字符值当指针 → strlen 访问低地址崩溃。 + pp: llvmlite.Param | t.CPtr = None + pty: llvmlite.LLVMType | t.CPtr = None + if found_func is not None: + pp = llvmlite.function_get_param_head(found_func) + pi: int = 0 + while pp is not None and pi < ai + 1: + if pi == ai + 1: + break + pp = llvmlite.param_get_next(pp) + pi += 1 + if pp is not None: + pty = llvmlite.param_get_ty(pp) + arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( + builder, pool, mod, arg_node, None, 0, trans, pty) + if arg_val is None: + continue + # 按形参类型 coerce(self 是形参 0,第 ai 个实参对应形参 ai+1) + # 修复: CDefine/i32 常量传给 i64 形参(如 MemBuddy.__init__ 的 size: t.CSizeT) + # 未提升导致 call(i32) 与 define(i64) 不匹配,运行时读到垃圾高 32 位崩溃 + if found_func is not None: + if pty is not None: + arg_val = self.Gen.ExprGenBase.coerce_to_type( + builder, arg_val, pty, arg_node) + else: + # found_func=None(跨模块 stub 未注入):数组实参按 C 语义 + # 衰减为元素指针(对齐 TPC _decay_array_to_ptr)。否则 + # _ensure_method_declare 用 [N x ty]* 实参类型建 declare, + # 与定义侧元素指针(如 md5.final 的 i8*)类型不匹配。 + if arg_val.Ty is not None and self.Gen.ExprGenBase.is_ptr_type(arg_val.Ty) != 0: + arr_pointee_ma: llvmlite.LLVMType | t.CPtr = arg_val.Ty.Pointee + if arr_pointee_ma is not None: + match arr_pointee_ma: + case llvmlite.LLVMType.Array(arr_et_ma, _): + decayed_ma: llvmlite.Value | t.CPtr = llvmlite.build_gep_array( + builder, arr_pointee_ma, arr_et_ma, arg_val, + llvmlite.const_int32(pool, 0)) + if decayed_ma is not None: + arg_val = decayed_ma + case _: + pass + llvmlite.value_set_next(arg_val, None) + llvmlite.value_set_next(tail, arg_val) + tail = arg_val + arg_count_eff += 1 + + total_count: int = 1 + arg_count_eff + + # 默认参数填充:如果调用点参数少于函数定义参数,用默认值填充缺失参数 + # 查找函数的 defaults 信息 + mdf_defaults: t.CVoid | t.CPtr = None + mdf_default_count: int = 0 + mdf_param_count: int = 0 + if trans is not None and trans._funcs is not None: + mdf_entry: GenTypes.FuncEntry | t.CPtr = ExprCallTable.find_func_entry_in_table( + trans._funcs, trans._func_count, lookup_name) + if mdf_entry is not None: + mdf_defaults = mdf_entry.Defaults + mdf_default_count = mdf_entry.DefaultCount + mdf_param_count = mdf_entry.ParamCount + # 全局默认参数表查询无条件执行:mdf_entry 命中时(泛型特化方法注册进 + # 调用方函数表)Defaults 非 None 会短路跳过,但下方"用快照重建默认值" + # 分支依赖 mdf_gd(唯一持有 stdlib 常量快照处)——缺它必 fatal_error + # (ListTest list[int].__new__ 根因) + mdf_gd: GenTypes.FuncDefaultsEntry | t.CPtr = self.Gen.CallGenLeafKit.find_func_defaults(lookup_name) + if mdf_defaults is None and mdf_gd is not None: + mdf_defaults = mdf_gd.Defaults + mdf_default_count = mdf_gd.DefaultCount + mdf_param_count = mdf_gd.ParamCount + + # 跨模块 stub 未注入兜底:如 self.Gen.ExprGenCdef.method 经 Generator + # 属性访问调用依赖模块方法,依赖收集不加载其 vpi → 函数表无 + # ParamCount/默认参数 → 调用点缺参 → 被调函数读栈残留 + # (IterTest 跨模块 CDefine 查找 depth=160 假递归根因)。用 vpi + # 文本缓存补全参数数,下方默认参数填充(含零值兜底)随即生效, + # declare/call 与 define 参数数对齐。 + vpi_pa: str = None + if mdf_param_count == 0 and found_func is None: + vpi_sha1_p: str = None + if mc_entry is not None and mc_entry.ModuleSha1 is not None: + vpi_sha1_p = mc_entry.ModuleSha1 + else: + vpi_sha1_p = self.Gen.StructGen.get_struct_sha1(class_name) + if vpi_sha1_p is not None: + vpi_pa = self.Gen.CallGenBase._vpi_method_param_annot( + pool, vpi_sha1_p, class_name, method_name) + if vpi_pa is not None and vpi_pa[0] != '\0': + vpi_pc2: int = 1 + vi2: t.CSizeT = 0 + while vpi_pa[vi2] != '\0': + if vpi_pa[vi2] == '@': + vpi_pc2 += 1 + vi2 += 1 + mdf_param_count = vpi_pc2 - 1 # 非 self 参数数 + + # [修复 2026-08-25] found_func 参数数回退:函数表/默认表无 ParamCount + # (mdf_param_count==0)但 found_func(模块内 declare)已找到时,用其 + # 参数数作补全基准。缺它:省略默认参数(如 translate_value 的 + # target_ty=None)的调用补全不触发 → call 实参(8) < define 形参(9) + # → x64 ABI 第 9 参读栈残留垃圾 → is_ptr_type(0x40) 崩溃(SlotsBugTest + # 二代编 translate_assign/AugAssign 崩溃根因)。 + if mdf_param_count == 0 and found_func is not None: + ppcnt_f: int = 0 + ppn_f: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) + while ppn_f is not None: + ppcnt_f += 1 + ppn_f = llvmlite.param_get_next(ppn_f) + if ppcnt_f > 1: + mdf_param_count = ppcnt_f - 1 # 非 self 参数数 + + # 计算需要填充的参数(不含 self) + # arg_count_eff = 已提供的非 self 参数数量(含关键字模式完整构建) + # mdf_param_count = 函数定义的非 self 参数数量 + if mdf_param_count > arg_count_eff: + mdf_offset: int = mdf_param_count - mdf_default_count + mdf_pi: int = arg_count_eff + while mdf_pi < mdf_param_count: + # 优先用默认值 + if mdf_defaults is not None and mdf_default_count > 0 and mdf_pi >= mdf_offset: + mdf_di: int = mdf_pi - mdf_offset + if mdf_di < mdf_default_count: + # 用 stdlib 常量快照重建(mdf_gd.Defaults 的 pool 内 AST 可能已被 reset 回收, + # 直接回退读 AST 会 use-after-free——快照失败时 fail-fast 暴露,绝不读悬空 AST) + mdf_val: llvmlite.Value | t.CPtr = None + if mdf_gd is not None: + mdf_val = self.Gen.CallGenLeafKit._build_const_from_snapshot( + pool, mod, mdf_gd, mdf_di, trans, builder) + if mdf_val is None: + self.Gen.TypeGenErr.fatal_error( + cl, + f"跨模块默认参数无法重建(快照缺失,默认值需为简单常量): {lookup_name}", + "TypeError") + continue + llvmlite.value_set_next(mdf_val, None) + llvmlite.value_set_next(tail, mdf_val) + tail = mdf_val + total_count += 1 + mdf_pi += 1 + continue + # 无默认值时用零值兜底(需要 found_func 获取参数类型)。 + # 默认参数表按裸名注册(如 "__init__"),跨模块 "ClassName.method" + # 查找常查不到——查不到 ≠ 无默认值(如 Buf.__init__(data, capacity) + # 缺的 length=0/owned=False 恰为零值),零值兜底语义一致。 + if found_func is not None: + param_node_z: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func) + skip_z: int = 0 + while skip_z <= mdf_pi and param_node_z is not None: + param_node_z = llvmlite.param_get_next(param_node_z) + skip_z += 1 + if param_node_z is not None: + zero_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(param_node_z) + zero_val: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, zero_ty) + if zero_val is not None: + llvmlite.value_set_next(zero_val, None) + llvmlite.value_set_next(tail, zero_val) + tail = zero_val + total_count += 1 + elif vpi_pa is not None: + # found_func 为 None(stub 未注入):用 vpi 参数注解解析 + # 缺失参数类型(段索引 mdf_pi+1,跳过首参 self 空段), + # 值取类型零值(默认参数值恰为零值;查不到 ≠ 无默认值)。 + seg_idx_v: int = mdf_pi + 1 + seg_no_v: int = 0 + seg_cur_v: t.CSizeT = 0 + while seg_no_v < seg_idx_v and vpi_pa[seg_cur_v] != '\0': + if vpi_pa[seg_cur_v] == '@': + seg_no_v += 1 + seg_cur_v += 1 + seg_start_v: t.CSizeT = seg_cur_v + seg_len_v: t.CSizeT = 0 + while vpi_pa[seg_start_v + seg_len_v] != '\0' \ + and vpi_pa[seg_start_v + seg_len_v] != '@': + seg_len_v += 1 + if seg_no_v == seg_idx_v: + pty_v: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) + if seg_len_v > 0: + seg_buf_v: bytes = stdlib.malloc(seg_len_v + 1) + if seg_buf_v is not None: + string.strncpy(seg_buf_v, vpi_pa + seg_start_v, seg_len_v) + seg_buf_v[seg_len_v] = '\0' + rty_v: llvmlite.LLVMType | t.CPtr = self.Gen.CallGenBase._vpi_resolve_param_annot_ty( + pool, seg_buf_v) + if rty_v is not None: + pty_v = rty_v + stdlib.free(seg_buf_v) + zero_val_v: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, pty_v) + if zero_val_v is not None: + llvmlite.value_set_next(zero_val_v, None) + llvmlite.value_set_next(tail, zero_val_v) + tail = zero_val_v + total_count += 1 + mdf_pi += 1 + + # 虚方法分发:如果类有虚表且该方法在虚表中,走间接调用 + # (不依赖 found_func — 继承的虚方法可能没有子类实现) + # 复用前面已获取的 mc_entry(find_struct_by_type 规避跨模块同名找错) + mc_has_vt: int = 0 + mc_method_idx: int = -1 + if mc_entry is not None: + mc_has_vt = mc_entry.HasVTable + if mc_has_vt == 1 and mc_entry.VTableMethods is not None: + mc_methods_arr: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(mc_entry.VTableMethods, t.CPtr)) + for mc_mi in range(mc_entry.VTableMethodCount): + mc_mname_addr: t.CSizeT = mc_methods_arr[mc_mi] + if mc_mname_addr == 0: + continue + mc_mname: str = (str | t.CPtr)(t.CVoid(mc_mname_addr, t.CPtr)) + if mc_mname is not None and string.strcmp(mc_mname, method_name) == 0: + mc_method_idx = mc_mi + break + if mc_has_vt == 1 and mc_method_idx >= 0: + # 虚分派 self 必须用调整后的 self_arg(多继承 this 调整), + # 否则 B.get_b 收到未调整的 AB*,方法体内按 B 布局读字段错位。 + # vtable 读取必须用对象头 obj_ptr(调整后 self 头部是父类字段值)。 + return self._do_virtual_call(pool, builder, mod, class_name, method_name, + self_arg, head, total_count, found_func, call_ret_ty, + obj_ptr) + + # 跨模块调用:创建 declare 声明,避免 llc 报 undefined value + self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, total_count) + # 检查变长函数 + C ABI 提升(cargs 是实参 AST,self_offset=1 跳过 self) + is_variadic_3441: int = self.Gen.CallGenVa._check_and_promote_variadic_call( + pool, builder, mod, call_name, head, total_count, cargs, 1, trans) + # try-except:方法含 raise(EH 输出参数)时追加输出实参 + 调用后检测 + return self.Gen.CallGenLeafKit._emit_eh_guarded_call( + builder, pool, mod, trans, found_func, call_name, head, total_count, + call_ret_ty, is_variadic_3441) + # ============================================================ + # _translate_super_call — super().method(args) → 静态调用父类方法 + # + # Python super 语义(2026-08-10 实现): + # - 当前类来自 self.CurClassName(_translate_method 翻译方法体时设置) + # - 父类查找:沿 get_mro_chain(CurClassName) 跳过自身,找第一个定义 method 的祖先 + # - self 指针:符号表 self 变量 alloca load(对象头,复用 c.Addr(self) 模式) + # - this_adjust:父类字段区在子类中字节偏移 delta>0 时 GEP i8* + delta + # (复用 _field_byte_offset 字节偏移法,与 _translate_method_call 一致) + # - 静态调用父类实现:不虚分派(super() 不触发子类 override) + # ============================================================ + + # ============================================================ + def _translate_super_call(self, pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + cl: ast.Call | t.CPtr, + method_name: str, + trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr: + """super().method(args) → 父类方法静态调用,返回 call 结果 Value""" + if pool is None or builder is None or cl is None or method_name is None: + return None + if trans is None: + return None + # 1. 当前方法所属类:从 self 变量的注解类名获取(_translate_method + # 注册 self 时 set_var_annot_class_name(SymTab, "self", class_name))。 + # 不用 Mixin 字段:TPV Mixin 字段按声明类独立布局(Generator 多继承 + # 去重只保留首个声明),ClassGenMixin 写的 CurClassName 与 CallGenMixin + # 读的位置偏移不一致 → 运行时读到空 → super() 误报"只能在类方法内使用"。 + self_entry: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry( + trans.SymTab, "self") + if self_entry is None: + self_entry = self.Gen.VarGen.lookup_var_entry(trans.SymTab, "cls") + if self_entry is None: + self.Gen.TypeGenErr.fatal_error(cl, "super(): 当前作用域无 self 变量(静态方法内不可用)", "TypeError") + return None + cur_class: str = self_entry.AnnotClassName + if cur_class is None: + self.Gen.TypeGenErr.fatal_error(cl, "super() 只能在类方法内使用(self 无注解类名)") + return None + # 2. 沿 MRO 跳过自身,找第一个定义 method 的祖先类 + anc: str = None + mro_sp: t.CSizeT | t.CPtr = self.Gen.StructGen.get_mro_chain(pool, cur_class) + mi_sp: int = 1 + while mro_sp is not None and mro_sp[mi_sp] != 0: + anc_sp: str = (str | t.CPtr)(t.CVoid(mro_sp[mi_sp], t.CPtr)) + if anc_sp is not None: + probe_sp: str = pool.alloc(160) + if probe_sp is not None: + viperlib.snprintf(probe_sp, 160, "%s.%s", anc_sp, method_name) + if self.Gen.CallGenLeafKit.find_func_global(probe_sp) is not None: + anc = anc_sp + break + mi_sp += 1 + if anc is None: + err_sp: str = pool.alloc(256) + if err_sp is not None: + viperlib.snprintf(err_sp, 256, "super(): 当前类 %s 的父类链未定义方法 %s", + cur_class, method_name) + self.Gen.TypeGenErr.fatal_error(cl, err_sp, "TypeError") + self.Gen.TypeGenErr.fatal_error(cl, "super(): 父类链未定义方法", "TypeError") + return None + # 3. self 指针(self 变量 alloca load,复用 c.Addr(self) 模式) + if self_entry.Alloca is None or self_entry.Alloca.Ty is None: + self.Gen.TypeGenErr.fatal_error(cl, "super(): 当前作用域无 self 变量(静态方法内不可用)", "TypeError") + return None + self_ptr: llvmlite.Value | t.CPtr = None + match self_entry.Alloca.Ty: + case llvmlite.LLVMType.Ptr(inner1): + match inner1: + case llvmlite.LLVMType.Ptr(inner2): + obj_ty_sp: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, inner2) + self_ptr = llvmlite.build_load(builder, obj_ty_sp, self_entry.Alloca) + case _: + self_ptr = llvmlite.build_load(builder, self_entry.Alloca.Ty, self_entry.Alloca) + case _: + self_ptr = self_entry.Alloca + if self_ptr is None: + return None + # 3. this_adjust(字节偏移法:父类字段区在子类中的偏移差) + self_arg: llvmlite.Value | t.CPtr = self_ptr + if string.strcmp(anc, cur_class) != 0: + anc_entry_sp: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(anc) + if anc_entry_sp is not None and anc_entry_sp.FieldCount > 0: + first_name_sp: str = None + afi_sp: int = 0 + while afi_sp < anc_entry_sp.FieldCount: + afe_sp: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen._get_field_entry( + anc_entry_sp, afi_sp) + if afe_sp is not None: + afn_sp: str = self.Gen.StructGen.get_field_name_ptr(afe_sp) + if afn_sp is not None and string.strcmp(afn_sp, "__vtable__") != 0: + first_name_sp = afn_sp + break + afi_sp += 1 + if first_name_sp is not None: + sub_entry_sp: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct( + cur_class) + if sub_entry_sp is not None: + anc_field_sp: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field( + anc_entry_sp.Ty, first_name_sp) + sub_field_sp: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field( + sub_entry_sp.Ty, first_name_sp) + if anc_field_sp is not None and sub_field_sp is not None: + anc_off_sp: int = self._field_byte_offset( + pool, anc_entry_sp, anc_field_sp.Index) + sub_off_sp: int = self._field_byte_offset( + pool, sub_entry_sp, sub_field_sp.Index) + delta_sp: int = sub_off_sp - anc_off_sp + if delta_sp > 0: + i8_ty_sp: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_sp: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_sp) + obj_i8_sp: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, self_ptr, i8_ptr_sp) + dval_sp: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, delta_sp) + adj_sp: llvmlite.Value | t.CPtr = llvmlite.build_gep( + builder, i8_ty_sp, obj_i8_sp, dval_sp) + if adj_sp is not None: + anc_ptr_sp: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, anc_entry_sp.Ty) + # 同上:跨模块祖先类型 bitcast 前确保 opaque 声明, + # 否则 llc 报 "use of undefined type" + llvmlite.module_ensure_opaque_for_type(mod, pool, anc_ptr_sp) + self_arg = llvmlite.build_bitcast(builder, adj_sp, anc_ptr_sp) + # 4. 查找父类方法函数 + 构造调用名(SHA1 mangled) + anc_lookup: str = pool.alloc(160) + if anc_lookup is None: + return None + viperlib.snprintf(anc_lookup, 160, "%s.%s", anc, method_name) + found_func: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, anc_lookup) + call_name: str = anc_lookup + anc_sha1_sp: str = self.Gen.StructGen.get_struct_sha1(anc) + if anc_sha1_sp is None: + anc_sha1_sp = trans.ModuleSha1 + if found_func is not None: + fname_sp: str = llvmlite.function_get_name(found_func) + if fname_sp is not None: + call_name = fname_sp + else: + if anc_sha1_sp is not None: + mangled_sp: str = self.Gen.FuncGenBase._mangle_name_with_sha1(anc_sha1_sp, anc_lookup) + if mangled_sp is not None: + call_name = mangled_sp + # 5. 翻译参数 + 按形参 coerce(self 是形参 0,第 ai 个实参对应形参 ai+1) + cargs_sp: list[ast.AST | t.CPtr] | t.CPtr = cl.args + can_sp: t.CSizeT = 0 + if cargs_sp is not None: + can_sp = len(cargs_sp) + llvmlite.value_set_next(self_arg, None) + head: llvmlite.Value | t.CPtr = self_arg + tail: llvmlite.Value | t.CPtr = self_arg + arg_count_sp: int = 1 + for ai_sp in range(can_sp): + arg_node_sp: ast.AST | t.CPtr = cargs_sp.get(ai_sp) + if arg_node_sp is None: + continue + # 先取形参类型(char 字面量 → str 形参需物化字符串,见 translate_constant) + pp_sp: llvmlite.Param | t.CPtr = None + pty_sp: llvmlite.LLVMType | t.CPtr = None + if found_func is not None: + pp_sp = llvmlite.function_get_param_head(found_func) + pi_sp: int = 0 + while pp_sp is not None and pi_sp < ai_sp + 1: + if pi_sp == ai_sp + 1: + break + pp_sp = llvmlite.param_get_next(pp_sp) + pi_sp += 1 + if pp_sp is not None: + pty_sp = llvmlite.param_get_ty(pp_sp) + arg_val_sp: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( + builder, pool, mod, arg_node_sp, None, 0, trans, pty_sp) + if arg_val_sp is None: + continue + if pty_sp is not None: + arg_val_sp = self.Gen.ExprGenBase.coerce_to_type( + builder, arg_val_sp, pty_sp, arg_node_sp) + else: + # found_func=None:数组实参按 C 语义衰减为元素指针 + # (与 _translate_method_call 一致,避免 declare 类型不匹配) + if arg_val_sp.Ty is not None and self.Gen.ExprGenBase.is_ptr_type(arg_val_sp.Ty) != 0: + arr_pointee_sp: llvmlite.LLVMType | t.CPtr = arg_val_sp.Ty.Pointee + if arr_pointee_sp is not None: + match arr_pointee_sp: + case llvmlite.LLVMType.Array(arr_et_sp, _): + decayed_sp: llvmlite.Value | t.CPtr = llvmlite.build_gep_array( + builder, arr_pointee_sp, arr_et_sp, arg_val_sp, + llvmlite.const_int32(pool, 0)) + if decayed_sp is not None: + arg_val_sp = decayed_sp + case _: + pass + llvmlite.value_set_next(arg_val_sp, None) + llvmlite.value_set_next(tail, arg_val_sp) + tail = arg_val_sp + arg_count_sp += 1 + # 6. 返回类型:found_func → 特殊方法 → text.ll/vpi + ret_ty_sp: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + if found_func is not None: + frt_sp: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(found_func) + if frt_sp is not None: + ret_ty_sp = frt_sp + else: + sp_ty_sp: llvmlite.LLVMType | t.CPtr = self._special_method_ret_ty(pool, anc, method_name) + if sp_ty_sp is not None: + ret_ty_sp = sp_ty_sp + else: + looked_sp: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL( + pool, anc_sha1_sp, anc, method_name) + if looked_sp is not None: + ret_ty_sp = looked_sp + else: + vpi_ret_sp: llvmlite.LLVMType | t.CPtr = self._lookup_method_ret_ty_via_vpi( + pool, anc_sha1_sp, anc, method_name) + if vpi_ret_sp is not None: + ret_ty_sp = vpi_ret_sp + else: + # 查找链(函数/text.ll/vpi stub)全部未命中:fail-fast, + # 绝不静默保持默认 i32(会截断指针/i64 返回值) + # Phase A 依赖未就绪(anc_sha1_sp None / 定义模块 text.ll + # 未生成):标记 pending 下遍重译(对齐 L4783-4790), + # 绝不 fail-fast 中断多遍收敛;真实错误由收敛检查兜底。 + if anc_sha1_sp is None \ + or self._dep_text_ll_exists(pool, anc_sha1_sp) == 0: + if trans is not None: + trans._deps_pending = 1 + # [DEPDBG] 临时诊断:特殊方法依赖未就绪即 pending 的根因(用后移除) + dbg_ms4: str = trans.ModuleSha1 if (trans is not None and trans.ModuleSha1 is not None) else "?" + stdio.printf("[DEPDBG] pending(special_ret): mod=%s cls=%s method=%s sha1=%s\n", + dbg_ms4, anc if anc is not None else "?", + method_name, + anc_sha1_sp if anc_sha1_sp is not None else "none") + # 依赖未就绪:保持默认 i32(build_call(Void) 返回 None + # 会破坏表达式翻译;pending 文件本遍不写盘,下遍重译) + ret_ty_sp = llvmlite.Int32(pool) + else: + self._fail_method_ret_ty(method_name, cl) + # 7. 跨模块:创建 declare;生成 call + if found_func is None: + 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) + # ============================================================ + # _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 | t.CPtr, + entry: GenTypes.StructEntry | t.CPtr, + field_index: int) -> int: + """计算字段在结构体中的字节偏移(含对齐 padding),无法计算返回 0""" + if pool is None or entry is None or entry.Ty is None or field_index < 0: + return 0 + match entry.Ty: + case llvmlite.LLVMType.Struct(fields, fcount, name): + offset_fb: int = 0 + cur_fb: llvmlite.ParamNode | t.CPtr = fields + i_fb: int = 0 + while cur_fb is not None and i_fb < field_index: + if cur_fb.Ty is not None: + fty_fb: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(cur_fb.Ty) + fsize_fb: int = self.Gen.ClassGenEnum._get_type_size(fty_fb) + falign_fb: int = self.Gen.ClassGenEnum._get_type_align(fty_fb) + if falign_fb > 1: + rem_fb: int = offset_fb % falign_fb + if rem_fb != 0: + offset_fb += falign_fb - rem_fb + offset_fb += fsize_fb + cur_fb = cur_fb.Next + i_fb += 1 + # 字段自身对齐 + if cur_fb is not None and cur_fb.Ty is not None: + fty2_fb: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(cur_fb.Ty) + falign2_fb: int = self.Gen.ClassGenEnum._get_type_align(fty2_fb) + if falign2_fb > 1: + rem2_fb: int = offset_fb % falign2_fb + if rem2_fb != 0: + offset_fb += falign2_fb - rem2_fb + return offset_fb + case _: + return 0 + # ============================================================ + # _kwarg_name_matches — 检查关键字参数名是否匹配函数参数名 + # + # 函数参数名在 LLVM IR 中带 % 前缀(如 "%a"),关键字参数名无前缀(如 "a") + # 跳过 % 前缀后逐字符比较 + # ============================================================ + def _kwarg_name_matches(self, kw_arg: str, + param_name: str) -> int: + """检查关键字参数名 kw_arg 是否匹配参数名 param_name(跳过 % 前缀)""" + if kw_arg is None or param_name is None: + return 0 + start: t.CSizeT = 0 + if param_name[0] == '%': + start = 1 + for i, ch in enumerate(kw_arg): + c: t.CChar = param_name[start + i] + if c == '\0' or c != ch: + return 0 + if param_name[start + i + 1] != '\0': + return 0 + return 1 \ No newline at end of file