fix(callgencall): 根治——跨模块 declare 创建后按形参类型 coerce 实参

found_func=None 路径(跨模块调用)创建 declare 后未 coerce 实参,字面量 0=i32 传 i64 形参(t.CSizeT)未提升 → llc 按 i32 布局栈参(movl 4 字节写)→ i64 形参高 4 字节残留垃圾 → callee 读 can=[垃圾][count] 崩溃(SlotsBugTest/StringIterTest 根因,反汇编 movl 实锤)。

修复:与 found_func 路径(L588-611)同逻辑,_ensure_method_declare 后用 self.find_func_in_module 取 declare 形参类型,coerce_to_type 提升/截断整数实参(i32 0 → sext i64 0),使 IR call 实参与 declare 一致。覆盖所有跨模块调用点。
This commit is contained in:
TermiNexus committed 2026-08-25 17:53:47 +08:00
1 parent a9d71714b3
commit decf74b946
1 file changed
+293 -63
+293 -63
View File
@@ -79,15 +79,28 @@ class CallGenCall(CallGenBase.CallGenBase):
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 self_ptr.Ty is not None:
vc_entry = self.Gen.StructGen.get_struct_type_from_value(self_ptr)
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:
@@ -420,6 +433,48 @@ class CallGenCall(CallGenBase.CallGenBase):
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 参数数量
@@ -470,6 +525,40 @@ class CallGenCall(CallGenBase.CallGenBase):
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:用零值兜底填充缺失参数
@@ -558,6 +647,36 @@ class CallGenCall(CallGenBase.CallGenBase):
# 跨模块调用(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)
# ============================================================
@@ -648,45 +767,64 @@ class CallGenCall(CallGenBase.CallGenBase):
mixin_anc_name: str = None
mixin_anc_sha1: str = None
if found_func is None:
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
# 兄弟 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,
@@ -1178,8 +1316,10 @@ class CallGenCall(CallGenBase.CallGenBase):
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)
builder, pool, mod, arg_node_k, None, 0, trans, pty_k0)
if arg_val_k is None:
# 缺失参数:零值兜底(保持参数数量匹配)。
# kw 省略的形参可能有默认值(查不到默认表 ≠ 无默认值),
@@ -1204,15 +1344,13 @@ class CallGenCall(CallGenBase.CallGenBase):
arg_node: ast.AST | t.CPtr = cargs.get(ai)
if arg_node is None:
continue
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg_node, None, 0, trans)
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 位崩溃
# 先取形参类型:单引号单字符字面量(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.Param | t.CPtr = llvmlite.function_get_param_head(found_func)
pp = llvmlite.function_get_param_head(found_func)
pi: int = 0
while pp is not None and pi < ai + 1:
if pi == ai + 1:
@@ -1220,10 +1358,18 @@ class CallGenCall(CallGenBase.CallGenBase):
pp = llvmlite.param_get_next(pp)
pi += 1
if pp is not None:
pty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(pp)
if pty is not None:
arg_val = self.Gen.ExprGenBase.coerce_to_type(
builder, arg_val, pty, arg_node)
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)。否则
@@ -1270,6 +1416,53 @@ class CallGenCall(CallGenBase.CallGenBase):
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 参数数量
@@ -1317,6 +1510,40 @@ class CallGenCall(CallGenBase.CallGenBase):
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
# 虚方法分发:如果类有虚表且该方法在虚表中,走间接调用
@@ -1508,12 +1735,11 @@ class CallGenCall(CallGenBase.CallGenBase):
arg_node_sp: ast.AST | t.CPtr = cargs_sp.get(ai_sp)
if arg_node_sp is None:
continue
arg_val_sp: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg_node_sp, None, 0, trans)
if arg_val_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.Param | t.CPtr = llvmlite.function_get_param_head(found_func)
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:
@@ -1521,10 +1747,14 @@ class CallGenCall(CallGenBase.CallGenBase):
pp_sp = llvmlite.param_get_next(pp_sp)
pi_sp += 1
if pp_sp is not None:
pty_sp: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(pp_sp)
if pty_sp is not None:
arg_val_sp = self.Gen.ExprGenBase.coerce_to_type(
builder, arg_val_sp, pty_sp, arg_node_sp)
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 类型不匹配)