Files
ViperC/App/lib/core/Gen/Call/CallGenMixin.vp
T
TermiNexus 15b416b527 fix(type): 统一注入期类归属与导入上下文记账,修占位表爆与属性标记误解析
- StubMergerStore 新增共享导入上下文记账(AppendImportedModule / AppendFromImport /
  ImportCtx / AllocImportCtx / NotePlainImport),ImportsGenMixin 与 VpiReader 共用同一份
  口径;顺带移除 add_from_import 的 256 缓冲静默截断,VpiReader 删除两份私有副本

- 注入窗口按「名字归属」判据解析(is_module_class + CurDefModuleSha1):
  _forward_declare_ptr_from_node 的 Name 分支、resolve_annotation_type 的 Name/Attribute
  分支均补齐,使同一 stub 内先声明/先引用的类产出**具名占位**而不是 Ptr(i8),并新增
  _is_tattr_marker 让 t.attr.* 属性标记不参与类型联合竞争

- VpiReader._inject_class_struct_declare:命中前向声明占位时不再早退,改为「占位 Ty
  原地写入」(对齐 ClassGenDef.fill_fwd),修 StructTable 爆表(table full, cannot
  register Translator)

- 清除全部一次性探针:[DBG-GEN](VpiReader / StructGenMixin / ClassGenDef)、
  [DBG-ATTR](ExprGenAttr)、[MODSHA1](CallGenMixin)

根因记录:vpi 注入期漏记 `import X as Y -> from_imports "Y:X"`,使 GenTypes.VarEntry.Spec
(TSpec.TypeSpec | t.CPtr)取不到模块 SHA1、退化为 i8* 并被永久冻结
(CallGenCtor.vp:897 .PtrLevel 属性访问失败)。
2026-10-02 17:29:38 +08:00

1595 lines
119 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 viperlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.core.Gen.Type.TypeSpec as TSpec
import lib.Stub.StubGen.VpiReader as VpiReader
from . import CallGenBase
import lib.core.Gen.GenApi as GenApi
# ============================================================
# CallGenMixin - 调用分派 Mixin(拆分后顶层,阶段 A:底层已拆至
# ============================================================
# ============================================================
# 常量(与 TPC 侧 HandlesExprCall 保持一致)
# ============================================================
define _CEXPORT_MAX = 256
define ASM_MAX_OPERANDS = 16
define ASM_TEXT_BUF_SIZE = 2048
define ASM_CONST_BUF_SIZE = 512
define ASM_ARGS_BUF_SIZE = 512
define ASM_LINE_BUF_SIZE = 4096
define LLVMIR_MAX_OPS = 16
define LLVMIR_TEMPLATE_BUF = 512
# ============================================================
# CallGenMixin - 调用分派中枢(顶层,继承 LeafKit)
# ============================================================
novirtual class CallGenMixin(CallGenBase.CallGenBase):
"""调用分派 Mixin(拆分后顶层,直接继承 CallGenBase——TPV self 调用仅支持 1 级父类)"""
# ============================================================
# _CheckStaticCallCrossModule - static 符号跨模块**调用**拦截(`mod.fn(...)`)
#
# 命中别的模块登记的 static 函数即 fail-fast。放在 translate_call 入口按 AST
# 形态统一拦截:后续分派有 found_func 命中 / vpi 解析 / 裸名兜底多条分支,
# 只在某一分支检查会漏。基座不是已导入模块(self.method / obj.method /
# Class.static_method)时放行;同模块自引用(sha1 相同)放行。
# 独立成函数:translate_call 极大,内联该检查会干扰其类型推断。
# ============================================================
def _CheckStaticCallCrossModule(self, pool: memhub.MemBuddy*,
trans: HT.Translator*,
cl: ast.Call*) -> int:
if cl is None or cl.func is None or trans is None:
return 0
if cl.func.kind() != ast.ASTKind.Attribute:
return 0
if trans.ModuleSha1 is None or trans._imported_modules is None:
return 0
# 快速短路:项目内无 static 符号时零开销(不查 sha1、不扫表)
if self.Gen.CallGenBase.StaticCount <= 0:
return 0
sc_at: ast.Attribute* = ast.Attribute*(cl.func)
if sc_at.value is None or sc_at.attr is None:
return 0
sc_mod: str = None
if sc_at.value.kind() == ast.ASTKind.Name:
sc_nm: ast.Name* = ast.Name*(sc_at.value)
sc_mod = sc_nm.id
elif sc_at.value.kind() == ast.ASTKind.Attribute:
sc_mod = self.Gen.ExprGenAttr._attr_module_path_str(pool, sc_at.value)
if sc_mod is None:
return 0
if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, sc_mod) == 0:
return 0
sc_sha1: str = self.Gen.StructGen._resolve_module_sha1(sc_mod, trans._from_imports)
if sc_sha1 is None:
return 0
if string.strncmp(sc_sha1, trans.ModuleSha1, 16) == 0:
return 0
if self.Gen.CallGenBase.IsStaticSymbol(sc_sha1, sc_at.attr) == 0:
return 0
self.Gen.TypeGenErr.fatal_error(cl,
f"'{sc_mod}.{sc_at.attr}' 是 static 符号,禁止跨模块使用(static 仅限定义模块内可见)",
"NameError")
return 0
# ============================================================
# 翻译函数调用 Call(func, args) — 模块级版本
# ============================================================
def translate_call(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
funcs_ptr: t.CPtr,
func_count: int,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""翻译函数调用,返回 call 结果 Value"""
cl: ast.Call* = ast.Call*(node)
# static 符号(禁止跨模块使用):`mod.fn(...)` 命中别的模块登记的 static 函数
# 即 fail-fast。逻辑收敛在 _CheckStaticCallCrossModule 内——本函数极大,
# 内联该检查(新增分支 + 跨函数调用)会干扰其类型推断。
self._CheckStaticCallCrossModule(pool, trans, cl)
if cl is None: return None
func_name: str = self.Gen.ExprGenBase.get_func_name(cl.func)
cargs: list[ast.AST*]* = cl.args
can: sizet = 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* = 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* = ast.Subscript*(cl.func)
if sub_node.value is not None and sub_node.value.kind() == ast.ASTKind.Name:
gen_nm_node: ast.Name* = ast.Name*(sub_node.value)
gen_class_name: str = gen_nm_node.id
# 检查是否是泛型类模板
template_cd: ast.ClassDef* = self.Gen.ClassGenGeneric._find_generic_template(gen_class_name)
if template_cd is not None:
# 提取类型实参
gen_type_args: list[str]* = self.Gen.CallGenCtor._extract_type_args_from_slice(pool, sub_node.slice, trans._from_imports)
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__ 方法名后缀)
# 末参传模板名:关键字实参需按 __init__ 形参名定位槽位
return self.Gen.CallGenCtor._translate_generic_ctor_call(pool, builder, mod, spec_name, cl, trans, gen_class_name)
else:
# 泛型类模板未找到:可能是泛型函数手动传类型参数调用 xxx[T](x)
# (对应 TPC _HandleGenericCallExplicitLlvm;TPV 无泛型函数特化,
# 泛型函数按普通函数编译,类型参数 [T] 仅在函数定义处消费)。
# 检查当前模块是否已定义同名函数(def double[T] 编译后即普通函数
# double),若是则丢弃 [int] 显式类型参数,退回普通函数调用。
if self.find_func_in_module(mod, gen_class_name) is not None:
func_name = gen_class_name
else:
# 内建指针/类型下标 cast:(rawptr[list[int]])(lst)、
# (t.CPtr[T])(x)、(t.CArray[i8, N])(x) —— rawptr/CPtr/
# CArray 不是泛型类模板,是类型注解下标。先按注解解析出
# 类型再单参转换(ast/base.vp L392 ops_list 行根因)。
if cl.args is not None and len(cl.args) == 1:
sub_cast_ty: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, cl.func, trans._imported_modules, trans._from_imports, trans, 0)
if sub_cast_ty is not None:
sub_cast_arg: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cl.args.get(0), funcs_ptr, func_count, trans)
if sub_cast_arg is not None:
return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, sub_cast_arg, sub_cast_ty,
cl.args.get(0), trans)
# 泛型类模板未找到:可能未 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* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, cl.func, trans._imported_modules, trans._from_imports, trans, 0)
if binop_ty is not None:
binop_args: list[ast.AST*]* = cl.args
if binop_args is not None and len(binop_args) == 1:
binop_arg_val: llvmlite.Value* = 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* = 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* = 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* = 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* = 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* = 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*]* = cl.keywords
kw_count: sizet = 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* = None
gen_ctor_entry: GenTypes.StructEntry* = None
# 模块限定构造 Module.Class(...) 优先:按模块名解析 SHA1 精确命中类。
# 必须在裸名可见性分支之前——跨模块同名类(VKernel gfx 与 App gfx 都
# 定义 Scene3D)存在时,裸名 find_struct 返回先注册的错误 entry
# (0ae → ctor 生成 @0ae.Scene3D.__before_init__ → 链接 undefined
# symbol,Scene3D 根因)。模块解析成功即锁定,跳过下方裸名回退。
mod_qual_done: int = 0
if trans is not None and cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at_qc0: ast.Attribute* = ast.Attribute*(cl.func)
mod_path0: str = None
if at_qc0.value is not None and at_qc0.value.kind() == ast.ASTKind.Name:
mod_nm0: ast.Name* = ast.Name*(at_qc0.value)
if mod_nm0.id is not None:
mod_path0 = mod_nm0.id
elif at_qc0.value is not None and at_qc0.value.kind() == ast.ASTKind.Attribute:
mod_path0 = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_qc0.value)
if mod_path0 is not None:
if self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, mod_path0) != 0:
mod_sha1_0: str = self._lookup_module_sha1(mod_path0)
if mod_sha1_0 is not None:
gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(func_name, mod_sha1_0)
mod_qual_done = 1
vis: int = self.Gen.StructGen.is_struct_visible(func_name)
if mod_qual_done == 0 and 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* = ast.Attribute*(cl.func)
if at_qc.value is not None and at_qc.value.kind() == ast.ASTKind.Name:
mod_nm_qc: ast.Name* = ast.Name*(at_qc.value)
imp_ok: int = self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, mod_nm_qc.id)
if imp_ok != 0:
# 模块限定构造 Module.Class(...):按模块名解析 SHA1 精确命中类。
# 不能用 trans.ModuleSha1(那是当前文件 sha1,类在 gfx 模块)——
# find_struct_by_module 落空后裸名回退 find_struct 会命中跨模块
# 同名类中先注册的 entry(VKernel gfx 与 App gfx 都定义 Scene3D,
# 0ae 先注册 → ctor 生成 @0ae.Scene3D.__before_init__ → 链接
# undefined symbol,Scene3D 根因)。
mod_sha1_qc2: str = None
if mod_nm_qc.id is not None:
mod_sha1_qc2 = self._lookup_module_sha1(mod_nm_qc.id)
if mod_sha1_qc2 is not None:
gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(func_name, mod_sha1_qc2)
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: sizet = 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, sizet(0), trans, cl.IsVolatile)
# @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, sizet(0), trans, cl.IsVolatile)
# 普通结构体:检查是否有默认值(用 entry 直接访问)
gen_has_defaults: int = 0
if gen_ctor_entry is not None:
for gdi in range(gen_ctor_entry.FieldCount):
gdf: GenTypes.FieldEntry* = 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, sizet(0), trans, cl.IsVolatile)
# 无默认值:返回 zeroinitializer
ret: llvmlite.Value* = 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, cl.IsVolatile)
return self.Gen.CallGenCtor._translate_struct_ctor(pool, builder, mod, struct_ty, func_name, cargs, can, trans, cl.IsVolatile)
# 裸名泛型类模板构造: 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* = ast.Attribute*(cl.func)
if at_gb.value is not None and at_gb.value.kind() == ast.ASTKind.Name:
am_gb: ast.Name* = ast.Name*(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* = 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]* = template_cd_bare.type_params
tp_count_bare: sizet = len(tp_bare)
if tp_count_bare > 0:
inferred_args_bare: list[str]* = list[str](pool, 8)
if inferred_args_bare is not None:
# 类型形参 → 位置实参对齐:取 __init__ 签名中注解为该类型形参的形参位次(Vector[T].__init__(self, pool, capacity, _hint: T) ⇒ T 对齐第 3 个位置实参)。不能按「类型形参序号 = 实参序号」硬对齐(第 1 个实参常是 pool/size 等非 T 参数);签名不可得(无 __init__/无 T 注解形参)时退化为同一序号。[C①] 注解上下文提供的类型实参优先级:显式 [...] > 位置/关键字实参推断 > 注解上下文 > fail-fast,仅当本次构造就是注解赋值/return 的直接值(identity 判定)时才返回非 None
ctx_args_bare: list[str]* = self.Gen.CallGenCtor._infer_ctor_type_args_from_ctx(pool, trans, func_name, int(tp_count_bare), cl)
tpi_bare: sizet = 0
for tpi_bare in range(tp_count_bare):
aip_bare: int = self.Gen.CallGenCtor._find_ctor_type_param_arg_index(func_name, tp_bare.get(tpi_bare))
if aip_bare < 0:
aip_bare = int(tpi_bare)
ais_bare: sizet = sizet(aip_bare)
# [D25 fail-fast] 禁止静默默认 "int":此前 ta_bare 初值为
# "int",在「hint 为空且位置实参也不足」时会凭空特化成
# list[int]/Box[int] 等错特化 —— 元素 4 字节、方法返回 i32,
# 与真实的指针元素不匹配 ⇒ 后续 inttoptr 截断(D21/D25 崩溃族)。
# 与 _infer_generic_ctor_type_arg 的注释「绝不默认 int」对齐,
# 改为 fail-fast 并指明显式标注写法。
ta_bare: str = None
if ais_bare < can:
ta_bare = self.Gen.CallGenCtor._infer_generic_ctor_type_arg(pool, cargs.get(ais_bare), func_name, trans)
if ta_bare is None:
# [C②] 仅关键字实参(Box(v=42)):按 __init__ 形参名
# 把该类型形参对应的关键字实参找出来再推断(关键字乱序
# 也能对齐;该形参位已被位置实参占用时返回 -1)。
kwi_bare: int = self.Gen.CallGenCtor._find_ctor_type_param_kw_arg_index(func_name, tp_bare.get(tpi_bare), keywords, kw_count, int(can))
if kwi_bare >= 0:
kwn_bare: rawptr[ast.Keyword] = rawptr[ast.Keyword](keywords.get(sizet(kwi_bare)))
if kwn_bare is not None and kwn_bare.value is not None:
ta_bare = self.Gen.CallGenCtor._infer_generic_ctor_type_arg(pool, kwn_bare.value, func_name, trans)
if ta_bare is None and ctx_args_bare is not None:
# [C①] 注解已显式给出类型实参(如
# s: Stack[int] = Stack())⇒ 用它特化,而非 fail-fast
ta_bare = ctx_args_bare.get(tpi_bare)
if ta_bare is None:
self.Gen.TypeGenErr.fatal_error(
cl,
f"无法从实参推断泛型类 '{func_name}' 的第 {tpi_bare + 1} 个类型实参,"
f"禁止静默默认 int(会产生 list[int] 等错误特化 → 元素按 4 字节/i32 处理 → 指针截断崩溃);"
f"请为该实参标注类型或显式标注如 {func_name}[类型](...)",
"TypeError")
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, func_name)
# 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* = ast.Attribute*(cl.func)
if rc_at.value is not None and rc_at.value.kind() == ast.ASTKind.Name:
rc_cls_node: ast.Name* = ast.Name*(rc_at.value)
if rc_cls_node.id is not None:
rc_cls_name: str = rc_cls_node.id
# 检查 ClassName 是否是已注册的 REnum 类
# [D32] 并上 _is_renum_by_entry(struct 带 __tag 即判 REnum):
# 变体表因任何原因缺失时,入口至少不被表的空状态挡住;
# 若 tag 仍查不到则下方 fail-fast 报出表状态(不再落到
# "外部函数返回类型未登记 ... Metadata" 这种误导报错)。
if self.Gen.ClassGenEnum._is_renum_class_name(rc_cls_name) != 0 \
or self.Gen.ClassGenEnum._is_renum_by_entry(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:
# [D32 fail-fast] 是 REnum 却查不到变体名 ⇒ 变体表不完整,
# 继续走外部函数路径会报无关的 "外部函数返回类型未登记"。
self.Gen.TypeGenErr.fatal_error(
cl,
f"REnum 类 '{rc_cls_name}' 的变体 '{func_name}' 未在变体表中找到"
f"({self.Gen.ClassGenEnum.RenumTableDump()});"
f"变体表未注册或变体名不匹配",
"NameError")
if rc_tag >= 0:
return self.Gen.CallGenCtor._translate_renum_construct(pool, builder, mod, rc_cls_name, func_name, rc_tag, cargs, can, trans, cl.IsVolatile)
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: sizet = 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, cl.IsVolatile)
# 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* = self.find_func_in_module(mod, create_an)
if create_fn_an is not None:
frame_ty_an: llvmlite.LLVMType* = self.Gen.AsyncGen._lookup_coro_frame(trans, func_name)
if frame_ty_an is not None:
ret_ptr_ty_an: llvmlite.LLVMType* = llvmlite.Ptr(pool, frame_ty_an)
i8_ptr_ty_an: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
# M2:async 调用参数(task(5, 6) → create_task(5, 6))
arg_head_an: llvmlite.Value* = None
arg_tail_an: llvmlite.Value* = None
arg_cnt_an: int = 0
if cl.args is not None:
nb_an: sizet = len(cl.args)
j_an: sizet = 0
for j_an in range(nb_an):
av_an: llvmlite.Value* = 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* = 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* = 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* = 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:
# 本模块已定义函数名的裸名调用:先按裸名在模块函数表(FuncEntry 数组,forward_declare_functions 已把所有模块级函数以 Name=裸名、Func=其 declare/define 登记进 trans._funcs)精确命中,再回落 find_func_in_module 的 LLVM 模块链表扫描。函数表才是同模块函数名的权威注册表(链表扫描漏掉本模块定义时会误走 C 库自动 declare / from_imports 回退)。模块表只含模块级函数(无方法),CExport 条目 Func 名即裸名,复用不改变既有符号语义
mod_fn_tb: llvmlite.Function* = None
if actual_funcs is not None:
# 同模块函数优先:_funcs 表在 forward_declare_functions 之前
# 已被 LoadDepVpiIntoModule 注入的依赖模块 stub 污染——外来
# 同名函数(如 lexer.vp 的 _advance)以裸名注册在表前部,
# 普通按名查找从 0 号槽先命中它 → 本模块 _advance 的 163 处
# 裸名调用全被绑成 lexer 版(AstTest 根因)。传 trans.ModuleSha1
# 使表查找优先返回 mangled 名为 "{ModuleSha1}.{name}" 的本模块
# 条目(forward_declare 已把本模块全部模块级函数登记进表)。
tbl_owner_sha1: str = None
if trans is not None:
tbl_owner_sha1 = trans.ModuleSha1
mod_fn_tb = self.Gen.CallGenLeafKit.find_func_in_table(actual_funcs, actual_func_count, func_name, tbl_owner_sha1)
if mod_fn_tb is not None:
found_func = mod_fn_tb
else:
# 表内无同名条目(如本模块函数未登记/表被清空)时回退扫
# FuncHead。同样传 owner_sha1:本模块同名前向 declare 尚未
# 激活(IsDeclared==1)时 pass2 会跳过、pass3 会按链表顺序
# 误绑排在前面的跨模块 stub declare,前缀优先扫描兜住该时序。
ff_owner_sha1: str = None
if trans is not None:
ff_owner_sha1 = trans.ModuleSha1
found_func = self.find_func_in_module(mod, func_name, ff_owner_sha1)
# 治本修复(对齐 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: sizet = 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: sizet = 0
while bn_name[bsi2] != '\0' and bsi2 < 17:
bsi2 += 1
bse2: sizet = bsi2
while bn_name[bse2] != '\0' and bn_name[bse2] != '.':
bse2 += 1
bsl2: sizet = bse2 - bsi2
if bsl2 > 0 and bsl2 < 63:
string.strncpy(bn_seg2, bn_name + bsi2, bsl2)
bn_seg2[bsl2] = '\0'
# 第 2 段是已注册结构体类 → 是方法,拒绝(走模块 SHA1 路径)。
# 类未注册时(Phase A 顺序)同样拒绝:{sha1}.X.Y 的 X
# 既非已注册类又非已导入模块 → 一定是方法(对齐下方
# Attribute 分支 L660 修复,防裸名误绑未注册类方法)。
bn_seg2_cls: int = 0
if self.Gen.StructGen.get_struct_type(bn_seg2) is not None:
bn_seg2_cls = 1
bn_seg2_mod: int = 0
if trans is not None and trans._imported_modules is not None:
if self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, bn_seg2) != 0:
bn_seg2_mod = 1
if bn_seg2_cls != 0 or bn_seg2_mod == 0:
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* = ast.Attribute*(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* = ast.Name*(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_or_known(trans._imported_modules, xm_mod_path) != 0:
mod_sha1_chk: str = self._lookup_module_sha1(xm_mod_path)
if mod_sha1_chk is None and trans._from_imports is not None:
# import X as Y(如 import fat32_diskio as diskio):
# 精确查不到别名,必须按别名还原后再查(精确→后缀→
# LookupModNameByAlias 三级)。否则本校验被跳过 →
# find_func_in_module 后缀匹配误命中本模块同名函数
# (diskio.read 命中 fat32 自身 read)→ call 到错误
# 符号 + 参数错位 + define 被 4 参调用污染(VKernel
# llc "instruction expected to be numbered" 根因)。
mod_sha1_chk = self.Gen.StructGen._resolve_module_sha1(xm_mod_path, trans._from_imports)
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: sizet = 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: sizet = 0
while ff_name[si] != '\0' and si < 17:
si += 1
seg_start: sizet = si
seg_end: sizet = si
while ff_name[seg_end] != '\0' and ff_name[seg_end] != '.':
seg_end += 1
seg_len: sizet = 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 路径)。
# 类未注册时(Phase A 顺序:定义模块尚未翻译,如
# ClassGenEnum 编译时 VLogger.Logger 未注册)也必须拒绝:
# 3 段名 {sha1}.X.Y 中 X 只可能是类名或子模块名,
# 既非已注册类又非已导入模块 → 一定是方法。否则模块级
# 函数调用 VLogger.info(...) 被 find_func_in_module 后缀
# 匹配误绑到 Logger.info → self 错位 NULL + 参数补零 →
# 日志退化为 `├ INFO: ENUM`(二代自举日志退化根因)。
is_seg2_cls: int = 0
if self.Gen.StructGen.get_struct_type(seg2) is not None:
is_seg2_cls = 1
is_seg2_mod: int = 0
if trans is not None and trans._imported_modules is not None:
if self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, seg2) != 0:
is_seg2_mod = 1
if is_seg2_cls != 0 or is_seg2_mod == 0:
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* = None
bi_p1: llvmlite.LLVMType* = None
bi_p2: llvmlite.LLVMType* = 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)
elif string.strcmp(func_name, "min") == 0 or string.strcmp(func_name, "max") == 0:
# min/max 数值比较(builtins.vp 定义 double 参数/返回,
# int/float 实参经 coerce 提升;两参数)
bi_ret = llvmlite.Double(pool)
bi_p1 = llvmlite.Double(pool)
bi_p2 = llvmlite.Double(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: sizet = 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* = llvmlite.create_declare(pool, mod, bi_sym, bi_ret)
if bi_decl is not None:
llvmlite.add_param(pool, bi_decl, bi_p1, "v")
if bi_p2 is not None:
llvmlite.add_param(pool, bi_decl, bi_p2, "w")
found_func = bi_decl
# 使用访问器绕过 stub 类型限制
found_ret_ty: llvmlite.LLVMType* = 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* = 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* = None
# 跨模块返回类型解析的来源模块 SHA1:仅在下方 cl.func 是
# Attribute(模块.函数) 分支赋值。必须显式初始化 None——此前声明在
# if 块内、elif mod_ret_sha1 is not None 在外层读取,裸名调用
# (cl.func 是 Name,如 LLVMModuleCore 方法内裸名 create_global_int)
# 时该 alloca 从未 store,读到栈残留垃圾(0x1)→
# _LookupMethodRetTyFromTextLL 的 strlen(0x1) 零页崩溃
# (llvmlite/__init__.vp CreateGlobalInt 翻译时 0xC0000005 根因)。
mod_ret_sha1: str = None
# 模块路径(cl.func 为 Attribute 时赋值,否则保持 None)。声明在 if 外,
# 使下方裸名兜底分支也能安全读取(读未声明的局部 = 栈垃圾)。
mod_path_ret: str = None
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at_ret: ast.Attribute* = ast.Attribute*(cl.func)
# 解析调用模块路径:单层(stdlib.free,value 是 Name)与多层
# (w32.win32file.CreatePipe,value 是嵌套 Attribute)都支持。
# 缺失多层路径 → mod_ret_sha1=None → 误走裸名外部函数路径,
# fail-fast 误杀可收敛的模块限定调用(CreatePipe 根因)。
if at_ret.value is not None and at_ret.value.kind() == ast.ASTKind.Name:
mod_ret_nm: ast.Name* = ast.Name*(at_ret.value)
mod_path_ret = mod_ret_nm.id
elif at_ret.value is not None and at_ret.value.kind() == ast.ASTKind.Attribute:
mod_path_ret = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_ret.value)
if mod_path_ret is not None:
# import X as Y 别名(如 import lib.core.Gen.GenApi as GenApi):
# 精确模块名查找(LookupSha1ByModName)查不到别名 → None → 误走
# 裸名外部函数路径 fail-fast 误杀可收敛的模块限定调用
# (CallGenLeafKit L1327 GenApi.translate_printf_call 根因,
# 同 CreatePipe 模式)。改经 _resolve_module_sha1(别名还原
# LookupModNameByAlias → 精确/后缀)解析。
fi_from_rt: str = None
if trans is not None:
fi_from_rt = trans._from_imports
mod_ret_sha1 = self.Gen.StructGen._resolve_module_sha1(mod_path_ret, fi_from_rt)
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 None:
# 包模块 vpi 未命中(函数定义在子模块,如
# fltk.Fl_Input_value 实际在 fltk._cfl_input):
# 经 reexport 定位定义模块(star import 多候选
# 逐个查返回注解)再查 vpi。
rr_src: str = self.Gen.ImportsGen.lookup_reexport(mod_ret_sha1, func_name)
if rr_src is None:
rr_src = self._lookup_reexport_from_vpi(pool, mod_ret_sha1, func_name)
if rr_src is not None:
rr_total: sizet = string.strlen(rr_src)
rr_i: sizet = 0
while rr_i < rr_total and ret_annot_fc is None:
while rr_i < rr_total and rr_src[rr_i] == ' ':
rr_i += 1
if rr_i >= rr_total:
break
rr_start: sizet = rr_i
while rr_i < rr_total and rr_src[rr_i] != ' ':
rr_i += 1
rr_len: sizet = rr_i - rr_start
rr_buf: str = pool.alloc(rr_len + 1)
if rr_buf is not None:
string.strncpy(rr_buf, rr_src + rr_start, rr_len)
rr_buf[rr_len] = '\0'
rr_sha1: str = self._lookup_module_sha1(rr_buf)
if rr_sha1 is not None:
VpiReader._ensure_vpi_ret_annot_loaded(rr_sha1, self.Gen.CallGenBase.TempDir)
ret_annot_fc = VpiReader._lookup_vpi_ret_annot(rr_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
elif mod_ret_sha1 is not None:
# 跨模块裸名 CExport 函数(如 mm.malloc → @malloc):found_func=None
# 且 vpi 返回注解缓存未命中时,仍从依赖模块 text.ll/vpi 精确解析
# 返回类型(对齐 mangled 路径),避免静默默认 i32 截断指针/i64
# (uhci.vp mm.malloc 根因:call i32 @malloc 截断 0xbb69000 → null)。
cross_ret: llvmlite.LLVMType* = None
if trans is not None and trans._declare_only == 0:
cross_ret = self._LookupMethodRetTyFromTextLL(pool, mod_ret_sha1, None, func_name, 1)
if cross_ret is None:
cross_ret = self._lookup_method_ret_ty_via_vpi(pool, mod_ret_sha1, None, func_name)
if cross_ret is not None:
call_ret_ty = cross_ret
else:
# 依赖模块 text.ll 未生成(Phase A 中间遍)→ fatal_if_unknown=0,
# 本遍仅临时占位。
call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name, node, 0)
else:
# 裸名调用(cl.func 是 Name,mod_ret_sha1=None)两类来源:from-import 的函数(后续解析模块归属构造 mangled call_name 并经 text.ll/vpi 精确解析返回类型,可收敛)→ fatal_if_unknown=0 仅临时占位,不 fail-fast;纯外部 C 函数(无登记、无模块归属,白名单未命中)→ fail-fast(fatal_if_unknown=1)。闭包变量与持有函数指针/闭包的局部变量同样按可解析占位——返回类型由 translate_closure_call/lookup_var 路径固定处理,不依赖外部返回类型推断,否则被 fail-fast 误杀
bare_resolvable: int = 0
if self.Gen.CallGenLeafKit.is_closure_name(func_name) != 0:
bare_resolvable = 1
if trans is not None and trans.SymTab is not None:
if self.Gen.VarGen.lookup_var(trans.SymTab, func_name) is not None:
bare_resolvable = 1
if trans is not None and trans._from_imports is not None:
if self._lookup_mod_sha1(pool, trans._from_imports, func_name, 0) is not None:
bare_resolvable = 1
# M3 会话函数(跨片段):名字命中会话函数表 ⇒ 同样按"可解析"占位,
# 不 fail-fast。返回类型由下方 found_func==None 分支的
# repl_try_session_func_call 按表内签名(宿主从定义片段 IR 解析)构造,
# 与 _infer_external_func_ret_ty 的白名单无关;此处的 call_ret_ty 只是
# 占位(命中后立即 return,不会用到)。
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 不经过;且限定裸名(Name)——
# 会话函数只以裸名导出,模块限定调用(mod.f)不走本表。
if trans is not None and trans.ReplCtx is not None \
and cl.func is not None and cl.func.kind() == ast.ASTKind.Name \
and self.Gen.VarGen.repl_find_func_entry(trans.ReplCtx, func_name) is not None:
bare_resolvable = 1
call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name, node, 1 - bare_resolvable)
if found_func is None:
fp_alloca: llvmlite.Value* = None
if cl.func is None or cl.func.kind() == ast.ASTKind.Name:
# 闭包内调用捕获的闭包变量(如 _cmd_cd 调 _build_path):
# 从 env 读闭包对象指针,不能 lookup_var 跨函数命中父函数
# 闭包 alloca(SSA 错乱 → llc "instruction expected to be
# numbered",Terminal _cmd_cd 根因)。
if trans is not None and GenApi.is_nonlocal_name(trans, func_name) != 0:
fp_alloca = self.Gen.StmtGenDecl.load_nonlocal_var(trans, func_name)
if fp_alloca is not None:
# translate_closure_call 期望 i8** 存储(load 一次得闭包
# 对象指针);load_nonlocal_var 已返回对象指针 → 临时
# alloca 暂存
i8_ty_nl: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty_nl: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_nl)
tmp_alloca_nl: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, i8_ptr_ty_nl)
if tmp_alloca_nl is not None:
llvmlite.build_store(builder, fp_alloca, tmp_alloca_nl)
fp_alloca = tmp_alloca_nl
else:
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* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, func_name)
is_cptr_annot_fp: int = 0
fp_annot: str = self.Gen.VarGen.var_annot_class_name(ve_fp)
if fp_annot is not None:
# [S4] CPtr 标记语义 = **注解基名精确等于 "CPtr"**
# (裸 t.CPtr / from t import CPtr 后的 CPtr;两者都只在
# 注解里写了指针标记本身,没有承载具体型别)。
# 为何不能用 Spec 判定:t.Callable[[A], R] 解析结果同样是
# Ptr(i8)(TypeGenResolve.vp:2525-2531),与 t.CPtr 的
# Spec 完全同型 ⇒ 类型层无区分度,标记只能由注解名承载。
# 原 strstr 子串匹配会误命中含 "CPtr" 的类名(如 CPtrHolder)。
# 已知局限(与改造前一致,非回归):`Foo | t.CPtr` 的
# SourceName 只留 "Foo"(取非 CPtr 侧类名)⇒ 此处判为非标记。
if TSpec.SpecTypeNameBaseIs(fp_annot, "CPtr") != 0:
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)
# ---- M3:会话函数表命中 → ctx 函数指针槽取址 + 间接调用 ----
# 位置(为什么在这里):本步位于 `if found_func is None:` 块**末尾**——
# ① found_func 为 None ⇒ 本片段的 LLVM 模块里没有该函数的定义/前向声明
# (会话函数定义在**先前片段**的 DLL 里,代码不在本模块内);
# ② 放在闭包名/局部函数指针变量分支**之后** ⇒ 局部名与闭包正确地**遮蔽**
# 会话函数(Python 的局部优先),只有"既非本模块函数/声明、又非局部/
# 闭包/函数指针变量"的裸名才查会话函数表;
# ③ 仍先于 from_imports 解析与"裸名调用未定义且未导入"的 fail-fast。
# 命中后从 ctx 的函数指针槽 load 地址、按表内签名间接调用:函数代码只在
# 定义片段里一份(不复制/不重编译),调用点只持指针。
sfn_box: t.CPtr = None
if trans is not None and trans.ReplCtx is not None \
and (cl.func is None or cl.func.kind() == ast.ASTKind.Name):
sfn_box = stdlib.malloc(4)
if sfn_box is not None:
sfn_box[0] = 0
sfn_val: llvmlite.Value* = self.Gen.VarGen.repl_try_session_func_call(trans, pool, builder, mod, func_name, cargs, int(can), int(kw_count), node, sfn_box)
sfn_handled: int = int(sfn_box[0])
stdlib.free(sfn_box)
if sfn_handled != 0:
# 命中(含命中但按 M3 规则明确报错的情形):不再走任何
# "未定义/未导入"路径。void 会话函数返回 None 属预期。
return sfn_val
# 翻译参数并链接
# 有关键字参数且找到函数定义时,按参数名匹配
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* = None
tail: llvmlite.Value* = None
# memcpy 专用: 收集前三个 Value 的独立引用(避免共享 alloca 对象的 Next 被污染)
mc_dst2: llvmlite.Value* = None
mc_src2: llvmlite.Value* = None
mc_num2: llvmlite.Value* = None
for ai in range(can):
arg: ast.AST* = cargs.get(ai)
if arg is not None:
# 先取形参类型:单引号单字符字面量(CONST_CHAR)传给 str/指针
# 形参时需物化 1 字符字符串(translate_constant 依据 target_ty
# 判定),否则 coerce inttoptr 把字符值当指针(如 '"'=0x22)→
# strlen 访问低地址 ASan 崩溃(二代编 StringTest PyI 生成根因)。
fp: llvmlite.Param* = None
fpty: llvmlite.LLVMType* = None
if found_func is not None:
fp = 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.param_get_ty(fp)
arg_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg, None, 0, trans, fpty, "形参")
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* = 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* = 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
# 已知变参 C 库函数(snprintf/sprintf/vsprintf)固定参数
# 按签名 coerce:stub 未注入时跨模块签名不可见,但固定
# 参数类型是 C ABI 硬约束(snprintf size 为 i64),不提升
# 则 call(i32 1024) 与 declare(i64 size) 不匹配,且 Win64
# 变参槽高 4 字节残留栈垃圾(clean 日志 0x7ff700000052 根因)。
kvc_ty: llvmlite.LLVMType* = self.Gen.CallGenVa._get_known_vararg_cfunc_param_ty(pool, func_name, ai)
if kvc_ty is not None:
arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, kvc_ty, arg)
ext_param_ty: llvmlite.LLVMType* = 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: sizet))
# 修复: CDefine/i32 常量传给 i64 形参未提升,导致 call(i32) 与 define(i64) 不匹配
if fp is not None:
if fpty is not None:
arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, fpty, arg, trans)
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* = 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.CPtr = None
df_default_count: int = 0
df_param_count: int = 0
# 计算被调模块 SHA1 作限定键(消除跨模块同名函数串味):
# module.func(...) → 该 module 的 SHA1;裸名调用 → 当前模块 SHA1。
# 模块表与全局默认参数表都按「名 + 定义模块 SHA1」精确匹配,避免
# usb.uhci.init(1 参) 误命中 mm.init(5 参/2 默认值) 而补出多余实参。
# 必须先算(模块表查找也要用它限定定义模块 → 见下)。
dfx_callee_sha: str = None
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
dfx_at: ast.Attribute* = ast.Attribute*(cl.func)
if dfx_at.value is not None and dfx_at.value.kind() == ast.ASTKind.Name:
dfx_mn: ast.Name* = ast.Name*(dfx_at.value)
if dfx_mn.id is not None:
dfx_callee_sha = self._lookup_module_sha1(dfx_mn.id)
if dfx_callee_sha is None and self.Gen.TypeGenBase is not None:
dfx_callee_sha = self.Gen.TypeGenBase.CurrentModuleSha1
# 从当前模块函数表查找 FuncEntry —— 必须按**定义模块 SHA1 限定**:
# 表内含 LoadDepVpiIntoModule 以裸名注入的依赖 stub(槽位在前),
# 裸名首命中会让 usb 的 `uhci.init(io_base)`(1 形参)拿到 mm.init 的
# 5 形参/2 默认值 → 调用点被补 2 个 i32 0(N17)。
# 注:仅做「精确到定义模块」这一项改动;下方交叉校验守卫(dfx_cnt)
# 保持原样(`dfx_cnt > 0 and ...`)——收紧它会让 dfx_cnt==0 的场景
# 落入 `elif found_func is not None` 零值兜底分支,改动面更大,
# 且本项改动已足以修掉 N17(受限查找命中本模块/同定义模块条目,
# Defaults 为 None 或本模块池内 AST)。
if trans is not None and trans._funcs is not None:
df_entry: GenTypes.FuncEntry* = self.Gen.CallGenLeafKit.find_func_entry_in_table_owned(trans._funcs, trans._func_count, func_name, dfx_callee_sha)
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* = self.Gen.CallGenLeafKit.find_func_defaults(func_name, dfx_callee_sha)
# 包重导出补查(如 json.write 实际定义在 includes/json/__writer.vp):
# AST 侧模块名 'json' 只能解析到包 __init__ 的 SHA1,而默认参数表按
# **定义该函数的模块 SHA1** 登记(_register_global_defaults),于是直接
# 查不到 → 缺参填充整体跳过 → call 实参数 < declare/define 形参数 →
# 被调函数读寄存器/栈残留垃圾(VET `json.write(pool, val, False)` 的
# 缺省 cap 读成指针量级 → pool.alloc(巨量) → [OOM] 根因)。
# 与下方 L1473-1480 调用名重定向同一套 re-export 解析:命中后用
# **定义模块 SHA1** 复查默认参数表(SHA1 已校验,不会跨模块串味)。
if gd_entry is None and dfx_callee_sha is not None:
rr_src_dx: str = self.Gen.ImportsGen.lookup_reexport(dfx_callee_sha, func_name)
if rr_src_dx is None:
rr_src_dx = self._lookup_reexport_from_vpi(pool, dfx_callee_sha, func_name)
if rr_src_dx is not None:
rr_sha_dx: str = self._lookup_module_sha1(rr_src_dx)
if rr_sha_dx is not None:
gd_entry = self.Gen.CallGenLeafKit.find_func_defaults(func_name, rr_sha_dx)
# 覆盖条件含 df_default_count<=0:调用方函数表可能命中一条同名条目但
# Defaults 为空/默认值为 0 条(VpiReader 注入的 declare 条目),
# 此时必须用全局默认参数表的真实条目覆盖,否则缺参填充整体不触发。
if gd_entry is not None and (df_defaults is None or df_default_count <= 0):
df_defaults = gd_entry.Defaults
df_default_count = gd_entry.DefaultCount
df_param_count = gd_entry.ParamCount
# 交叉校验:全局默认参数表按「裸函数名」登记/查询,跨模块同名函数会串味
# (如 usb.uhci.init 只有 1 形参,却命中 mm.init 的 5 形参/2 默认值记录
# → 被补 2 个 i32 0 → call 3 实参 vs declare 1 形参 → ABI 错位)。
# 若被调声明(found_func)的实际形参数与表记录 ParamCount 不符,则丢弃该表,
# 交由下方 `elif found_func is not None` 分支按真实形参数零值兜底。
if found_func is not None and df_default_count > 0:
dfx_cnt: int = 0
dfx_p: llvmlite.Param* = llvmlite.function_get_param_head(found_func)
while dfx_p is not None:
dfx_nm: str = llvmlite.param_get_name(dfx_p)
if dfx_nm is None or (string.strcmp(dfx_nm, "%__eh_msg_out__") != 0
and string.strcmp(dfx_nm, "%__eh_code_out__") != 0):
dfx_cnt += 1
dfx_p = llvmlite.param_get_next(dfx_p)
if dfx_cnt > 0 and dfx_cnt != df_param_count:
df_defaults = None
df_default_count = 0
df_param_count = 0
# 填充缺失参数
if df_default_count > 0 and can < df_param_count and (df_defaults is not None or gd_entry is not None):
df_offset: int = df_param_count - df_default_count
for df_di in range(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* = 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 and df_defaults is not None:
df_defaults_list: list[ast.AST*]* = (list[ast.AST*] | t.CPtr)(uint64(df_defaults))
df_default_node: ast.AST* = 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
# 追加的默认实参必须计入实参数:下方
# _coerce_args_to_def_params/_ensure_method_declare 等
# 均以 can 为界重建/校验实参链,count 不增会把补齐的
# 尾随默认实参整体截断丢弃(VET json.write 缺省 cap
# 丢失 → 被调方读寄存器垃圾 → pool.alloc 巨量 OOM 根因)。
can += 1
elif found_func is not None:
# 默认参数缺失/表信息不全时的零值兜底:函数表有参数数但无默认值记录、或调用方先于定义文件编译导致查不到默认值时,用 found_func 实际参数数(stub declare,与 define 一致)兜底并缺参填零值(对齐 TPC _fill_default_args 语义)。注意排除 EH 输出参数(%__eh_msg_out__/%__eh_code_out__),否则会与 _emit_eh_guarded_call 追加的真实 EH 实参重复导致 ABI 错位
fc_cnt: int = 0
fc_p: llvmlite.Param* = llvmlite.function_get_param_head(found_func)
while fc_p is not None:
fp_nm_z: str = llvmlite.param_get_name(fc_p)
if fp_nm_z is None or (string.strcmp(fp_nm_z, "%__eh_msg_out__") != 0
and string.strcmp(fp_nm_z, "%__eh_code_out__") != 0):
fc_cnt += 1
fc_p = llvmlite.param_get_next(fc_p)
if fc_cnt == 0:
fc_cnt = df_param_count if df_param_count > 0 else 0
if can < fc_cnt:
fp_z: llvmlite.Param* = llvmlite.function_get_param_head(found_func)
fpi_z: int = 0
while fp_z is not None and fpi_z < can:
fp_z = llvmlite.param_get_next(fp_z)
fpi_z += 1
while fp_z is not None and fpi_z < fc_cnt:
zero_ty_z: llvmlite.LLVMType* = llvmlite.param_get_ty(fp_z)
zero_val_z: llvmlite.Value* = llvmlite.ConstZero(pool, zero_ty_z)
if zero_val_z is not None:
zero_val_z.Next = None
if head is None:
head = zero_val_z
else:
tail.Next = zero_val_z
tail = zero_val_z
can += 1 # 零值兜底实参同样计入实参数(同默认实参)
fp_z = llvmlite.param_get_next(fp_z)
fpi_z += 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
# CExport 裸名兜底:跨模块 stub declare 可能带 SHA1 前缀
# (@"1fdad1c76fb116bc.malloc"),但定义端 CExport 函数
# (mm.vp malloc/free/malloc_direct,返回注解含 t.CExport)
# 用裸名 @malloc → 链接 undefined symbol: 1fdad1c76fb116bc.malloc。
# found_func 非 None 时下方 is_cexport_func 裸名分支不执行
# (该分支仅 found_func None 的模块限定路径),这里对带前缀
# 的 declare 提取 sha1 再查 CExport 注册表,命中则改用裸名。
if found_func.IsDeclared != 0:
# 判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛:此前只判
# 首个 '.' 前恰 16 字节、不校验 hex 形态)。
if GenApi.sha1_prefix_len(found_name) != 0:
cx_sha1_buf: str = pool.alloc(17)
if cx_sha1_buf is not None:
string.memcpy(cx_sha1_buf, found_name, 16)
cx_sha1_buf[16] = '\0'
if self.Gen.CallGenBase.is_cexport_func(cx_sha1_buf, func_name) != 0:
call_name = func_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* = ast.Attribute*(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* = ast.Name*(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_or_known(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)
rs_found: int = 0
if reexport_src is not None:
# star import 多候选(空格分隔,如 fltk/__init__ 依次
# from _types/_enums/_cfl import *)逐个验证定义模块:
# is_cexport_func/_is_state_func_from_vpi 按 vpi 返回注解
# 确认 func_name 真实定义处(Fl_init_all → _cfl)。
cs_total: sizet = string.strlen(reexport_src)
cs_i: sizet = 0
while cs_i < cs_total:
while cs_i < cs_total and reexport_src[cs_i] == ' ':
cs_i += 1
if cs_i >= cs_total:
break
cs_start: sizet = cs_i
while cs_i < cs_total and reexport_src[cs_i] != ' ':
cs_i += 1
cs_seg: sizet = cs_i - cs_start
cs_buf: str = pool.alloc(cs_seg + 1)
if cs_buf is not None:
string.strncpy(cs_buf, reexport_src + cs_start, cs_seg)
cs_buf[cs_seg] = '\0'
cand_sha1: str = self._lookup_module_sha1(cs_buf)
if cand_sha1 is not None:
if self.Gen.CallGenBase.is_cexport_func(cand_sha1, func_name) != 0 or \
self._is_state_func_from_vpi(pool, cand_sha1, func_name) != 0:
mod_sha1 = cand_sha1
call_name = func_name
rs_found = 1
break
if rs_found == 0:
# star import 候选验证全部失败:func_name 可能定义在
# 原模块自身(extern def 如 stdlib.malloc 定义在 stdlib,
# 而 stdlib 又 `from stdint import *` 产生误候选 stdint)。
# 先验证原模块:extern/State → 裸名(rs_found=1 下方置裸名)。
if self.Gen.CallGenBase.is_cexport_func(mod_sha1, func_name) != 0 or \
self._is_state_func_from_vpi(pool, mod_sha1, func_name) != 0:
rs_found = 1
call_name = func_name
else:
# 原模块 vpi 自含普通 def(非 CExport/State):如
# string.vp 自身定义 strncmp,而其 `from stdint
# import *` 产生的 star 候选 stdint 会劫持解析。
# vpi 命中 → 保持原模块 sha(生成 @{mod_sha1}.func,
# 普通跨模块函数带 sha1 前缀),不走候选回退。
orig_has_def: int = 0
if self.Gen.CallGenBase.TempDir is not None \
and self.Gen.CallGenBase.TempDirLen > 0:
VpiReader._ensure_vpi_ret_annot_loaded(mod_sha1, self.Gen.CallGenBase.TempDir)
if VpiReader._lookup_vpi_ret_annot(mod_sha1, None, func_name) is not None:
orig_has_def = 1
if orig_has_def == 0:
# 原模块未定义:回退首个候选(旧行为,普通跨模块
# 函数带 sha1 前缀,如 ast._lexer_init 定义在 ast.lexer)
reexport_sha1: str = self._lookup_module_sha1(reexport_src)
if reexport_sha1 is not None:
mod_sha1 = reexport_sha1
# CExport 函数定义用裸名(@strlen),跨模块调用需用裸名而非 @{sha1}.func
if rs_found != 0:
pass # 已验证 reexport 定义模块并置裸名
elif 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: sizet = 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: sizet = 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: sizet = 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: sizet = 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: sizet = 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:
# 别名附加尝试:仅作为 mangled 精确解析前的临时推断(fatal_if_unknown=0)。
# 若 call_name 是 mangled,L1272 起的 text.ll/vpi 精确解析会覆盖并 fail-fast;
# 未命中时保持白名单/默认 i32 由后续路径兜底。
re_inferred_ty: llvmlite.LLVMType* = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, orig_func_name, node, 0)
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: sizet = string.strlen(call_name) - \
(sizet(uint64(cf_last) - uint64(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* = 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* = 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* = 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:
# 依赖模块 text.ll 不存在(模块尚未翻译)→ Phase A 按文件
# 顺序翻译,main.vp 先于其 import 的 mbuddytest.vp(同项目
# 多源文件):本遍保持默认类型,不 fail-fast。
if self._dep_text_ll_exists(pool, cf_sha1) != 0:
self.Gen.TypeGenErr.fatal_error(node, f"跨模块函数返回类型无法精确获取(text.ll/vpi 均未命中),默认 i32 会截断指针: {call_name}", "TypeError")
stdlib.free(cf_sha1)
stdlib.free(cf_bare)
# 裸名 C 库函数(string.memset → @memset):实参按标准签名提升
# (第三参 i32 字面量 → i64),再建 declare,保证 call 实参与
# declare/定义宽度一致(B3:process 固化 i32 vs 定义 sizet=i64)。
head = self.Gen.CallGenLib._promote_clib_call_args(pool, builder, mod, call_name, head, can)
# 跨模块模块级调用({sha1}.{func}):实参按 vpi 定义侧形参类型 coerce
# (整型宽度精确对齐,i32 字面量 → 定义侧 i8/i16),再建 declare →
# call 实参 / declare / define 三者宽度一致(B3 同族通用化;反例:
# su8250.putchar 定义 (i16,i8) 而调用点字面量 1016=i32 固化宽)。
head = self.Gen.CallGenLib._coerce_args_to_def_params(pool, builder, call_name, head, can, cargs, 0, trans)
# 跨模块调用:创建 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, sizet(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)