Files
ViperC/App/lib/core/Gen/Class/ClassGenVTable.vp
T
TermiNexus 2f6db41b11 fix(void): 形参与字段注解禁止 void,改为编译期 fail-fast
void 只允许用于 void* 与 typedef 不透明结构体别名(用户 2026-10-01 定)。
此前 def f(p: void) / class C: x: void 不走 translate_annassign,会按 LLVM void
直接建形参/字段槽,生成非法 IR 拖到 llc 阶段才暴露。现补 6 处形参落点
(FuncGenDef 前向声明/外部声明/函数体/alloca 各一、ClassGenVTable 签名/alloca 各一)
与 1 处字段落点(ClassGenDef),统一编译期 TypeError。
void* 已在上游归并为 i8*,不受影响。
2026-10-01 21:43:11 +08:00

1281 lines
73 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 lib.core.Gen.Type.TypeSpec as TSpec
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.core.CompilerFactory as CompilerFactory
from . import ClassGenBase
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.DebugInfoGen as DebugInfoGen
# 方法元数据 flag:TLS(@t.TLS 装饰器,对齐 ClassGenBase 本地常量;本地副本避免跨模块 CDefine 查找)
define META_TLS = 32
# 虚表方法数上限
define VTBL_METHOD_MAX = 32
define SCOPE_FUNCTION = 1
# ============================================================
# ClassGenVTable - ClassGenMixin 拆分(阶段 D):vtable 收集/生成/方法翻译
# ============================================================
novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
"""vtable 收集/生成/方法翻译"""
# ============================================================
# _vtable_strcat — 将 src 追加到 dst 末尾
# ============================================================
def _vtable_strcat(self, dst: str, dst_size: sizet,
src: str):
"""将 src 追加到 dst 末尾"""
if dst is None or src is None:
return
dlen: sizet = string.strlen(dst)
slen: sizet = string.strlen(src)
remain: sizet = dst_size - dlen
if remain <= 0: return
i: sizet = 0
while i < slen and i + 1 < remain:
dst[dlen + i] = src[i]
i += 1
dst[dlen + i] = '\0'
# ============================================================
# _vtable_name_needs_quote — 检查 LLVM 标识符是否需要引号
# ============================================================
def _vtable_name_needs_quote(self, name: str) -> int:
"""检查 LLVM 标识符是否需要引号(以数字开头或含 '.')"""
if name is None:
return 0
if '0' <= name[0] <= '9':
return 1
for i in name:
if i == '.':
return 1
return 0
# ============================================================
# _collect_vtable_methods — 收集类的虚方法信息
#
# 1. 继承父类虚方法(父类方法排在子类方法之前,保持槽位顺序一致)
# 2. 收集当前类虚方法(跳过已继承的)
#
# 结果存入 self.VtFullNames/VtMethodNames/VtParentSha1s/VtMethodCount
# (原模块级 _g_vt_*),并设置 entry.VTableMethodCount /
# VTableMethods(stdlib 分配,供虚分派跨模块查询)。
#
# 返回 1=有虚方法 / 0=无方法或失败
# ============================================================
def _collect_vtable_methods(self, trans: HT.Translator*,
cd: ast.ClassDef*,
class_name: str) -> int:
"""收集虚方法信息,设置 entry.VTableMethods/VTableMethodCount"""
if trans is None or cd is None or class_name is None:
return 0
pool: memhub.MemBuddy* = trans.Pool
mod: llvmlite.LLVMModule* = trans.Module
children: list[ast.AST*]* = cd.children
if children is None:
return 0
cn: sizet = len(children)
# 1. 收集虚方法信息
# stdlib 分配:method_names_buf 存到 StructEntry.VTableMethods(跨文件永久存活),
# 必须迁出 mb 池 reset 区间,否则 1b 每文件 reset_to(mark1) 会回收导致悬空。
full_names_buf: sizet* = CompilerFactory.factory.PermPool.alloc(8 * VTBL_METHOD_MAX)
method_names_buf: sizet* = CompilerFactory.factory.PermPool.alloc(8 * VTBL_METHOD_MAX)
# 记录每个方法的父类 SHA1(非 0=继承方法用父模块 SHA1 mangling,0=当前类方法)
method_parent_sha1s: sizet* = CompilerFactory.factory.PermPool.alloc(8 * VTBL_METHOD_MAX)
if full_names_buf is None or method_names_buf is None or method_parent_sha1s is None:
return 0
string.memset(full_names_buf, 0, 8 * VTBL_METHOD_MAX)
string.memset(method_names_buf, 0, 8 * VTBL_METHOD_MAX)
string.memset(method_parent_sha1s, 0, 8 * VTBL_METHOD_MAX)
method_count: int = 0
# ============================================================
# 1.5 继承父类虚方法
#
# 父类虚方法排在子类虚方法之前,保持 vtable 槽位顺序一致。
# 如果子类覆盖了方法,full_name 用子类的;否则用父类的。
# 用 SHA1 感知查找,规避跨模块同名 find_struct 找错
# ============================================================
vt_self_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(class_name, trans.ModuleSha1)
# 多继承:遍历所有父类(声明顺序),vtable 方法合并(短名去重,先声明父类优先)
parent_count: int = self.Gen.StructGen.get_parent_count(class_name)
if parent_count > 0:
for pi in range(parent_count):
pname: str = self.Gen.StructGen.get_parent_name_at(class_name, pi)
if pname is None:
continue
# 查找父类 entry(优先同模块 SHA1 匹配)
vt_parent_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(pname, trans.ModuleSha1)
if vt_parent_entry is None:
# 跨模块继承: 父类在不同模块定义(SHA1 不同),回退到按类名查找
vt_parent_entry = self.Gen.StructGen.find_struct(pname)
parent_vt_count: int = 0
if vt_parent_entry is not None:
parent_vt_count = vt_parent_entry.VTableMethodCount
if parent_vt_count > 0:
for pvi in range(parent_vt_count):
if method_count >= VTBL_METHOD_MAX:
break
pmethod_name: str = None
if vt_parent_entry is not None:
pmethod_name = self.Gen.StructGen.get_vtable_method_name(pname, pvi)
if pmethod_name is None:
continue
# 多继承 vtable 去重:方法短名已存在跳过(先声明父类优先)
if self.Gen.ClassGenBase._field_name_exists(method_names_buf, method_count, pmethod_name) != 0:
continue
# 检查子类是否覆盖了该方法,以及是否标记 @t.NoVTable
child_overrides: int = 0
child_nvt: int = 0
for ci2 in range(cn):
stmt2: ast.AST* = children.get(ci2)
if stmt2 is None:
continue
if stmt2.kind() != ast.ASTKind.FunctionDef:
continue
fd2: ast.FunctionDef* = ast.FunctionDef*(stmt2)
if fd2 is not None and fd2.name is not None:
if string.strcmp(fd2.name, pmethod_name) == 0:
child_overrides = 1
# 检查子类覆盖方法是否标记 @t.NoVTable
if fd2.decorator_list is not None:
if self.Gen.ClassGenBase._has_decorator(fd2.decorator_list, "NoVTable") == 1:
child_nvt = 1
break
# 构造 full_name
# 子类覆盖且未标记 NoVTable → 用子类实现
# 子类未覆盖 或 子类覆盖但标记 NoVTable → 用实际定义类实现(保持 vtable 槽位)
#
# 多层继承修复: 直接父类可能只是继承而非定义该方法
# (如 ElectricCar 继承 Car 的 is_running 实际定义在 Vehicle),
# 沿父类链向上用 find_func_global("类名.方法名") 探测实际定义类,
# 避免生成 "Car.is_running" 这类找不到函数的 full_name → vtable 槽变 null。
def_class: str = class_name
if child_overrides == 1 and child_nvt == 0:
def_class = class_name
else:
def_class = pname
# 多继承:沿 pname 的 MRO 祖先链探测实际定义类(跳过自身,先声明父类优先)
mro_ptr: sizet* = self.Gen.StructGen.get_mro_chain(pool, pname)
mro_i: int = 1
while mro_ptr is not None and mro_ptr[mro_i] != 0:
anc: str = rawptr(mro_ptr[mro_i])
if anc is not None:
probe_buf: str = pool.alloc(128)
if probe_buf is not None:
viperlib.snprintf(probe_buf, 128, "%s.%s", anc, pmethod_name)
if self.Gen.CallGenLeafKit.find_func_global(probe_buf) is not None:
def_class = anc
break
mro_i += 1
pfname_buf: str = CompilerFactory.factory.PermPool.alloc(128)
if pfname_buf is not None:
viperlib.snprintf(pfname_buf, 128, "%s.%s", def_class, pmethod_name)
full_names_buf[method_count] = sizet(pfname_buf)
# 复制方法名(短名)
pmname_len: sizet = string.strlen(pmethod_name)
pmname_buf: str = CompilerFactory.factory.PermPool.alloc(pmname_len + 1)
if pmname_buf is not None:
string.strcpy(pmname_buf, pmethod_name)
method_names_buf[method_count] = sizet(pmname_buf)
# 记录 mangling 用的 SHA1(继承未覆盖的方法用实际定义类的模块 SHA1)
if child_overrides == 1 and child_nvt == 0:
# 子类覆盖: 用当前模块 SHA1(保持 0)
method_parent_sha1s[method_count] = 0
else:
# 继承未覆盖: 用实际定义类的模块 SHA1(跨模块继承时 mangling 正确)
def_entry2: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(def_class)
if def_entry2 is not None and def_entry2.ModuleSha1 is not None:
method_parent_sha1s[method_count] = sizet(def_entry2.ModuleSha1)
else:
method_parent_sha1s[method_count] = 0
method_count += 1
# 2. 收集当前类的虚方法(跳过已从父类继承的)
for ci in range(cn):
stmt: ast.AST* = children.get(ci)
if stmt is None:
continue
if stmt.kind() != ast.ASTKind.FunctionDef:
continue
fd: ast.FunctionDef* = ast.FunctionDef*(stmt)
if fd is None or fd.name is None:
continue
if method_count >= VTBL_METHOD_MAX:
break
mname: str = fd.name
# 跳过构造函数和特殊方法(不应放入虚表)
if string.strcmp(mname, "__init__") == 0:
continue
if string.strcmp(mname, "__before_init__") == 0:
continue
if string.strcmp(mname, "__new__") == 0:
continue
# 函数级装饰器过滤:判断方法是否应该进入虚表
if self.Gen.ClassGenBase._should_method_be_virtual(cd, fd, trans) == 0:
continue
# 检查是否已从父类继承(避免重复)
already_in_vt: int = 0
for mi in range(method_count):
exist_addr: sizet = method_names_buf[mi]
if exist_addr != 0:
exist_name: str = rawptr(exist_addr)
if exist_name is not None and string.strcmp(exist_name, mname) == 0:
already_in_vt = 1
break
if already_in_vt == 1:
continue
# 构造 full_name = "ClassName.method_name"
# 内容必须落在 perm_pool:1a(mark_only=1)收集的 VTableMethods 被
# Phase 1b 依赖模块翻译跨文件读取,1a 每文件 reset 后 pool 裸字符串悬空
fname_buf: str = CompilerFactory.factory.PermPool.alloc(128)
if fname_buf is not None:
viperlib.snprintf(fname_buf, 128, "%s.%s", class_name, mname)
full_names_buf[method_count] = sizet(fname_buf)
# 复制方法名
mname_len: sizet = string.strlen(mname)
mname_buf: str = CompilerFactory.factory.PermPool.alloc(mname_len + 1)
if mname_buf is not None:
string.strcpy(mname_buf, mname)
method_names_buf[method_count] = sizet(mname_buf)
method_count += 1
# 暂存结果(_generate_vtable 复用)
self.VtFullNames = full_names_buf
self.VtMethodNames = method_names_buf
self.VtParentSha1s = method_parent_sha1s
self.VtMethodCount = method_count
# 设置 entry 元数据(虚分派查询用;必须在本类方法体翻译前就绪,
# 否则方法体内 self.虚方法 调用无法虚分派)
if vt_self_entry is not None and method_count > 0:
vt_self_entry.VTableMethodCount = method_count
vt_self_entry.VTableMethods = method_names_buf
if method_count == 0:
return 0
return 1
# ============================================================
# _generate_vtable — 生成 VTable 全局变量
#
# 1. 收集 class body 中所有 FunctionDef 作为虚方法
# 2. 构造 VTable 类型 { i8*, i8*, ... }
# 3. 构造初始化字符串 { i8* bitcast (...), ... }
# 4. 创建全局变量 @"SHA1.ClassName.vtable"
# 5. 存储虚方法名列表到 StructEntry
# ============================================================
def _generate_vtable(self, trans: HT.Translator*,
cd: ast.ClassDef*,
class_name: str) -> int:
"""生成 VTable 全局变量"""
if trans is None or cd is None or class_name is None:
return 0
pool: memhub.MemBuddy* = trans.Pool
mod: llvmlite.LLVMModule* = trans.Module
# 收集虚方法信息(继承父类 + 自身,结果存 self.VtFullNames 等暂存区)
# 同时设置 entry.VTableMethodCount/VTableMethods(虚分派查询用)
if self._collect_vtable_methods(trans, cd, class_name) == 0:
return 0
full_names_buf: sizet* = self.VtFullNames
method_names_buf: sizet* = self.VtMethodNames
method_parent_sha1s: sizet* = self.VtParentSha1s
method_count: int = self.VtMethodCount
# 重新获取 entry(后续存储 VTableMethods 用)
vt_self_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(class_name, trans.ModuleSha1)
# 2. 构造 VTable 类型: { i8*, i8*, ..., i8* }
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
first_node: llvmlite.ParamNode* = None
tail_node: llvmlite.ParamNode* = None
for i in range(method_count):
pnode: llvmlite.ParamNode* = llvmlite.new_param_node(pool, i8_ptr_ty)
if pnode is None:
continue
tail_node = llvmlite.param_list_append(first_node, tail_node, pnode)
if first_node is None:
first_node = tail_node
vtable_ty: llvmlite.LLVMType* = llvmlite.Struct(pool, first_node, method_count, None)
if vtable_ty is None:
return 0
# 3. 构造初始化字符串
# 大 vtable(如 MemBuddy 28+ 方法)每个方法 bitcast 约 100+ 字节,
# 4096 可能写满导致截断/strlen 越界,用 16384 预留足够空间。
init_buf: str = pool.alloc(16384)
if init_buf is None:
return 0
init_buf[0] = '\0'
self._vtable_strcat(init_buf, 16384, "{ ")
for i in range(method_count):
if i > 0:
self._vtable_strcat(init_buf, 16384, ", ")
full_name_addr: sizet = full_names_buf[i]
if full_name_addr == 0:
self._vtable_strcat(init_buf, 16384, "i8* null")
continue
full_name: str = rawptr(full_name_addr)
# 检查是否是继承方法(method_parent_sha1s 非 0 表示用父模块 SHA1 mangling)
parent_sha1_addr: sizet = method_parent_sha1s[i]
if parent_sha1_addr != 0:
# 继承方法: 用父类模块 SHA1 做 mangling
parent_sha1: str = rawptr(parent_sha1_addr)
mangled_name: str = self.Gen.FuncGenBase._mangle_name_with_sha1(parent_sha1, full_name)
else:
# 当前类方法: 用当前模块 SHA1
mangled_name: str = self.Gen.FuncGenBase._mangle_name(trans, full_name)
# 统一从全局表 entry.Spec 用 SpecToLLVMText 派生函数类型文本。
# 全局表 entry.Spec 在方法 add_param 完成后由 update_global_func_spec 填
# (PermPool 驻留,跨 1b reset 安全),故不再解引用 func(func 可能已被
# reset_to(mark1) 回收悬空)。
ge: GenTypes.FuncEntry* = self.Gen.CallGenLeafKit.find_func_entry_global(full_name)
func_ty_buf: str = pool.alloc(1024)
if func_ty_buf is None:
self._vtable_strcat(init_buf, 16384, "i8* null")
continue
func_ty_buf[0] = '\0'
if ge is None or ge.Spec is None or TSpec.SpecToLLVMText(ge.Spec, func_ty_buf, 1024) != 0:
self.Gen.TypeGenErr.fatal_error(cd, f"vtable 方法 '{full_name}' 找不到函数类型", "InternalCompilerError")
continue
# 构造 bitcast 字符串: i8* bitcast (func_ty* @mangled to i8*)
self._vtable_strcat(init_buf, 16384, "i8* bitcast (")
self._vtable_strcat(init_buf, 16384, func_ty_buf)
self._vtable_strcat(init_buf, 16384, "* @")
if self._vtable_name_needs_quote(mangled_name) != 0:
self._vtable_strcat(init_buf, 16384, "\"")
self._vtable_strcat(init_buf, 16384, mangled_name)
self._vtable_strcat(init_buf, 16384, "\"")
else:
self._vtable_strcat(init_buf, 16384, mangled_name)
self._vtable_strcat(init_buf, 16384, " to i8*)")
self._vtable_strcat(init_buf, 16384, " }")
# 4. 构造 VTable 全局变量名
vtable_name_buf: str = pool.alloc(128)
if vtable_name_buf is None:
return 0
viperlib.snprintf(vtable_name_buf, 128, "%s.vtable", class_name)
vtable_mangled: str = self.Gen.FuncGenBase._mangle_name(trans, vtable_name_buf)
# 5. 创建全局变量
gv: llvmlite.GlobalVariable* = llvmlite.new_global_variable(pool, vtable_mangled, vtable_ty)
if gv is None:
return 0
gv.Initializer = init_buf
gv.IsConstant = 1
# 默认 linkage(external,2026-08-10):isinstance 内建函数需要跨模块
# 引用其他模块类的 vtable 符号({模块sha1}.Class.vtable),internal 不对外
# 导出会导致链接失败。vtable 名含模块 sha1 前缀全局唯一,外部可见不冲突。
# llvmlite 打印规则:Initializer 非空 + external 时不打印 external 关键字,
# 输出 @name = constant {...}(合法外部强定义)。
# 添加到模块
llvmlite.module_add_global(mod, gv)
# 6. 存储虚方法名列表到 StructEntry(直接用 entry,规避跨模块同名 find_struct 找错)
if vt_self_entry is not None:
vt_self_entry.VTableMethods = method_names_buf
vt_self_entry.VTableMethodCount = method_count
return 0
# ============================================================
# _translate_oop_methods — 扫描 class body 中的方法并翻译
#
# 遍历 ClassDef.children,对每个 FunctionDef:
# - 标记 OOP(mark_as_oop)
# - 如果是 __init__,标记 has_init
# - 翻译方法(生成 SHA1.ClassName.method_name 函数)
# 翻译完所有方法后,生成 __before_init__ 函数
# ============================================================
def _translate_oop_methods(self, trans: HT.Translator*,
cd: ast.ClassDef*,
struct_ty: llvmlite.LLVMType*,
class_name: str,
mark_only: int = 0,
reemit: int = 0) -> int:
"""扫描 class body 中的方法并翻译,生成 __before_init__
mark_only: 0=全量翻译(默认),1=只设置 IsOOP/HasNew/HasInit 标志(不翻译方法体)
reemit: 1=补译模式(_specialize_generic_class Phase B 补译分支调用)。
补译分支为防重入已提前置 MethodsEmitted=1,但方法体尚未发射,
必须跳过下方 MethodsEmitted==1 的防重复检查强制发射,否则补译
被自身拦截 → 特化方法只有 call 无 define → 链接 undefined symbol。
"""
if trans is None or cd is None or struct_ty is None or class_name is None:
return 0
children: list[ast.AST*]* = cd.children
if children is None:
return 0
cn: sizet = len(children)
has_method: int = 0
has_init: int = 0
has_new: int = 0
# 提前用类型指针定位 entry(规避跨模块同名类 find_struct 找错)
oop_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(struct_ty)
# 第一遍:检测是否有方法,标记 OOP,并注册装饰器元数据
for ci in range(cn):
stmt: ast.AST* = children.get(ci)
if stmt is None:
continue
if stmt.kind() == ast.ASTKind.FunctionDef:
has_method = 1
fd_check: ast.FunctionDef* = ast.FunctionDef*(stmt)
if fd_check is not None and fd_check.name is not None:
if string.strcmp(fd_check.name, "__init__") == 0:
has_init = 1
if string.strcmp(fd_check.name, "__new__") == 0:
has_new = 1
# 注册装饰器元数据(@property/@staticmethod/@classmethod/@x.setter 等)。
# 此处(含 mark_only=1 的 struct 预注册阶段)注册,
# 确保其他模块在 App 文件翻译时能识别属性访问。
fd_meta: int = self.Gen.ClassGenBase._extract_func_meta(fd_check.decorator_list)
if fd_meta != 0:
self.Gen.ClassGenBase.register_method_meta(class_name, fd_check.name, fd_meta)
if has_method == 0:
# 子类没有自己的方法,但可能继承了父类的虚方法
# 用 oop_entry 直接获取 ParentName,规避 find_struct 按名查找找错
parent_name: str = None
if oop_entry is not None:
parent_name = oop_entry.ParentName
if parent_name is not None:
parent_vt_count: int = self.Gen.StructGen.get_vtable_method_count(parent_name)
if parent_vt_count > 0:
has_method = 1 # 标记为有方法(继承的虚方法)
if has_method == 0:
return 0
# 标记为 OOP 结构体
if oop_entry is not None:
oop_entry.IsOOP = 1
if has_init != 0:
oop_entry.HasInit = 1
if has_new != 0:
oop_entry.HasNew = 1
# mark_only 模式:只设置标志,不翻译方法体
if mark_only != 0:
# Phase 1a 预注册也收集 vtable 元数据(VTableMethods/VTableMethodCount):
# 若 1b 中依赖模块先于其依赖类全量翻译,调用方会查到 HasVTable=1 但
# VTableMethods=None → 虚分派条件不成立 → 方法被静态绑定到基类实现(返回
# None)→ __new__ 返回 null → 崩溃。短名顺序与 Phase 1b 全量收集一致
# (父类方法在前 + 本类方法);多遍重复收集用新缓冲区覆盖(幂等)。
mk_has_vt: int = 0
if oop_entry is not None:
mk_has_vt = oop_entry.HasVTable
if mk_has_vt == 1:
self._collect_vtable_methods(trans, cd, class_name)
return 0
# 防重复生成:MethodsEmitted==1 说明方法体/__before_init__ 已生成过。
# 场景:_specialize_generic_class 的 Phase B 补译分支(MethodsEmitted==0
# 时补译方法体并置 1)之后,同一特化类又经 translate_class_def 全量
# 翻译进入本方法 → 重复生成 __before_init__ → ld.lld duplicate symbol。
# 已生成过则直接跳过(方法+__before_init__ 都产出过,跳过安全)。
# reemit=1(补译模式)时跳过本检查:补译分支为防重入已提前置
# MethodsEmitted=1,但方法体尚未发射,必须强制发射。
if oop_entry is not None and oop_entry.MethodsEmitted == 1 and reemit == 0:
return 0
# 防重入:方法体翻译前先标记 MethodsEmitted=1。
# 根因:若等全部方法译完再标记,翻译 __iter__(返回 list[T]|t.CPtr)时,
# 返回类型注解会再次触发 _specialize_generic_class,此时 MethodsEmitted
# 仍为 0,走补译分支把同一特化类方法全部重发 → invalid redefinition。
if oop_entry is not None:
oop_entry.MethodsEmitted = 1
# 第二遍:翻译每个方法
# 翻译方法体前先预注册 vtable 元数据(VTableMethods/VTableMethodCount)。
# 根因:方法体翻译先于 _generate_vtable,若不预注册,方法体内的
# self.虚方法 调用(如 MemManager.calloc 里的 self.alloc)读不到
# VTableMethods,虚分派失败,被静态绑定到本类实现(基类 alloc 返回
# None),导致 calloc 永远返回 NULL。
oop_has_vt_pre: int = 0
if oop_entry is not None:
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:
continue
if stmt.kind() == ast.ASTKind.FunctionDef:
fd: ast.FunctionDef* = ast.FunctionDef*(stmt)
if fd is not None and fd.name is not None:
self._translate_method(trans, fd, struct_ty, class_name)
# 如果有虚表,先生成 VTable 全局变量(设置 VTableMethodCount)
# 必须在 __before_init__ 之前,否则 __before_init__ 读不到 VTableMethodCount
# 用 oop_entry 直接检查,规避跨模块同名 find_struct 找错
has_vt_flag: int = 0
if oop_entry is not None:
has_vt_flag = oop_entry.HasVTable
if has_vt_flag == 1:
self._generate_vtable(trans, cd, class_name)
# 生成 __before_init__ 函数(零值填充 + 默认值赋值 + vtable 指针设置)
self._generate_before_init(trans, struct_ty, class_name)
return 0
# ============================================================
# _translate_method — 翻译单个方法
#
# 生成: define <ret_ty> @SHA1.ClassName.method_name(Ptr(StructTy) %self, ...)
#
# self 参数处理:
# - 类型 Ptr(struct_ty),直接注册为 SSA 值(不创建 alloca)
# - 使 self.field 能通过 GEP 直接访问原始结构体
# ============================================================
def _translate_method(self, trans: HT.Translator*,
fd: ast.FunctionDef*,
struct_ty: llvmlite.LLVMType*,
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
pool: memhub.MemBuddy* = trans.Pool
mod: llvmlite.LLVMModule* = trans.Module
imported_modules: str = trans._imported_modules
from_imports: str = trans._from_imports
funcs_ptr: GenTypes.FuncEntry* = trans._funcs
func_count: int = trans._func_count
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
self_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, struct_ty)
method_name: str = fd.name
if method_name is None:
# fail-fast:方法名缺失(解析器不可能产出无名 def)——静默打印
VLogger.error("方法名缺失(fd.name is None)", "OOP")
self.Gen.TypeGenErr.fatal_error(fd, "方法名缺失(fd.name is None)", "TypeError")
# 提取装饰器元数据(@property/@staticmethod/@classmethod/@x.setter/@x.deleter)
func_meta: int = self.Gen.ClassGenBase._extract_func_meta(fd.decorator_list)
# property setter/deleter 函数名带 $set/$del 后缀,避免与 getter 冲突
method_name_buf: str = pool.alloc(128)
if method_name_buf is None:
return 0
viperlib.snprintf(method_name_buf, 128, "%s", fd.name)
if func_meta & GenTypes.META_PROP_SETTER != 0:
# 128 字节缓冲:追加 "$set" 前检查剩余容量,防 strcat 无界写穿
mn_len2: sizet = string.strlen(method_name_buf)
if mn_len2 + 5 < 128:
string.strcat(method_name_buf, "$set")
elif func_meta & GenTypes.META_PROP_DELETER != 0:
mn_len2: sizet = string.strlen(method_name_buf)
if mn_len2 + 5 < 128:
string.strcat(method_name_buf, "$del")
method_name = method_name_buf
is_static_m: int = 0
is_class_m: int = 0
is_tls_m: int = 0
if func_meta & GenTypes.META_STATIC != 0:
is_static_m = 1
if func_meta & GenTypes.META_CLASS != 0:
is_class_m = 1
if func_meta & META_TLS != 0:
is_tls_m = 1
# 构建方法全名: ClassName.method_name
full_name_buf: str = pool.alloc(128)
if full_name_buf is None:
return 0
viperlib.snprintf(full_name_buf, 128, "%s.%s", class_name, method_name)
full_name: str = full_name_buf
# SHA1 命名空间混淆
mangled_name: str = self.Gen.FuncGenBase._mangle_name(trans, full_name)
# 推断返回类型
ret_ty: llvmlite.LLVMType* = None
if fd.returns is not None:
ret_ty = self.Gen.TypeGenResolve.resolve_annotation_type(pool, fd.returns, imported_modules, from_imports, trans, 0)
if ret_ty is None and fd.returns is not None:
# 存储类标记(t.State / t.CExport / t.CExtern)语义为 Void:这类注解
# 不携带真实返回类型(extern/export 语法写成 "<原类型> | t.<标记>",
# 原类型已解析成功时不会走到这里,此处只处理标记本身/解析后仍为 None)。
if self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "State", 0) != 0 \
or self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "CExport", 0) != 0 \
or self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "CExtern", 0) != 0:
ret_ty = llvmlite.Void(pool)
else:
# [S4 fail-fast] 有返回注解但解析失败 → 不再退回 infer_return_type()
# (推断可能给出与真实注解不符的类型,静默错到运行期才暴露)
self.Gen.TypeGenErr.fatal_error(fd, f"方法 '{class_name}.{method_name}' 返回类型注解解析失败", "NameError")
return 0
if ret_ty is None:
# __init__ 和 __before_init__ 返回 void
if string.strcmp(method_name, "__init__") == 0:
ret_ty = llvmlite.Void(pool)
# __new__ 返回 Ptr(struct_ty)(结构体指针作为存储空间)
elif string.strcmp(method_name, "__new__") == 0:
ret_ty = llvmlite.Ptr(pool, struct_ty)
else:
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 函数。
# 复用场景:本类的"先声明后定义"预扫描(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
if sig_only != 0:
# 前向声明模式:已声明过直接返回;否则下方登记完参数/函数表后返回。
if decl_reuse != 0:
return 0
else:
# 发射函数体前必须把 IsDeclared 清零,否则打印端按 IsDeclared 输出
# declare、函数体整体丢失(对齐 FuncGenDef.vp:544-545)。
func.IsDeclared = 0
# 形参表:下方"函数体段"(形参建 alloca + 登记符号)与参数创建块都要用,
# 必须声明在 decl_reuse 分支之外——复用前向声明时不再走参数创建块,
# 但函数体段仍要遍历形参,否则实参名不进符号表(NameError: 未定义名称)。
args_node: ast.Arguments* = fd.args
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)
# 注册到函数表(用 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
# 添加 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)
# 添加其他参数(支持类型注解;@staticmethod 从索引 0 开始,其余跳过索引 0 的 self/cls)
# args_node 已在 decl_reuse 分支之外声明(函数体段同样要用)
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.Void():
# void 不是合法形参类型(用户 2026-10-01:void 只能用于
# `void*` 与 `typedef 不透明结构体 = void`);`void*` 已在上游
# 归并为 i8*,不会命中此分支。放任会按 LLVM void 建形参 ⇒
# 非法 IR 拖到 llc 才暴露,改编译期 fail-fast。
self.Gen.TypeGenErr.fatal_error(arg, f"方法 '{class_name}.{method_name}' 形参 '{arg.arg}' 不能是 void 类型(void 只能用于 void* 与 typedef 别名)", "TypeError")
return 0
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:
return 0
# 创建方法专属 builder
func_builder: llvmlite.IRBuilder* = llvmlite.new_builder(pool, func)
if func_builder is None:
return 0
llvmlite.position_at_end(func_builder, entry_blk)
# --traceback: attach a DISubprogram to this method define (same as the
# module-level function path). No-op when DI is disabled.
dbg_pushed: int = DebugInfoGen.PushFunction(pool, func_builder, func,
fd.lineno, fd.col_offset + 1)
# @t.TLS 一次性初始化守卫(对齐 TPC HandlesFunctions.py L1763-1792;
# 与 FuncGenMixin.translate_function_def 同一实现,类方法同样支持)
if is_tls_m != 0:
tls_m_flag_name: str = pool.alloc(128)
if tls_m_flag_name is not None:
viperlib.snprintf(tls_m_flag_name, 128, "__tls_init_done_%s", method_name)
tls_m_gv: llvmlite.GlobalVariable* = llvmlite.create_global_int(pool, mod, tls_m_flag_name, 1, 0)
if tls_m_gv is not None:
i1_ty_m: llvmlite.LLVMType* = llvmlite.Int1(pool)
if i1_ty_m is not None:
# GlobalVariable 非 Value 子类:构造 Value 视图后再 load/store
tls_m_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i1_ty_m)
tls_m_rn: str = pool.alloc(64)
if tls_m_rn is not None:
viperlib.snprintf(tls_m_rn, 64, "@%s", tls_m_flag_name)
tls_m_ref: llvmlite.Value* = llvmlite.SSAValue(pool, tls_m_ptr_ty, tls_m_rn)
if tls_m_ref is not None:
tls_m_ld: llvmlite.Value* = llvmlite.build_load(func_builder, i1_ty_m, tls_m_ref)
lc_m: int = trans._label_counter
trans._label_counter = lc_m + 1
tls_m_buf: str = pool.alloc(64)
if tls_m_buf is not None:
viperlib.snprintf(tls_m_buf, 64, "tls.skip.%d", lc_m)
tls_m_skip: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, tls_m_buf)
viperlib.snprintf(tls_m_buf, 64, "tls.cont.%d", lc_m)
tls_m_cont: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, tls_m_buf)
if tls_m_skip is not None and tls_m_cont is not None:
llvmlite.build_cond_br(func_builder, tls_m_ld, tls_m_skip, tls_m_cont)
llvmlite.position_at_end(func_builder, tls_m_skip)
match ret_ty:
case llvmlite.LLVMType.Void():
llvmlite.build_ret_void(func_builder)
case llvmlite.LLVMType.Int(_):
tlm0: llvmlite.Value* = llvmlite.ConstInt(pool, ret_ty, 0, "0")
if tlm0 is not None:
llvmlite.build_ret(func_builder, tlm0)
case llvmlite.LLVMType.Float(_):
tlmf: llvmlite.Value* = llvmlite.ConstFloat(pool, ret_ty, 0.0)
if tlmf is not None:
llvmlite.build_ret(func_builder, tlmf)
case _:
tlmn: llvmlite.Value* = llvmlite.ConstNull(pool, ret_ty, "null")
if tlmn is not None:
llvmlite.build_ret(func_builder, tlmn)
llvmlite.position_at_end(func_builder, tls_m_cont)
tlmo: llvmlite.Value* = llvmlite.ConstInt(pool, i1_ty_m, 1, "1")
if tlmo is not None:
llvmlite.build_store(func_builder, tlmo, tls_m_ref)
# 进入函数作用域
self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_FUNCTION)
# 为 self/cls 创建 alloca 并 store(与其他参数一致);@staticmethod 无 self,跳过。
# self 必须走 alloca:注册为 SSA 值会让 translate_name_value 多 load 一次,
# 把 Ptr(struct_ty) 变成 struct 值 → 变参函数 ABI 不匹配崩溃。
if is_static_m == 0:
self_alloca: llvmlite.Value* = llvmlite.build_alloca(func_builder, self_ptr_ty)
if self_alloca is not None:
self_var_name: str = "self"
self_ssa_name: str = "%self"
if is_class_m != 0 and args_node is not None:
cags: ast.Arguments* = ast.Arguments*(args_node)
if cags.args is not None and len(cags.args) > 0:
cfirst: ast.Arg* = ast.Arg*(cags.args.get(0))
if cfirst is not None and cfirst.arg is not None:
self_var_name = cfirst.arg
self_ssa_name_buf: str = pool.alloc(32)
if self_ssa_name_buf is not None:
viperlib.snprintf(self_ssa_name_buf, 32, "%%%s", cfirst.arg)
self_ssa_name = self_ssa_name_buf
self.Gen.VarGen.define_var(trans.SymTab, self_var_name, self_alloca)
# 设置 self/cls 的类型注解类名(属性访问 lookup_field 回退查找用)
self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, self_var_name, class_name)
self_val: llvmlite.Value* = llvmlite.SSAValue(pool, self_ptr_ty, self_ssa_name)
llvmlite.build_store(func_builder, self_val, self_alloca)
# 为其他参数创建 alloca 并 store(与普通函数一致;
# @staticmethod 从索引 0 开始,其余跳过索引 0 的 self/cls 参数)
if args_node is not None:
ags2: ast.Arguments* = ast.Arguments*(args_node)
if ags2.args is not None:
alist2: list[ast.AST*]* = ags2.args
an2: sizet = len(alist2)
start_idx2: int = 0
if is_static_m == 0:
start_idx2 = 1
for ai2 in range(start_idx2, an2):
arg2: ast.Arg* = ast.Arg*(alist2.get(ai2))
if arg2 is not None and arg2.arg is not None:
param_ty2: llvmlite.LLVMType* = i32_ty
if arg2.annotation is not None:
resolved2: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg2.annotation, imported_modules, from_imports, trans, 0)
if resolved2 is None:
# fail-fast:参数有注解但类型解析失败,不再静默保持 i32
self.Gen.TypeGenErr.fatal_error(arg2, f"方法 '{class_name}.{method_name}' 参数 '{arg2.arg}' 类型解析失败", "NameError")
resolved2 = i32_ty # 不可达(fatal_error 已退出)
if resolved2 is not None:
# 与 add_param 保持一致:裸类形参 → Ptr(struct)(对象即指针)
# CArray 形参 → 元素指针 ty*(数组衰减,与 add_param 一致)
match resolved2:
case llvmlite.LLVMType.Void():
# void 不是合法形参类型(用户 2026-10-01,见上方同款判据)
self.Gen.TypeGenErr.fatal_error(arg2, f"方法 '{class_name}.{method_name}' 形参 '{arg2.arg}' 不能是 void 类型(void 只能用于 void* 与 typedef 别名)", "TypeError")
return 0
case llvmlite.LLVMType.Struct(_, _, _):
param_ty2 = llvmlite.Ptr(pool, resolved2)
case llvmlite.LLVMType.Array(aelem_ty2, _):
param_ty2 = llvmlite.Ptr(pool, aelem_ty2)
case _:
param_ty2 = resolved2
alloca: llvmlite.Value* = llvmlite.build_alloca(func_builder, param_ty2)
if alloca is not None:
self.Gen.VarGen.define_var(trans.SymTab, arg2.arg, alloca)
# 存储原始类型注解的类名(方法调用检测时,Ptr(i8) 回退到类名查找结构体)
# 传 pool:泛型 Subscript 返回 mangled name(对齐 HandlesAnnAssign 修复)
if arg2.annotation is not None:
cls_nm_oc: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(arg2.annotation, imported_modules, pool, trans._from_imports)
if cls_nm_oc is not None:
self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, arg2.arg, cls_nm_oc)
pname2: str = pool.alloc(32)
if pname2 is not None:
viperlib.snprintf(pname2, 32, "%%%s", arg2.arg)
param_val: llvmlite.Value* = llvmlite.SSAValue(pool, param_ty2, pname2)
llvmlite.build_store(func_builder, param_val, alloca)
# OOP iter 协议:__next__ 的 stop-flag 参数初始化(store 0)并保存
# 指针到 trans,供 translate_raise 的 raise StopIteration 写 1。
# 参数名必须与上面 add_param 的 "%__stop_iter_flag__" 一致。
if string.strcmp(method_name, "__next__") == 0:
i1_ty_sf: llvmlite.LLVMType* = llvmlite.Int1(pool)
if i1_ty_sf is not None:
i1_ptr_ty_sf: llvmlite.LLVMType* = llvmlite.Ptr(pool, i1_ty_sf)
if i1_ptr_ty_sf is not None:
flag_ref_sf: llvmlite.Value* = llvmlite.SSAValue(pool, i1_ptr_ty_sf, "%__stop_iter_flag__")
if flag_ref_sf is not None:
trans._stop_iter_flag_ptr = flag_ref_sf
zero_i1_sf: llvmlite.Value* = llvmlite.ConstInt(pool, i1_ty_sf, 0, "0")
if zero_i1_sf is not None:
llvmlite.build_store(func_builder, zero_i1_sf, flag_ref_sf)
# 保存模块级作用域状态
old_func: llvmlite.Function* = trans._cur_func
old_builder: llvmlite.IRBuilder* = trans._cur_builder
old_global_count: int = trans._global_name_count
old_nonlocal_count: int = trans._nonlocal_name_count
old_env_count: int = trans._closure_env_count
old_stop_iter_flag: llvmlite.Value* = trans._stop_iter_flag_ptr
trans._cur_func = func
trans._cur_builder = func_builder
GenApi.clear_scope_names(trans)
# 预扫描方法体:为局部变量提前创建 alloca
# 必须先预扫描 Global/Nonlocal 声明(对齐 FuncGenDef 模块级函数路径),
# 否则 `global BootInfo` 未注册时 prescan 把 BootInfo 当局部创建 alloca,
# 后续翻译 is_global_name 为真又写全局 → 局部 alloca 永不写入 → 读全 0
# (main.vp __init__ BootInfo memmap 全 0 根因)。
body: list[ast.AST*]* = fd.children
if body is not None:
bn: sizet = len(body)
for bi in range(bn):
stmt: ast.AST* = body.get(bi)
if stmt is not None:
self.Gen.StmtGenDecl.pre_scan_scope_decls(trans, stmt)
for bi in range(bn):
stmt: ast.AST* = body.get(bi)
if stmt is not None:
self.Gen.StmtGenDecl.pre_scan_allocas(trans, stmt)
# 翻译方法体
if body is not None:
bn2: sizet = len(body)
for bi2 in range(bn2):
stmt2: ast.AST* = body.get(bi2)
if stmt2 is not None:
self.Gen.StmtGen.translate_stmt(trans, stmt2)
# 方法末尾收尾与模块级函数(FuncGenMixin.translate_function_def)同语义——
# 编译器不替代码发明返回值,三种合法收尾:
# ① void 方法掉出末尾 → ret void(无值可发明,Python 语义)
# ② 末句是控制流语句(match/if 全分支 return)→ merge 块结构性不可达 →
# build_unreachable(GCC/Clang 对 fall-through 视为 UB 同语义)
# ③ 无返回类型注解的方法 → 视为无返回值过程,ConstZero 兜底
# 有 -> 注解且末句普通语句掉出末尾 → fail-fast;不做 noreturn 函数名白名单
# (错误终止路径也必须在源码层面保证块收尾)。返回类型按实际 ret_ty 构造
# (硬编码 ret i32 0 会对 i64 返回方法报 llc 结果类型不匹配)。
last_is_return: int = 0
# 追加89:bn3 提前初始化,避免 body is None 时下方引用未定义变量
bn3: sizet = 0
if body is not None:
bn3 = len(body)
if bn3 > 0:
last_stmt_m: ast.AST* = body.get(bn3 - 1)
if last_stmt_m is not None and last_stmt_m.kind() == ast.ASTKind.Return:
last_is_return = 1
if last_is_return == 0:
if llvmlite.builder_cur_block_is_terminated(func_builder) == 0:
last_is_ctrl: int = 0
if bn3 > 0:
last_stmt3_m: ast.AST* = body.get(bn3 - 1)
if last_stmt3_m is not None:
k3m: int = last_stmt3_m.kind()
if k3m == ast.ASTKind.If or k3m == ast.ASTKind.While or \
k3m == ast.ASTKind.For or k3m == ast.ASTKind.Try or \
k3m == ast.ASTKind.With or k3m == ast.ASTKind.Match:
last_is_ctrl = 1
_is_void: int = 0
if ret_ty is not None:
match ret_ty:
case llvmlite.LLVMType.Void():
_is_void = 1
case _:
pass
# 追加91:返回注解层面的"无返回值"判据(与模块级函数
# FuncGenDef.vp「_is_void 双保险」同款、同语义,追加89 的
# 方法/函数同步约定)。void 方法无需显式 return,掉出末尾合法;
# 报文里的"(返回类型 void)"来自 TypePrint 的 `case _` 兜底亦可
# 打印 void,不能当作已识别依据,故补注解判据。
# ① t.CVoiX / ② 裸名 CVoid:经既有判据
# TypeGenResolve._is_cvoid_marker(TypeGenResolve.vp:1484)
# ③ -> None(CONST_NONE):解析即 Void(TypeGenResolve.vp:1996-1999)
if _is_void == 0 and fd.returns is not None:
if self.Gen.TypeGenResolve._is_cvoid_marker(fd.returns, imported_modules) != 0:
_is_void = 1
elif fd.returns.kind() == ast.ASTKind.Constant:
ret_none_cn_m: ast.Constant* = (ast.Constant*)(fd.returns)
if ret_none_cn_m.const_kind == ast.CONST_NONE:
_is_void = 1
if _is_void != 0:
llvmlite.build_ret_void(func_builder)
elif last_is_ctrl != 0 and trans._ctrl_merge_reachable == 0:
# 非 void 方法末句是控制流且 merge 块**结构性**不可达(全分支 return,无 br
# 到 merge)→ LLVM 语义 unreachable。merge 可达时须正常收尾(下方
# ConstZero / fail-fast):可达块的 unreachable 是 UB,llc 不生成指令 ⇒
# 执行掉进布局紧随其后的块(同 FuncGenDef 模块级函数口径;实测反例见该处
# 注释里的 _TypePrintDepth 用 snprintf 返回值当 tag 走进 switch 跳转表)。
llvmlite.build_unreachable(func_builder)
elif fd.returns is None:
# 无返回类型注解 → 无返回值过程方法,默认零值返回
if ret_ty is not None:
_m_zero_val: llvmlite.Value* = llvmlite.ConstZero(pool, ret_ty)
if _m_zero_val is not None:
llvmlite.build_ret(func_builder, _m_zero_val)
else:
self.Gen.TypeGenErr.fatal_error(fd, "方法流程掉出末尾但未显式 return", "TypeError")
else:
self.Gen.TypeGenErr.fatal_error(fd, "方法流程掉出末尾但未显式 return", "TypeError")
else:
# fail-fast:有 -> 注解但流程真实掉出末尾(traceback 风格)
if ret_ty is not None:
rt_name_m: str = pool.alloc(64)
if rt_name_m is not None:
llvmlite.TypePrint(rt_name_m, 64, ret_ty, pool)
if rt_name_m[0] == '\0':
rt_name_m = "?"
err_fn_m: str = pool.alloc(256)
if err_fn_m is not None:
viperlib.snprintf(err_fn_m, 256, "方法 '%s' 流程掉出末尾但未显式 return(返回类型 %s),请补显式 return(如 return 0 / return null)", method_name, rt_name_m)
self.Gen.TypeGenErr.fatal_error(fd, err_fn_m, "TypeError")
self.Gen.TypeGenErr.fatal_error(fd, "方法流程掉出末尾但未显式 return", "TypeError")
else:
self.Gen.TypeGenErr.fatal_error(fd, "方法流程掉出末尾但未显式 return", "TypeError")
# 恢复模块级作用域
self.Gen.VarGen.exit_scope(trans.SymTab)
trans._cur_func = old_func
trans._cur_builder = old_builder
trans._global_name_count = old_global_count
trans._nonlocal_name_count = old_nonlocal_count
trans._closure_env_count = old_env_count
trans._stop_iter_flag_ptr = old_stop_iter_flag
if dbg_pushed != 0:
DebugInfoGen.PopFunction(func_builder)
return 0
# ============================================================
# _generate_before_init — 生成 __before_init__ 函数
#
# 生成: define void @SHA1.ClassName.__before_init__(Ptr(StructTy) %self)
#
# 执行:
# 1. store zeroinitializer 到 %self(零值填充)
# 2. 逐字段 store 默认值(如有)
# 3. ret void
# ============================================================
def _generate_before_init(self, trans: HT.Translator*,
struct_ty: llvmlite.LLVMType*,
class_name: str) -> int:
"""生成 __before_init__ 函数,返回 0"""
if trans is None or struct_ty is None or class_name is None:
return 0
pool: memhub.MemBuddy* = trans.Pool
mod: llvmlite.LLVMModule* = trans.Module
funcs_ptr: GenTypes.FuncEntry* = trans._funcs
func_count: int = trans._func_count
self_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, struct_ty)
void_ty: llvmlite.LLVMType* = llvmlite.Void(pool)
# 构建函数名: ClassName.__before_init__
full_name_buf: str = pool.alloc(128)
if full_name_buf is None: return 0
viperlib.snprintf(full_name_buf, 128, "%s.__before_init__", class_name)
full_name: str = full_name_buf
mangled_name: str = self.Gen.FuncGenBase._mangle_name(trans, full_name)
# 创建函数
func: llvmlite.Function* = llvmlite.create_function(pool, mod, mangled_name, void_ty)
if func is None: return 0
# 注册到函数表
max_funcs: int = 1024
rc_add: int = self.Gen.CallGenLeafKit.add_func_to_table(funcs_ptr, func_count, full_name, func, max_funcs, None, 0, 0, pool)
if rc_add == 0:
trans._func_count = func_count + 1
# 添加 self 参数
llvmlite.add_param(pool, func, self_ptr_ty, "%self")
# 参数添加完成,补全全局函数表的 Spec
rc_ut: int = self.Gen.CallGenLeafKit.update_global_func_spec(full_name, func)
# 创建 entry 块 + builder
entry_blk: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, "entry")
if entry_blk is None: return 0
func_builder: llvmlite.IRBuilder* = llvmlite.new_builder(pool, func)
if func_builder is None: return 0
llvmlite.position_at_end(func_builder, entry_blk)
# 注册 self 为 SSA 值
self_val: llvmlite.Value* = llvmlite.SSAValue(pool, self_ptr_ty, "%self")
# 1. 零值填充: store zeroinitializer, Ptr(struct_ty)* %self
# 空 struct({})在 LLVM 中不允许 zeroinitializer(invalid type for null constant),
# 无字段类(接口/标记类,如多继承的 Vec2Extra)跳过零值填充;
# @t.CVTable 类必有 vtable 指针字段(非空),不受影响。
struct_fcount_zi: int = llvmlite.get_struct_fcount(struct_ty)
if struct_fcount_zi > 0:
zero: llvmlite.Value* = llvmlite.ConstZero(pool, struct_ty)
if zero is not None:
llvmlite.build_store(func_builder, zero, self_val)
# 1.5 如果有虚表,store vtable 全局地址到 __vtable__ 字段(索引 0)
# 用 find_struct_by_type 定位 entry,规避跨模块同名 find_struct 找错
bi_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(struct_ty)
bi_has_vt: int = 0
if bi_entry is not None:
bi_has_vt = bi_entry.HasVTable
if bi_has_vt == 1:
vt_method_count: int = 0
if bi_entry is not None:
vt_method_count = bi_entry.VTableMethodCount
if vt_method_count > 0:
# 构造 vtable 类型 { i8*, i8*, ..., i8* }
vt_i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
vt_i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, vt_i8_ty)
vt_first_node: llvmlite.ParamNode* = None
vt_prev_node: llvmlite.ParamNode* = None
for vti in range(vt_method_count):
vtnode: llvmlite.ParamNode* = llvmlite.new_param_node(pool, vt_i8_ptr_ty)
if vtnode is None:
continue
if vt_first_node is None:
vt_first_node = vtnode
if vt_prev_node is not None:
llvmlite.paramnode_set_next(vt_prev_node, vtnode)
vt_prev_node = vtnode
vt_ty: llvmlite.LLVMType* = llvmlite.Struct(pool, vt_first_node, vt_method_count, None)
vt_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, vt_ty)
# 构造 vtable 全局变量名
vt_name_buf: str = pool.alloc(128)
if vt_name_buf is not None:
viperlib.snprintf(vt_name_buf, 128, "%s.vtable", class_name)
vt_full_name: str = vt_name_buf
vt_mangled: str = self.Gen.FuncGenBase._mangle_name(trans, vt_full_name)
# GEP 到 __vtable__ 字段(索引 0)
vt_slot: llvmlite.Value* = llvmlite.build_gep_struct(func_builder, struct_ty, vt_i8_ptr_ty, self_val, 0)
if vt_slot is not None:
# 创建全局变量引用 Value
vt_ref: llvmlite.Value* = llvmlite.new_value(pool)
if vt_ref is not None:
llvmlite.value_set_ty(vt_ref, vt_ptr_ty)
llvmlite.value_set_isconst(vt_ref, 1)
# Name 格式: @"mangled" 或 @mangled
vt_ref_name: str = pool.alloc(256)
if vt_ref_name is not None:
vt_ref_name[0] = '\0'
if self._vtable_name_needs_quote(vt_mangled) != 0:
viperlib.snprintf(vt_ref_name, 256, "@\"%s\"", vt_mangled)
else:
viperlib.snprintf(vt_ref_name, 256, "@%s", vt_mangled)
llvmlite.value_set_name(vt_ref, vt_ref_name)
# bitcast 到 i8*
vt_as_i8: llvmlite.Value* = llvmlite.build_bitcast(func_builder, vt_ref, vt_i8_ptr_ty)
if vt_as_i8 is not None:
llvmlite.build_store(func_builder, vt_as_i8, vt_slot)
# 2. 逐字段 store 默认值(用 bi_entry 直接访问,规避跨模块同名 find_struct 找错)
if bi_entry is not None:
for fi in range(bi_entry.FieldCount):
fe: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(bi_entry, fi)
if fe is None or fe.DefaultVal is None:
continue
# 静态数组字段 + 列表字面量默认值 → 整体置零 + 逐元素 GEP+store
# (与局部 AnnAssign 数组初始化一致;translate_value 会把列表字面量
# 误译成动态 list 对象指针,coerce 到 [N x elem] 会失败)。
# 单参 CArray 字段(如 t.CArray[str] = [...])经类体字段收集
# 推断为静态数组,此处保证 __before_init__ 填充真实字符串指针。
if fe.Ty is not None and self.Gen.ExprGenBase.is_array_type(fe.Ty) != 0 \
and fe.DefaultVal.kind() == ast.ASTKind.List:
field_ptr_arr: llvmlite.Value* = llvmlite.build_gep_struct(func_builder, struct_ty, fe.Ty, self_val, fe.Index)
if field_ptr_arr is not None:
zero_arr_f: llvmlite.Value* = llvmlite.ConstZero(pool, fe.Ty)
if zero_arr_f is not None:
llvmlite.build_store(func_builder, zero_arr_f, field_ptr_arr)
# Mixin 静态绑定:self._xxx 只沿本类查找,跨 Mixin 方法
# (AssignGenMixin._init_global_array_from_list)会生成不存在
# 符号 → 必须走 HandlesAnnAssign 模块级委托(延迟 import Gen)。
# owner_ctx 只服务空元素 fail-fast 报文(类名 + 字段名 + 下标)。
arr_ctx: str = pool.alloc(192)
if arr_ctx is not None:
fname_dbg_v: str = fe.Name
if fname_dbg_v is None: fname_dbg_v = "?"
viperlib.snprintf(arr_ctx, 192, "类 %s 字段 %s:", class_name, fname_dbg_v)
self.Gen.AssignGen._init_global_array_from_list(func_builder, pool, mod, field_ptr_arr, fe.DefaultVal, trans, arr_ctx)
continue
# 翻译默认值表达式
default_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(func_builder, pool, mod, fe.DefaultVal, None, 0, trans)
if default_val is None: continue
# GEP 到字段
field_ptr: llvmlite.Value* = llvmlite.build_gep_struct(func_builder, struct_ty, fe.Ty, self_val, fe.Index)
if field_ptr is None: continue
# 类型转换并 store
default_val = self.Gen.ExprGenBase.coerce_to_type(func_builder, default_val, fe.Ty, fe.DefaultVal)
llvmlite.build_store(func_builder, default_val, field_ptr)
# 3. ret void
llvmlite.build_ret_void(func_builder)
return 0