Files
ViperC/App/lib/core/Gen/CallGenMixin.vp
T

1202 lines
81 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.Stub.StubGen.VpiReader as VpiReader
import lib.core.Gen.CallGenBase as CallGenBase
import lib.core.Handles.ExprCallTable as ExprCallTable
# ============================================================
# CallGenMixin - 调用分派 Mixin(拆分后顶层,阶段 A:底层已拆至
# CallGenBase/CallGenCBuiltin/CallGenLeafKit)
#
# 500~1500 行/文件架构重构阶段 A:底层自包含组(模块查找/返回类型解析/
# c.*/asm/llvmir/EH/isinstance/闭包/print/函数表)已拆出,本文件保留
# 调用分派中枢(translate_call 等)。继承链 CallGenMixin → LeafKit →
# CBuiltin → Base,@t.NoVTable 静态绑定沿 MRO 解析 self 调用。
#
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
# ============================================================
# ============================================================
# 常量(与 HandlesExprCall 保持一致,本地副本避免跨模块 CDefine 查找 bug)
# ============================================================
GLOBAL_FUNC_MAX: t.CDefine = 8192
GLOBAL_DEFAULTS_MAX: t.CDefine = 4096
_CEXPORT_MAX: t.CDefine = 256
ASM_MAX_OPERANDS: t.CDefine = 16
ASM_TEXT_BUF_SIZE: t.CDefine = 2048
ASM_CONST_BUF_SIZE: t.CDefine = 512
ASM_ARGS_BUF_SIZE: t.CDefine = 512
ASM_LINE_BUF_SIZE: t.CDefine = 4096
LLVMIR_MAX_OPS: t.CDefine = 16
LLVMIR_TEMPLATE_BUF: t.CDefine = 512
# 闭包名表大小(原 HandlesExprCall 模块级常量)
MAX_CLOSURE_NAMES: t.CDefine = 128
# 函数指针签名表大小(原 HandlesExprCall 模块级常量)
MAX_FUNC_PTR_SIGS: t.CDefine = 64
# isinstance 子类收集上限(防数组越界;t.CDefine 注解由 TPC 解析为 i32)
ISINSTANCE_CLASS_MAX: t.CDefine = 64
# ============================================================
# CallGenMixin - 调用分派中枢(顶层,继承 LeafKit)
# ============================================================
@t.NoVTable
class CallGenMixin(CallGenBase.CallGenBase):
"""调用分派 Mixin(拆分后顶层,直接继承 CallGenBase——TPV self 调用仅支持 1 级父类)"""
# ============================================================
# 翻译函数调用 Call(func, args) — 模块级版本
# ============================================================
def translate_call(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
funcs_ptr: t.CPtr,
func_count: int,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译函数调用,返回 call 结果 Value"""
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
if cl is None:
return None
func_name: str = self.Gen.ExprGenBase.get_func_name(cl.func)
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
can: t.CSizeT = 0
if cargs is not None:
can = len(cargs)
# super()/ctraits 检测:void 方法(如 __init__)命中后 build_call 返回
# None 属预期(void call 无 SSA 值),必须用 hit_box 区分"命中已处理"
# 与"未命中",否则 None 被误判继续主路径 → 内层 Call(super) 当裸名
# 调用报"super 未定义且未导入"(SuperTest 根因)。
h1_hit: t.CPtr = stdlib.malloc(4)
if h1_hit is not None:
h1_hit[0] = 0
_tc_h1_super_ctraits_res: llvmlite.Value | t.CPtr = self.Gen.CallGenDispatch._tc_h1_super_ctraits(
pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can, h1_hit)
h1_hit_val: int = 0
if h1_hit is not None:
h1_hit_val = h1_hit[0]
stdlib.free(h1_hit)
if _tc_h1_super_ctraits_res is not None:
return _tc_h1_super_ctraits_res
if h1_hit_val != 0:
# 命中 super()/ctraits 但无返回值(void 方法/宏调用):视为已处理
return None
# ---- func_name is None:泛型实例化 + 类型转换 ----
if func_name is None:
# 检查是否是 list[int](pool) 形式的泛型实例化调用
# Subscript 节点作为 Call func: 泛型类构造函数
if cl.func is not None and cl.func.kind() == ast.ASTKind.Subscript and trans is not None:
sub_node: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(cl.func)
if sub_node.value is not None and sub_node.value.kind() == ast.ASTKind.Name:
gen_nm_node: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub_node.value)
gen_class_name: str = gen_nm_node.id
# 检查是否是泛型类模板
template_cd: ast.ClassDef | t.CPtr = self.Gen.ClassGenGeneric._find_generic_template(gen_class_name)
if template_cd is not None:
# 提取类型实参
gen_type_args: list[str] | t.CPtr = self.Gen.CallGenCtor._extract_type_args_from_slice(pool, sub_node.slice)
if gen_type_args is not None and len(gen_type_args) > 0:
# 特化泛型类,返回特化类名
spec_name: str = self.Gen.ClassGenGeneric._specialize_generic_class(
trans, gen_class_name, gen_type_args)
if spec_name is not None:
# 泛型类构造调用: list[str](pool, 8) → __new__ + __before_init__ + __init__
# 不能直接用 spec_name 作为 func_name(会走裸名调用路径,
# 被 star import 回退误导用错误的 SHA1,且缺少 __new__ 方法名后缀)
return self.Gen.CallGenCtor._translate_generic_ctor_call(
pool, builder, mod, spec_name, cl, trans)
else:
# 泛型类模板未找到:可能是泛型函数手动传类型参数调用 xxx[T](x)
# (对应 TPC _HandleGenericCallExplicitLlvm;TPV 无泛型函数特化,
# 泛型函数按普通函数编译,类型参数 [T] 仅在函数定义处消费)。
# 检查当前模块是否已定义同名函数(def double[T] 编译后即普通函数
# double),若是则丢弃 [t.CInt] 显式类型参数,退回普通函数调用。
if self.find_func_in_module(mod, gen_class_name) is not None:
func_name = gen_class_name
else:
# 泛型类模板未找到:可能未 import 定义模块
gt_err: str = pool.alloc(256)
if gt_err is not None:
viperlib.snprintf(gt_err, 256, "泛型类 '%s' 未找到模板,可能未 import 定义模块", gen_class_name)
self.Gen.TypeGenErr.fatal_error(gen_nm_node, gt_err)
self.Gen.TypeGenErr.fatal_error(gen_nm_node, "泛型类模板未找到")
if func_name is None:
# 检查是否是 (A | t.CPtr)(value) 形式的类型转换
# BinOp 节点作为 Call func: 联合类型注解用作类型转换
if cl.func is not None and cl.func.kind() == ast.ASTKind.BinOp and trans is not None:
binop_ty: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, cl.func, trans._imported_modules, trans._from_imports, trans)
if binop_ty is not None:
binop_args: list[ast.AST | t.CPtr] | t.CPtr = cl.args
if binop_args is not None and len(binop_args) == 1:
binop_arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, binop_args.get(0), funcs_ptr, func_count, trans)
if binop_arg_val is not None:
return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, binop_arg_val, binop_ty)
return None
# c.Asm/c.LLVMIR 等特殊调用:void(无输出)时 translate_c_asm/llvmir
# 返回 None 表示"已处理无返回值",需用 hit_box 区分"未命中",
# 否则会误判未命中继续走外部函数路径(外部函数返回类型未登记 Asm +
# t.ASM_DESCR 被当 CDefine 常量赋值)
h2_hit: t.CPtr = stdlib.malloc(4)
if h2_hit is not None:
h2_hit[0] = 0
_tc_h2_c_special_res: llvmlite.Value | t.CPtr = self.Gen.CallGenDispatch._tc_h2_c_special(
pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can, h2_hit)
h2_hit_val: int = 0
if h2_hit is not None:
h2_hit_val = h2_hit[0]
stdlib.free(h2_hit)
if _tc_h2_c_special_res is not None:
return _tc_h2_c_special_res
if h2_hit_val != 0:
# 命中 c.Asm/c.LLVMIR 但无返回值(void):视为已处理
return None
_tc_h3_stdarg_cast_res: llvmlite.Value | t.CPtr = self.Gen.CallGenDispatch._tc_h3_stdarg_cast(
pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can)
if _tc_h3_stdarg_cast_res is not None:
return _tc_h3_stdarg_cast_res
_tc_h4_type_casts_res: llvmlite.Value | t.CPtr = self.Gen.CallGenDispatch._tc_h4_type_casts(
pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can)
if _tc_h4_type_casts_res is not None:
return _tc_h4_type_casts_res
_tc_h5_sizeof_len_isinst_res: llvmlite.Value | t.CPtr = self.Gen.CallGenDispatch._tc_h5_sizeof_len_isinst(
pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can)
if _tc_h5_sizeof_len_isinst_res is not None:
return _tc_h5_sizeof_len_isinst_res
# c.Asm/c.LLVMIR 等方法调用分派:方法调用识别成功但返回 None
# (如编译器生成方法 __before_init__/__init__ 恒返回 void,_translate_
# method_call 或 _emit_eh_guarded_call 处理后可能返回 None)时,须用
# hit_box 区分"已识别为方法调用"与"未命中",否则 translate_call 误判
# 未命中继续走跨模块函数路径,把对象名当模块名查 SHA1 →
# "跨模块调用 'buf.__before_init__' 找不到模块 SHA1"(memhub.alloc_buf 根因)。
h6_hit: t.CPtr = stdlib.malloc(4)
if h6_hit is not None:
h6_hit[0] = 0
_tc_h6_method_dispatch_res: llvmlite.Value | t.CPtr = self.Gen.CallGenDispatch._tc_h6_method_dispatch(
pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can, h6_hit)
h6_hit_val: int = 0
if h6_hit is not None:
h6_hit_val = h6_hit[0]
stdlib.free(h6_hit)
if _tc_h6_method_dispatch_res is not None:
return _tc_h6_method_dispatch_res
if h6_hit_val != 0:
# 命中方法调用路径但无返回值(编译器生成方法等 void 语义):视为已处理
return None
# 获取关键字参数
keywords: list[ast.AST | t.CPtr] | t.CPtr = cl.keywords
kw_count: t.CSizeT = 0
if keywords is not None:
kw_count = len(keywords)
# 检查是否是结构体构造函数: Point() / Point(10, 20) / Point(x=10, y=20)
# 命名空间隔离:严格模式下检查结构体可见性
# - 裸名 Class(): 需要 from X import Class 或本地定义
# - 模块限定 Module.Class(): 需要 import Module
# 用 find_struct_by_module 规避跨模块同名 find_struct 找错
struct_ty: llvmlite.LLVMType | t.CPtr = None
gen_ctor_entry: GenTypes.StructEntry | t.CPtr = None
vis: int = self.Gen.StructGen.is_struct_visible(func_name)
if vis != 0:
if trans is not None:
gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(func_name, trans.ModuleSha1)
if gen_ctor_entry is None:
gen_ctor_entry = self.Gen.StructGen.find_struct(func_name)
if gen_ctor_entry is not None:
struct_ty = gen_ctor_entry.Ty
elif trans is not None and cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at_qc: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if at_qc.value is not None and at_qc.value.kind() == ast.ASTKind.Name:
mod_nm_qc: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_qc.value)
imp_ok: int = self.Gen.ImportsGen.is_module_imported(trans._imported_modules, mod_nm_qc.id)
if imp_ok != 0:
if trans is not None:
gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(func_name, trans.ModuleSha1)
if gen_ctor_entry is None:
gen_ctor_entry = self.Gen.StructGen.find_struct(func_name)
if gen_ctor_entry is not None:
struct_ty = gen_ctor_entry.Ty
elif at_qc.value is not None and at_qc.value.kind() == ast.ASTKind.Attribute:
# 多层模块路径类构造: w32.fileio.File(...)(value 是嵌套 Attribute)。
# 仅两层(fileio.File)支持时,三层会被跳过 → 退化走跨模块普通函数
# 调用生成 @File 裸名 → 链接 undefined symbol: File(SysLibTest 根因)。
# 模块路径经 attr_module_path_str 递归拼接("w32.fileio"),
# import 检查用完整路径(import w32.fileio 注册 "w32.fileio"),
# 结构体查找用该模块自身 SHA1 精确命中(find_struct_by_module)。
# 注:经 HandlesExpr 委托调用(跨 mixin 不能 self. 直接调,
# @t.NoVTable 静态绑定会 mangle 成 CallGenMixin. 前缀 → undefined reference)。
mod_path_qc: str = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_qc.value)
if mod_path_qc is not None:
imp_ok_qc: int = self.Gen.ImportsGen.is_module_imported(
trans._imported_modules, mod_path_qc)
if imp_ok_qc != 0:
mod_sha1_qc: str = self._lookup_module_sha1(mod_path_qc)
if mod_sha1_qc is not None:
gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(
func_name, mod_sha1_qc)
if gen_ctor_entry is None:
gen_ctor_entry = self.Gen.StructGen.find_struct(func_name)
if gen_ctor_entry is not None:
struct_ty = gen_ctor_entry.Ty
# 别名导入的类构造: from X import Class as Alias → Alias(args)
# 可见性列表按别名注册(HandleImportFromNames 用 local_name=asname),但结构体
# 按原名注册(如 Keyword as KwNode → 可见 "KwNode"、注册名 "Keyword")。
# 直接按别名 find_struct 查不到 → 误走普通函数路径生成 @sha1.Keyword 调用
# (该函数不存在)→ 链接期 undefined reference。此处解析别名→原名再查结构体。
if gen_ctor_entry is None and trans is not None and cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
if trans._from_imports is not None:
ctor_sha1: str = self._lookup_mod_sha1(pool, trans._from_imports, func_name, 0)
if ctor_sha1 is not None:
ctor_orig: str = self.Gen.ImportsGen.lookup_from_import_original(
trans._from_imports, func_name)
if ctor_orig is not None:
# 复制到独立 null 终止缓冲区(from_imports 内部缓冲未终止)
co_len: t.CSizeT = 0
while ctor_orig[co_len] != ' ' and ctor_orig[co_len] != '\0':
co_len += 1
co_buf: str = pool.alloc(co_len + 1)
if co_buf is not None:
for co_i in range(co_len):
co_buf[co_i] = ctor_orig[co_i]
co_buf[co_len] = '\0'
# 原名结构体查找(可见性已由别名保证,原名无需再查)
gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(co_buf, ctor_sha1)
if gen_ctor_entry is None:
gen_ctor_entry = self.Gen.StructGen.find_struct(co_buf)
if gen_ctor_entry is not None:
struct_ty = gen_ctor_entry.Ty
func_name = co_buf
if struct_ty is not None:
if can == 0 and kw_count == 0:
# OOP 结构体:即使无参数也调用 __before_init__
# 用 gen_ctor_entry 规避跨模块同名 find_struct 找错
ctor_is_oop: int = 0
if gen_ctor_entry is not None:
ctor_is_oop = gen_ctor_entry.IsOOP
if ctor_is_oop != 0:
return self.Gen.CallGenCtor._translate_struct_ctor(
pool, builder, mod, struct_ty, func_name, cargs, 0, trans)
# @t.NoVTable 类(无方法、类语义/指针对象):无参构造也应分配内存
# 返回指针。否则按值结构体返回 ConstZero(struct_ty),LHS 联合注解
# (如 Plain|CPtr)存 0 指针 → p.v 解引用 NULL 段错误
# (TpvIsinstanceTest Plain 场景崩溃根因)。
ctor_is_novt: int = 0
if gen_ctor_entry is not None:
ctor_is_novt = gen_ctor_entry.IsNoVTable
if ctor_is_novt != 0:
return self.Gen.CallGenCtor._translate_struct_ctor(
pool, builder, mod, struct_ty, func_name, cargs, 0, trans)
# 普通结构体:检查是否有默认值(用 entry 直接访问)
gen_has_defaults: int = 0
if gen_ctor_entry is not None:
for gdi in range(gen_ctor_entry.FieldCount):
gdf: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen._get_field_entry(
gen_ctor_entry, gdi)
if gdf is not None and gdf.DefaultVal is not None:
gen_has_defaults = 1
break
else:
gen_has_defaults = self.Gen.StructGen.struct_has_defaults(func_name)
if gen_has_defaults == 1:
# 有默认值:走构造函数路径应用默认值
return self.Gen.CallGenCtor._translate_struct_ctor(
pool, builder, mod, struct_ty, func_name, cargs, 0, trans)
# 无默认值:返回 zeroinitializer
ret: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, struct_ty)
return ret
if kw_count > 0:
# 有关键字参数:按字段名赋值
return self.Gen.CallGenCtor._translate_struct_ctor_kw(
pool, builder, mod, struct_ty, func_name, cargs, can,
keywords, kw_count, trans)
# 仅位置参数:按字段顺序构建
return self.Gen.CallGenCtor._translate_struct_ctor(
pool, builder, mod, struct_ty, func_name, cargs, can, trans)
# 裸名泛型类模板构造: Box(42) → 从实参推断类型实参 → 特化 → 构造
# (TPC 参考: _HandleGenericClassNewLlvm;GenericTest Test4 Box(42)/Test5
# Pair(10,20)/Test10 Counter(0) 依赖此路径。显式类型实参 list[int](mb)
# 走 translate_call 上方的 Subscript 分支,此处处理裸名 + 推断。)
# 支持模块限定调用(vector.Vector(...)):func_name 是 attr("Vector"),
# 需确认模块已导入(模板查找兜底——普通函数如 stdlib.free 的 attr 找
# 不到模板 → 回落普通调用)。
if struct_ty is None and gen_ctor_entry is None and trans is not None:
is_gen_bare: int = 0
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
is_gen_bare = 1
elif cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at_gb: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if at_gb.value is not None and at_gb.value.kind() == ast.ASTKind.Name:
am_gb: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_gb.value)
if am_gb.id is not None:
if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, am_gb.id) != 0:
is_gen_bare = 1
if is_gen_bare != 0:
template_cd_bare: ast.ClassDef | t.CPtr = self.Gen.ClassGenGeneric._find_generic_template(func_name)
if template_cd_bare is not None and template_cd_bare.type_params is not None:
tp_bare: list[str] | t.CPtr = template_cd_bare.type_params
tp_count_bare: t.CSizeT = len(tp_bare)
if tp_count_bare > 0:
inferred_args_bare: list[str] | t.CPtr = list[str](pool, 8)
if inferred_args_bare is not None:
# 对齐 TPC _HandleGenericClassNewLlvm:先收集全部实参
# 推断类型,过滤纯 int(排除 pool 等非 T 参数),
# hint_types 优先填充(Vector(pool,8,CFloat64T(0.0))
# → Vector[double];Vector(pool,10,CInt(0)) → Vector[int])
hint_bare: list[str] | t.CPtr = list[str](pool, 8)
if hint_bare is not None:
hi_bare: t.CSizeT
for hi_bare in range(can):
hty_bare: str = self.Gen.CallGenCtor._infer_generic_ctor_type_arg(pool, cargs.get(hi_bare), func_name)
if string.strcmp(hty_bare, "int") != 0:
hint_bare.append(hty_bare)
tpi_bare: t.CSizeT = 0
for tpi_bare in range(tp_count_bare):
ta_bare: str = "int"
if tpi_bare < len(hint_bare):
ta_bare = hint_bare.get(tpi_bare)
elif tpi_bare < can:
ta_bare = self.Gen.CallGenCtor._infer_generic_ctor_type_arg(pool, cargs.get(tpi_bare), func_name)
inferred_args_bare.append(ta_bare)
spec_name_bare: str = self.Gen.ClassGenGeneric._specialize_generic_class(
trans, func_name, inferred_args_bare)
if spec_name_bare is not None:
return self.Gen.CallGenCtor._translate_generic_ctor_call(
pool, builder, mod, spec_name_bare, cl, trans)
# REnum 变体构造检测: ClassName.VariantName(args...)
# 如 LLVMType.Int(1) → 构造 LLVMType 的 Int 变体
# 必须在跨模块调用查找之前,否则 ClassName 会被误判为模块名
# 对应 TPC HandlesExprCall.py:585-644 的三条检测路径之一
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
rc_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if rc_at.value is not None and rc_at.value.kind() == ast.ASTKind.Name:
rc_cls_node: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rc_at.value)
if rc_cls_node.id is not None:
rc_cls_name: str = rc_cls_node.id
# 检查 ClassName 是否是已注册的 REnum 类
if self.Gen.ClassGenEnum._is_renum_class_name(rc_cls_name) != 0:
# 在该 REnum 类中查找变体名 func_name
rc_tag: int = self.Gen.ClassGenEnum._lookup_renum_variant_in_class(
rc_cls_name, func_name)
if rc_tag >= 0:
return self.Gen.CallGenCtor._translate_renum_construct(
pool, builder, mod, rc_cls_name, func_name,
rc_tag, cargs, can, trans)
elif rc_at.value is not None and rc_at.value.kind() == ast.ASTKind.Attribute:
# 模块限定 REnum 变体构造: mod.Class.Variant(args...)
# 如 llvmlite.LLVMType.Struct(...) → 构造 llvmlite 模块 LLVMType 的 Struct 变体。
# 根因:此前只支持单层 Name(ClassName.Variant),嵌套属性漏检 → 误走跨模块
# 函数路径 → _lookup_module_sha1("llvmlite.LLVMType") 查无 → 报"跨模块调用
# 'llvmlite.LLVMType.Struct' 找不到模块 SHA1"。跨模块任意同名可接受(REnum
# 表按类名全局注册):仅要求最左模块已导入 + 末段类名在全局 REnum 表中命中,
# 不做"类必须属于该模块"校验(未命中则按后续函数路径 fast-fail)。
rc_mq_path: str = self.Gen.ExprGenAttr._attr_module_path_str(pool, rc_at.value)
if rc_mq_path is not None:
rc_mq_dot: str = string.strrchr(rc_mq_path, 46) # 46 = ord('.')
if rc_mq_dot is not None and rc_mq_dot[1] != '\0':
# 模块路径 = 末段之前(支持多级: w32.fileio.Class.Variant)。
# 注意:不能用 rc_mq_dot - rc_mq_path 指针减法(TPC 直接发
# sub i8*, i8* 非法 IR),用 strlen 整数差扣除末段与分隔点。
rc_mq_len: t.CSizeT = string.strlen(rc_mq_path) - string.strlen(rc_mq_dot + 1) - 1
rc_mq_mod: str = pool.alloc(rc_mq_len + 1)
if rc_mq_mod is not None:
string.memcpy(rc_mq_mod, rc_mq_path, rc_mq_len)
rc_mq_mod[rc_mq_len] = '\0'
rc_mq_imp: int = 1
if trans is not None and trans._imported_modules is not None:
rc_mq_imp = self.Gen.ImportsGen.is_module_imported(
trans._imported_modules, rc_mq_mod)
if rc_mq_imp != 0:
rc_mq_cls: str = rc_mq_dot + 1
# 检查类名是否是已注册的 REnum 类
if self.Gen.ClassGenEnum._is_renum_class_name(rc_mq_cls) != 0:
rc_mq_tag: int = self.Gen.ClassGenEnum._lookup_renum_variant_in_class(
rc_mq_cls, func_name)
if rc_mq_tag >= 0:
return self.Gen.CallGenCtor._translate_renum_construct(
pool, builder, mod, rc_mq_cls, func_name,
rc_mq_tag, cargs, can, trans)
# async 裸名调用(TPV-M1/M2):普通函数调用 async 函数(如 task(5, 6))→
# 生成 create_<name>(args...) 调用(返回 Ptr(帧),bitcast 成 i8* 供变量存储)。
# 对齐 TPC HandlesAsyncFunctions._HandleAsyncCreateCallLlvm。必须在裸名
# 函数查找之前拦截——async 函数只注册了 create_/resume_,裸名 task 不在
# 函数表,走普通路径会报"裸名调用未定义且未导入"。
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
if func_name is not None and trans is not None:
if self.Gen.AsyncGen.is_async_func(trans, func_name) != 0:
can_buf: str = pool.alloc(64)
if can_buf is not None:
viperlib.snprintf(can_buf, 64, "create_%s", func_name)
create_an: str = self.Gen.FuncGenBase._mangle_func_name(
trans, can_buf, 0)
create_fn_an: llvmlite.Function | t.CPtr = self.find_func_in_module(
mod, create_an)
if create_fn_an is not None:
frame_ty_an: llvmlite.LLVMType | t.CPtr = self.Gen.AsyncGen._lookup_coro_frame(
trans, func_name)
if frame_ty_an is not None:
ret_ptr_ty_an: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
pool, frame_ty_an)
i8_ptr_ty_an: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
pool, llvmlite.Int8(pool))
# M2:async 调用参数(task(5, 6) → create_task(5, 6))
arg_head_an: llvmlite.Value | t.CPtr = None
arg_tail_an: llvmlite.Value | t.CPtr = None
arg_cnt_an: int = 0
if cl.args is not None:
nb_an: t.CSizeT = len(cl.args)
j_an: t.CSizeT = 0
for j_an in range(nb_an):
av_an: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, cl.args.get(j_an),
None, 0, trans)
if av_an is None:
av_an = llvmlite.const_int32(pool, 0)
av_an.Next = None
if arg_head_an is None:
arg_head_an = av_an
arg_tail_an = av_an
else:
arg_tail_an.Next = av_an
arg_tail_an = av_an
arg_cnt_an = arg_cnt_an + 1
child_an: llvmlite.Value | t.CPtr = llvmlite.build_call(
builder, create_an, arg_head_an, arg_cnt_an,
ret_ptr_ty_an, 0)
if child_an is not None:
return llvmlite.build_bitcast(builder, child_an, i8_ptr_ty_an)
return llvmlite.const_int32(pool, 0)
# 查找函数:优先从函数表按裸名精确匹配(同模块函数),
# 再从模块函数链表按后缀匹配(SHA1 前缀名)。
# 注意:模块限定/属性调用(如 stdlib.free)不能做后缀匹配——
# "free" 会误命中 viperio.Buf.free 等方法,导致普通 malloc 指针被
# 当作 Buf 对象释放(Buf.free 读 self 字段/写 self.data),
# 传入句柄数组等指针时把句柄值当地址写入直接崩溃。
# 后缀匹配仅用于裸名调用(cl.func 是 Name,如 _thread_alloc_free)。
# stubs 在翻译后才注入,翻译时跨模块函数 found_func 通常为 None,
# 此时根据函数名推断 includes 库函数的返回类型(如 strchr 返回 i8*)
# 如果调用方未传 funcs_ptr/func_count(如 AnnAssign 路径),从 trans 获取
actual_funcs: GenTypes.FuncEntry | t.CPtr = funcs_ptr
actual_func_count: int = func_count
if actual_funcs is None and trans is not None:
actual_funcs = trans._funcs
actual_func_count = trans._func_count
found_func: llvmlite.Function | t.CPtr = None
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
# 裸名调用:允许精确 + 后缀匹配
# 自定义装饰器 wrapper 重写:原函数名 → __decor_wrap_{name}
# (对齐 TPC DecoratorPass.run 的 _redirect_calls;wrapper 在函数
# 翻译末尾生成,同 pass 内 main 调用点晚于被装饰函数定义,可达)
decor_wrap_nm: str = self.Gen.ClassGenBase.lookup_decor_wrapper(func_name)
if decor_wrap_nm is not None:
found_func = self.find_func_in_module(mod, decor_wrap_nm)
else:
found_func = self.find_func_in_module(mod, func_name)
# 治本修复(对齐 Attribute 路径 L6579-6613):后缀匹配误命中类方法
# (SHA1.Class.method,第 2 段是已注册结构体类)必须拒绝——否则方法体内
# 与 from-import 模块函数同名的裸名调用(如 LLVMModuleCore.SSAValue 内
# 的 SSAValue(self.Pool, ...))绑定到本类方法 define → 无限递归
# (栈溢出 0xC0000005)。裸名调用应优先解析为模块级函数
# (同模块函数表 / from_imports),方法调用需显式 obj.method。
if found_func is not None:
bn_name: str = llvmlite.function_get_name(found_func)
if bn_name is not None:
bn_dots: int = 0
bni2: t.CSizeT = 0
while bn_name[bni2] != '\0':
if bn_name[bni2] == '.':
bn_dots += 1
bni2 += 1
if bn_dots >= 2:
# 提取第 2 段(SHA1.Class.method → Class;SHA1.submod.func → submod)
bn_seg2: str = pool.alloc(64)
if bn_seg2 is not None:
bn_seg2[0] = '\0'
bsi2: t.CSizeT = 0
while bn_name[bsi2] != '\0' and bsi2 < 17:
bsi2 += 1
bse2: t.CSizeT = bsi2
while bn_name[bse2] != '\0' and bn_name[bse2] != '.':
bse2 += 1
bsl2: t.CSizeT = bse2 - bsi2
if bsl2 > 0 and bsl2 < 63:
string.strncpy(bn_seg2, bn_name + bsi2, bsl2)
bn_seg2[bsl2] = '\0'
# 第 2 段是已注册结构体类 → 是方法,拒绝(走模块 SHA1 路径)
if self.Gen.StructGen.get_struct_type(bn_seg2) is not None:
found_func = None
else:
# 模块限定/属性调用:精确匹配优先,后缀匹配仅接受模块函数。
# 后缀匹配命中的"方法"(SHA1.Class.method,Class 是已注册类)必须拒绝——
# 短方法名(free/reset/close 等)会误命中其他模块的方法
# (如 stdlib.free 命中 viperio.Buf.free,普通指针被当 Buf 释放崩溃;
# 且 atom.__atomic_test_and_set 若被误拒,返回类型从 i1 推断成 i32,
# 调用按 i32 读 i1 返回值读到垃圾高位,spinlock 恒非 0 卡死)。
# 模块函数(SHA1.func)或子模块函数(SHA1.submod.func)的后缀匹配保留。
found_func = self.find_func_in_module(mod, func_name)
if found_func is not None:
# 模块限定调用(mod.func,value 是已导入模块名):校验 found_func
# 的 SHA1 前缀必须等于该模块。否则 find_func_in_module 第 3 遍对
# stub declare 的后缀匹配会误命中其他模块的同名函数(如 main 模块
# 里 subprocess 的 declare 先注入,syslibtest_impl.run() 后缀匹配
# 误命中 subprocess.run → call 到错误符号 → 参数错位解引用崩溃,
# SysLibTest 0xC0000005 根因)。误命中时拒绝(found_func=None),
# 落到下方跨模块模块限定解析用模块自身 SHA1 构造正确符号。
xm_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if xm_at is not None and xm_at.value is not None and trans is not None \
and trans._imported_modules is not None:
# 模块路径:单层 Name(syslibtest_impl.run)或嵌套 Attribute
# (os._win32.exists)。后者此前不校验 → os/__init__.vp 内部
# 调 os._win32.exists 后缀匹配命中本模块同名 define →
# 无限递归栈溢出(SysLibTest 0xC00000FD 根因)。
xm_mod_path: str = None
if xm_at.value.kind() == ast.ASTKind.Name:
xm_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(xm_at.value)
if xm_nm.id is not None:
xm_mod_path = xm_nm.id
elif xm_at.value.kind() == ast.ASTKind.Attribute:
xm_mod_path = self.Gen.ExprGenAttr._attr_module_path_str(
pool, xm_at.value)
if xm_mod_path is not None:
if self.Gen.ImportsGen.is_module_imported(
trans._imported_modules, xm_mod_path) != 0:
mod_sha1_chk: str = self._lookup_module_sha1(xm_mod_path)
if mod_sha1_chk is not None:
ff_nm2: str = llvmlite.function_get_name(found_func)
if ff_nm2 is not None:
if string.strncmp(ff_nm2, mod_sha1_chk, 16) != 0:
found_func = None
ff_name: str = llvmlite.function_get_name(found_func)
if ff_name is not None:
ff_dots: int = 0
ffi2: t.CSizeT = 0
while ff_name[ffi2] != '\0':
if ff_name[ffi2] == '.':
ff_dots += 1
ffi2 += 1
if ff_dots >= 2:
# 提取第 2 段(SHA1.Class.method → Class;SHA1.submod.func → submod)
seg2: str = pool.alloc(64)
if seg2 is not None:
seg2[0] = '\0'
si: t.CSizeT = 0
while ff_name[si] != '\0' and si < 17:
si += 1
seg_start: t.CSizeT = si
seg_end: t.CSizeT = si
while ff_name[seg_end] != '\0' and ff_name[seg_end] != '.':
seg_end += 1
seg_len: t.CSizeT = seg_end - seg_start
if seg_len > 0 and seg_len < 63:
string.strncpy(seg2, ff_name + seg_start, seg_len)
seg2[seg_len] = '\0'
# 第 2 段是已注册结构体类 → 是方法,拒绝(走模块 SHA1 路径)
if self.Gen.StructGen.get_struct_type(seg2) is not None:
found_func = None
# C 库函数裸名调用支持:若模块中找不到且是已知 C 库函数,自动创建 declare
# 对应 TPC 的 _C_LIB_FUNCS 机制,支持 memset/memmove/memcmp/strcmp 等裸名调用
if found_func is None and cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
found_func = self.Gen.CallGenLib._ensure_c_lib_declare(pool, mod, func_name)
# builtins 自动关联:C 库未命中时,自动关联 builtins 模块的内置函数(ord/chr 等)。
# builtins 已由编译链预加入(StubMerger 预加入列表)。PhaseA 期 builtins 尚未编译,
# 因此按约定签名创建 {builtins_sha1}.{name} 的 mangled declare(链接时解析到
# builtins.vp 的真实定义);签名与 builtins.vp 保持一致:ord(i8)->i32, chr(i32)->i8。
if found_func is None:
bi_sha1: str = self._lookup_module_sha1("builtins")
if bi_sha1 is not None:
bi_ret: llvmlite.LLVMType | t.CPtr = None
bi_p1: llvmlite.LLVMType | t.CPtr = None
if string.strcmp(func_name, "ord") == 0:
bi_ret = llvmlite.Int32(pool)
bi_p1 = llvmlite.Int8(pool)
elif string.strcmp(func_name, "chr") == 0:
bi_ret = llvmlite.Int8(pool)
bi_p1 = llvmlite.Int32(pool)
if bi_ret is not None:
bi_sym: str = func_name
if self.Gen.CallGenBase.is_cexport_func(bi_sha1, func_name) == 0:
bi_sym_len: t.CSizeT = string.strlen(bi_sha1) + string.strlen(func_name) + 2
bi_sym_buf: str = pool.alloc(bi_sym_len)
if bi_sym_buf is not None:
viperlib.snprintf(bi_sym_buf, bi_sym_len, "%s.%s", bi_sha1, func_name)
bi_sym = bi_sym_buf
bi_decl: llvmlite.Function | t.CPtr = llvmlite.create_declare(
pool, mod, bi_sym, bi_ret)
if bi_decl is not None:
llvmlite.add_param(pool, bi_decl, bi_p1, "v")
found_func = bi_decl
# 使用访问器绕过 stub 类型限制
found_ret_ty: llvmlite.LLVMType | t.CPtr = None
found_params: t.CPtr = None
if found_func is not None:
found_ret_ty = llvmlite.function_get_ret_ty(found_func)
found_params = llvmlite.function_get_params(found_func)
if found_ret_ty is not None:
call_ret_ty: llvmlite.LLVMType | t.CPtr = found_ret_ty
else:
# found_func=None(跨模块 stub 未注入):先从 vpi stub 返回注解
# 缓存精确获取返回类型(含 tuple[T1,T2] → 匿名结构体,如
# b2a_hqx -> tuple[str, t.CUnsignedShort] → {i8*, i16}),
# 未命中才回退 _infer_external_func_ret_ty 白名单推断。
# 根因:_infer 白名单无 tuple 概念,未知函数默认 i32 会截断
# 指针/i64;b2a_hqx 被推断为单值 i8*/i32 后调用侧返回值类型
# 与定义侧 {i8*, i16} 不匹配 → HandlesAssign 的 tuple 解包
# (依赖 rhs_val.Ty 是 Struct)静默跳过 → 目标变量从未赋值 →
# free 未初始化栈垃圾指针崩溃(HashLibTest hqx 段)。
vpi_ret_ty: llvmlite.LLVMType | t.CPtr = None
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at_ret: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if at_ret.value is not None and at_ret.value.kind() == ast.ASTKind.Name:
mod_ret_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_ret.value)
if mod_ret_nm.id is not None:
mod_ret_sha1: str = self._lookup_module_sha1(mod_ret_nm.id)
if mod_ret_sha1 is not None:
# 函数内懒 import:VpiReader imports HandlesExprCall,
# 顶层 import 会成环(与 _lookup_method_ret_ty_via_vpi 一致)
VpiReader._ensure_vpi_ret_annot_loaded(mod_ret_sha1, self.Gen.CallGenBase.TempDir)
ret_annot_fc: str = VpiReader._lookup_vpi_ret_annot(
mod_ret_sha1, None, func_name)
if ret_annot_fc is not None:
vpi_ret_ty = VpiReader._resolve_vpi_ret_annot_str(
pool, ret_annot_fc,
llvmlite.Int32(pool),
llvmlite.Ptr(pool, llvmlite.Int8(pool)))
if vpi_ret_ty is not None:
call_ret_ty = vpi_ret_ty
else:
call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name)
# 如果模块中找不到函数,仅裸名调用(Name)才回退查局部变量(函数指针/
# 闭包调用);模块属性调用(VLogger.fmt_buf())的目标是模块符号,绝不
# 回退局部变量——跨模块任意同名可接受(CallGenLeafKit 局部 fmt_buf
# 曾劫持 VLogger.fmt_buf → 误走函数指针调用报 t.Callable 缺失),
# 仅 from-import 的裸名 yyy 与局部重名才可能冲突(该场景 func 是 Name)。
if found_func is None:
fp_alloca: llvmlite.Value | t.CPtr = None
if cl.func is None or cl.func.kind() == ast.ASTKind.Name:
fp_alloca = self.Gen.VarGen.lookup_var(trans.SymTab, func_name)
if fp_alloca is not None:
# 闭包变量(嵌套函数提升,已注册到闭包名集合):提取 fn_ptr/env_ptr 间接调用
if self.Gen.CallGenLeafKit.is_closure_name(func_name) != 0:
return self.Gen.CallGenLeafKit.translate_closure_call(pool, builder, mod, fp_alloca,
cargs, can, trans)
# 函数指针变量(t.Callable 注解 → i8** alloca):按实参构造签名间接调用
if self.Gen.CallGenLeafKit._is_func_ptr_alloca(fp_alloca) != 0:
# 区分 t.Callable(真函数指针)vs t.CPtr(闭包指针):
# t.CPtr 变量(如 f: t.CPtr = make_counter())存的是闭包结构
# {fn_ptr, env_ptr},调用必须提取 env 传参;若按函数指针直接
# call,跳转到闭包结构数据地址崩溃(ClosureCallTest 运行时崩溃)。
ve_fp: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, func_name)
is_cptr_annot_fp: int = 0
if ve_fp is not None and ve_fp.AnnotClassName is not None:
if string.strstr(ve_fp.AnnotClassName, "CPtr") is not None:
is_cptr_annot_fp = 1
if is_cptr_annot_fp == 0:
return self.Gen.CallGenLeafKit._translate_func_ptr_call(pool, builder, mod, fp_alloca,
func_name, cargs, can, trans)
return self.Gen.CallGenLeafKit.translate_closure_call(pool, builder, mod, fp_alloca,
cargs, can, trans)
# 其他 i8* 变量按闭包调用路径处理
return self.Gen.CallGenLeafKit.translate_closure_call(pool, builder, mod, fp_alloca,
cargs, can, trans)
# 翻译参数并链接
# 有关键字参数且找到函数定义时,按参数名匹配
if kw_count > 0 and found_func is not None and found_params is not None:
return self.Gen.CallGenLib._translate_call_with_kwargs(
pool, builder, mod, func_name, found_func,
cargs, can, keywords, kw_count, trans)
head: llvmlite.Value | t.CPtr = None
tail: llvmlite.Value | t.CPtr = None
# memcpy 专用: 收集前三个 Value 的独立引用(避免共享 alloca 对象的 Next 被污染)
mc_dst2: llvmlite.Value | t.CPtr = None
mc_src2: llvmlite.Value | t.CPtr = None
mc_num2: llvmlite.Value | t.CPtr = None
for ai in range(can):
arg: ast.AST | t.CPtr = cargs.get(ai)
if arg is not None:
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg, None, 0, trans)
if arg_val is not None:
# 参数类型提升:当 found_func 为 None(stub 未注入)时,
# 根据外部函数签名推断期望类型,提升字面量(如 i32→i64)
# 避免 declare(i64) 与 call(i32) 类型不匹配
if found_func is None and arg_val.Ty is not None:
# 数组实参按 C 语义衰减为元素指针(对齐 TPC
# _decay_array_to_ptr),与 _translate_method_call 一致。
if self.Gen.ExprGenBase.is_ptr_type(arg_val.Ty) != 0:
arr_pointee_fc: llvmlite.LLVMType | t.CPtr = arg_val.Ty.Pointee
if arr_pointee_fc is not None:
match arr_pointee_fc:
case llvmlite.LLVMType.Array(arr_et_fc, _):
decayed_fc: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, arr_pointee_fc, arr_et_fc, arg_val,
llvmlite.const_int32(pool, 0))
if decayed_fc is not None:
arg_val = decayed_fc
case _:
pass
ext_param_ty: llvmlite.LLVMType | t.CPtr = self.Gen.CallGenLib._get_external_func_param_ty(
pool, func_name, ai)
if ext_param_ty is not None:
val_bits_ap: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_val.Ty)
tgt_bits_ap: int = self.Gen.ExprGenBase.get_llvm_type_bits(ext_param_ty)
if val_bits_ap != 0 and tgt_bits_ap != 0 and val_bits_ap != tgt_bits_ap:
if val_bits_ap == 1 and val_bits_ap < tgt_bits_ap:
arg_val = llvmlite.build_zext(builder, arg_val, ext_param_ty)
else:
arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, ext_param_ty, arg)
elif found_func is not None:
# found_func 存在:按形参类型 coerce(如 stdlib.malloc(size: t.CSizeT))
# 修复: CDefine/i32 常量传给 i64 形参未提升,导致 call(i32) 与 define(i64) 不匹配
fp: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func)
fpi: int = 0
while fp is not None and fpi < ai:
fp = llvmlite.param_get_next(fp)
fpi += 1
if fp is not None:
fpty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(fp)
if fpty is not None:
arg_val = self.Gen.ExprGenBase.coerce_to_type(
builder, arg_val, fpty, arg)
elif found_func.IsVarArg == 1:
# Win64 变参 8 字节槽位:<64 位整数按 i64 传(sext 符号扩展),
# f32 提升为 f64。若按原窄类型传,调用点只写 4 字节进 8 字节槽,
# 高 4 字节残留旧栈数据,va_arg(i64) 读到垃圾(如 DefineTest
# PAGE=%d 读到 0x400921FB00001000,高位是 PI 残留)。
# 对齐 TPC FuncGen/Python-llvmlite 的 ABI 自动提升。
ab_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_val.Ty)
af_va: int = self.Gen.ExprGenBase.get_llvm_float_bits(arg_val.Ty)
i64_va: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
if ab_va != 0 and ab_va < 64:
arg_val = llvmlite.build_sext(builder, arg_val, i64_va)
elif af_va == 32:
arg_val = llvmlite.build_fpext(
builder, arg_val, llvmlite.Double(pool))
arg_val.Next = None
if head is None:
head = arg_val
else:
tail.Next = arg_val
tail = arg_val
# memcpy 专用: 保存前三个参数的独立引用
if ai == 0:
mc_dst2 = arg_val
elif ai == 1:
mc_src2 = arg_val
elif ai == 2:
mc_num2 = arg_val
# 默认参数填充:当实参数量 < 形参数量时,用默认值填充缺失参数
# defaults 数组右对齐到 args:defaults[0] 对应 args[offset],offset = param_count - default_count
if can > 0 or (can == 0 and cargs is not None):
df_defaults: t.CVoid | t.CPtr = None
df_default_count: int = 0
df_param_count: int = 0
# 先从当前模块函数表查找 FuncEntry
if trans is not None and trans._funcs is not None:
df_entry: GenTypes.FuncEntry | t.CPtr = ExprCallTable.find_func_entry_in_table(
trans._funcs, trans._func_count, func_name)
if df_entry is not None:
df_defaults = df_entry.Defaults
df_default_count = df_entry.DefaultCount
df_param_count = df_entry.ParamCount
# 无条件查询全局默认参数表(跨模块/同模块都要用 stdlib 常量快照
# 重建默认值:模块表 Defaults 指向 pool 内 AST,跨 1b reset 后悬空;
# 快照才是跨文件永久存活。同模块命中时 gd_entry 也必须已赋值,
# 否则下方 _build_const_from_snapshot 读到未初始化栈垃圾指针崩溃。
# 与 _translate_method_call 的 mdf_gd 无条件查询(L2677)保持一致。
gd_entry: GenTypes.FuncDefaultsEntry | t.CPtr = self.Gen.CallGenLeafKit.find_func_defaults(func_name)
if df_defaults is None and gd_entry is not None:
df_defaults = gd_entry.Defaults
df_default_count = gd_entry.DefaultCount
df_param_count = gd_entry.ParamCount
# 填充缺失参数
if df_defaults is not None and df_default_count > 0 and can < df_param_count:
df_offset: int = df_param_count - df_default_count
df_di: int = 0
while df_di < df_default_count:
df_param_idx: int = df_offset + df_di
if df_param_idx >= can:
# 优先用 stdlib 常量快照重建(gd_entry.Defaults 的 pool 内 AST 可能已被 reset 回收)
df_arg_val: llvmlite.Value | t.CPtr = None
if gd_entry is not None:
df_arg_val = self.Gen.CallGenLeafKit._build_const_from_snapshot(
pool, mod, gd_entry, df_di, trans, builder)
if df_arg_val is None:
df_defaults_list: list[ast.AST | t.CPtr] | t.CPtr = (list[ast.AST | t.CPtr] | t.CPtr)(t.CVoid(t.CUInt64T(df_defaults), t.CPtr))
df_default_node: ast.AST | t.CPtr = df_defaults_list.get(df_di)
if df_default_node is not None:
df_arg_val = self.Gen.ExprGen.translate_value(
builder, pool, mod, df_default_node, None, 0, trans)
if df_arg_val is not None:
df_arg_val.Next = None
if head is None:
head = df_arg_val
else:
tail.Next = df_arg_val
tail = df_arg_val
df_di += 1
# 使用模块中的实际函数名(可能带 SHA1 前缀,如 "83210a72e829a086.atoi")
# t.CExtern/t.State/t.CExport 函数不加 SHA1 前缀,使用裸名
# declare 和 define 统一用 found_func.Name:
# - 普通函数的 declare/define 都带 SHA1 前缀(forward_declare_functions 和 stub 注入都用 mangled_name)
# - t.CExtern/t.State/t.CExport 函数的 declare/define 都用裸名
# 旧逻辑(IsDeclared==1 时用 func_name 裸名)是错误的:stub 注入的 declare Name 已带 SHA1 前缀,
# 用裸名会导致 call @_PathToModuleName 而 define 是 @"sha1._PathToModuleName",链接报 undefined reference
call_name: str = func_name
# 原始函数名(别名导入时记录,如 from X import parse as json_parse → orig_func_name="parse")
# 用于在别名处理后重新推断返回类型,避免 _infer_external_func_ret_ty 用别名匹配失败
orig_func_name: str = None
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 时,检查是否是模块限定调用
# 如 string.strchr(...) → @{sha1_of_string}.strchr
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
xmod_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if xmod_at.value is not None and (xmod_at.value.kind() == ast.ASTKind.Name
or xmod_at.value.kind() == ast.ASTKind.Attribute):
# 模块路径:单层 Name(string.strchr)或嵌套 Attribute
# (os._win32.exists)。嵌套路径此前不支持 → os/__init__.vp
# 内部调 os._win32.exists 只能被 find_func_in_module 后缀匹配
# 命中本模块同名 define(无限递归),或找不到模块路径而走
# 错误 fallback;此处用完整模块路径解析正确 SHA1(与校验段
# L7344 对齐,SysLibTest 0xC00000FD 根因)。
xmod_path: str = None
if xmod_at.value.kind() == ast.ASTKind.Name:
xmod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(xmod_at.value)
if xmod_nm.id is not None:
xmod_path = xmod_nm.id
else:
xmod_path = self.Gen.ExprGenAttr._attr_module_path_str(pool, xmod_at.value)
if xmod_path is not None:
mod_sha1: str = self._lookup_module_sha1(xmod_path)
if mod_sha1 is not None:
# fast-fail: 模块限定调用要求模块已被当前文件 import。
# 全局 SHA1 存储包含所有被扫描的 includes,未导入的模块
# (如 this.this() 但 import this 被注释)也会命中存储,
# 静默生成 @{sha1}.func 调用而非报错——违反"找不到 fast-fail"原则。
is_xmod_imported: int = 1
if trans is not None and trans._imported_modules is not None:
is_xmod_imported = self.Gen.ImportsGen.is_module_imported(
trans._imported_modules, xmod_path)
if is_xmod_imported == 0:
err_ni: str = pool.alloc(256)
if err_ni is not None:
viperlib.snprintf(err_ni, 256,
"跨模块调用 '%s.%s':模块 '%s' 未导入(请先 import %s)",
xmod_path, func_name, xmod_path, xmod_path)
self.Gen.TypeGenErr.fatal_error(xmod_at, err_ni)
self.Gen.TypeGenErr.fatal_error(xmod_at, "跨模块调用:模块未导入")
# 检查是否是 re-export 函数(如 ast._lexer_init 实际定义在 ast.lexer)
reexport_src: str = self.Gen.ImportsGen.lookup_reexport(mod_sha1, func_name)
if reexport_src is None:
# re-export 表未命中(includes 模块 stub/text 已存在时
# 不被按需全量翻译,表只在 declare_only=0 时注册,可能为空):
# 按需解析该模块 .vpi 的 from-import 定位定义模块
reexport_src = self._lookup_reexport_from_vpi(pool, mod_sha1, func_name)
if reexport_src is not None:
reexport_sha1: str = self._lookup_module_sha1(reexport_src)
if reexport_sha1 is not None:
mod_sha1 = reexport_sha1
# CExport 函数定义用裸名(@strlen),跨模块调用需用裸名而非 @{sha1}.func
if self.Gen.CallGenBase.is_cexport_func(mod_sha1, func_name) != 0:
call_name = func_name
elif self._is_state_func_from_vpi(pool, mod_sha1, func_name) != 0:
# t.State 标记的外部 C 函数(如 CPython PyList_Size):
# 定义端用裸名 @PyList_Size(链接外部库),跨模块调用
# 必须用裸名——带 sha1 前缀 → ld.lld undefined symbol
call_name = func_name
else:
mangled_len: t.CSizeT = string.strlen(mod_sha1) + string.strlen(func_name) + 2
mangled: str = pool.alloc(mangled_len)
if mangled is not None:
viperlib.snprintf(mangled, mangled_len, "%s.%s", mod_sha1, func_name)
call_name = mangled
else:
# t 和 c 是内部库,豁免 SHA1 查找:使用裸名调用
if string.strcmp(xmod_path, "t") == 0 or string.strcmp(xmod_path, "c") == 0:
call_name = func_name
else:
# Bug 修复: 先检查是否是已导入的模块名(如 BuildPipeline.run_pipeline)
# 如果是已导入模块但 SHA1 未注册,说明该模块未被编译(如 Phase2 未扫描到),
# 应该立即报错,而不是错误地走 from_imports 查找(会被 star import 回退误导)
is_imported_mod: int = 0
if trans is not None and trans._imported_modules is not None:
is_imported_mod = self.Gen.ImportsGen.is_module_imported(
trans._imported_modules, xmod_path)
if is_imported_mod != 0:
# 别名导入(import X as Y):Y 在 _imported_modules 中,
# 但 _lookup_module_sha1("Y") 精确匹配失败(mod_arr 只存文件名)。
# 先尝试 from_imports 查找别名→完整模块名→后缀匹配 SHA1。
# allow_star_fallback=0 避免被 star import 回退误导。
alias_sha1: str = None
if trans is not None and trans._from_imports is not None:
alias_sha1 = self._lookup_mod_sha1(pool, trans._from_imports, xmod_path, 0)
if alias_sha1 is not None:
if self.Gen.CallGenBase.is_cexport_func(alias_sha1, func_name) != 0:
call_name = func_name
elif self._is_state_func_from_vpi(pool, alias_sha1, func_name) != 0:
# t.State 外部 C 函数:裸名(同主路径 L7918)
call_name = func_name
else:
AliasMangledLen: t.CSizeT = string.strlen(alias_sha1) + string.strlen(func_name) + 2
alias_mangled: str = pool.alloc(AliasMangledLen)
if alias_mangled is not None:
viperlib.snprintf(alias_mangled, AliasMangledLen, "%s.%s", alias_sha1, func_name)
call_name = alias_mangled
else:
# 已导入模块但 SHA1 未注册:该模块未被编译,立即报错
err_msg_imp: str = pool.alloc(256)
if err_msg_imp is not None:
viperlib.snprintf(err_msg_imp, 256, "模块 '%s' 已导入但 SHA1 未注册(该模块未被编译)", xmod_path)
self.Gen.TypeGenErr.fatal_error(xmod_at, err_msg_imp)
self.Gen.TypeGenErr.fatal_error(xmod_at, "已导入模块 SHA1 未注册")
else:
# 检查是否是通过 from import 导入的名称(如 from .__types import LLVMType)
# LLVMType.Ptr(ty) 中 LLVMType 是类名不是模块名,
# 但 Ptr 是 __types 模块的模块级函数
# 禁用 star import 回退:xmod_path 是明确名称,不应回退到 star import 模块
from_sha1: str = None
if trans is not None and trans._from_imports is not None:
from_sha1 = self._lookup_mod_sha1(pool, trans._from_imports, xmod_path, 0)
if from_sha1 is not None:
# CExport 函数定义用裸名,跨模块调用需用裸名
if self.Gen.CallGenBase.is_cexport_func(from_sha1, func_name) != 0:
call_name = func_name
else:
fmangled_len: t.CSizeT = string.strlen(from_sha1) + string.strlen(func_name) + 2
fmangled: str = pool.alloc(fmangled_len)
if fmangled is not None:
viperlib.snprintf(fmangled, fmangled_len, "%s.%s", from_sha1, func_name)
call_name = fmangled
else:
err_msg2: str = pool.alloc(256)
if err_msg2 is not None:
viperlib.snprintf(err_msg2, 256, "跨模块调用 '%s.%s' 找不到模块 SHA1", xmod_path, func_name)
self.Gen.TypeGenErr.fatal_error(xmod_at, err_msg2)
self.Gen.TypeGenErr.fatal_error(xmod_at, "跨模块调用找不到模块 SHA1")
else:
# 裸名调用(非 Attribute):检查 from_imports
# 治本修复:函数调用禁用 star import 回退(allow_star_fallback=0)。
# star import(from stdint import *)本意是导入类型/常量(如 BYTEPTR),而非函数;
# 函数应显式 from import。漏导函数若走 star 回退会被错归到 star 模块,生成错误 SHA1 符号
# (如 block_insert_text_before_terminator 漏导后被错归 stdint,生成
# f5522571bcce7bcb.block_insert... 而非 874bac141c97bae9.block_insert...)。
# 禁用回退后,漏导函数编译期即报"未定义且未导入"(fast-fail),优于链接期 undefined reference。
if trans is not None and trans._from_imports is not None:
bare_sha1: str = self._lookup_mod_sha1(pool, trans._from_imports, func_name, 0)
if bare_sha1 is not None:
# 别名处理: from X import Y as Z → 调用 Z 时需用 Y 作为源模块函数名
# 例如 from json.__parser import parse as json_parse → 修饰为 SHA1.parse 而非 SHA1.json_parse
bare_orig: str = self.Gen.ImportsGen.lookup_from_import_original(
trans._from_imports, func_name)
bare_func_name: str = func_name
if bare_orig is not None:
# bare_orig 指向 from_imports 内部缓冲区,未 null 终止
# 需复制到独立缓冲区(截断于空格或 null)
bo_len: t.CSizeT = 0
while bare_orig[bo_len] != ' ' and bare_orig[bo_len] != '\0':
bo_len += 1
bo_buf: str = pool.alloc(bo_len + 1)
if bo_buf is not None:
for bo_i in range(bo_len):
bo_buf[bo_i] = bare_orig[bo_i]
bo_buf[bo_len] = '\0'
bare_func_name = bo_buf
# 记录原始函数名,供后续重新推断返回类型
# 例如 from json.__parser import parse as json_parse
# func_name="json_parse", bare_func_name="parse"
# _infer_external_func_ret_ty 用 "parse" 能正确匹配,用 "json_parse" 则返回默认 i32
orig_func_name = bare_func_name
# re-export 解析(对齐模块限定路径 L6598):
# 裸名 from import 的函数可能实际定义在子模块(re-export)。
# 例:from ast import dump → dump 定义在 ast.base(sha1=71b2),
# ast/__init__(sha1=876e)只是 re-export。若沿用包 sha1 构造
# call_name="876e.dump",text.ll/vpi 返回类型解析均未命中,
# fail-fast 报"返回类型无法精确获取";需重定向到定义模块 SHA1。
reexport_src2: str = self.Gen.ImportsGen.lookup_reexport(bare_sha1, bare_func_name)
if reexport_src2 is None:
# re-export 表未命中:按需解析该模块 .vpi 的 from-import
reexport_src2 = self._lookup_reexport_from_vpi(pool, bare_sha1, bare_func_name)
if reexport_src2 is not None:
reexport_sha1_2: str = self._lookup_module_sha1(reexport_src2)
if reexport_sha1_2 is not None:
bare_sha1 = reexport_sha1_2
if self.Gen.CallGenBase.is_cexport_func(bare_sha1, bare_func_name) != 0:
call_name = bare_func_name
else:
bare_mangled_len: t.CSizeT = string.strlen(bare_sha1) + string.strlen(bare_func_name) + 2
bare_mangled: str = pool.alloc(bare_mangled_len)
if bare_mangled is not None:
viperlib.snprintf(bare_mangled, bare_mangled_len, "%s.%s", bare_sha1, bare_func_name)
call_name = bare_mangled
else:
err_msg3: str = pool.alloc(256)
if err_msg3 is not None:
viperlib.snprintf(err_msg3, 256, "裸名调用 '%s' 未定义且未导入", func_name)
self.Gen.TypeGenErr.fatal_error(node, err_msg3)
self.Gen.TypeGenErr.fatal_error(node, "裸名调用未定义且未导入")
else:
err_msg4: str = pool.alloc(256)
if err_msg4 is not None:
viperlib.snprintf(err_msg4, 256, "裸名调用 '%s' 未定义且未导入", func_name)
self.Gen.TypeGenErr.fatal_error(node, err_msg4)
self.Gen.TypeGenErr.fatal_error(node, "裸名调用未定义且未导入")
# 直接调用
# memcpy → @llvm.memcpy 内联函数(避免 monomorphization 跨模块 @memcpy 声明缺失)
if string.strcmp(func_name, "memcpy") == 0 and can >= 3:
return self.Gen.CallGenLib._emit_llvm_memcpy_intrinsic(pool, builder, mod, mc_dst2, mc_src2, mc_num2)
# 别名导入重新推断返回类型:
# from X import parse as json_parse → func_name="json_parse" 时 _infer_external_func_ret_ty 返回默认 i32
# 用 orig_func_name="parse" 重新推断,能正确匹配返回 i8*(JsonValue*)
# 避免指针被截断为 i32 后 inttoptr 丢失高 32 位导致崩溃
if orig_func_name is not None and orig_func_name != func_name:
re_inferred_ty: llvmlite.LLVMType | t.CPtr = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, orig_func_name)
if re_inferred_ty is not None:
call_ret_ty = re_inferred_ty
# ============================================================
# 跨模块 Viper 函数返回类型精确解析(对齐方法路径,fail-fast)
# 根因:_infer_external_func_ret_ty 白名单未覆盖返回指针的模块函数
# (new_lexer→Lexer* / tokenize→Token* / parse_tokens→AST*),兜底 i32
# 导致 64 位指针被截断高 32 位 → inttoptr 无效指针 → 解引用崩溃
# (AstTest 的 _lexer_init 崩溃链)。declare 也可能是错误 i32(_ensure_method_declare
# 曾用兜底类型创建),因此不信任 found_func 的 declare 返回类型。
# 权威来源:text.ll define;次选:vpi stub 返回注解;均未命中立即终止。
# 仅对 mangled("{16位hex}.func")调用生效;C 库/Win32 裸名走原白名单。
# Phase 1a(declare_only)期间依赖 text.ll 未生成,保持原白名单行为不 fail-fast。
# ============================================================
if self.Gen.CallGenLib._is_sha1_mangled_call(call_name) != 0:
if trans is None or trans._declare_only == 0:
cf_dot: str = string.strchr(call_name, '.')
cf_last: str = string.strrchr(call_name, '.')
if cf_dot is not None and cf_last is not None:
cf_sha1: str = stdlib.malloc(17)
cf_bare_len: t.CSizeT = string.strlen(call_name) - \
(t.CSizeT(t.CUInt64T(cf_last) - t.CUInt64T(call_name))) - 1
cf_bare: str = stdlib.malloc(cf_bare_len + 1)
if cf_sha1 is not None and cf_bare is not None:
string.strncpy(cf_sha1, call_name, 16)
cf_sha1[16] = '\0'
string.strcpy(cf_bare, cf_last + 1)
# 本模块自身函数(泛型特化等):从当前 IR 已声明的函数
# 取返回类型;不走跨模块 text.ll 查找——自己的 text.ll
# 在 Phase A 翻译中尚未生成,直接查会误报"依赖未生成"
# 并保持默认类型(GenericTest 泛型 add/combine 等警告
# 根因)。未命中(特化尚未声明)保持默认推断。
is_local_mod: int = 0
if trans is not None and trans.ModuleSha1 is not None:
if string.strncmp(cf_sha1, trans.ModuleSha1, 16) == 0:
is_local_mod = 1
if is_local_mod != 0:
local_fn: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, call_name)
if local_fn is not None and local_fn.RetTy is not None:
call_ret_ty = local_fn.RetTy
else:
cf_ret: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL(
pool, cf_sha1, None, cf_bare, 1)
if cf_ret is not None:
call_ret_ty = cf_ret
else:
cf_vpi: llvmlite.LLVMType | t.CPtr = self._lookup_method_ret_ty_via_vpi(
pool, cf_sha1, None, cf_bare)
if cf_vpi is not None:
call_ret_ty = cf_vpi
else:
# 区分两种失败场景:
# 1. 依赖模块 text.ll 存在但函数未命中 → 真错误,fail-fast
# 2. 依赖模块 text.ll 不存在(模块尚未翻译)→ Phase A 按
# 文件顺序翻译,main.vp 先于其 import 的 mbuddytest.vp
# (同项目多源文件)。此时标记 trans._deps_pending=1,
# 保持白名单类型继续,Phase A 多遍循环下遍重译——
# 届时依赖已生成 text.ll,可精确解析(治本:依赖先翻译)。
if self._dep_text_ll_exists(pool, cf_sha1) != 0:
cf_err: str = VLogger.fmt_buf()
if cf_err is not None:
viperlib.snprintf(
cf_err, 1024,
"跨模块函数 '%s' 返回类型无法精确获取(text.ll/vpi 均未命中),"
"默认 i32 会截断指针/i64,请检查依赖模块是否已编译/注册", call_name)
VLogger.error(cf_err, "HandlesExprCall")
self.Gen.TypeGenErr.fatal_error(
node,
f"跨模块函数返回类型无法精确获取(text.ll/vpi 均未命中),默认 i32 会截断指针: {call_name}",
"TypeError")
else:
if trans is not None:
trans._deps_pending = 1
cf_dbg: str = VLogger.fmt_buf()
if cf_dbg is not None:
viperlib.snprintf(
cf_dbg, 1024,
"跨模块函数 '%s' 依赖模块 text.ll 尚未生成(Phase A 顺序),"
"标记重试,本遍保持默认类型", call_name)
if VLogger.nowarn_register() == 0:
VLogger.warning(cf_dbg, "HandlesExprCall")
stdlib.free(cf_sha1)
stdlib.free(cf_bare)
# 跨模块调用:创建 declare 声明,避免 llc 报 undefined value
self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, can)
# 检查被调用函数是否为变长函数(*args),若是则对变长参数进行 C ABI 提升
# (cargs 为本函数参数 AST 链表,self_offset=0 普通函数调用)
is_variadic_call: int = self.Gen.CallGenVa._check_and_promote_variadic_call(
pool, builder, mod, call_name, head, can, cargs, 0, trans)
# try-except:被调函数带 EH 输出参数时,追加输出实参 + 调用后检测异常
return self.Gen.CallGenLeafKit._emit_eh_guarded_call(
builder, pool, mod, trans, found_func, call_name, head, can,
call_ret_ty, is_variadic_call)