Files
ViperC/App/lib/core/Gen/CallGenCall.vp
T
2026-08-27 09:48:20 +08:00

1910 lines
120 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 错位读栈残留垃圾。
# 排除 EH 输出参数(%__eh_msg_out__/%__eh_code_out__):含 raise 函数
# 定义尾部注入 2 个 EH 形参,计入会让 _emit_eh_guarded_call 追加真实
# EH 实参前先补 zeroinitializer 占位 → 双重注入 call 实参多 2(TestFBProject
# raise_disk() 崩溃根因,与 CallGenMixin 零值兜底同源)。
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:
fp_nm_f2: str = llvmlite.param_get_name(ppn_f2)
if fp_nm_f2 is None or (string.strcmp(fp_nm_f2, "%__eh_msg_out__") != 0
and string.strcmp(fp_nm_f2, "%__eh_code_out__") != 0):
ppcnt_f2 += 1
ppn_f2 = llvmlite.param_get_next(ppn_f2)
if ppcnt_f2 > 1:
mdf_param_count = ppcnt_f2 - 1 # 非 self 非 EH 参数数
# 计算需要填充的参数(不含 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)
# [根治] declare 创建后按形参类型 coerce 实参(与上方 found_func 路径
# 的 L588-611 同逻辑,缺失导致字面量 0=i32 传 i64 形参(t.CSizeT)未提升:
# llc 按 i32 布局栈参(movl 4 字节写)→ i64 形参高 4 字节残留垃圾 →
# callee 读 can=[垃圾][count] 崩溃,SlotsBugTest/StringIterTest 根因,
# 反汇编 movl 实锤)。coerce 使 IR call 实参与 declare 形参类型一致。
dc_func: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, call_name)
if dc_func is not None and head is not None:
dc_param: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(dc_func)
dc_node: llvmlite.Value | t.CPtr = head
dc_new_head: llvmlite.Value | t.CPtr = head
dc_prev: llvmlite.Value | t.CPtr = None
while dc_node is not None:
dc_next: llvmlite.Value | t.CPtr = dc_node.Next
dc_cur: llvmlite.Value | t.CPtr = dc_node
if dc_param is not None:
dc_pty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(dc_param)
if dc_pty is not None:
dc_c: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(
builder, dc_node, dc_pty, None)
if dc_c is not None:
dc_cur = dc_c
dc_param = llvmlite.param_get_next(dc_param)
dc_cur.Next = None
if dc_prev is None:
dc_new_head = dc_cur
else:
dc_prev.Next = dc_cur
dc_prev = dc_cur
dc_node = dc_next
head = dc_new_head
return llvmlite.build_call(builder, call_name, head, total_count, call_ret_ty, 0)
# ============================================================
# _translate_method_call - 翻译方法调用 obj.method(args)
#
# 生成: call <ret_ty> @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)
# [MMDBG] 诊断:mc_entry 解析(用后移除)
if string.strcmp(method_name, "Sub") == 0:
if mc_entry is not None:
stdio.printf("[MMDBG] mc_entry.Name=%s\n", mc_entry.Name)
else:
stdio.printf("[MMDBG] mc_entry=None\n")
# 构造方法查找名: 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)
# [MMDBG] 诊断:found_func 查找(用后移除)
if string.strcmp(method_name, "Sub") == 0:
if found_func is not None:
stdio.printf("[MMDBG] found_func=%s\n", llvmlite.function_get_name(found_func))
else:
stdio.printf("[MMDBG] found_func=None lookup=%s\n", 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 崩溃根因)。
# 排除 EH 输出参数(%__eh_msg_out__/%__eh_code_out__):含 raise 方法
# 定义尾部注入 2 个 EH 形参,计入会让补全先补 zeroinitializer 占位,
# _emit_eh_guarded_call 再追加真实 EH 实参 → 双重注入(TestFBProject
# raise_disk() 崩溃同源,与 _call_method_on_ptr/CallGenMixin 一致)。
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:
fp_nm_f: str = llvmlite.param_get_name(ppn_f)
if fp_nm_f is None or (string.strcmp(fp_nm_f, "%__eh_msg_out__") != 0
and string.strcmp(fp_nm_f, "%__eh_code_out__") != 0):
ppcnt_f += 1
ppn_f = llvmlite.param_get_next(ppn_f)
if ppcnt_f > 1:
mdf_param_count = ppcnt_f - 1 # 非 self 非 EH 参数数
# 计算需要填充的参数(不含 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 输出参数)时追加输出实参 + 调用后检测
# [MMDBG] 诊断:调用发射前状态(用后移除)
if string.strcmp(method_name, "Sub") == 0:
stdio.printf("[MMDBG] pre-call call_name=%s ret_ty_ptr=%llu head=%llu count=%d\n",
call_name, t.CUInt64T(call_ret_ty), t.CUInt64T(head), total_count)
res_final: llvmlite.Value | t.CPtr = self.Gen.CallGenLeafKit._emit_eh_guarded_call(
builder, pool, mod, trans, found_func, call_name, head, total_count,
call_ret_ty, is_variadic_3441)
# [MMDBG] 诊断:调用发射结果(用后移除)
if string.strcmp(method_name, "Sub") == 0:
if res_final is not None:
stdio.printf("[MMDBG] _emit_eh_guarded_call ok\n")
else:
stdio.printf("[MMDBG] _emit_eh_guarded_call None\n")
return res_final
# ============================================================
# _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