fix(TPV): 方法先声明后定义(消除方法查找的源顺序依赖),删除兄弟 Mixin 兜底

现场:Test/FileIoTest 编译 includes/w32/fileio.vp 报
  CastError: 源 %"b0721da228be3d55.FileW"* → 目标 %"b0721da228be3d55.File"*
  (指针隐式转指针已禁止:被指类型是非基本类型)

根因(有旧 IR 佐证,非本轮改动引起):FileW.__init__(源 304 行)里的
self.seek(0, SEEK_END) 被解析成 File.seek,而不是 FileW.seek:
  1) _translate_method 只在方法体翻译时才 create_function,于是方法查找
     (find_func_in_module)依赖方法在类体内的书写顺序:FileW.seek 在源 403 行,
     晚于 __init__ → 查不到 → None;
  2) MRO 无父类 → None;
  3) 兄弟 Mixin 兜底随即遍历全部已注册 struct,取"本模块存在同名方法"的
     第一个候选类 → 命中 File → 绑成 File.seek,self 再被 this-adjust
     bitcast 成 File*。
旧 IR(TransPyV\.tpv_build\temp\b0\b0721da228be3d55.text.ll,mtime 9/27,
早于本轮全部改动)里就是:
  %97 = bitcast %"...FileW"* %96 to %"...File"*
  %98 = call i32 @"...File.seek"(%"...File"* %97, i32 0, i32 2)
即靠一层隐式跨类 bitcast 静默通过;指针规则收紧后以 CastError 报出。
(File.__init__ 的 self.seek 同样查不到,只是兜底恰好扫到 File.seek 而"碰巧正确"。)

改动:
1. ClassGenVTable._translate_oop_methods:翻译方法体之前,先对本类**全部方法**
   跑一遍 sig_only=1 建前向声明(declare + 参数 + 函数表登记)——与
   FuncGenDef.forward_declare_functions 对模块级函数做的事完全同构,
   使方法查找不再依赖类体内的书写顺序。
2. ClassGenVTable._translate_method 新增 sig_only 形参:
   · 签名部分改用 create_declare(IsDeclared=1),已存在同名前向声明则复用;
   · 发射函数体前把 IsDeclared 清零转 define(对齐 FuncGenDef.vp:544-545),
     否则打印端按 IsDeclared 输出 declare、函数体整体丢失;
   · 复用声明时跳过重复的 add_param 与 add_func_to_table;
   · 命中同名函数且 IsDeclared==0(函数体已发射)时直接返回,杜绝重复发射
     (llvmlite.create_function 不做同名去重,旧写法会造 duplicate symbol)。
3. CallGenCall._translate_method_call:按"不兜底、fail-fast"决定,整块删除
   兄弟 Mixin 兜底(遍历 struct 表猜候选类)及其死分支
   (mixin_hit / mixin_ret_ty / mixin_anc_name / mixin_anc_sha1 的声明与消费点)。
   方法未命中即为真未定义,交下方 text.ll / vpi / 本模块豁免路径
   (CallGenCall.vp:962 起的 is_local_mod 分支,从已声明的本模块函数直接取
   返回类型)处理,不再猜候选类。
4. 同步修正 ClassGenVTable 顶部 _translate_method 的注释块与函数文档串。

TPC 侧无需同步:TPC 的声明阶段把类成员全部登记进 SymbolTable,本就不存在
"方法查找依赖源顺序",也没有这段兄弟 Mixin 扫描(HandlesExprCall.py:3877-3914
的兜底带"调用类自身未声明该方法"守卫,与本次删除的 TPV 版不同)。

待验证(不自行执行编译命令):重编译 TransPyV(自举需编两遍),再跑
FileIoTest / NoVTableCrossMixinTest / DictTest / 泛型相关测试。
This commit is contained in:
TermiNexus committed 2026-10-01 13:45:10 +08:00
1 parent 875a9beb08
commit 455c7608b1
2 files changed
+157 -167

No files matched your search

+11 -76
View File
@@ -807,72 +807,16 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
break
mi += 1
# 兄弟 Mixin 兜底(与 TPC HandlesExprCall 一致):方法不在本类 MRO 链时遍历已注册 struct 表找定义类方法,this 由下方 _adjust_this_for_inherited_method 按组合布局前移 sizeof(定义类)。仅对普通类名兜底——泛型特化类名(list[X]/dict[K,V])必须过滤,否则普通方法在 found_func 未命中时会误绑到 list 特化函数,this 前移错位导致运行期崩溃
# 跨模块兄弟 Mixin 兜底命中(定义类在另一文件、本地 IR 无 define)需经候选类模块 sha1 查 text.ll 返回类型。
mixin_hit: int = 0
mixin_ret_ty: llvmlite.LLVMType* = None
mixin_anc_name: str = None
mixin_anc_sha1: str = None
if found_func is None:
# 兄弟 Mixin 兜底仅对"接收者类属于当前模块"的场景适用:self 调用兄弟
# Mixin 方法时接收者是本类(与候选兄弟类同模块)。接收者类是跨模块类
# (如 self.Gen.TypeGenResolve 的 TypeGenResolve,定义在另一模块)时,
# 其方法必须经该类自身模块(text.ll/vpi)解析——若本模块恰好定义了
# 同名方法 {Cand}.{method},本兜底会误绑定:最坏情况绑定到正在编译的
# 函数自身 → ImportsGenMixin.set_current_file 内 self.Gen.TypeGenResolve.
# set_current_file(...) 被编译成自调用 → 无限递归 stack-overflow
# (二代编 FuncTest 崩溃根因)。接收者类模块无法确定时保持原行为。
sib_recv_local: int = 1
if trans is not None and trans.ModuleSha1 is not None:
sib_recv_sha1: str = None
if mc_entry is not None and mc_entry.ModuleSha1 is not None:
sib_recv_sha1 = mc_entry.ModuleSha1
else:
sib_recv_sha1 = self.Gen.StructGen.get_struct_sha1(class_name)
if sib_recv_sha1 is not None:
if string.strcmp(sib_recv_sha1, trans.ModuleSha1) != 0:
sib_recv_local = 0
if sib_recv_local != 0:
sc_i: int = 0
sc_n: int = self.Gen.StructGen.get_struct_count()
# 【守卫】这是按序号(0..StructCount-1)的全量遍历,非按名查找,
# 不走 StructNameIdx 索引:兄弟 Mixin 候选是"名字不等 class_name
# 且本模块存在同名方法"的任意条目,需逐条枚举探测,不可改为按名
# find_struct(会退化为单条精确查找、漏掉全部兄弟候选)。
while sc_i < sc_n and found_func is None and mixin_hit == 0:
cand_entry: GenTypes.StructEntry* = self.Gen.StructGen._get_struct_entry(sc_i)
if cand_entry is not None and cand_entry.Name is not None:
cand_name: str = cand_entry.Name
if string.strcmp(cand_name, class_name) != 0:
# 泛型特化类名含 '['(list[...]/dict[...]):跳过,
# 不是兄弟 Mixin 候选(见上方注释)
if string.strchr(cand_name, '[') is not None:
sc_i += 1
continue
cand_lookup: str = pool.alloc(128)
if cand_lookup is not None:
viperlib.snprintf(cand_lookup, 128, "%s.%s", cand_name, method_name)
cand_func: llvmlite.Function* = self.find_func_in_module(mod, cand_lookup)
if cand_func is not None:
found_func = cand_func
mc_anc = cand_name
else:
# 跨模块 Mixin(定义类在另一文件,本地 IR 无 define):
# 用候选类模块 sha1 查 text.ll 返回类型,命中即锁定定义类,
# 后续 ret_anc_* 据此构造 {sha1}.{class}.{method} 调用名。
# 否则 ret_anc_sha1 停在调用类自身 sha1,_dep_text_ll_exists
# 查自身 text.ll(Phase A 翻译中未生成)→ 永久 pending 死锁
# (ClassGenVTable._mangle_name → 7871cc1aabebfc33 根因)。
if mixin_hit == 0:
cand_sha1_cx: str = self.Gen.StructGen.get_struct_sha1(cand_name)
if cand_sha1_cx is not None and self._dep_text_ll_exists(pool, cand_sha1_cx) != 0:
cand_ret_cx: llvmlite.LLVMType* = self._LookupMethodRetTyFromTextLL(pool, cand_sha1_cx, cand_name, method_name)
if cand_ret_cx is not None:
mixin_hit = 1
mixin_ret_ty = cand_ret_cx
mixin_anc_name = cand_name
mixin_anc_sha1 = cand_sha1_cx
sc_i += 1
# 【已删除:兄弟 Mixin 兜底(2026-10-01,用户决定"不兜底、fail-fast")】
# 原实现:found_func 未命中时遍历全部已注册 struct,取"本模块存在同名方法"的
# 第一个候选类并绑定(this 再按组合布局前移)。该扫描的候选判据里**没有**
# "接收类自身是否声明了该方法"这一条(TPC 侧 HandlesExprCall.py:3877-3914 有),
# 因而会把同类的方法误绑到无关类:includes/w32/fileio.vp 的 FileW.__init__
# 调用 self.seek(...) 时(FileW.seek 尚未发射)被绑成 File.seek——旧 IR 靠
# `bitcast %"…FileW"* to %"…File"*` 静默掩盖,指针规则收紧后以 CastError 报出。
# 根因已由"先声明后定义"消除(ClassGenVTable._translate_oop_methods 在翻译
# 方法体前对本类全部方法跑 sig_only=1 建前向声明):方法查找不再依赖类体
# 书写顺序,未命中即为真未定义,落下方 fail-fast,不再猜候选类。
# 确定调用名(默认 "ClassName.method";found_func 未命中时沿 MRO 用
# 方法实际定义类的 SHA1 构造,如 Module.append → AST 定义的 append,
@@ -938,13 +882,6 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
ret_anc_sha1 = mc_entry.ModuleSha1
else:
ret_anc_sha1 = self.Gen.StructGen.get_struct_sha1(class_name)
# 跨模块兄弟 Mixin 兜底命中:定义类锁定为候选类(如 FuncGenBase),
# 返回类型与调用名前缀以其模块 sha1 为准,避免 ret_anc_sha1 停在
# 调用类自身 sha1 → 查自身 text.ll(未生成)→ 永久 pending 死锁
if mixin_hit != 0:
ret_anc_name = mixin_anc_name
ret_anc_sha1 = mixin_anc_sha1
call_ret_ty = mixin_ret_ty
if ret_anc_sha1 is None:
# 沿 MRO 找第一个有 SHA1 的类
mro_s0: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
@@ -1022,9 +959,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
call_ret_ty = frt_g
found_func = spec_found_g
gen_done = 1
# mixin_hit:跨模块 Mixin 兜底已锁定定义类与返回类型,跳过
# text.ll/vpi/pending 重查段,直接构造 {sha1}.{class}.{method} 调用名
if gen_done == 0 and mixin_hit == 0:
if gen_done == 0:
# ============================================================
# is_local_mod 豁免(对齐 CallGenMixin L1336-1343 的 is_local_mod):
# 方法定义在当前正在翻译的模块(ret_anc_sha1 == trans.ModuleSha1)
+146 -91
View File
@@ -527,6 +527,23 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
oop_has_vt_pre = oop_entry.HasVTable
if oop_has_vt_pre == 1:
self._collect_vtable_methods(trans, cd, class_name)
# 先声明后定义:翻译方法体之前,先为本类**全部方法**建立前向声明
# (declare + 参数 + 函数表登记),使方法查找(find_func_in_module)不再
# 依赖方法在类体中的书写顺序。与 FuncGenDef.forward_declare_functions
# 对模块级函数做的事完全同构。缺此步的现场:includes/w32/fileio.vp 的
# FileW.__init__(源 304 行)调 self.seek(...),而 FileW.seek 定义在源
# 403 行 → 查不到 → 被同名方法扫描误绑到 File.seek → 指针规则以
# FileW* → File* 报 CastError(旧 IR 靠 bitcast 静默掩盖)。
for ci_pd in range(cn):
stmt_pd: ast.AST* = children.get(ci_pd)
if stmt_pd is None:
continue
if stmt_pd.kind() == ast.ASTKind.FunctionDef:
fd_pd: ast.FunctionDef* = ast.FunctionDef*(stmt_pd)
if fd_pd is not None and fd_pd.name is not None:
self._translate_method(trans, fd_pd, struct_ty, class_name, 1)
for ci in range(cn):
stmt: ast.AST* = children.get(ci)
if stmt is None:
@@ -561,8 +578,19 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
def _translate_method(self, trans: HT.Translator*,
fd: ast.FunctionDef*,
struct_ty: llvmlite.LLVMType*,
class_name: str) -> int:
"""翻译单个方法,返回 0"""
class_name: str,
sig_only: int = 0) -> int:
"""翻译单个方法,返回 0
sig_only: 1=只建前向声明(declare + 参数 + 函数表登记),不发射函数体。
_translate_oop_methods 在翻译方法体之前对本类**全部方法**先跑一遍
sig_only=1(与 FuncGenDef.forward_declare_functions 对模块级函数做的事
完全同构),使方法查找(find_func_in_module)不再依赖方法在类体中的
书写顺序。反例:includes/w32/fileio.vp 的 FileW.__init__(源 304 行)
调用 self.seek(...),而 FileW.seek 定义在源 403 行——没有预声明时
查不到 FileW.seek,会落到"同名方法扫描"并误绑 File.seek,再被指针
规则以 FileW* → File* 报 CastError。
"""
if trans is None or fd is None or struct_ty is None or class_name is None:
return 0
@@ -649,104 +677,131 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
param_types_str: str = self.Gen.TypeGenErr.build_param_types_str(pool, fd.args)
ret_ty = self.Gen.TypeGenErr.infer_return_type(pool, fd.children, param_types_str)
# 创建 LLVM 函数
func: llvmlite.Function* = llvmlite.create_function(pool, mod, mangled_name, ret_ty)
if func is None:
return 0
# 创建/复用 LLVM 函数。
# 复用场景:本类的"先声明后定义"预扫描(sig_only=1)已用 create_declare 建好
# 同名前向声明(IsDeclared=1),此处跳过参数创建与函数表登记(声明期已完成),
# 只把 IsDeclared 清零转 define——与 FuncGenDef.forward_declare_functions
# (FuncGenDef.vp:25-30)→ translate_function_def(FuncGenDef.vp:544-545)的
# 模块级函数前向声明机制同构。
func: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, mangled_name)
decl_reuse: int = 0
if func is not None:
if func.IsDeclared == 0:
# 同名函数体已发射:禁止重复发射(duplicate symbol)
return 0
decl_reuse = 1
else:
func = llvmlite.create_declare(pool, mod, mangled_name, ret_ty)
if func is None:
return 0
# 提取默认参数信息(普通方法 args[0] 是 self,不含在 param_count 中;
# @staticmethod 无 self,param_count 为完整参数数)
# 注意: 必须先把属性赋给显式类型为 list[...] | t.CPtr 的局部变量再调用 __len__(),
# 否则编译器无法识别属性返回的 list 类型,GEP base 会变成 i32 0 导致 llc 报错
md_defaults: t.CPtr = None
md_default_count: int = 0
md_param_count: int = 0
md_args_node: ast.Arguments* = fd.args
if md_args_node is not None:
md_ags: ast.Arguments* = ast.Arguments*(md_args_node)
if md_ags.args is not None:
md_alist: list[ast.AST*]* = md_ags.args
if is_static_m != 0:
md_param_count = len(md_alist)
else:
md_param_count = len(md_alist) - 1
if md_ags.defaults is not None:
md_dlist: list[ast.AST*]* = md_ags.defaults
md_defaults = md_dlist
md_default_count = len(md_dlist)
if sig_only != 0:
# 前向声明模式:已声明过直接返回;否则下方登记完参数/函数表后返回。
if decl_reuse != 0:
return 0
else:
# 发射函数体前必须把 IsDeclared 清零,否则打印端按 IsDeclared 输出
# declare、函数体整体丢失(对齐 FuncGenDef.vp:544-545)。
func.IsDeclared = 0
# 注册到函数表(用 ClassName.method_name 作为查找名,支持后缀匹配)
max_funcs: int = 1024
if self.Gen.CallGenLeafKit.add_func_to_table(funcs_ptr, func_count, full_name, func, max_funcs,
md_defaults, md_default_count, md_param_count,
pool) == 0:
trans._func_count = func_count + 1
if decl_reuse == 0:
# 提取默认参数信息(普通方法 args[0] 是 self,不含在 param_count 中;
# @staticmethod 无 self,param_count 为完整参数数)
# 注意: 必须先把属性赋给显式类型为 list[...] | t.CPtr 的局部变量再调用 __len__(),
# 否则编译器无法识别属性返回的 list 类型,GEP base 会变成 i32 0 导致 llc 报错
md_defaults: t.CPtr = None
md_default_count: int = 0
md_param_count: int = 0
md_args_node: ast.Arguments* = fd.args
if md_args_node is not None:
md_ags: ast.Arguments* = ast.Arguments*(md_args_node)
if md_ags.args is not None:
md_alist: list[ast.AST*]* = md_ags.args
if is_static_m != 0:
md_param_count = len(md_alist)
else:
md_param_count = len(md_alist) - 1
if md_ags.defaults is not None:
md_dlist: list[ast.AST*]* = md_ags.defaults
md_defaults = md_dlist
md_default_count = len(md_dlist)
# 添加 self/cls 参数(Ptr(struct_ty));@staticmethod 无 self,跳过
# @classmethod 的 cls 也用 Ptr(struct_ty),首参名取 args[0](如 cls)
if is_static_m == 0:
first_pname: str = "self"
if is_class_m != 0 and fd.args is not None:
fd_ags: ast.Arguments* = ast.Arguments*(fd.args)
if fd_ags.args is not None and len(fd_ags.args) > 0:
fd_first_arg: ast.Arg* = ast.Arg*(fd_ags.args.get(0))
if fd_first_arg is not None and fd_first_arg.arg is not None:
first_pname = fd_first_arg.arg
first_pname_buf: str = pool.alloc(32)
if first_pname_buf is not None:
viperlib.snprintf(first_pname_buf, 32, "%%%s", first_pname)
llvmlite.add_param(pool, func, self_ptr_ty, first_pname_buf)
# 注册到函数表(用 ClassName.method_name 作为查找名,支持后缀匹配)
max_funcs: int = 1024
if self.Gen.CallGenLeafKit.add_func_to_table(funcs_ptr, func_count, full_name, func, max_funcs,
md_defaults, md_default_count, md_param_count,
pool) == 0:
trans._func_count = func_count + 1
# 添加其他参数(支持类型注解;@staticmethod 从索引 0 开始,其余跳过索引 0 的 self/cls)
args_node: ast.Arguments* = fd.args
if args_node is not None:
ags: ast.Arguments* = ast.Arguments*(args_node)
if ags.args is not None:
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
start_idx: int = 0
if is_static_m == 0:
start_idx = 1
for ai in range(start_idx, an):
arg: ast.Arg* = ast.Arg*(alist.get(ai))
if arg is not None and arg.arg is not None:
param_ty: llvmlite.LLVMType* = i32_ty
if arg.annotation is not None:
resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, imported_modules, from_imports, trans, 0)
if resolved is None:
# [S4 fail-fast] 有注解但解析失败:不再静默用 i32
# (方法签名与调用点/实现体三方不一致)
self.Gen.TypeGenErr.fatal_error(arg, f"方法 '{class_name}.{method_name}' 形参 '{arg.arg}' 类型注解解析失败", "NameError")
return 0
# 裸类形参 → Ptr(struct):按"对象即指针"引用传参。
# 若按 struct 值传参,self.Head = node 会取局部拷贝地址
# (append 返回后悬垂,G5 Test4 head.Value 读到垃圾)。
# CArray 形参 → 元素指针 ty*(数组衰减,C 语义,与函数形参一致)。
match resolved:
case llvmlite.LLVMType.Struct(_, _, _):
param_ty = llvmlite.Ptr(pool, resolved)
case llvmlite.LLVMType.Array(aelem_ty, _):
param_ty = llvmlite.Ptr(pool, aelem_ty)
case _:
param_ty = resolved
pname: str = pool.alloc(32)
if pname is not None:
viperlib.snprintf(pname, 32, "%%%s", arg.arg)
llvmlite.add_param(pool, func, param_ty, pname)
# 添加 self/cls 参数(Ptr(struct_ty));@staticmethod 无 self,跳过
# @classmethod 的 cls 也用 Ptr(struct_ty),首参名取 args[0](如 cls)
if is_static_m == 0:
first_pname: str = "self"
if is_class_m != 0 and fd.args is not None:
fd_ags: ast.Arguments* = ast.Arguments*(fd.args)
if fd_ags.args is not None and len(fd_ags.args) > 0:
fd_first_arg: ast.Arg* = ast.Arg*(fd_ags.args.get(0))
if fd_first_arg is not None and fd_first_arg.arg is not None:
first_pname = fd_first_arg.arg
first_pname_buf: str = pool.alloc(32)
if first_pname_buf is not None:
viperlib.snprintf(first_pname_buf, 32, "%%%s", first_pname)
llvmlite.add_param(pool, func, self_ptr_ty, first_pname_buf)
# OOP iter 协议:__next__ 方法注入隐藏参数 __stop_iter_flag__(i1*)。
# raise StopIteration 时置 1(对齐 TPC HandlesFunctions.py L703-705)。
# 该参数不计入 md_param_count(逻辑参数),调用侧(for 循环)显式传。
if string.strcmp(method_name, "__next__") == 0:
i1_ty_si: llvmlite.LLVMType* = llvmlite.Int1(pool)
if i1_ty_si is not None:
i1_ptr_ty_si: llvmlite.LLVMType* = llvmlite.Ptr(pool, i1_ty_si)
if i1_ptr_ty_si is not None:
llvmlite.add_param(pool, func, i1_ptr_ty_si, "%__stop_iter_flag__")
# 添加其他参数(支持类型注解;@staticmethod 从索引 0 开始,其余跳过索引 0 的 self/cls)
args_node: ast.Arguments* = fd.args
if args_node is not None:
ags: ast.Arguments* = ast.Arguments*(args_node)
if ags.args is not None:
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
start_idx: int = 0
if is_static_m == 0:
start_idx = 1
for ai in range(start_idx, an):
arg: ast.Arg* = ast.Arg*(alist.get(ai))
if arg is not None and arg.arg is not None:
param_ty: llvmlite.LLVMType* = i32_ty
if arg.annotation is not None:
resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, imported_modules, from_imports, trans, 0)
if resolved is None:
# [S4 fail-fast] 有注解但解析失败:不再静默用 i32
# (方法签名与调用点/实现体三方不一致)
self.Gen.TypeGenErr.fatal_error(arg, f"方法 '{class_name}.{method_name}' 形参 '{arg.arg}' 类型注解解析失败", "NameError")
return 0
# 裸类形参 → Ptr(struct):按"对象即指针"引用传参。
# 若按 struct 值传参,self.Head = node 会取局部拷贝地址
# (append 返回后悬垂,G5 Test4 head.Value 读到垃圾)。
# CArray 形参 → 元素指针 ty*(数组衰减,C 语义,与函数形参一致)。
match resolved:
case llvmlite.LLVMType.Struct(_, _, _):
param_ty = llvmlite.Ptr(pool, resolved)
case llvmlite.LLVMType.Array(aelem_ty, _):
param_ty = llvmlite.Ptr(pool, aelem_ty)
case _:
param_ty = resolved
pname: str = pool.alloc(32)
if pname is not None:
viperlib.snprintf(pname, 32, "%%%s", arg.arg)
llvmlite.add_param(pool, func, param_ty, pname)
# OOP iter 协议:__next__ 方法注入隐藏参数 __stop_iter_flag__(i1*)。
# raise StopIteration 时置 1(对齐 TPC HandlesFunctions.py L703-705)。
# 该参数不计入 md_param_count(逻辑参数),调用侧(for 循环)显式传。
if string.strcmp(method_name, "__next__") == 0:
i1_ty_si: llvmlite.LLVMType* = llvmlite.Int1(pool)
if i1_ty_si is not None:
i1_ptr_ty_si: llvmlite.LLVMType* = llvmlite.Ptr(pool, i1_ty_si)
if i1_ptr_ty_si is not None:
llvmlite.add_param(pool, func, i1_ptr_ty_si, "%__stop_iter_flag__")
# 参数添加完成,补全全局函数表的 Spec(供 vtable 在 func 被 reset 后派生类型文本)
self.Gen.CallGenLeafKit.update_global_func_spec(full_name, func)
# 前向声明模式:签名与函数表登记已完成,不发射函数体
if sig_only != 0:
return 0
# 创建 entry 块
entry_blk: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, "entry")
if entry_blk is None: