Files
ViperC/App/lib/core/Gen/ExprGenConst.vp
T
TermiNexus 89995dffde refactor(gen): 去除 Handles 委托层——链式化 + GenTypes/GenApi 迁移
- Gen 内部 1742 处 HandlesX.func() 改 self.Gen.Y.method() 链式调用
- 数据类/常量迁移 GenTypes.vp(10 类 + CLASS_META_*/META_*/SEQ_KIND_*/FIELD_MAX 等)
- 管线/保留文件经 GenApi.vp 模块级入口(48 函数带类型注解,从 Gen 方法签名自动生成)
- 删除 26 个纯委托 Handles 文件,仅留 5 个实体文件(Base/Translator/Expr/ExprCall/ExprCallTable)
- 修复字段分裂:TypeGenErr CurrentFile/TraceCount 链式化;CallGenBase TempDir/Cexport 真源化
2026-08-21 06:32:40 +08:00

390 lines
19 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 w32.fileio as fileio
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.StubMerger as StubMerger
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config
import lib.Projectrans.Utils as Utils
import lib.core.Gen.ExprGenBase as ExprGenBase
# ============================================================
# ExprGenConst - ExprGenMixin 拆分(阶段 D):常量/全局字符串/list 字面量翻译
#
# 继承 ExprGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesExpr 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class ExprGenConst(ExprGenBase.ExprGenBase):
"""常量/全局字符串/list 字面量翻译"""
# ============================================================
# 创建全局字符串常量
# ============================================================
def create_global_string(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
str_val: str,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""创建全局字符串常量并返回 i8* bitcast"""
escaped: str = self.escape_llvm_string(pool, str_val)
if escaped is None:
return None
slen: t.CSizeT = string.strlen(str_val)
count: int = slen + 1
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
arr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty, count)
# 使用模块级计数器(trans._label_counter)避免跨函数命名冲突
str_idx: int = trans._label_counter
trans._label_counter += 1
# 字符串名加 SHA1 前缀,和函数导出规则一致,避免跨模块重名
gv_name: str = pool.alloc(48)
if gv_name is None:
return None
if trans.ModuleSha1 is not None:
viperlib.snprintf(gv_name, 48, ".str.%s.%d", trans.ModuleSha1, str_idx)
else:
viperlib.snprintf(gv_name, 48, ".str.%d", str_idx)
gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gv_name, arr_ty)
if gv is None:
return None
llvmlite.module_add_global(mod, gv)
# 去掉 private 链接,和函数一致:stub 中 declare,text 中 define
llvmlite.global_set_unnamed_addr(gv, 1)
llvmlite.global_set_constant(gv, 1)
# LLVM 22+ 不允许 external linkage 全局变量带初始值,设为 internal
gv.Linkage = "internal"
gv.Initializer = escaped
arr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, arr_ty)
gv_ref_name: str = pool.alloc(64)
if gv_ref_name is None:
return None
viperlib.snprintf(gv_ref_name, 64, "@%s", gv.Name)
gv_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, arr_ptr_ty, gv_ref_name)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
bc: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, gv_ref, i8_ptr_ty)
return bc
# ============================================================
# 翻译常量表达式
# ============================================================
def translate_constant(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译常量(int/str/bool)"""
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn is None:
return None
ck: int = cn.const_kind
if cn.const_kind == ast.CONST_INT:
# 超出 i32 范围则用 i64(避免常量创建时被截断)
iv: t.CInt64T = cn.int_val
if iv > 2147483647 or iv < -2147483648:
return llvmlite.const_int64(pool, iv)
return llvmlite.const_int32(pool, iv)
elif cn.const_kind == ast.CONST_FLOAT:
# 浮点常量默认创建为 double(64 位),赋值时由 coerce_to_type 自动 fptrunc
double_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
return llvmlite.ConstFloat(pool, double_ty, cn.float_val)
elif cn.const_kind == ast.CONST_CHAR:
# 单引号单字符 → i32 值(字符 ASCII 码),coerce_to_type 会 trunc 为 i8
cv: t.CInt64T = cn.int_val
return llvmlite.const_int32(pool, cv)
elif cn.const_kind == ast.CONST_STR:
sv: str = cn.str_val
if sv is None:
return None
r: llvmlite.Value | t.CPtr = self.create_global_string(builder, pool, mod, sv, trans)
return r
elif cn.const_kind == ast.CONST_BOOL:
if cn.int_val != 0:
return llvmlite.const_int32(pool, 1)
return llvmlite.const_int32(pool, 0)
elif cn.const_kind == ast.CONST_NONE:
# None → i8* null(空指针常量),用于 `p is None` / `p is not None` 比较
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
if i8_ty is None:
return None
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
if i8_ptr_ty is None:
return None
rv_none: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, i8_ptr_ty, "null")
return rv_none
return None
# ============================================================
# _infer_elem_type_from_value — 从 LLVM Value 推断 list 元素类型名
#
# 用于 list 字面量 [a, b, c] 的元素类型推断
# ============================================================
def _infer_elem_type_from_value(self, pool: memhub.MemBuddy | t.CPtr,
value: llvmlite.Value | t.CPtr) -> str:
"""从 LLVM Value 的类型推断 list 元素类型名
fail-fast:值类型缺失/无法映射时返回 None(调用方携带节点报错终止),
绝不静默回退 "str"(掩盖上游 Value 类型缺失根因)。
"""
if value is None or value.Ty is None:
return None
ty: llvmlite.LLVMType | t.CPtr = value.Ty
bits: int = self.get_llvm_type_bits(ty)
if bits != 0:
if bits == 32:
return "int"
if bits == 64:
return "CSizeT"
if bits == 8:
return "CInt8T"
if bits == 16:
return "CInt16T"
return None
if self.is_ptr_type(ty) != 0:
return "str"
fbits: int = self.get_llvm_float_bits(ty)
if fbits != 0:
if fbits == 64:
return "CDouble"
if fbits == 32:
return "CFloat"
return None
# ============================================================
# _find_pool_var — 查找上下文中的 pool 变量
#
# 依次尝试 "pool", "_mbuddy", "mbuddy", "mb"
# ============================================================
def _find_pool_var(self, trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""查找上下文中的 pool 变量,返回 alloca 或 None
查找顺序: pool → _mbuddy → mbuddy → mb
兜底: 若都未找到,注册外部全局 @_mbuddy (i8*) 供函数内动态 list 创建使用。
注意: 模块级 t.CArray 初始化不走此路径(由 _init_global_array_from_list 处理),
此兜底仅用于函数内的动态 list 字面量(如 op=[...])。
@_mbuddy 由入口模块(如 Phase2.py)定义并初始化,其他模块以 external 引用。
"""
if trans is None:
return None
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, "pool")
if alloca is not None:
return alloca
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "_mbuddy")
if alloca is not None:
return alloca
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "mbuddy")
if alloca is not None:
return alloca
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "mb")
if alloca is not None:
return alloca
# 兜底: 创建当前模块 mangled 的 @_mbuddy 定义(linkonce_odr + zeroinitializer)
# 对齐模块级全局带 SHA1 前缀命名:裸名 @_mbuddy 依赖链接合并共享
# (--allow-multiple-definition),带前缀后每模块独立。linkonce_odr 允许
# 链接器合并同模板重复特化定义,zeroinitializer 保证编译链接通过。
# 运行时若该模块未在入口 main 赋值 _mbuddy(null pool 上动态建 list),
# 由真实崩溃暴露(fail-fast 哲学,不掩盖上游未初始化)。
pool: memhub.MemBuddy | t.CPtr = trans.Pool
if pool is None or trans.Module is None or trans.SymTab is None:
return None
# 先检查是否已注册到模块作用域(避免重复创建全局变量)
existing: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_module_var(trans.SymTab, "_mbuddy")
if existing is not None:
return existing
# 创建 i8* 类型(t.CVoid | t.CPtr = i8*)
i8_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_fb)
# 当前模块 mangled 名(@<模块sha1>._mbuddy)
mb_name_fb: str = self.Gen.FuncGenBase._mangle_name(trans, "_mbuddy")
# 创建 linkonce_odr 定义(可被链接器合并,自给自足保证链接通过)
gv_ext: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, mb_name_fb, i8_ptr_ty_fb)
if gv_ext is not None:
gv_ext.Linkage = "linkonce_odr"
gv_ext.Initializer = "zeroinitializer"
llvmlite.module_add_global(trans.Module, gv_ext)
# 创建 Value 引用(@<模块sha1>._mbuddy, 类型为 i8**)
i8_ptr_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty_fb)
ref_name_fb: str = pool.alloc(96)
if ref_name_fb is not None:
# mangled 名带 SHA1 前缀(数字开头),引用必须加引号(见 make_global_ref)
if self.Gen.ClassGenVTable._vtable_name_needs_quote(mb_name_fb) != 0:
viperlib.snprintf(ref_name_fb, 96, "@\"%s\"", mb_name_fb)
else:
viperlib.snprintf(ref_name_fb, 96, "@%s", mb_name_fb)
gv_ref_fb: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb)
# 注册到模块作用域,避免重复创建
self.Gen.VarGen.define_module_var(trans.SymTab, "_mbuddy", gv_ref_fb)
return gv_ref_fb
# ============================================================
# translate_list_literal — 翻译 list 字面量 [a, b, c]
#
# 将 list 字面量翻译为 list[T](pool) 构造 + append 调用序列
#
# Args:
# builder: IRBuilder
# pool: 编译器内存池(MemBuddy)
# mod: LLVMModule
# node: ast.List 节点
# elem_type_name: 元素类型名(如 "str", "int"),None 表示从元素推断
# trans: Translator 对象
#
# Returns:
# list 对象指针(Ptr(list[T] struct)),None 失败
# ============================================================
def translate_list_literal(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
elem_type_name: str,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""翻译 list 字面量 [a, b, c] → list[T](pool) + append 序列"""
if node is None or trans is None:
return None
list_node: ast.List | t.CPtr = (ast.List | t.CPtr)(node)
if list_node is None:
return None
elts: list[ast.AST | t.CPtr] | t.CPtr = list_node.elts
elts_count: t.CSizeT = 0
if elts is not None:
elts_count = len(elts)
# 推断元素类型
inferred_type_name: str = elem_type_name
first_elem_val: llvmlite.Value | t.CPtr = None
if inferred_type_name is None:
if elts_count > 0:
first_elem_node: ast.AST | t.CPtr = elts.get(0)
first_elem_val = self.Gen.ExprGen.translate_value(builder, pool, mod, first_elem_node, None, 0, trans)
if first_elem_val is None or first_elem_val.Ty is None:
# fail-fast:首元素翻译失败/Value 类型缺失,立即终止编译(暴露上游根因)
self.Gen.TypeGenErr.fatal_error(
first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
inferred_type_name = self._infer_elem_type_from_value(pool, first_elem_val)
if inferred_type_name is None:
# fail-fast:首元素 Value 类型无法映射为 list 元素类型,立即终止编译
self.Gen.TypeGenErr.fatal_error(
first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
else:
# 空 list 无元素可推断,保持默认 str(list[str])
inferred_type_name = "str"
# 查找 pool 变量
pool_alloca: llvmlite.Value | t.CPtr = self._find_pool_var(trans)
if pool_alloca is None:
self.Gen.TypeGenErr.fatal_error(node, "list literal requires pool variable in context")
return None
# load pool 变量值(i8*)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
pool_val: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, pool_alloca)
if pool_val is None:
return None
# 特化 list[T] 类(elem_size=8:str 是指针,4 会导致双元素重叠损坏)
type_args: list[str] | t.CPtr = list[str](pool, 8)
type_args.append(inferred_type_name)
spec_name: str = self.Gen.ClassGenGeneric._specialize_generic_class(trans, "list", type_args)
if spec_name is None:
self.Gen.TypeGenErr.fatal_error(node, "list literal generic specialization failed")
return None
# 查找 struct entry
struct_entry: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(spec_name)
if struct_entry is None:
self.Gen.TypeGenErr.fatal_error(node, "list literal specialized class not registered")
return None
struct_ty: llvmlite.LLVMType | t.CPtr = struct_entry.Ty
if struct_ty is None:
return None
# alloca 临时 list 空间
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, struct_ty)
tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty)
if tmp is None:
return None
# 存储指针:默认用 alloca,如果有 __new__ 则用 __new__ 返回的指针
# __new__ 是可选的,仅当泛型类模板定义了 __new__ 方法时才调用
# (与 _translate_struct_ctor/_translate_generic_ctor_call 保持一致,
# 避免为无 __new__ 的类生成过度声明)
storage_ptr: llvmlite.Value | t.CPtr = tmp
has_new_flag: int = 0
if struct_entry is not None:
has_new_flag = struct_entry.HasNew
if has_new_flag != 0:
new_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
if new_args is None:
return None
string.memset(new_args, 0, 8 * 32)
new_args[0] = t.CSizeT(pool_val)
new_ptr: llvmlite.Value | t.CPtr = self.Gen.CallGenCall._call_method_on_ptr(
pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans)
if new_ptr is not None:
storage_ptr = new_ptr
# 调用 __before_init__(storage_ptr)
self.Gen.CallGenCall._call_method_on_ptr(
pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans)
# 调用 __init__(storage_ptr, pool_val, elem_size=0)
init_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
if init_args is None:
return None
string.memset(init_args, 0, 8 * 32)
init_args[0] = t.CSizeT(pool_val)
elem_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
init_args[1] = t.CSizeT(elem_size_val)
self.Gen.CallGenCall._call_method_on_ptr(
pool, builder, mod, spec_name, "__init__", storage_ptr, init_args, 2, trans)
# 对每个元素调用 append(storage_ptr, elem_val)
if elts_count > 0:
# 第一个元素可能已经翻译过
if first_elem_val is not None:
append_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
if append_args is not None:
string.memset(append_args, 0, 8 * 32)
append_args[0] = t.CSizeT(first_elem_val)
self.Gen.CallGenCall._call_method_on_ptr(
pool, builder, mod, spec_name, "append", storage_ptr, append_args, 1, trans)
# 翻译剩余元素
i: t.CSizeT = 1
while i < elts_count:
elem_node_i: ast.AST | t.CPtr = elts.get(i)
elem_val_i: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(builder, pool, mod, elem_node_i, None, 0, trans)
if elem_val_i is not None:
ap_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
if ap_args is not None:
string.memset(ap_args, 0, 8 * 32)
ap_args[0] = t.CSizeT(elem_val_i)
self.Gen.CallGenCall._call_method_on_ptr(
pool, builder, mod, spec_name, "append", storage_ptr, ap_args, 1, trans)
i += 1
return storage_ptr