Files
ViperC/_ExprGenMixin_backup.vp
T
2026-08-17 11:23:55 +08:00

6204 lines
333 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 t, c
from stdint import *
import ast
import memhub
import string
import llvmlite
import stdlib
import stdio
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.HandlesVar as HandlesVar
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprOps as HandlesExprOps
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
import lib.core.Handles.HandlesType as HandlesType
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.Handles.HandlesEnum as HandlesEnum
import lib.core.Handles.HandlesClassDef as HandlesClassDef
import lib.core.Handles.HandlesImports as HandlesImports
import lib.core.StubMerger as StubMerger
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config
import lib.Projectrans.Utils as Utils
# ============================================================
# docstring 登记表容量(__doc__ 魔法属性支持)
#
# DOC_MAX: 登记条目上限(预注册阶段所有 App/includes 文件的
# 函数/类/方法 docstring,幂等查重)
# DOC_KEY_SZ: key "{module_sha1}.{symbol}" 缓冲宽度
# DOC_VAL_SZ: docstring 内容缓冲宽度
# ============================================================
DOC_MAX: t.CDefine = 512
DOC_KEY_SZ: t.CDefine = 192
DOC_VAL_SZ: t.CDefine = 4096
# ============================================================
# f-string 翻译常量(对齐 TPC HandlesExprFormat)
#
# FSTR_BUF_SIZE: 运行时栈缓冲大小(snprintf 结果缓冲)
# FSTR_SPEC_MAX: 编译期 format_spec 解析缓冲宽度
# FSTR_FMT_MAX: 编译期 C printf 格式串缓冲宽度
# ============================================================
FSTR_BUF_SIZE: t.CDefine = 1024
FSTR_SPEC_MAX: t.CDefine = 64
FSTR_FMT_MAX: t.CDefine = 64
# ============================================================
# ExprGenMixin - 表达式处理 Mixin(对象化 P3,wiki/14-oo-refactor.md)
#
# 从 HandlesExpr.vp(48 函数,无全局状态)与 HandlesExprOps.vp
# 迁移:模块级纯函数 → 类方法(self 替代,参数保留含 builder/pool)。
#
# 迁移原则(与 P2 一致,行为零变化):
# 1. 方法保留原函数签名(含 builder/pool 首参),仅函数体内
# 本模块函数调用改为 self.xxx(...)
# 2. 外部模块调用(HandlesExprCall.xxx / HandlesType.xxx 等)保留模块前缀
# 3. HandlesExpr.vp / HandlesExprOps.vp 原函数保留为"委托":本地
# 模块级单例 _g_gen(BuildPipeline/main 注入)cast 成 Generator 类型
# 4. ExprHandle 类保留在 HandlesExpr.vp(translator handler 模式)
#
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
# ============================================================
# ============================================================
# ExprGenMixin - 表达式/常量/下标/属性访问/运算符
# ============================================================
@t.NoVTable
class ExprGenMixin:
"""表达式处理 Mixin(纯方法化迁移,无状态字段)"""
# --- docstring 登记表字段(__doc__ 魔法属性支持)---
DocKeys: bytes
DocValues: bytes
DocCount: int
# --- OOM 诊断:跨模块 CDefine 查找调用计数(CONST_INT 参数翻译疑似循环查找) ---
DiagCDefineCount: int
# --- OOM 诊断:translate_name_value 调用计数(顶层 CDefine 查找 14593 次来源) ---
DiagNameValueCount: int
# --- OOM 诊断:translate_value 调用计数(CONST_INT 循环调用源定位) ---
DiagTranslateValueCount: int
# ============================================================
# P3 迁移方法(函数体来自 HandlesExpr.vp 模块级函数,
# 本模块函数调用改 self.,外部模块调用保留模块前缀)
# ============================================================
# ============================================================
# 从 AST 节点提取函数名
# ============================================================
def get_func_name(self, func_node: ast.AST | t.CPtr) -> str:
"""从函数节点提取函数名"""
if func_node is None:
return None
k: int = func_node.kind()
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(func_node)
return nm.id
elif k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(func_node)
return at.attr
return None
# ============================================================
# 转义 LLVM IR 字符串字面量
# ============================================================
def escape_llvm_string(self, pool: memhub.MemBuddy | t.CPtr,
src: str) -> str:
"""将字符串转换为 LLVM IR 字符串字面量 c"...\\00" """
if src is None:
return None
slen: t.CSizeT = string.strlen(src)
alloc_size: t.CSizeT = 4 * slen + 8
buf: str = pool.alloc(alloc_size)
if buf is None:
return None
pos: t.CSizeT = 0
buf[pos] = 'c'
pos += 1
buf[pos] = '"'
pos += 1
for i in range(slen):
ch: t.CChar = src[i]
if ch == '\0':
break
if ch == '\"' or ch == '\\' or ch < 0x20:
buf[pos] = '\\'
pos += 1
hi: int = (ch >> 4) & 0xF
lo: int = ch & 0xF
buf[pos] = Utils.nibble_to_hex_char(hi, 1)
pos += 1
buf[pos] = Utils.nibble_to_hex_char(lo, 1)
pos += 1
else:
buf[pos] = ch
pos += 1
buf[pos] = '\\'
pos += 1
buf[pos] = '0'
pos += 1
buf[pos] = '0'
pos += 1
buf[pos] = '"'
pos += 1
buf[pos] = '\0'
return buf
# ============================================================
# 获取 LLVM 整数类型的位宽
# ============================================================
def get_llvm_type_bits(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
"""获取 LLVM 整数类型的位宽,非整数类型返回 0"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Int(bits):
return bits
case _:
return 0
# ============================================================
# 获取 LLVM 浮点类型的位宽
# ============================================================
def get_llvm_float_bits(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
"""获取 LLVM 浮点类型的位宽,非浮点类型返回 0"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Float(bits):
return bits
case _:
return 0
# ============================================================
# is_ptr_type - 检查类型是否为 Ptr
#
# 返回 1=是, 0=否。作为类型检查工具函数被广泛使用(50+ 处调用)。
# ============================================================
def is_ptr_type(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
"""检查 ty 是否是 Ptr 类型,返回 1=是, 0=否"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Ptr(pointee):
return 1
case _:
return 0
# ============================================================
# get_ptr_level - 计算指针层级
#
# 返回值:
# 0 = 非指针(数值/结构体等)
# 1 = 一级指针(如 i8*, Struct*)
# 2 = 二级指针(如 i8**, Struct**)
# 3+ = 更高级指针
#
# 用于比较运算的类型安全检查:
# - 数值(0) vs 数值(0) → 允许
# - 一级指针(1) vs 一级指针(1) → 允许
# - 数值(0) vs 指针(1+) → 禁止(fast-fail)
# - 不同级指针(1 vs 2) → 禁止(fast-fail)
# ============================================================
def get_ptr_level(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
"""计算指针层级,0=非指针, 1=一级指针, 2=二级指针..."""
if ty is None:
return 0
level: int = 0
cur: llvmlite.LLVMType | t.CPtr = ty
while cur is not None:
match cur:
case llvmlite.LLVMType.Ptr(pointee):
level += 1
cur = pointee
case _:
break
return level
# ============================================================
# _is_zero_int_literal - 判断 AST 节点是否为整数字面量 0
#
# 用于指针判空比较(ptr != 0 / ptr == 0):指针与字面量 0 比较时
# 0 视为 NULL,允许并转成 null 指针(比较类型检查的例外)。
# ============================================================
def _is_zero_int_literal(self, node: ast.AST | t.CPtr) -> int:
"""判断节点是否为整数字面量 0,返回 1=是 / 0=否"""
if node is None:
return 0
if node.kind() != ast.ASTKind.Constant:
return 0
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn is None or cn.const_kind != ast.CONST_INT:
return 0
if cn.int_val == 0:
return 1
return 0
# ============================================================
# is_array_type - 检查类型是否为 Array
#
# 返回 1=是, 0=否。类型检查工具函数。
# ============================================================
def is_array_type(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
"""检查 ty 是否是 Array 类型,返回 1=是, 0=否"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Array(_, _):
return 1
case _:
return 0
# ============================================================
# _zext_index_to_i32 - 数组下标索引零扩展
#
# LLVM GEP 对窄整数索引默认符号扩展:i8 0xE2(226) 会被当成 -30,
# 导致数组下标越界读(字节 >= 128 的乱码根因)。
# 数组下标语义为非负,<32 位整数索引一律 zext 到 i32。
# 仅用于数组 GEP;指针负偏移(p[-1])不经过此路径,不受影响。
# ============================================================
def _zext_index_to_i32(self, builder: llvmlite.IRBuilder | t.CPtr,
idx_val: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
"""将 <32 位整数下标零扩展到 i32(数组下标非负语义)"""
if idx_val is None or idx_val.Ty is None or builder is None:
return idx_val
idx_bits: int = self.get_llvm_type_bits(idx_val.Ty)
if idx_bits != 0 and idx_bits < 32:
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(builder.Pool)
return llvmlite.build_zext(builder, idx_val, i32_ty)
return idx_val
# ============================================================
# _get_custom_struct_cls_nm - 检查类型是否为已注册的自定义结构体
#
# 处理两种情况:
# - ty 直接是 Struct → 检查是否注册
# - ty 是 Ptr(Struct) → 检查 pointee 是否注册
#
# 返回类名字符串或 None。用于 subscript 操作中判断是否需要
# 转发到 __getitem__/__setitem__ 而非生成 GEP。
# ============================================================
def _get_custom_struct_cls_nm(self, pool: memhub.MemBuddy | t.CPtr,
ty: llvmlite.LLVMType | t.CPtr) -> str:
"""检查 ty 或 ty 的 pointee 是否是已注册的自定义结构体,返回类名或 None"""
if ty is None:
return None
# 直接检查 ty 是否是注册的结构体
cls_nm_direct: str = HandlesStruct.get_class_name_by_type(pool, ty)
if cls_nm_direct is not None:
return cls_nm_direct
# 如果 ty 是 Ptr,检查 pointee
if self.is_ptr_type(ty) != 0:
pointee_cs: llvmlite.LLVMType | t.CPtr = ty.Pointee
if pointee_cs is not None:
return HandlesStruct.get_class_name_by_type(pool, pointee_cs)
return None
# ============================================================
# _is_list_subscript_base - 判断下标基座是否为 list[T] 变量
#
# 属性访问的 Subscript 基座(如 lst[i].x)需要区分:
# - list[T] 变量:translate_subscript 走 __getitem__ 内联返回元素地址
# - 普通结构体指针数组(如 pts: Point | t.CPtr):get_subscript_ptr 返回元素地址
# 路由错误会让 list[T] 被当作结构体数组 GEP,访问错位崩溃。
# ============================================================
def _is_list_subscript_base(self, pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
sub_node: ast.AST | t.CPtr) -> int:
"""判断下标基座是否为 list[T] 变量(1=是)"""
if sub_node is None or trans is None:
return 0
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(sub_node)
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name:
return 0
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if nm.id is None:
return 0
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
if alloca is None or alloca.Ty is None:
return 0
if self.is_ptr_type(alloca.Ty) == 0:
return 0
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is None:
return 0
match pointee:
case llvmlite.LLVMType.Ptr(inner_ty):
lsn_ls: str = None
match inner_ty:
case llvmlite.LLVMType.Struct(_, _, lsn3):
lsn_ls = lsn3
case _:
pass
if lsn_ls is not None and string.strstr(lsn_ls, "list[") is not None:
return 1
case _:
pass
return 0
# ============================================================
# _sub_base_class_name - 解析下标基座变量的结构体类名
# ============================================================
def _sub_base_class_name(self, pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
sub_node: ast.AST | t.CPtr) -> str:
"""解析下标基座变量的结构体类名(无则 None)"""
if sub_node is None or trans is None:
return None
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(sub_node)
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name:
return None
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if nm.id is None:
return None
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
if alloca is None or alloca.Ty is None:
return None
if self.is_ptr_type(alloca.Ty) == 0:
return None
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is None:
return None
if self.is_ptr_type(pointee) != 0:
inner: llvmlite.LLVMType | t.CPtr = pointee.Pointee
if inner is not None:
return HandlesStruct.get_class_name_by_type(pool, inner)
return HandlesStruct.get_class_name_by_type(pool, pointee)
# ============================================================
# _sub_base_uses_getitem - 下标基座是否应走 __getitem__ 语义
#
# 属性访问的 Subscript 基座路由依据:
# - list[T] / OOP 定义 __getitem__ 的类(如 dict[key])→ translate_value
# (translate_subscript 走 __getitem__ 内联/方法调用返回元素值或地址)
# - 普通结构体指针数组(如 pts: Point | t.CPtr)→ get_subscript_ptr
# (GEP 元素地址,避免结构体值 base=非指针 报错)
# ============================================================
def _sub_base_uses_getitem(self, pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
trans: HT.Translator | t.CPtr,
sub_node: ast.AST | t.CPtr) -> int:
"""下标基座应走 __getitem__ 语义时返回 1,否则 0(应走 GEP 地址语义)"""
if self._is_list_subscript_base(pool, trans, sub_node) != 0:
return 1
cls_ugi: str = self._sub_base_class_name(pool, trans, sub_node)
if cls_ugi is None:
return 0
lookup_ugi: str = pool.alloc(128)
if lookup_ugi is None:
return 0
viperlib.snprintf(lookup_ugi, 128, "%s.__getitem__", cls_ugi)
if HandlesExprCall.find_func_in_module(mod, lookup_ugi) is not None:
return 1
return 0
# ============================================================
# _deref_if_ptr_ptr - 如果 obj_ptr 是 Ptr(Ptr(...)),load 解引用
#
# 用于支持 X|t.CPtr 类型变量的属性访问:
# - 值类型变量 (cnt: Counter): alloca 类型是 Ptr(Struct) → 不解引用
# - 指针类型变量 (r: Vec2|t.CPtr): alloca 类型是 Ptr(Ptr(Struct)) → load 解引用
# ============================================================
def _deref_if_ptr_ptr(self, builder: llvmlite.IRBuilder | t.CPtr,
obj_ptr: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
"""如果 obj_ptr 是 Ptr(Ptr(...)),load 解引用获取内层指针"""
if obj_ptr is None or obj_ptr.Ty is None:
return obj_ptr
if self.is_ptr_type(obj_ptr.Ty) == 0:
return obj_ptr
pointee: llvmlite.LLVMType | t.CPtr = obj_ptr.Ty.Pointee
if pointee is None:
return obj_ptr
if self.is_ptr_type(pointee) == 0:
return obj_ptr
# obj_ptr 是 Ptr(Ptr(...)),load 解引用
loaded: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, pointee, obj_ptr)
if loaded is None:
return obj_ptr
return loaded
# ============================================================
# 类型强制转换:整数 sext/trunc,浮点 fpext/fptrunc,int↔float si2fp/fp2si
# ============================================================
def coerce_to_type(self, builder: llvmlite.IRBuilder | t.CPtr,
val: llvmlite.Value | t.CPtr,
target_ty: llvmlite.LLVMType | t.CPtr,
node: ast.AST | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""将 val 强制转换为 target_ty
整数: sext(扩展)/ trunc(截断)
浮点: fpext(扩展)/ fptrunc(截断)
int→float: sitofp
float→int: fptosi
node: AST 节点(用于错误定位),可为 None
"""
if val is None or target_ty is None:
return val
# 函数 → 指针: bitcast(如 CreateThread(..., _thread_alloc_free, ...) 传函数指针)
# 函数 Value 的 Ty 是 Func,既不是整数也不是指针,需在整数/浮点转换之前处理
match val.Ty:
case llvmlite.LLVMType.Func(_, _, _):
if self.is_ptr_type(target_ty) != 0:
return llvmlite.build_bitcast(builder, val, target_ty)
return val
case _:
pass
# 整数转换
val_bits: int = self.get_llvm_type_bits(val.Ty)
target_bits: int = self.get_llvm_type_bits(target_ty)
# 整数 → 指针: inttoptr(必须在纯整数转换之前检查,因为 ptr 的 bits 非零会误入 sext/trunc 路径)
# 适用于: 跨模块方法返回 i64/i32(默认推断),目标变量是指针类型
if val_bits != 0 and self.is_ptr_type(target_ty) != 0:
return llvmlite.build_inttoptr(builder, val, target_ty)
# 指针 → 整数: ptrtoint(当目标明确是整数而非指针时)
# 注意: target_bits == 8 时跳过 ptrtoint,走后面的 build_load 解引用首字符
# 修复: buf[idx] = '\0' 中 '\0' 是 CONST_STR → i8* 指针,
# ptrtoint 会截断地址低 8 位(非 0),应 load 解引用取首字符 0
if self.is_ptr_type(val.Ty) != 0 and target_bits != 0 and target_bits != 8 and self.is_ptr_type(target_ty) == 0:
return llvmlite.build_ptrtoint(builder, val, target_ty)
if val_bits != 0 and target_bits != 0:
if val_bits == target_bits:
return val
if val_bits < target_bits:
# i1 (bool) → 更宽整数用 zext(bool 语义:1 保持 1,而非 sext 的 0xFF...)
# 与 HandlesReturn 中 return 语句的 i1 扩展规则保持一致
if val_bits == 1:
return llvmlite.build_zext(builder, val, target_ty)
return llvmlite.build_sext(builder, val, target_ty)
return llvmlite.build_trunc(builder, val, target_ty)
# 浮点转换
val_fbits: int = self.get_llvm_float_bits(val.Ty)
target_fbits: int = self.get_llvm_float_bits(target_ty)
# float ↔ ptr: 禁止(无意义的转换,fast-fail 防止误走入 build_load 路径)
if (val_fbits != 0 and self.is_ptr_type(target_ty) != 0) or \
(self.is_ptr_type(val.Ty) != 0 and target_fbits != 0):
HandlesType.fatal_error(node, "unsupported type conversion: float <-> ptr", "CastError")
return val
if val_fbits != 0 and target_fbits != 0:
if val_fbits == target_fbits:
return val
if val_fbits < target_fbits:
return llvmlite.build_fpext(builder, val, target_ty)
return llvmlite.build_fptrunc(builder, val, target_ty)
# int → float
if val_bits != 0 and target_fbits != 0:
return llvmlite.build_si2fp(builder, val, target_ty)
# float → int
if val_fbits != 0 and target_bits != 0:
return llvmlite.build_fp2si(builder, val, target_ty)
# 指针 → 非指针值: build_load 解引用
# 适用于: 指针 → 整数 (如 i8* → i8), 指针 → 结构体值
# 当构造器返回 Ptr(Struct) 但目标变量是 Struct 值类型时,需要 load
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) == 0:
return llvmlite.build_load(builder, target_ty, val)
# 指针 → 指针: bitcast (如 i8* → i8** 当目标是全局变量存储指针)
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) != 0:
return llvmlite.build_bitcast(builder, val, target_ty)
return val
# ============================================================
# translate_ptr_arith - 指针算术: ptr + int / ptr - int
#
# 用 getelementptr(按 pointee 元素大小缩放索引),对齐 TPC
# ExprGen.emit_binary_op / HandlesAugAssign.py。
# 旧实现用 ptrtoint + add + inttoptr(按字节加):i8* 元素大小 1
# 恰好正确,但 i32*/struct* 等指针 `p + 3` 只前进 3 字节(应 12),
# 导致 StringIterTest Test 4/8 失败(指针偏移/指针算术)。
#
# 数组 pointee([N x ty]*)先 decay 为元素指针(GEP 0,0),
# 避免以整个数组为步长(对齐 TPC ExprGen L53-55)。
# ============================================================
def translate_ptr_arith(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
ptr_val: llvmlite.Value | t.CPtr,
int_val: llvmlite.Value | t.CPtr,
op: int) -> llvmlite.Value | t.CPtr:
"""指针算术: ptr + int / ptr - int → GEP(按元素大小缩放索引)"""
if ptr_val is None or int_val is None or ptr_val.Ty is None:
return None
# 提取 pointee 类型(非指针 → 不处理返回 None)
elem_ty: llvmlite.LLVMType | t.CPtr = None
match ptr_val.Ty:
case llvmlite.LLVMType.Ptr(pointee):
elem_ty = pointee
case _:
return None
if elem_ty is None:
return None
# 索引统一到 i64(对齐 TPC zext 到 i64)
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
idx: llvmlite.Value | t.CPtr = self.coerce_to_type(builder, int_val, i64_ty, None)
if idx is None:
return None
# ptr - int → 负索引(LLVM GEP 索引为加法语义)
if op == ast.OpKind.Sub:
neg_idx: llvmlite.Value | t.CPtr = llvmlite.build_sub(
builder, llvmlite.const_int64(pool, 0), idx)
if neg_idx is None:
return None
idx = neg_idx
# 数组 pointee → 先 decay 为元素指针(getelementptr [N x ty], [N x ty]* %p, i32 0, i32 0)
if self.is_array_type(elem_ty) != 0:
inner_ty: llvmlite.LLVMType | t.CPtr = None
match elem_ty:
case llvmlite.LLVMType.Array(inner, _):
inner_ty = inner
case _:
return None
if inner_ty is None:
return None
decayed: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, elem_ty, inner_ty, ptr_val, llvmlite.const_int64(pool, 0))
if decayed is None:
return None
return llvmlite.build_gep(builder, inner_ty, decayed, idx)
return llvmlite.build_gep(builder, elem_ty, ptr_val, idx)
# ============================================================
# 创建全局字符串常量
# ============================================================
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 = HandlesVar.lookup_var(trans.SymTab, "pool")
if alloca is not None:
return alloca
alloca = HandlesVar.lookup_var(trans.SymTab, "_mbuddy")
if alloca is not None:
return alloca
alloca = HandlesVar.lookup_var(trans.SymTab, "mbuddy")
if alloca is not None:
return alloca
alloca = HandlesVar.lookup_var(trans.SymTab, "mb")
if alloca is not None:
return alloca
# 兜底: 创建外部全局 @_mbuddy 引用(i8** 类型)
# 仅用于函数内动态 list 字面量,@_mbuddy 由入口模块定义并初始化
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 = HandlesVar.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)
# 创建外部全局 @_mbuddy(external linkage,无初始值)
gv_ext: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, "_mbuddy", i8_ptr_ty_fb)
if gv_ext is not None:
gv_ext.Linkage = "external"
llvmlite.module_add_global(trans.Module, gv_ext)
# 创建 Value 引用(@_mbuddy, 类型为 i8**)
i8_ptr_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty_fb)
ref_name_fb: str = pool.alloc(16)
if ref_name_fb is not None:
string.strcpy(ref_name_fb, "@_mbuddy")
gv_ref_fb: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb)
# 注册到模块作用域,避免重复创建
HandlesVar.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.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 类型缺失,立即终止编译(暴露上游根因)
HandlesType.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 元素类型,立即终止编译
HandlesType.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:
HandlesType.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 = HandlesClassDef._specialize_generic_class(trans, "list", type_args)
if spec_name is None:
HandlesType.fatal_error(node, "list literal generic specialization failed")
return None
# 查找 struct entry
struct_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(spec_name)
if struct_entry is None:
HandlesType.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 分配空间)
HandlesStruct.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 = HandlesExprCall._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)
HandlesExprCall._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)
HandlesExprCall._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)
HandlesExprCall._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.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)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, spec_name, "append", storage_ptr, ap_args, 1, trans)
i += 1
return storage_ptr
# ============================================================
# 翻译值表达式(RHS 分派)— 模块级版本
# ============================================================
def translate_value(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译值表达式为 LLVM Value"""
if node is None:
return None
k: int = node.kind()
if k == ast.ASTKind.Constant:
rv: llvmlite.Value | t.CPtr = self.translate_constant(builder, pool, mod, node, trans)
return rv
elif k == ast.ASTKind.Name:
return self.translate_name_value(builder, pool, node, trans)
elif k == ast.ASTKind.BinOp:
return self.translate_binop(pool, builder, mod, node, trans)
elif k == ast.ASTKind.Call:
return HandlesExprCall.translate_call(pool, builder, mod, node,
funcs_ptr, func_count, trans)
elif k == ast.ASTKind.Compare:
return self.translate_compare(builder, pool, mod, node, trans)
elif k == ast.ASTKind.UnaryOp:
return self.translate_unaryop(builder, pool, mod, node, trans)
elif k == ast.ASTKind.BoolOp:
return self.translate_boolop(builder, pool, mod, node,
funcs_ptr, func_count, trans)
elif k == ast.ASTKind.Subscript:
return self.translate_subscript(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Attribute:
return self.translate_attribute(builder, pool, mod, node, trans)
elif k == ast.ASTKind.List:
return self.translate_list_literal(builder, pool, mod, node, None, trans)
elif k == ast.ASTKind.IfExp:
return self.translate_ifexp(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Lambda:
# 模块级委托:translate_lambda 定义在 FuncGenMixin(跨 Mixin
# 方法不能 self. 直接调用,见 HandlesExpr.translate_lambda)
return HandlesExpr.translate_lambda(builder, pool, mod, node, trans)
elif k == ast.ASTKind.JoinedStr:
return self.translate_joined_str(builder, pool, mod, node, trans)
elif k == ast.ASTKind.FormattedValue:
# 解析器对 f"{expr:spec}"(单 FormattedValue 无字面量)返回裸 FormattedValue
# 而非 JoinedStr(见 ast/parser.vp _parse_fstring),需单独翻译
return self.translate_formatted_value(builder, pool, mod, node, trans)
return None
# ============================================================
# translate_joined_str - f-string 翻译(对齐 TPC _HandleJoinedStrLlvm)
#
# f"..."(JoinedStr)→ 拼接字面量与格式说明符 → snprintf 生成 i8*
# 栈缓冲。之前 f-string 未翻译导致 print/赋值拿到 NULL 指针
# ("(null)"输出 + printf("%s", 垃圾) 崩溃 0xC0000005)。
# ============================================================
def translate_joined_str(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:
"""f-string → snprintf 生成 i8* 栈缓冲"""
if node is None or builder is None or trans is None:
return None
js: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(node)
if js is None or js.values is None:
return None
# 编译期格式串缓冲(pool 分配)
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
if fmt_buf is None:
return None
fmt_buf[0] = '\0'
# 运行时参数链表(Value.Next 链接,与 printf 调用一致)
arg_head: llvmlite.Value | t.CPtr = None
arg_tail: llvmlite.Value | t.CPtr = None
arg_count: int = 0
parts: list[ast.AST | t.CPtr] | t.CPtr = js.values
pn: t.CSizeT = len(parts)
for pi in range(pn):
part: ast.AST | t.CPtr = parts.get(pi)
if part is None:
continue
if part.kind() == ast.ASTKind.Constant:
pcn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(part)
if pcn.const_kind == ast.CONST_STR and pcn.str_val is not None:
self._fstr_append_literal(fmt_buf, pcn.str_val)
continue
if part.kind() != ast.ASTKind.FormattedValue:
continue
fv: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(part)
if fv.value is None:
continue
val: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, fv.value, None, 0, trans)
if val is None:
# fail-fast:f-string 格式化值翻译失败必须终止编译,绝不在
HandlesType.fatal_error(
fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
continue
# 确定 C 格式说明符(format_spec 优先,否则按值类型推断)
c_fmt: str = None
if fv.format_spec is not None:
c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
if c_fmt is None:
c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
if c_fmt is None:
# fail-fast:f-string 格式无法推断(值类型缺失),立即终止编译
HandlesType.fatal_error(fv.value, "f-string 格式无法推断(值类型缺失)", "TypeError")
# 追加格式说明符
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
string.strcat(fmt_buf, c_fmt)
# 转换参数类型(i8/i16→i32、bool→i32、f32→f64)
cast_val: llvmlite.Value | t.CPtr = self._fstr_cast_value(
builder, pool, val, c_fmt, fv.value, trans)
if cast_val is None:
cast_val = val
cast_val.Next = None
if arg_head is None:
arg_head = cast_val
else:
arg_tail.Next = cast_val
arg_tail = cast_val
arg_count += 1
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
def translate_formatted_value(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:
"""翻译裸 FormattedValue(f"{expr:spec}" 无字面量时解析器返回 FormattedValue 而非 JoinedStr)"""
if node is None or builder is None or trans is None:
return None
fv: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(node)
if fv is None or fv.value is None:
return None
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
if fmt_buf is None:
return None
fmt_buf[0] = '\0'
arg_head: llvmlite.Value | t.CPtr = None
arg_tail: llvmlite.Value | t.CPtr = None
arg_count: int = 0
val: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, fv.value, None, 0, trans)
if val is None:
# fail-fast:裸 FormattedValue 翻译失败必须终止编译,绝不在
HandlesType.fatal_error(
fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
else:
c_fmt: str = None
if fv.format_spec is not None:
c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
if c_fmt is None:
c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
if c_fmt is None:
# fail-fast:f-string 格式无法推断(值类型缺失),立即终止编译
HandlesType.fatal_error(fv.value, "f-string 格式无法推断(值类型缺失)", "TypeError")
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
string.strcat(fmt_buf, c_fmt)
cast_val: llvmlite.Value | t.CPtr = self._fstr_cast_value(
builder, pool, val, c_fmt, fv.value, trans)
if cast_val is None:
cast_val = val
cast_val.Next = None
arg_head = cast_val
arg_tail = cast_val
arg_count = 1
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
def _fstr_finalize(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
fmt_buf: str,
arg_head: llvmlite.Value | t.CPtr,
arg_tail: llvmlite.Value | t.CPtr,
arg_count: int,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""f-string 尾部:全局格式串 + 固定栈缓冲 + snprintf,返回 i8*"""
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
if fmt_buf is None or fmt_buf[0] == '\0':
# 空 f-string(f"")→ 空字符串常量
return HandlesExpr.create_global_string(builder, pool, mod, "", trans)
# 格式串 → 全局字符串常量(i8*)
fmt_ptr: llvmlite.Value | t.CPtr = HandlesExpr.create_global_string(
builder, pool, mod, fmt_buf, trans)
if fmt_ptr is None:
return None
# 固定栈缓冲 [FSTR_BUF_SIZE x i8],GEP(0,0) 取 i8*
buf_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty, FSTR_BUF_SIZE)
buf_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, buf_ty)
if buf_alloca is None:
return None
zero_i32: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
buf_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, buf_ty, i8_ty, buf_alloca, zero_i32)
if buf_ptr is None:
return None
# snprintf(buf, FSTR_BUF_SIZE, fmt, args...)
size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, FSTR_BUF_SIZE)
buf_ptr.Next = size_val
size_val.Next = fmt_ptr
fmt_ptr.Next = arg_head
if arg_tail is not None:
arg_tail.Next = None
llvmlite.build_call(builder, "snprintf", buf_ptr, 3 + arg_count, i32_ty, 1)
return buf_ptr
def _fstr_append_literal(self, fmt_buf: str,
lit: str) -> None:
"""追加字面量到格式串(% 转义为 %%)"""
if fmt_buf is None or lit is None:
return
cur_len: t.CSizeT = string.strlen(fmt_buf)
rem: t.CSizeT = FSTR_BUF_SIZE - 1 - cur_len
if rem == 0:
return
src_len: t.CSizeT = string.strlen(lit)
si: t.CSizeT = 0
while si < src_len and rem > 0:
ch: t.CChar = lit[si]
if ch == '%':
if rem < 2:
break
fmt_buf[cur_len] = '%'
fmt_buf[cur_len + 1] = '%'
cur_len += 2
rem -= 2
else:
fmt_buf[cur_len] = ch
cur_len += 1
rem -= 1
si += 1
fmt_buf[cur_len] = '\0'
def _fstr_append_raw(self, fmt_buf: str, s: str) -> None:
"""追加原始字符串(不做 % 转义)"""
if fmt_buf is None or s is None:
return
cur_len: t.CSizeT = string.strlen(fmt_buf)
src_len: t.CSizeT = string.strlen(s)
if cur_len + src_len < FSTR_BUF_SIZE:
string.strcpy(fmt_buf + cur_len, s)
def _fstr_parse_format_spec(self, pool: memhub.MemBuddy | t.CPtr,
spec_node: ast.AST | t.CPtr,
val: llvmlite.Value | t.CPtr,
arg_node: ast.AST | t.CPtr) -> str:
"""Python format_spec → C printf 格式串(pool 分配),无法解析返回 None"""
if spec_node is None or pool is None or val is None:
return None
# 1. 提取 spec 字符串(JoinedStr 仅常量拼接)
spec: str = pool.alloc(FSTR_SPEC_MAX)
if spec is None:
return None
spec[0] = '\0'
sk: int = spec_node.kind()
if sk == ast.ASTKind.JoinedStr:
jss: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(spec_node)
if jss.values is None:
return None
vn: t.CSizeT = len(jss.values)
for vi in range(vn):
vpart: ast.AST | t.CPtr = jss.values.get(vi)
if vpart is None:
continue
if vpart.kind() != ast.ASTKind.Constant:
return None
vcn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(vpart)
if vcn.const_kind != ast.CONST_STR or vcn.str_val is None:
return None
if string.strlen(spec) + string.strlen(vcn.str_val) < FSTR_SPEC_MAX - 1:
string.strcat(spec, vcn.str_val)
elif sk == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(spec_node)
if cn.const_kind != ast.CONST_STR or cn.str_val is None:
return None
string.strncpy(spec, cn.str_val, FSTR_SPEC_MAX - 1)
else:
return None
slen: t.CSizeT = string.strlen(spec)
if slen == 0:
return None
# 2. 逐段解析: [fill]align / sign / # / 0 / width / , / .precision / type
pos: t.CSizeT = 0
align_char: t.CChar = 0
if pos < slen:
ch0: t.CChar = spec[pos]
if ch0 == '<' or ch0 == '>' or ch0 == '^' or ch0 == '=':
align_char = ch0
pos += 1
elif pos + 1 < slen:
ch1: t.CChar = spec[pos + 1]
if ch1 == '<' or ch1 == '>' or ch1 == '^' or ch1 == '=':
align_char = ch1
pos += 2
c_flags: str = pool.alloc(FSTR_FMT_MAX)
c_width: str = pool.alloc(32)
c_prec: str = pool.alloc(32)
if c_flags is None or c_width is None or c_prec is None:
return None
c_flags[0] = '\0'
c_width[0] = '\0'
c_prec[0] = '\0'
# sign
if pos < slen and (spec[pos] == '+' or spec[pos] == '-' or spec[pos] == ' '):
if spec[pos] == '+':
string.strcat(c_flags, "+")
elif spec[pos] == ' ':
string.strcat(c_flags, " ")
pos += 1
# alt '#'
if pos < slen and spec[pos] == '#':
string.strcat(c_flags, "#")
pos += 1
# zero '0'
if pos < slen and spec[pos] == '0':
string.strcat(c_flags, "0")
pos += 1
# 左对齐 '<'/'^' → '-'(与 0 同时存在时 C 语义 '-' 优先)
if align_char == '<' or align_char == '^':
string.strcat(c_flags, "-")
# width digits
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
wl: t.CSizeT = string.strlen(c_width)
if wl < 30:
c_width[wl] = spec[pos]
c_width[wl + 1] = '\0'
pos += 1
# comma ',' → C printf 不支持千分位,忽略
if pos < slen and spec[pos] == ',':
pos += 1
# .precision
if pos < slen and spec[pos] == '.':
string.strcat(c_prec, ".")
pos += 1
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
pl: t.CSizeT = string.strlen(c_prec)
if pl < 30:
c_prec[pl] = spec[pos]
c_prec[pl + 1] = '\0'
pos += 1
if string.strlen(c_prec) == 1:
return None # 只有 '.' 无效
# type char
fmt_type: t.CChar = 0
if pos < slen:
fmt_type = spec[pos]
pos += 1
if pos != slen:
return None # 多余字符无法解析
# 3. 长度修饰(按值类型位宽)
len_mod: str = pool.alloc(4)
if len_mod is None:
return None
len_mod[0] = '\0'
ibits: int = HandlesExpr.get_llvm_type_bits(val.Ty)
if ibits == 64:
string.strcpy(len_mod, "ll")
elif ibits == 32:
string.strcpy(len_mod, "l")
elif ibits == 16:
string.strcpy(len_mod, "h")
elif ibits == 8:
string.strcpy(len_mod, "hh")
# 4. 组装 C 格式串
out: str = pool.alloc(FSTR_FMT_MAX)
if out is None:
return None
out[0] = '\0'
string.strcat(out, "%")
string.strcat(out, c_flags)
string.strcat(out, c_width)
string.strcat(out, c_prec)
if fmt_type == 0:
# 无 type:按值类型推断
fbits2: int = HandlesExpr.get_llvm_float_bits(val.Ty)
if ibits != 0:
if ibits == 8:
string.strcat(out, "c")
elif ibits == 1:
string.strcat(out, "d")
else:
string.strcat(out, len_mod)
string.strcat(out, "d")
elif fbits2 != 0:
string.strcat(out, "f")
elif HandlesExpr.is_ptr_type(val.Ty) != 0:
string.strcat(out, "s")
else:
string.strcat(out, "d")
elif fmt_type == 'c':
string.strcat(out, "c")
elif fmt_type == 'f' or fmt_type == 'F':
string.strcat(out, "f")
elif fmt_type == 'e' or fmt_type == 'E' or fmt_type == 'g' or fmt_type == 'G':
tmp_ch: str = pool.alloc(2)
if tmp_ch is None:
return None
tmp_ch[0] = fmt_type
tmp_ch[1] = '\0'
string.strcat(out, tmp_ch)
elif fmt_type == 's':
string.strcat(out, "s")
elif fmt_type == '%':
string.strcat(out, "%")
elif fmt_type == 'b':
return None # 二进制不支持
elif fmt_type == 'd' or fmt_type == 'i':
string.strcat(out, len_mod)
string.strcat(out, "d")
elif fmt_type == 'x' or fmt_type == 'X' or fmt_type == 'o' or fmt_type == 'u':
string.strcat(out, len_mod)
tmp_ch2: str = pool.alloc(2)
if tmp_ch2 is None:
return None
tmp_ch2[0] = fmt_type
tmp_ch2[1] = '\0'
string.strcat(out, tmp_ch2)
else:
return None
return out
def _fstr_infer_c_fmt(self, pool: memhub.MemBuddy | t.CPtr,
val: llvmlite.Value | t.CPtr,
arg_node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> str:
"""无 format_spec 时按值类型推断 C printf 格式(对齐 TPC 无 spec 分支,带 % 前缀)"""
if val is None or pool is None:
return None
out: str = pool.alloc(16)
if out is None:
return None
out[0] = '\0'
ibits: int = HandlesExpr.get_llvm_type_bits(val.Ty)
fbits: int = HandlesExpr.get_llvm_float_bits(val.Ty)
if ibits == 1:
return "%d" # bool
if ibits == 8:
return "%c" # 字符
if ibits != 0:
if self._node_type_unsigned(pool, arg_node, trans) != 0:
if ibits == 64:
string.strcpy(out, "%llu")
else:
string.strcpy(out, "%u")
else:
if ibits == 64:
string.strcpy(out, "%lld")
else:
string.strcpy(out, "%d")
return out
if fbits != 0:
return "%f"
if HandlesExpr.is_ptr_type(val.Ty) != 0:
return "%s"
# fail-fast:值类型缺失(非整数/浮点/指针),无法推断 C 格式,立即终止编译
HandlesType.fatal_error(arg_node, "f-string 格式无法推断(值类型缺失)", "TypeError")
return None
def _fstr_cast_value(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
val: llvmlite.Value | t.CPtr,
c_fmt: str,
arg_node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""按 C 格式转换值类型(i8/i16→i32、bool→i32、f32→f64)"""
if val is None or c_fmt is None:
return None
# 找格式类型字符(最后一个字母,忽略 l/h 长度修饰)
fmt_char: t.CChar = 0
flen: t.CSizeT = string.strlen(c_fmt)
fi: t.CSizeT = flen
while fi > 0:
fi -= 1
ch: t.CChar = c_fmt[fi]
if ch == '%':
break
if (ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z'):
if ch != 'l' and ch != 'h':
fmt_char = ch
break
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
if fmt_char == 's':
return val
if fmt_char == 'c':
ibits: int = HandlesExpr.get_llvm_type_bits(val.Ty)
if ibits != 0 and ibits < 32:
return llvmlite.build_zext(builder, val, i32_ty)
return val
if fmt_char == 'd' or fmt_char == 'i' or fmt_char == 'x' \
or fmt_char == 'X' or fmt_char == 'o' or fmt_char == 'u':
ibits = HandlesExpr.get_llvm_type_bits(val.Ty)
if ibits == 1:
return llvmlite.build_zext(builder, val, i32_ty)
if ibits == 8 or ibits == 16:
if self._node_type_unsigned(pool, arg_node, trans) != 0:
return llvmlite.build_zext(builder, val, i32_ty)
return llvmlite.build_sext(builder, val, i32_ty)
return val
if fmt_char == 'f' or fmt_char == 'e' or fmt_char == 'E' \
or fmt_char == 'g' or fmt_char == 'G':
fbits: int = HandlesExpr.get_llvm_float_bits(val.Ty)
if fbits == 32:
return llvmlite.build_fpext(builder, val, llvmlite.Double(pool))
return val
return val
# ============================================================
# 翻译三元表达式 IfExp(test, body, orelse)
#
# 生成: cond ? body : orelse
# then_blk: 翻译 body → br merge
# else_blk: 翻译 orelse → br merge
# merge_blk: phi [body_val, then_blk], [orelse_val, else_blk]
# ============================================================
def translate_ifexp(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:
"""翻译三元表达式 (body if test else orelse),返回 phi 结果 Value"""
if node is None or builder is None or trans is None:
return None
ie: ast.IfExp | t.CPtr = (ast.IfExp | t.CPtr)(node)
if ie is None:
return None
func: llvmlite.Function | t.CPtr = trans._cur_func
if func is None:
return None
# 1. 求值条件
cond_val: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, ie.test, None, 0, trans)
if cond_val is None:
return None
# 转换为 i1
cond_bits: int = self.get_llvm_type_bits(cond_val.Ty)
if cond_bits == 1:
cond_i1: llvmlite.Value | t.CPtr = cond_val
else:
if self.is_ptr_type(cond_val.Ty) != 0:
# 指针类型:与 null 比较
null_cond: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, cond_val.Ty, "null")
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, null_cond)
else:
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, zero)
# 2. 创建三个基本块
cnt: int = trans._label_counter
trans._label_counter = cnt + 1
name_buf: str = pool.alloc(32)
if name_buf is None:
return None
viperlib.snprintf(name_buf, 32, "ifexp.then.%d", cnt)
then_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
viperlib.snprintf(name_buf, 32, "ifexp.else.%d", cnt)
else_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
viperlib.snprintf(name_buf, 32, "ifexp.merge.%d", cnt)
merge_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
if then_bb is None or else_bb is None or merge_bb is None:
return None
# 3. 条件跳转
llvmlite.build_cond_br(builder, cond_i1, then_bb, else_bb)
# 4. then 分支
llvmlite.position_at_end(builder, then_bb)
body_val: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, ie.body, None, 0, trans)
then_blk_for_phi: llvmlite.BasicBlock | t.CPtr = trans._cur_builder.CurBlock
if body_val is None:
# fail-fast:三元表达式 then 分支翻译失败,立即终止编译(暴露上游根因)
HandlesType.fatal_error(ie.body, "三元表达式分支翻译失败", "TypeError")
# 暂不 br,等类型对齐后再 br
# 5. else 分支
llvmlite.position_at_end(builder, else_bb)
orelse_val: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, ie.orelse, None, 0, trans)
else_blk_for_phi: llvmlite.BasicBlock | t.CPtr = trans._cur_builder.CurBlock
if orelse_val is None:
# fail-fast:三元表达式 else 分支翻译失败,立即终止编译(暴露上游根因)
HandlesType.fatal_error(ie.orelse, "三元表达式分支翻译失败", "TypeError")
# 暂不 br,等类型对齐后再 br
# 类型对齐 (两分支类型可能不同)
# 必须在各自分支中执行 coerce_to_type,确保 merge 块中 PHI 是第一条指令
# (否则 coerce_to_type 生成的 sext/zext/ptrtoint/inttoptr 指令在 PHI 之前,违反 LLVM 规则)
body_bits: int = self.get_llvm_type_bits(body_val.Ty)
orelse_bits: int = self.get_llvm_type_bits(orelse_val.Ty)
body_is_ptr: int = self.is_ptr_type(body_val.Ty)
orelse_is_ptr: int = self.is_ptr_type(orelse_val.Ty)
# 情况0: 一侧是指针,另一侧是整数(如 i64 vs i8* null)
# 必须优先处理,因为 Ptr 的 bits=0 会导致下面三个分支全部跳过
# 选择 body_val.Ty 作为 phi_ty(then 分支优先),orelse_val 对齐到它
# coerce_to_type 内部会调用 ptrtoint(指针→整数)或 inttoptr(整数→指针)
if body_is_ptr != orelse_is_ptr:
llvmlite.position_at_end(builder, else_blk_for_phi)
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
elif body_bits > orelse_bits and orelse_bits > 0:
# 在 else 块中转换 orelse_val
llvmlite.position_at_end(builder, else_blk_for_phi)
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
elif orelse_bits > body_bits and body_bits > 0:
# 回到 then 块转换 body_val
llvmlite.position_at_end(builder, then_blk_for_phi)
body_val = self.coerce_to_type(builder, body_val, orelse_val.Ty, node)
elif body_bits == orelse_bits and body_bits > 0:
# bits 相同但类型不同(如 i64 vs Ptr):null 不能用于 i64 PHI
# 此分支理论上不会触发(已被情况0覆盖),保留作为防御
if self.is_ptr_type(body_val.Ty) != self.is_ptr_type(orelse_val.Ty):
llvmlite.position_at_end(builder, else_blk_for_phi)
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
# 现在分别在 then/else 块中 br 到 merge(在类型转换之后)
llvmlite.position_at_end(builder, then_blk_for_phi)
llvmlite.build_br(builder, merge_bb)
llvmlite.position_at_end(builder, else_blk_for_phi)
llvmlite.build_br(builder, merge_bb)
# 6. merge 块: phi(PHI 是 merge 块的第一条指令)
llvmlite.position_at_end(builder, merge_bb)
phi_ty: llvmlite.LLVMType | t.CPtr = body_val.Ty
if phi_ty is None:
# fail-fast:三元表达式分支类型缺失,立即终止编译(暴露上游根因)
HandlesType.fatal_error(node, "三元表达式分支类型缺失", "TypeError")
inc1: llvmlite.PhiIncoming | t.CPtr = llvmlite.new_phi_incoming(
pool, body_val, then_blk_for_phi)
inc2: llvmlite.PhiIncoming | t.CPtr = llvmlite.new_phi_incoming(
pool, orelse_val, else_blk_for_phi)
if inc1 is None or inc2 is None:
return None
# 链表顺序: inc1 (head) -> inc2 (tail)
# 之前错误写成 inc2.Next = inc1,导致 build_phi 从 inc1 遍历时
# inc1.Next 为 None,只输出一个 phi 条目,引发 LLVM 验证错误:
# "PHINode should have one entry for each predecessor"
inc1.Next = inc2
return llvmlite.build_phi(builder, phi_ty, inc1, 2)
# ============================================================
# 翻译比较表达式 Compare(left, ops, comparators)
# ============================================================
def should_zext_widen(self, pool: memhub.MemBuddy | t.CPtr,
narrow_val: llvmlite.Value | t.CPtr,
wide_val: llvmlite.Value | t.CPtr,
narrow_node: ast.AST | t.CPtr = None) -> int:
"""判断整数拓宽方向:1=zext(无符号语义),0=sext
对齐 TPC 的 _check_node_unsigned / HandlesExprOps 拓宽规则:
1. i1 (bool) → zext(1 保持 1,而非 sext 的 0xFF...)
2. i8 → zext(系统编程中 i8 几乎总是无符号字节)
3. 宽侧是常量且其值超出窄侧位宽的有符号范围 → zext
(常量"想要"无符号解释,如 0xAABBCCDD > INT32_MAX 与
t.CUInt32T 值比较,sext 会把 0xAABBCCDD 变成
0xFFFFFFFFAABBCCDD 导致恒不等)
"""
if narrow_val is None or narrow_val.Ty is None:
return 0
narrow_bits: int = self.get_llvm_type_bits(narrow_val.Ty)
if narrow_bits == 1:
return 1
if narrow_bits == 8:
return 1
# 宽侧常量启发式
if wide_val is not None and wide_val.IsConst == 1:
wide_bits: int = self.get_llvm_type_bits(wide_val.Ty)
if wide_bits > narrow_bits and narrow_bits < 64:
signed_max: t.CInt64T = (t.CInt64T(1) << (narrow_bits - 1)) - 1
if wide_val.IntVal > signed_max:
return 1
# 窄侧 AST 节点解析为无符号类型(如 t.CUIntXXT 字面量/强转)
if narrow_node is not None:
if self._node_type_unsigned(pool, narrow_node) != 0:
return 1
return 0
def _node_type_unsigned(self, pool: memhub.MemBuddy | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> int:
"""轻量判断 AST 节点是否解析为无符号整数类型(1=是,0=否/未知)
覆盖常见形态(对齐 TPC _check_node_unsigned 的 Call/Subscript 分支):
- t.CUInt8T/CUInt16T/CUInt32T/CUInt64T/CUnsigned* 等显式无符号类型强转调用
- 无符号类型名的 Name 引用(如 UInt32T 别名)
- 变量名(trans 可用时):查询 SymTab 中变量的 AnnotClassName(如 v: t.CUInt32T)
"""
if node is None:
return 0
k: int = node.kind()
if k == ast.ASTKind.Call:
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
mod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if mod_nm.id is not None and string.strcmp(mod_nm.id, "t") == 0 \
and at.attr is not None:
if self._type_name_unsigned(at.attr) != 0:
return 1
return 0
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm.id is not None:
# 1) 名字本身是无符号类型名(如 UInt32T 别名)
if self._type_name_unsigned(nm.id) != 0:
return 1
# 2) 变量注解类型为无符号类型(trans 可用时查 SymTab)
if trans is not None:
ve: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, nm.id)
if ve is not None and ve.AnnotClassName is not None:
if self._type_name_unsigned(ve.AnnotClassName) != 0:
return 1
return 0
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
if self._node_type_unsigned(pool, bop.left, trans) != 0:
return 1
if self._node_type_unsigned(pool, bop.right, trans) != 0:
return 1
return 0
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
if self._node_type_unsigned(pool, uop.operand, trans) != 0:
return 1
return 0
if k == ast.ASTKind.Subscript:
# 数组/指针下标: md5_buf[i](t.CArray[t.CUInt8T, N])或 buf[i]
# (形参 t.CUInt8T | t.CPtr)元素是无符号整型 → printf("%02x", ...) zext。
sub_su: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
if sub_su.value is not None and sub_su.value.kind() == ast.ASTKind.Name:
snm_su: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub_su.value)
if snm_su.id is not None and trans is not None:
ve_su: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, snm_su.id)
if ve_su is not None and ve_su.AnnotClassName is not None:
if self._annot_str_unsigned(ve_su.AnnotClassName) != 0:
return 1
return 0
return 0
def _type_name_unsigned(self, type_name: str) -> int:
"""类型名是否为无符号整数类型(精确匹配,避免子串误伤如 nUIntCount)"""
if type_name is None:
return 0
# t.CUIntXXT 形式
if string.strcmp(type_name, "CUInt8T") == 0: return 1
if string.strcmp(type_name, "CUInt16T") == 0: return 1
if string.strcmp(type_name, "CUInt32T") == 0: return 1
if string.strcmp(type_name, "CUInt64T") == 0: return 1
# stdint 裸别名(from stdint import *)
if string.strcmp(type_name, "UInt8T") == 0: return 1
if string.strcmp(type_name, "UInt16T") == 0: return 1
if string.strcmp(type_name, "UInt32T") == 0: return 1
if string.strcmp(type_name, "UInt64T") == 0: return 1
# CUnsigned* 形式
if string.strcmp(type_name, "CUnsignedChar") == 0: return 1
if string.strcmp(type_name, "CUnsignedShort") == 0: return 1
if string.strcmp(type_name, "CUnsignedInt") == 0: return 1
if string.strcmp(type_name, "CUnsignedLong") == 0: return 1
if string.strcmp(type_name, "CUnsignedLongLong") == 0: return 1
# 常见无符号别名
if string.strcmp(type_name, "BYTE") == 0: return 1
if string.strcmp(type_name, "UCHAR") == 0: return 1
if string.strcmp(type_name, "USHORT") == 0: return 1
if string.strcmp(type_name, "UINT") == 0: return 1
if string.strcmp(type_name, "ULONG") == 0: return 1
return 0
def _annot_str_unsigned(self, annot: str) -> int:
"""检查类型注解字符串是否引用无符号整型(子串扫描)
用于复合注解(t.CArray[t.CUInt8T, 16] / t.CUInt8T | t.CPtr),
_type_name_unsigned 的精确匹配对这类串不适用。
"""
if annot is None:
return 0
if string.strstr(annot, "CUInt8T") is not None: return 1
if string.strstr(annot, "CUInt16T") is not None: return 1
if string.strstr(annot, "CUInt32T") is not None: return 1
if string.strstr(annot, "CUInt64T") is not None: return 1
if string.strstr(annot, "CUnsigned") is not None: return 1
if string.strstr(annot, "BYTE") is not None: return 1
if string.strstr(annot, "UCHAR") is not None: return 1
if string.strstr(annot, "USHORT") is not None: return 1
if string.strstr(annot, "UINT") is not None: return 1
if string.strstr(annot, "ULONG") is not None: return 1
return 0
def translate_compare(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:
"""翻译比较表达式,返回 i1 结果"""
cmp: ast.Compare | t.CPtr = (ast.Compare | t.CPtr)(node)
if cmp is None:
return None
ops_list: list[t.CInt] | t.CPtr = (list[t.CInt] | t.CPtr)(cmp.ops)
if ops_list is None or len(ops_list) == 0:
return None
op: int = ops_list.get(0)
comparators: list[ast.AST | t.CPtr] | t.CPtr = cmp.comparators
if comparators is None or len(comparators) == 0:
return None
rhs_node: ast.AST | t.CPtr = comparators.get(0)
rhs: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, rhs_node, None, 0, trans)
if rhs is None:
return None
# === None 比较优化: x == None / x != None 走身份比较(icmp eq/ne null)===
# 避免对结构体指针生成无效的 __eq__/__ne__ 调用(方法未定义但类有 SHA1 时
# try_operator_overload 会误生成 call void @...__eq__(...) 导致链接错误)
# 语义依据: Python 中 == None / != None 默认走身份比较,不调用 __eq__/__ne__
lhs_node: ast.AST | t.CPtr = cmp.left
cmp_with_none: int = 0
if op == ast.OpKind.Eq or op == ast.OpKind.Ne or op == ast.OpKind.Is or op == ast.OpKind.IsNot:
if rhs_node is not None and rhs_node.kind() == ast.ASTKind.Constant:
rhs_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(rhs_node)
if rhs_cn is not None and rhs_cn.const_kind == ast.CONST_NONE:
cmp_with_none = 1
if cmp_with_none == 0 and lhs_node is not None and lhs_node.kind() == ast.ASTKind.Constant:
lhs_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(lhs_node)
if lhs_cn is not None and lhs_cn.const_kind == ast.CONST_NONE:
cmp_with_none = 1
# === 指针判空模式: ptr != 0 / ptr == 0 ===
# 跳过运算符重载走身份比较(icmp null)。跨模块类(如 cpython.PyObject)
# 未定义 __eq__/__ne__ 时,try_operator_overload 无法判定方法不存在
# (类在依赖模块,cls_sha1 != trans.ModuleSha1),会误发幽灵
# `call void @...__ne__` → 链接 undefined reference(CPythonTest main.vp
# `py_num != 0` 根因)。C 语义:指针与字面量 0 比较 = 判空。
cmp_zero: int = 0
if op == ast.OpKind.Eq or op == ast.OpKind.Ne:
if rhs_node is not None and self._is_zero_int_literal(rhs_node) != 0:
cmp_zero = 1
if cmp_zero == 0 and lhs_node is not None and self._is_zero_int_literal(lhs_node) != 0:
cmp_zero = 1
# === 比较运算符重载路径 1: lhs 是 Name 且对应结构体变量 ===
# 对于值类型变量(如 cnt: Counter),用 alloca 指针尝试重载
# None 比较跳过重载(走身份比较)
if cmp_with_none == 0 and lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(lhs_node)
if nm is not None and nm.id is not None:
lhs_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
trans.SymTab, nm.id)
if lhs_alloca is not None:
if cmp_zero == 0 or self.get_ptr_level(lhs_alloca.Ty) == 0:
ovl_self1: llvmlite.Value | t.CPtr = lhs_alloca
# Ptr(Ptr(Struct)) 对象存储槽:load 出对象指针作 self
# (与 translate_binop 路径 1 一致,避免槽地址当对象地址)
if lhs_alloca.Ty is not None and lhs_alloca.Ty.Pointee is not None \
and HandlesExpr.is_ptr_type(lhs_alloca.Ty.Pointee) != 0:
load_self1: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, lhs_alloca.Ty.Pointee, lhs_alloca)
if load_self1 is not None:
ovl_self1 = load_self1
ovl_result: llvmlite.Value | t.CPtr = self.try_operator_overload(
pool, builder, mod, ovl_self1, rhs, op, trans, 1)
if ovl_result is not None:
return ovl_result
# 正常翻译 lhs
lhs: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, cmp.left, None, 0, trans)
if lhs is None:
return None
# === in / not in:容器右操作数调用 __contains__(对齐 TPC
# HandlesExprOps.py L261-281)。x in y → y.__contains__(x);
# x not in y → !y.__contains__(x)。容器无 __contains__ →
# 返回 i1 0(TPC 同:恒 False)。
if op == ast.OpKind.In or op == ast.OpKind.NotIn:
cn_st: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type_from_value(rhs)
if cn_st is not None:
cn_cls: str = HandlesStruct.get_class_name_by_type(pool, cn_st)
if cn_cls is not None:
cn_lookup: str = pool.alloc(128)
if cn_lookup is not None:
viperlib.snprintf(cn_lookup, 128, "%s.__contains__", cn_cls)
cn_fn: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(
mod, cn_lookup)
if cn_fn is not None:
cn_ret: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(cn_fn)
cn_fn_name: str = llvmlite.function_get_name(cn_fn)
if cn_ret is not None and cn_fn_name is not None:
# self 参数(容器)按 __contains__ 首参类型对齐
cn_self: llvmlite.Value | t.CPtr = rhs
cn_first: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(cn_fn)
if cn_first is not None:
cn_self_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cn_first)
if cn_self_ty is not None:
cn_self = llvmlite.build_bitcast(builder, rhs, cn_self_ty)
if cn_self is not None:
# 参数链表: self → key(独立 null 结尾)
cn_self.Next = lhs
lhs.Next = None
cn_res: llvmlite.Value | t.CPtr = llvmlite.build_call(
builder, cn_fn_name, cn_self, 2, cn_ret, 0)
if cn_res is not None:
# 归一化到 i1(__contains__ 返回 t.CInt/i32 等)
cn_i1: llvmlite.Value | t.CPtr = cn_res
match cn_ret:
case llvmlite.LLVMType.Int(cn_bits):
if cn_bits > 1:
cn_zero: llvmlite.Value | t.CPtr = llvmlite.ConstInt(
pool, cn_ret, 0, "0")
if cn_zero is not None:
cn_i1 = llvmlite.build_icmp(
builder, llvmlite.ICMP_NE, cn_res, cn_zero)
case _:
pass
if op == ast.OpKind.NotIn:
i1_ty_ni: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool)
if i1_ty_ni is not None and cn_i1 is not None:
one_i1_ni: llvmlite.Value | t.CPtr = llvmlite.ConstInt(
pool, i1_ty_ni, 1, "1")
if one_i1_ni is not None:
cn_i1 = llvmlite.build_xor(builder, cn_i1, one_i1_ni)
if cn_i1 is not None:
return cn_i1
# 容器无 __contains__:TPC 语义恒 False(i1 0)
i1_ty_in: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool)
if i1_ty_in is not None:
return llvmlite.ConstInt(pool, i1_ty_in, 0, "0")
return None
# === 比较运算符重载路径 2: lhs 是 Ptr(Struct) ===
# Is/IsNot 不在 _cmpop_to_dunder 映射中,会自动返回 None 回退原生比较
# None 比较跳过重载(走身份比较);指针判空(cmp_zero + 指针)同样跳过
if cmp_with_none == 0 and (cmp_zero == 0 or self.get_ptr_level(lhs.Ty) == 0):
ovl_result2: llvmlite.Value | t.CPtr = self.try_operator_overload(
pool, builder, mod, lhs, rhs, op, trans, 1)
if ovl_result2 is not None:
return ovl_result2
predicate: int = llvmlite.ICMP_SLT
if op == ast.OpKind.Lt:
predicate = llvmlite.ICMP_SLT
elif op == ast.OpKind.Le:
predicate = llvmlite.ICMP_SLE
elif op == ast.OpKind.Gt:
predicate = llvmlite.ICMP_SGT
elif op == ast.OpKind.Ge:
predicate = llvmlite.ICMP_SGE
elif op == ast.OpKind.Eq:
predicate = llvmlite.ICMP_EQ
elif op == ast.OpKind.Ne:
predicate = llvmlite.ICMP_NE
elif op == ast.OpKind.Is:
# `x is y` → 指针/值相等比较
predicate = llvmlite.ICMP_EQ
elif op == ast.OpKind.IsNot:
# `x is not y` → 指针/值不等比较
predicate = llvmlite.ICMP_NE
# === 类型安全检查(BUG2 修复)===
# 规则:
# - 数值(0) vs 数值(0) → 允许(无关类型,按位宽 sext 对齐)
# - 一级指针(1) vs 一级指针(1) → 允许(无关 pointee 类型,地址比较)
# - 数值(0) vs 指针(1+) → 禁止(fast-fail,除 None 比较外)
# - 不同级指针(1 vs 2 等) → 禁止(fast-fail)
#
# None 比较跳过此检查(None 被翻译为 null 指针或 0,与任意指针/数值比较合法)
if cmp_with_none == 0:
lhs_ptr_level: int = self.get_ptr_level(lhs.Ty)
rhs_ptr_level: int = self.get_ptr_level(rhs.Ty)
if lhs_ptr_level != rhs_ptr_level:
# 指针 vs 整数字面量 0(判空模式 ptr != 0 / ptr == 0):
# 0 视为 NULL,转成与指针侧同类型的 null 指针(icmp 需要同类型),
# 不报类型不匹配(ClassArrayTest 的 if ptr != 0 等常见模式)。
zero_is_rhs: int = 0
zero_is_lhs: int = 0
if rhs_ptr_level == 0 and self._is_zero_int_literal(rhs_node) != 0:
zero_is_rhs = 1
if lhs_ptr_level == 0 and self._is_zero_int_literal(lhs_node) != 0:
zero_is_lhs = 1
if zero_is_rhs != 0 or zero_is_lhs != 0:
if zero_is_rhs != 0:
rhs = llvmlite.ConstNull(pool, lhs.Ty, "null")
else:
lhs = llvmlite.ConstNull(pool, rhs.Ty, "null")
else:
# 指针 vs 非零整数(C 哨兵值模式,如 w32.win32base.INVALID_HANDLE_VALUE = -1):
# 对齐 TPC HandlesExprOps.py L388-398 —— 指针 ptrtoint 成整数做地址值比较。
# (指针 vs 整数字面量 0 已在上方转 null 处理;此处处理非零整数哨兵)
if lhs_ptr_level != 0 and rhs_ptr_level == 0:
lhs = llvmlite.build_ptrtoint(builder, lhs, rhs.Ty)
elif rhs_ptr_level != 0 and lhs_ptr_level == 0:
rhs = llvmlite.build_ptrtoint(builder, rhs, lhs.Ty)
else:
# 不同级指针(1 vs 2 等)→ 禁止(fast-fail)
HandlesType.fatal_error(
cmp,
f"比较类型不匹配: 不同级指针比较 (lhs 层级={lhs_ptr_level}, rhs 层级={rhs_ptr_level})",
"TypeError")
else:
# None 比较: None 被翻译为 i8* null(一级指针)
# 如果 lhs 是指针,将 rhs (i8* null) bitcast 为 lhs 的指针类型
# 如果 lhs 是数值,将 rhs 转为数值 0(走正常整数比较)
# 如果 lhs 是结构体值类型,结构体永远不等于 None,返回常量 i1
lhs_ptr_level_none: int = self.get_ptr_level(lhs.Ty)
rhs_ptr_level_none: int = self.get_ptr_level(rhs.Ty)
if lhs_ptr_level_none != 0 and rhs_ptr_level_none != 0:
# 两边都是指针,类型可能不同(如 lhs=Struct*, rhs=i8* null)
# 由下方 bitcast 对齐处理,此处不处理
pass
elif lhs_ptr_level_none != 0 and rhs_ptr_level_none == 0:
# lhs 是指针, rhs 是数值 → 将 rhs 转为同层级 null 指针
rhs = llvmlite.ConstNull(pool, lhs.Ty, "null")
elif rhs_ptr_level_none != 0 and lhs_ptr_level_none == 0:
# 检查 lhs 是否是结构体值类型(非指针的 Struct)
lhs_is_struct_none: int = 0
match lhs.Ty:
case llvmlite.LLVMType.Struct(_, _, _):
lhs_is_struct_none = 1
case _:
lhs_is_struct_none = 0
if lhs_is_struct_none != 0:
# 结构体值永远不等于 None:==/is 返回 false,!=/is not 返回 true
if op == ast.OpKind.Eq or op == ast.OpKind.Is:
return llvmlite.const_int(pool, 1, 0)
else: # Ne, IsNot
return llvmlite.const_int(pool, 1, 1)
# lhs 是数值, rhs 是 i8* null → 将 rhs 转为数值 0(保持 lhs 不变)
rhs = llvmlite.const_int32(pool, 0)
# 类型对齐:icmp 要求两边类型相同
# 仅对数值类型做对齐(指针类型无需对齐,LLVM 允许同层级指针直接 icmp)
# i1 (bool) 侧用 zext 对齐(1 保持 1,而非 sext 的 0xFF...)
lhs_bits: int = self.get_llvm_type_bits(lhs.Ty)
rhs_bits: int = self.get_llvm_type_bits(rhs.Ty)
if lhs_bits != 0 and rhs_bits != 0 and lhs_bits != rhs_bits:
if lhs_bits < rhs_bits:
if self.should_zext_widen(pool, lhs, rhs, cmp.left) != 0:
lhs = llvmlite.build_zext(builder, lhs, rhs.Ty)
else:
lhs = llvmlite.build_sext(builder, lhs, rhs.Ty)
else:
if self.should_zext_widen(pool, rhs, lhs, rhs_node) != 0:
rhs = llvmlite.build_zext(builder, rhs, lhs.Ty)
else:
rhs = llvmlite.build_sext(builder, rhs, lhs.Ty)
# 指针类型对齐:同层级但不同 pointee 类型的指针需要 bitcast 统一类型
# 例如 i8* vs Struct* → 将 rhs bitcast 为 lhs 的类型
if lhs_bits == 0 and rhs_bits == 0:
lhs_is_ptr_now: int = self.is_ptr_type(lhs.Ty)
rhs_is_ptr_now: int = self.is_ptr_type(rhs.Ty)
if lhs_is_ptr_now != 0 and rhs_is_ptr_now != 0:
# 两边都是指针,用 TypePrint 比较类型字符串
lhs_ty_buf: str = pool.alloc(128)
rhs_ty_buf: str = pool.alloc(128)
if lhs_ty_buf is not None and rhs_ty_buf is not None:
llvmlite.TypePrint(lhs_ty_buf, 128, lhs.Ty, pool)
llvmlite.TypePrint(rhs_ty_buf, 128, rhs.Ty, pool)
if string.strcmp(lhs_ty_buf, rhs_ty_buf) != 0:
rhs = llvmlite.build_bitcast(builder, rhs, lhs.Ty)
# 浮点比较:任一操作数为浮点类型时使用 fcmp 而非 icmp
lhs_fbits: int = self.get_llvm_float_bits(lhs.Ty)
rhs_fbits: int = self.get_llvm_float_bits(rhs.Ty)
if lhs_fbits != 0 or rhs_fbits != 0:
# 浮点类型对齐(fpext/fptrunc)
if lhs_fbits != 0 and rhs_fbits != 0 and lhs_fbits != rhs_fbits:
if lhs_fbits < rhs_fbits:
lhs = llvmlite.build_fpext(builder, lhs, rhs.Ty)
else:
rhs = llvmlite.build_fptrunc(builder, rhs, lhs.Ty)
elif lhs_fbits != 0 and rhs_fbits == 0:
# int → float
rhs = llvmlite.build_si2fp(builder, rhs, lhs.Ty)
elif rhs_fbits != 0 and lhs_fbits == 0:
lhs = llvmlite.build_si2fp(builder, lhs, rhs.Ty)
# 映射比较谓词到 fcmp
fpredicate: int = llvmlite.FCMP_OEQ
if op == ast.OpKind.Eq or op == ast.OpKind.Is:
fpredicate = llvmlite.FCMP_OEQ
elif op == ast.OpKind.Ne or op == ast.OpKind.IsNot:
fpredicate = llvmlite.FCMP_ONE
elif op == ast.OpKind.Lt:
fpredicate = llvmlite.FCMP_OLT
elif op == ast.OpKind.Le:
fpredicate = llvmlite.FCMP_OLE
elif op == ast.OpKind.Gt:
fpredicate = llvmlite.FCMP_OGT
elif op == ast.OpKind.Ge:
fpredicate = llvmlite.FCMP_OGE
return llvmlite.build_fcmp(builder, fpredicate, lhs, rhs)
return llvmlite.build_icmp(builder, predicate, lhs, rhs)
# ============================================================
# 翻译一元运算表达式 UnaryOp(op, operand)
# ============================================================
def translate_unaryop(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:
"""翻译一元运算(-x, +x, not x, ~x)"""
uo: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
if uo is None:
return None
operand: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, uo.operand, None, 0, trans)
if operand is None:
return None
# === 一元运算符重载: -obj/+obj/~obj 调用 __neg__/__pos__/__invert__ ===
# 仅 USub/UAdd/Invert 有重载语义(not 无 dunder 方法)
if uo.op == ast.OpKind.USub or uo.op == ast.OpKind.UAdd or uo.op == ast.OpKind.Invert:
ovl_u: llvmlite.Value | t.CPtr = self.try_unary_operator_overload(
pool, builder, mod, operand, uo.op, trans)
if ovl_u is not None:
return ovl_u
if uo.op == ast.OpKind.USub:
# -x
# 浮点类型用 fneg(直接生成 fneg 指令,避免 0.0 - x 的类型不匹配)
operand_fbits: int = self.get_llvm_float_bits(operand.Ty)
if operand_fbits > 0:
return llvmlite.build_fneg(builder, operand)
# 整数类型:-x = 0 - x,根据位宽选择零值常量(避免 i32 与 i64 类型不匹配)
operand_bits: int = self.get_llvm_type_bits(operand.Ty)
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
if operand_bits == 64:
zero = llvmlite.const_int64(pool, 0)
elif operand_bits > 0 and operand_bits != 32:
# 其他位宽(i8/i16 等):用 i32 零值,operand 会被 coerce
zero = llvmlite.const_int32(pool, 0)
return llvmlite.build_sub(builder, zero, operand)
elif uo.op == ast.OpKind.UAdd:
# +x = x
return operand
elif uo.op == ast.OpKind.Not:
# not x = (x == 0/null),返回 i1
if self.is_ptr_type(operand.Ty) != 0:
# 指针类型:与 null 比较
null_op: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, operand.Ty, "null")
return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, operand, null_op)
# 结构体值类型:永远为真(非空),not 返回 false
operand_is_struct_not: int = 0
match operand.Ty:
case llvmlite.LLVMType.Struct(_, _, _):
operand_is_struct_not = 1
case _:
operand_is_struct_not = 0
if operand_is_struct_not != 0:
return llvmlite.const_int(pool, 1, 0)
operand_bits_not: int = self.get_llvm_type_bits(operand.Ty)
zero = llvmlite.const_int32(pool, 0)
if operand_bits_not == 64:
zero = llvmlite.const_int64(pool, 0)
return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, operand, zero)
elif uo.op == ast.OpKind.Invert:
# ~x = x ^ -1
operand_bits_inv: int = self.get_llvm_type_bits(operand.Ty)
neg1: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, -1)
if operand_bits_inv == 64:
neg1 = llvmlite.const_int64(pool, -1)
return llvmlite.build_xor(builder, operand, neg1)
return operand
# ============================================================
# 翻译布尔运算 BoolOp(op, values) — 短路求值,返回 i1
#
# and: a and b and c → 若 a 为假短路到 merge(返回 a 的 i1),否则求 b,
# 最后一个值直接求值并跳 merge
# or: a or b or c → 若 a 为真短路到 merge(返回 a 的 i1),否则求 b,
# 最后一个值直接求值并跳 merge
# merge 块用 phi 合并所有入边的 i1
# ============================================================
def translate_boolop(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译布尔运算(and/or)的短路求值,返回 i1"""
bo: ast.BoolOp | t.CPtr = (ast.BoolOp | t.CPtr)(node)
if bo is None:
return None
op: int = bo.op
values: list[ast.AST | t.CPtr] | t.CPtr = bo.values
if values is None:
return None
count: t.CSizeT = len(values)
if count == 0:
return None
if count == 1:
return self.translate_value(builder, pool, mod, values.get(0),
funcs_ptr, func_count, trans)
func: llvmlite.Function | t.CPtr = builder.Func
if func is None:
return None
# 创建 merge BB(用 builder.Counter 生成唯一标签名,position_at_end 的
# move_to_end 保证 BB 文本顺序与控制流顺序一致,标签名用 counter 安全)
cnt: int = builder.Counter
builder.Counter = cnt + 1
name_buf: str = pool.alloc(32)
if name_buf is None:
return None
viperlib.snprintf(name_buf, 32, "bool.merge.%d", cnt)
merge_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
# phi 入边链表头/尾
phi_head: llvmlite.PhiIncoming | t.CPtr = None
phi_tail: llvmlite.PhiIncoming | t.CPtr = None
phi_count: int = count - 1 # 循环次数
# 对每个值(除最后一个)求值并短路
for i in range(count - 1):
val_node: ast.AST | t.CPtr = values.get(i)
val: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, val_node,
funcs_ptr, func_count, trans)
if val is None:
return None
# 转为 i1(已经是 i1 的直接用,否则与 0/null 比较)
val_bits: int = self.get_llvm_type_bits(val.Ty)
val_i1: llvmlite.Value | t.CPtr = val
if val_bits != 1:
if self.is_ptr_type(val.Ty) != 0:
# 指针类型:与 null 比较(避免 i8* 与 i32 类型不匹配)
null_val: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, val.Ty, "null")
val_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, null_val)
else:
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
val_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, zero)
# 创建 next BB(求值下一个值)
cnt = builder.Counter
builder.Counter = cnt + 1
viperlib.snprintf(name_buf, 32, "bool.next.%d", cnt)
next_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
# and: 短路假→merge,真→next;or: 短路真→merge,假→next
if op == ast.OpKind.And:
llvmlite.build_cond_br(builder, val_i1, next_bb, merge_bb)
else:
llvmlite.build_cond_br(builder, val_i1, merge_bb, next_bb)
# 添加 phi 入边:(val_i1, cond_br 所在 BB)
cur_bb: llvmlite.BasicBlock | t.CPtr = builder.CurBlock
inc: llvmlite.PhiIncoming | t.CPtr = llvmlite.new_phi_incoming(pool, val_i1, cur_bb)
if phi_head is None:
phi_head = inc
phi_tail = inc
else:
phi_tail.Next = inc
phi_tail = inc
# 定位到 next BB,求值下一个值
llvmlite.position_at_end(builder, next_bb)
# 最后一个值:求值后直接跳 merge
last_node: ast.AST | t.CPtr = values.get(count - 1)
last_val: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, last_node,
funcs_ptr, func_count, trans)
if last_val is None:
return None
last_bits: int = self.get_llvm_type_bits(last_val.Ty)
last_i1: llvmlite.Value | t.CPtr = last_val
if last_bits != 1:
if self.is_ptr_type(last_val.Ty) != 0:
# 指针类型:与 null 比较(避免 i8* 与 i32 类型不匹配)
null_val2: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, last_val.Ty, "null")
last_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, last_val, null_val2)
else:
zero = llvmlite.const_int32(pool, 0)
last_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, last_val, zero)
llvmlite.build_br(builder, merge_bb)
cur_bb = builder.CurBlock
inc = llvmlite.new_phi_incoming(pool, last_i1, cur_bb)
if phi_head is None:
phi_head = inc
else:
phi_tail.Next = inc
phi_count += 1
# 定位到 merge,创建 phi 合并所有入边
llvmlite.position_at_end(builder, merge_bb)
i1_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool)
return llvmlite.build_phi(builder, i1_ty, phi_head, phi_count)
# ============================================================
# _build_source_path_from_sha1 - 通过 SHA1 从存储器构建源 .vp/.py 文件路径
#
# .vpi 文件可能未生成(Phase1 未输出 vpi),此时回退读取源 .py 文件。
# 从 StubMerger 全局存储器查找 SHA1 对应的相对路径,
# 根据 rel_path 前缀选择基础目录:
# - rel_path 以 "lib/" 开头 → App 源文件,用 {Config.SourceDir}/{rel_path}
# - 其他 → includes 文件,用 {Config.IncludesDir}/{rel_path}
#
# Returns:
# stdlib.malloc 分配的路径字符串(调用者负责 free),None 失败
# ============================================================
def _build_source_path_from_sha1(self, sha1: str) -> str:
"""通过 SHA1 从全局存储器构建源 .py 文件路径"""
if sha1 is None:
return None
if Config.IncludesDir is None:
return None
# 从 StubMerger 全局存储器获取相对路径数组
rel_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr()
count: int = StubMerger.GetSha1StoreCount()
if rel_arr_ptr is None or count <= 0:
return None
sha1_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr()
if sha1_arr_ptr is None:
return None
# 遍历查找匹配的 SHA1
idx_s: int = -1
for i in range(count):
sidx: t.CSizeT = t.CSizeT(i) * 17
cur_sha1: str = sha1_arr_ptr + sidx
if cur_sha1[0] == '\0':
continue
if string.strcmp(cur_sha1, sha1) == 0:
idx_s = i
break
if idx_s < 0:
return None
# 获取相对路径(MAX_REL_PATH_LEN=256,避免跨模块 CDefine 解析问题)
MAX_RPL: t.CSizeT = 256
ridx: t.CSizeT = t.CSizeT(idx_s) * MAX_RPL
rel_path: str = rel_arr_ptr + ridx
if rel_path[0] == '\0':
return None
# 选择基础目录:rel_path 以 "lib/" 开头 → App 源文件(SourceDir);
# 以 "includes/" 开头 → includes 文件(去掉 "includes/" 前缀)。
# 其他 rel_path(如 VKernel 的 "drivers/core/cpu/cpu.vp")无前缀,
# 无法从 rel_path 判断归属:先试 SourceDir(App 文件),文件不存在
# 再试 IncludesDir(includes 文件的 RelPath 同样无前缀存储)。
# 避免路径构建错误导致跨模块 CDefine 查找静默失败。
base_dir: str = None
rel_used: str = rel_path
if string.strlen(rel_path) >= 4:
if (rel_path[0] == 'l' and rel_path[1] == 'i' and
rel_path[2] == 'b' and rel_path[3] == '/'):
base_dir = Config.SourceDir if Config.SourceDir is not None else Config.IncludesDir
if base_dir is None and string.strlen(rel_path) >= 9:
if (rel_path[0] == 'i' and rel_path[1] == 'n' and
rel_path[2] == 'c' and rel_path[3] == 'l' and
rel_path[4] == 'u' and rel_path[5] == 'd' and
rel_path[6] == 'e' and rel_path[7] == 's' and
rel_path[8] == '/'):
rel_used = rel_path + 9
base_dir = Config.IncludesDir
if base_dir is None:
base_dir = Config.SourceDir if Config.SourceDir is not None else Config.IncludesDir
# 构建完整路径: {base_dir}/{rel_used}
base_len: t.CSizeT = string.strlen(base_dir)
rel_len: t.CSizeT = string.strlen(rel_used)
path_len: t.CSizeT = base_len + rel_len + 2
buf: str = stdlib.malloc(path_len)
if buf is None:
return None
viperlib.snprintf(buf, path_len, "%s/%s", base_dir, rel_used)
# 存在性检查:SourceDir 下不存在且 IncludesDir 不同 → 重试 IncludesDir
pf_chk: fileio.File | t.CPtr = fileio.File(buf, fileio.MODE.R)
if pf_chk.closed:
pf_chk.close()
if Config.IncludesDir is not None and base_dir != Config.IncludesDir:
stdlib.free(buf)
base_len2: t.CSizeT = string.strlen(Config.IncludesDir)
path_len2: t.CSizeT = base_len2 + rel_len + 2
buf = stdlib.malloc(path_len2)
if buf is None:
return None
viperlib.snprintf(buf, path_len2, "%s/%s", Config.IncludesDir, rel_used)
pf_chk2: fileio.File | t.CPtr = fileio.File(buf, fileio.MODE.R)
if pf_chk2.closed:
pf_chk2.close()
stdlib.free(buf)
return None
pf_chk2.close()
else:
stdlib.free(buf)
return None
else:
pf_chk.close()
return buf
# ============================================================
# _lookup_cross_module_cdefine - 跨模块查找 CDefine 常量
#
# 当当前模块的 CDefine 表中找不到某个 Name 时,从 from_imports
# 解析出源模块,再从该模块的 pyi 文件中解析 CDefine 常量值。
# 找到后缓存到当前模块的 CDefine 表中,避免重复查找。
#
# pyi 文件格式: "NAME: t.CDefine = value"
# ============================================================
def _lookup_cross_module_cdefine(self, pool: memhub.MemBuddy | t.CPtr,
name: str,
from_imports: str,
imported_modules: str = None,
depth: int = 0,
node: ast.AST | t.CPtr = None) -> t.CInt64T:
"""跨模块查找 CDefine 常量,返回值(64 位)或 -1(未找到)
查找顺序:
1. from_imports 精确匹配(from X import NAME)
2. from_imports star 回退(from X import *)
3. imported_modules 遍历(import X):当通过 import X 导入模块时,
模块中定义的 CDefine 常量可通过此路径找到(如方法签名默认参数引用)
depth: 递归深度(子模块递归/imported_modules 遍历的内部调用递增)。
node: 触发查找的 AST 节点(递归内传 None,line 0;顶层调用点传实际节点)。
"""
# 递归深度守卫:子模块递归(from .X import NAME re-export 链)与
# imported_modules 遍历组合可能形成循环(模块间互相 from import),
# 无上限时无限递归 → 每次递归 pool.alloc 缓冲(rc_sub_buf/rc_full_mod/
# rc_fi/im_buf/sub_fi)→ O(n²) 累积 → 主池 1.5GB OOM
# (NewAstTest CONST_INT 单次查找 14593 次调用,fi 恒为 CONST_INT:ast)。
# 正常跨模块 CDefine 查找深度 < 8(如 ast → ast.base),32 上限 fail-fast
# 暴露循环 import 链(而非静默 OOM)。报错带 node 行号 + 当前递归模块链。
if depth > 32:
err_buf: str = pool.alloc(256)
if err_buf is not None:
viperlib.snprintf(err_buf, 256,
"跨模块 CDefine 查找递归过深(疑似模块循环 import/re-export): %s (depth=%d fi=%s)",
name, depth,
from_imports if from_imports is not None else "(nil)")
HandlesType.fatal_error(node, err_buf, "InternalCompilerError")
HandlesType.fatal_error(node,
"跨模块 CDefine 查找递归过深(疑似模块循环 import/re-export)", "InternalCompilerError")
# 入口处清零标志,确保所有早退路径(return -1)都返回"未找到"状态
# 避免递归调用时前一次成功的标志残留导致误判
HandlesType.set_cdefine_found(0)
if name is None:
return -1
if from_imports is None:
from_imports = ""
# 1. 从 from_imports 查找名称对应的模块名
# 先尝试精确匹配(allow_star_fallback=0),避免 CDefine 常量被 star import 误导
mod_name_raw: str = HandlesImports.lookup_from_import(from_imports, name, 0)
if mod_name_raw is None:
# 精确匹配失败:尝试 star import 回退
mod_name_raw = HandlesImports.lookup_from_import(from_imports, name, 1)
# 截断模块名到 NUL 结尾:lookup_from_import 返回指向 from_imports 内部的
# 指针(非 NUL 结尾,后随空格分隔的其他条目,如 "ast CONST_STR:ast ...")。
# 不截断会污染 base_mod:strlen 读到整个 from_imports 末尾 → rc_full_mod
# 每层累积整个列表 + ".base"(dump:ast.base.base.base 无限递归根因),
# 且缓存 key 错乱("CONST_INT@ast CONST_STR:ast ...")无法命中。
# 与 _lookup_sha1_from_modname_raw 的截断逻辑一致(空格/冒号/NUL 分隔)。
base_mod: str = None
if mod_name_raw is not None:
mbl: t.CSizeT = 0
while mod_name_raw[mbl] != ' ' and mod_name_raw[mbl] != '\0' \
and mod_name_raw[mbl] != ':':
mbl += 1
mb_buf: str = pool.alloc(mbl + 1)
if mb_buf is not None:
mi2: t.CSizeT
for mi2 in range(mbl):
mb_buf[mi2] = mod_name_raw[mi2]
mb_buf[mbl] = '\0'
base_mod = mb_buf
while base_mod is not None and base_mod[0] == '.':
base_mod = base_mod + 1
# 跨模块 CDefine 查找缓存(perm_pool 永久,跨文件复用):
# 首次查找成功后结果缓存,后续文件(含 reset_to(mark2)/模块切换)直接命中,
# 避免 CONST_INT 跨模块递归查找反复触发(NewAstTest OOM 根因——
# 14593 次调用全部重复递归 ast → ast.base → ...)。
# key = "name@源模块"(源模块维度,用去点后的 base_mod 保证一致),
# 保持模块隔离:不同源模块的同名 CDefine 值不同时不串值。
# 仅 ALL_CAPS 全局唯一宏名才进入本函数(_is_cdefine_name_pattern 已过滤)。
xkey_buf: str = None
if base_mod is not None:
xk_len: t.CSizeT = string.strlen(name) + string.strlen(base_mod) + 2
xkey_buf = pool.alloc(xk_len)
if xkey_buf is not None:
viperlib.snprintf(xkey_buf, xk_len, "%s@%s", name, base_mod)
xval: str = HandlesType.lookup_xmod_cdefine(xkey_buf)
if HandlesType.is_cdefine_found() != 0:
if HandlesType.is_cdefine_str() != 0:
HandlesType.register_cdefine_string(pool, name, xval)
elif HandlesType.is_cdefine_double() != 0:
xd_v: double = string.atof(xval)
HandlesType.register_cdefine_double(pool, name, xd_v)
else:
xv_i: t.CInt64T = string.atoll(xval)
HandlesType.register_cdefine_constant(pool, name, xv_i)
return string.atoll(xval)
# 2. from_imports 查找失败:直接回退到 imported_modules 遍历(步骤 9.6)
# 注意:star fallback 可能返回错误模块(如 stdint),常量实际不在其中,
# 步骤 9.6 会在该模块查找失败后继续遍历 imported_modules
if mod_name_raw is None:
# from_imports 完全没有匹配(精确和 star 都失败)
# 直接进入 imported_modules 遍历(步骤 9.6)
if imported_modules is None:
return -1
# 跳过 from_imports 模块读取,直接走步骤 9.6 的 imported_modules 遍历
# 设置 result_found=0 跳过步骤 3-9,直接到 9.6
HandlesType.set_cdefine_found(0)
# 直接遍历 imported_modules
im_len: t.CSizeT = string.strlen(imported_modules)
im_ci: t.CSizeT = 0
while im_ci < im_len:
while im_ci < im_len and imported_modules[im_ci] == ' ':
im_ci += 1
if im_ci >= im_len:
break
im_start: t.CSizeT = im_ci
while im_ci < im_len and imported_modules[im_ci] != ' ':
im_ci += 1
im_end: t.CSizeT = im_ci
im_nlen: t.CSizeT = im_end - im_start
if im_nlen > 0 and im_nlen < 256:
im_buf: str = pool.alloc(im_nlen + 1)
if im_buf is not None:
mk: t.CSizeT = 0
while mk < im_nlen:
im_buf[mk] = imported_modules[im_start + mk]
mk += 1
im_buf[im_nlen] = '\0'
sub_fi: str = pool.alloc(im_nlen + 3)
if sub_fi is not None:
sub_fi[0] = '*'
sub_fi[1] = ':'
mk2: t.CSizeT = 0
while mk2 < im_nlen:
sub_fi[2 + mk2] = im_buf[mk2]
mk2 += 1
sub_fi[2 + im_nlen] = '\0'
sub_val: t.CInt64T = self._lookup_cross_module_cdefine(pool, name, sub_fi, None, depth + 1, node)
if HandlesType.is_cdefine_found() != 0:
return sub_val
return -1
# 2. 截断模块名 → 去前导点 → 精确+后缀查找 SHA1(复用 HandlesExprCall 公共逻辑)
sha1: str = HandlesExprCall._lookup_sha1_from_modname_raw(pool, mod_name_raw)
if sha1 is None:
return -1
# 5. 获取 temp_dir
temp_dir: str = HandlesType.get_temp_dir()
if temp_dir is None:
return -1
# 6. 构建 vpi 文件路径
td_len: t.CSizeT = string.strlen(temp_dir)
pyi_path: str = StubMerger._sliced_path(temp_dir, td_len, sha1, "vpi")
if pyi_path is None:
return -1
# 7. 读取 vpi 文件;若不存在或内容过短(空 stub)则回退读取源 .vp/.py 文件
PYI_READ_BUF_SIZE: t.CSizeT = 65536
pyi_buf: bytes = stdlib.malloc(PYI_READ_BUF_SIZE)
if pyi_buf is None:
stdlib.free(pyi_path)
return -1
pf: fileio.File | t.CPtr = fileio.File(pyi_path, fileio.MODE.R)
need_py_fallback: int = 0
bytes_read: LONG = 0
if pf.closed:
# .vpi 文件不存在:回退读取源 .vp/.py 文件
need_py_fallback = 1
else:
bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
pf.close()
# vpi 内容过短(< 10 字节)视为空 stub,回退 .py
if bytes_read < 10:
need_py_fallback = 1
else:
if bytes_read < PYI_READ_BUF_SIZE:
pyi_buf[bytes_read] = 0
else:
pyi_buf[PYI_READ_BUF_SIZE - 1] = 0
# vpi(存根签名文件)中的 CDefine 声明可能只带名字不带值
# (Converter 旧版只对 CONST_INT/BOOL 等输出 "= value",CONST_FLOAT
# 等不输出 → numpy 的 pi/e 在 stub 中无值),真正的值在源
# .vp/.py 文件中。旧启发式"vpi 中任一 CDefine 带值 → 不读源"是错的:
# 模块同时含 int CDefine(带值,如 numpy.MAX_NDIM=4)与 float
# CDefine(无值,如 pi)时,int 行让 cd_sig=1 而漏回退源 → pi
# 解析失败 → 退化 @pi 全局指针 → float<->ptr 报错(NumpyTest 根因)。
# 改为按目标名判定:仅当 vpi 中 name 的 CDefine 行带值时才信任
# vpi,否则(name 行无值 / name 不在 vpi / 整个 vpi 无带值 CDefine)
# 一律回退读源文件。
cd_sig: int = 0
name_has_val: int = 0
cs_name_len: t.CSizeT = string.strlen(name)
cs_total: t.CSizeT = string.strlen(pyi_buf)
cs_i: t.CSizeT = 0
while cs_i + 9 < cs_total:
if (pyi_buf[cs_i] == 't' and pyi_buf[cs_i + 1] == '.' and
pyi_buf[cs_i + 2] == 'C' and pyi_buf[cs_i + 3] == 'D' and
pyi_buf[cs_i + 4] == 'e' and pyi_buf[cs_i + 5] == 'f' and
pyi_buf[cs_i + 6] == 'i' and pyi_buf[cs_i + 7] == 'n' and
pyi_buf[cs_i + 8] == 'e'):
# 检查该行剩余部分是否有 '='(带值声明才在 vpi 中匹配)
cd_eq: int = 0
cs_j: t.CSizeT = cs_i + 9
while cs_j < cs_total and pyi_buf[cs_j] != '\n':
if pyi_buf[cs_j] == '=':
cd_eq = 1
break
cs_j += 1
if cd_eq != 0:
cd_sig = 1
# 定位该行行首,检查是否以 "name:" 开头(目标名带值)
cs_line_start: t.CSizeT = cs_i
while cs_line_start > 0 and pyi_buf[cs_line_start - 1] != '\n':
cs_line_start -= 1
if cs_i - cs_line_start >= cs_name_len + 1:
nm_ok: int = 1
for ki2 in range(cs_name_len):
if pyi_buf[cs_line_start + ki2] != name[ki2]:
nm_ok = 0
break
if nm_ok == 1 and pyi_buf[cs_line_start + cs_name_len] == ':':
name_has_val = 1
cs_i += 1
if cd_sig == 0 or name_has_val == 0:
need_py_fallback = 1
stdlib.free(pyi_path)
if need_py_fallback != 0:
# 从 SHA1 存储器查找相对路径,构建 {base_dir}/{rel_path}
src_path: str = self._build_source_path_from_sha1(sha1)
if src_path is None:
# 存储器中找不到 SHA1 = 编译器 bug(Phase1/Phase2 未填充存储器)
# 报错终止,不静默返回 -1(避免后续生成错误的 @UNDEFINED 全局变量引用)
err_buf: str = pool.alloc(512)
if err_buf is not None:
viperlib.snprintf(err_buf, 512,
"跨模块 CDefine 查找失败:SHA1 %s 不在存储器中(base_mod=%s name=%s)。"
"Phase1/Phase2 未正确填充 PopulateSha1MapStore/AppendToSha1MapStore",
sha1, base_mod, name)
VLogger.error(err_buf, "XMOD-CD")
stdlib.free(pyi_buf)
return -1
pf = fileio.File(src_path, fileio.MODE.R)
stdlib.free(src_path)
if pf.closed:
stdlib.free(pyi_buf)
return -1
bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
pf.close()
if bytes_read <= 0:
stdlib.free(pyi_buf)
return -1
if bytes_read < PYI_READ_BUF_SIZE:
pyi_buf[bytes_read] = 0
else:
pyi_buf[PYI_READ_BUF_SIZE - 1] = 0
# 8. 在 pyi_buf 中查找 "name: t.CDefine = value" 行
# 格式: NAME: t.CDefine = value
name_len: t.CSizeT = string.strlen(name)
total_len: t.CSizeT = string.strlen(pyi_buf)
pos: t.CSizeT = 0
result_val: t.CInt64T = 0
result_found: int = 0
res_is_double: int = 0
res_is_str: int = 0
while pos < total_len:
# 找到行首
line_start: t.CSizeT = pos
# 找到行尾
while pos < total_len and pyi_buf[pos] != '\n':
pos += 1
line_len: t.CSizeT = pos - line_start
if pos < total_len:
pos += 1 # 跳过 '\n'
# 检查行是否以 "name:" 开头
if line_len < name_len + 1:
continue
match_ok: int = 1
for ki in range(name_len):
if pyi_buf[line_start + ki] != name[ki]:
match_ok = 0
break
if match_ok == 0:
continue
if pyi_buf[line_start + name_len] != ':':
continue
# 检查行是否包含 "t.CDefine"
# 在行中查找 "t.CDefine"
has_cdefine: int = 0
ci: t.CSizeT = line_start + name_len + 1
while ci + 8 <= line_start + line_len:
if (pyi_buf[ci] == 't' and pyi_buf[ci + 1] == '.' and
pyi_buf[ci + 2] == 'C' and pyi_buf[ci + 3] == 'D' and
pyi_buf[ci + 4] == 'e' and pyi_buf[ci + 5] == 'f' and
pyi_buf[ci + 6] == 'i' and pyi_buf[ci + 7] == 'n' and
pyi_buf[ci + 8] == 'e'):
has_cdefine = 1
break
ci += 1
if has_cdefine == 0:
continue
# 找到 "=" 后面的值
eq_pos: t.CSizeT = line_start + name_len + 1
while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '=':
eq_pos += 1
if eq_pos >= line_start + line_len:
continue
eq_pos += 1 # 跳过 '='
# 跳过空格
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
eq_pos += 1
# 检查是否是 t.CUnsignedLong(...) / t.CLong(...) / t.CInt(...) 等类型构造函数
# 格式: t.CUnsignedLong(-11) / t.CUnsignedLong(0xFFFFFFFF)
# 如果是,跳过 "t.CXxx(" 前缀,解析括号内的值,忽略结尾 ')'
is_type_ctor: int = 0
if eq_pos + 2 < line_start + line_len:
if pyi_buf[eq_pos] == 't' and pyi_buf[eq_pos + 1] == '.':
# 找到 '(' 的位置
paren_pos: t.CSizeT = eq_pos + 2
while paren_pos < line_start + line_len and pyi_buf[paren_pos] != '(':
paren_pos += 1
if paren_pos < line_start + line_len and pyi_buf[paren_pos] == '(':
is_type_ctor = 1
eq_pos = paren_pos + 1 # 跳过 '('
# 跳过括号内可能的前导空格
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
eq_pos += 1
# 括号包裹标记:(1 << 23) / (512) / (A | B)
is_paren_expr: int = 0
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '(':
is_paren_expr = 1
eq_pos += 1
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
eq_pos += 1
# === 解析左操作数 token(数字 / hex / 负号 / 名称) ===
# 负号标记
is_neg: int = 0
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-':
is_neg = 1
eq_pos += 1
val_str_start: t.CSizeT = eq_pos
val_str_len: t.CSizeT = 0
is_hex: int = 0
is_float_val: int = 0
is_name_val: int = 0
is_str_val: int = 0
# 字符串值(如 ZLIB_VERSION: t.CDefine = "1.3.2"):读引号内内容。
# 跨模块读回此前不支持引号 → val_str_len=0 被跳过 → "跨模块 CDefine
# 未找到"(test_pyzlib.vp pyzlib.ZLIB_VERSION 根因)
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '"':
is_str_val = 1
eq_pos += 1 # 跳过开引号
val_str_start = eq_pos
while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '"':
val_str_len += 1
eq_pos += 1
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '"':
eq_pos += 1 # 跳过闭引号
if eq_pos + 1 < line_start + line_len:
if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'):
is_hex = 1
val_str_len = 2
eq_pos += 2
elif (pyi_buf[eq_pos] >= 'A' and pyi_buf[eq_pos] <= 'Z') or \
(pyi_buf[eq_pos] >= 'a' and pyi_buf[eq_pos] <= 'z') or \
pyi_buf[eq_pos] == '_':
is_name_val = 1
while eq_pos < line_start + line_len:
c_tk: t.CChar = pyi_buf[eq_pos]
if (c_tk >= 'A' and c_tk <= 'Z') or (c_tk >= 'a' and c_tk <= 'z') or \
(c_tk >= '0' and c_tk <= '9') or c_tk == '_':
val_str_len += 1
eq_pos += 1
else:
break
# 数字/hex 字符续读(字符串值已整体读取,跳过)
while eq_pos < line_start + line_len and is_name_val == 0 and is_str_val == 0:
ch: t.CChar = pyi_buf[eq_pos]
if is_hex == 1:
if ((ch >= '0' and ch <= '9') or
(ch >= 'a' and ch <= 'f') or
(ch >= 'A' and ch <= 'F')):
val_str_len += 1
eq_pos += 1
else:
break
else:
if ch >= '0' and ch <= '9':
val_str_len += 1
eq_pos += 1
elif ch == '.' and is_float_val == 0:
# 浮点值:'3.14159'('.' 只允许一次)
is_float_val = 1
val_str_len += 1
eq_pos += 1
else:
break
if val_str_len == 0 and is_str_val == 0:
continue
# 复制左 token 到缓冲区
val_buf: str = pool.alloc(val_str_len + 1)
if val_buf is None:
continue
for vi in range(val_str_len):
val_buf[vi] = pyi_buf[val_str_start + vi]
val_buf[val_str_len] = '\0'
# 左 token 求值
lval: t.CInt64T = 0
l_is_double: int = 0
ldbl: double = 0.0
if is_str_val != 0:
# 字符串 CDefine(如 ZLIB_VERSION = "1.3.2"):注册字符串值
# (含空串 ""),result_found 标记 + res_is_str 供尾部跳过整数
# 注册/缓存(否则 register_cdefine_constant 把字符串当 0 整数)
HandlesType.register_cdefine_string(pool, name, val_buf)
result_val = 0
result_found = 1
res_is_str = 1
elif is_name_val != 0:
# True/False 布尔字面量 → 1/0(对齐 TPC:bool 在 CDefine 语境按整数处理)
if string.strcmp(val_buf, "True") == 0:
lval = 1
elif string.strcmp(val_buf, "False") == 0:
lval = 0
else:
# 名称引用:查当前 CDefine 表(如 FOREGROUND_COMBO = FOREGROUND_RED)
lval = HandlesType.lookup_cdefine_constant(val_buf)
if HandlesType.is_cdefine_found() == 0:
# 名称未注册:该行无法求值,跳过
continue
elif is_hex == 1:
# 手动解析十六进制(跳过 0x 前缀)——64 位累加,
# 0xFFFFFFFF=4294967295 保持全精度(常量生成端 i32 截断回 -1)
hex_result: t.CInt64T = 0
hex_buf: str = val_buf + 2 # 跳过 "0x"
for hi in range(val_str_len - 2):
hc: t.CChar = hex_buf[hi]
hd: int = 0
if hc >= '0' and hc <= '9':
hd = hc - '0'
elif hc >= 'a' and hc <= 'f':
hd = hc - 'a' + 10
elif hc >= 'A' and hc <= 'F':
hd = hc - 'A' + 10
else:
break
hex_result = hex_result * 16 + hd
lval = hex_result
elif is_float_val != 0:
l_is_double = 1
ldbl = string.atof(val_buf)
lval = string.atoll(val_buf)
else:
lval = string.atoll(val_buf)
if is_neg != 0:
lval = -lval
if l_is_double != 0:
ldbl = -ldbl
# 跳过空格,检查是否有二元运算符
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
eq_pos += 1
# 解析运算符(<< >> | & ^ + - * /)
cd_op: int = 0
if eq_pos + 1 < line_start + line_len:
if pyi_buf[eq_pos] == '<' and pyi_buf[eq_pos + 1] == '<':
cd_op = 1 # LShift
eq_pos += 2
elif pyi_buf[eq_pos] == '>' and pyi_buf[eq_pos + 1] == '>':
cd_op = 2 # RShift
eq_pos += 2
if cd_op == 0 and eq_pos < line_start + line_len:
if pyi_buf[eq_pos] == '|':
cd_op = 3 # BitOr
eq_pos += 1
elif pyi_buf[eq_pos] == '&':
cd_op = 4 # BitAnd
eq_pos += 1
elif pyi_buf[eq_pos] == '^':
cd_op = 5 # BitXor
eq_pos += 1
elif pyi_buf[eq_pos] == '+':
cd_op = 6 # Add
eq_pos += 1
elif pyi_buf[eq_pos] == '-':
cd_op = 7 # Sub
eq_pos += 1
elif pyi_buf[eq_pos] == '*':
cd_op = 8 # Mult
eq_pos += 1
elif pyi_buf[eq_pos] == '/':
cd_op = 9 # Div
eq_pos += 1
if cd_op != 0:
# === 解析右操作数 token ===
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
eq_pos += 1
r_neg: int = 0
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-':
r_neg = 1
eq_pos += 1
r_start: t.CSizeT = eq_pos
r_len: t.CSizeT = 0
r_hex: int = 0
r_name: int = 0
if eq_pos + 1 < line_start + line_len:
if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'):
r_hex = 1
r_len = 2
eq_pos += 2
elif (pyi_buf[eq_pos] >= 'A' and pyi_buf[eq_pos] <= 'Z') or \
(pyi_buf[eq_pos] >= 'a' and pyi_buf[eq_pos] <= 'z') or \
pyi_buf[eq_pos] == '_':
r_name = 1
while eq_pos < line_start + line_len:
c_r: t.CChar = pyi_buf[eq_pos]
if (c_r >= 'A' and c_r <= 'Z') or (c_r >= 'a' and c_r <= 'z') or \
(c_r >= '0' and c_r <= '9') or c_r == '_':
r_len += 1
eq_pos += 1
else:
break
while eq_pos < line_start + line_len and r_name == 0:
c_r2: t.CChar = pyi_buf[eq_pos]
if r_hex == 1:
if ((c_r2 >= '0' and c_r2 <= '9') or
(c_r2 >= 'a' and c_r2 <= 'f') or
(c_r2 >= 'A' and c_r2 <= 'F')):
r_len += 1
eq_pos += 1
else:
break
else:
if c_r2 >= '0' and c_r2 <= '9':
r_len += 1
eq_pos += 1
else:
break
if r_len == 0:
# 运算符后无右操作数:无法求值,跳过该行
continue
r_buf: str = pool.alloc(r_len + 1)
if r_buf is None:
continue
for r_i in range(r_len):
r_buf[r_i] = pyi_buf[r_start + r_i]
r_buf[r_len] = '\0'
rval: t.CInt64T = 0
if r_name != 0:
# True/False 布尔字面量 → 1/0
if string.strcmp(r_buf, "True") == 0:
rval = 1
elif string.strcmp(r_buf, "False") == 0:
rval = 0
else:
rval = HandlesType.lookup_cdefine_constant(r_buf)
if HandlesType.is_cdefine_found() == 0:
# 名称未注册:无法求值,跳过该行
continue
elif r_hex != 0:
rhex_v: t.CInt64T = 0
rh_buf: str = r_buf + 2
for rh_i in range(r_len - 2):
rc: t.CChar = rh_buf[rh_i]
rd: int = 0
if rc >= '0' and rc <= '9':
rd = rc - '0'
elif rc >= 'a' and rc <= 'f':
rd = rc - 'a' + 10
elif rc >= 'A' and rc <= 'F':
rd = rc - 'A' + 10
else:
break
rhex_v = rhex_v * 16 + rd
rval = rhex_v
else:
rval = string.atoll(r_buf)
if r_neg != 0:
rval = -rval
# 计算二元表达式结果(对齐 TPC ConstEvaluator)
if cd_op == 1:
result_val = lval << rval
elif cd_op == 2:
result_val = lval >> rval
elif cd_op == 3:
result_val = lval | rval
elif cd_op == 4:
result_val = lval & rval
elif cd_op == 5:
result_val = lval ^ rval
elif cd_op == 6:
result_val = lval + rval
elif cd_op == 7:
result_val = lval - rval
elif cd_op == 8:
result_val = lval * rval
else:
if rval == 0:
continue
result_val = lval // rval
result_found = 1
break
else:
# 单值(无运算符):括号内单值如 (512) / (-11) 也走这里
if is_paren_expr != 0:
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
eq_pos += 1
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == ')':
eq_pos += 1
if is_float_val != 0:
# 浮点 CDefine(如 U_M_PI = 3.14159...):
# 值缓存为 double(register_cdefine_double),供调用处生成
# double 常量;整数部分供 int 路径使用(is_cdefine_double 标志)。
HandlesType.register_cdefine_double(pool, name, ldbl)
result_val = lval
result_found = 1
res_is_double = 1
else:
result_val = lval
result_found = 1
break
# 9.5 如果当前模块文件中没找到,递归查找子模块
# 处理 from .X import NAME 和 from .X import * 的情况
# 例如 ast.__init__.vp 中 from .base import CONST_INT, ...
# 需要递归查找 ast.base 子模块中的 CONST_INT: t.CDefine = 1
if result_found == 0:
# base_mod 已在函数开头计算(mod_name_raw 去前导点),
# 供子模块递归构建 "base_mod.X" 全名。
name_len_rc: t.CSizeT = string.strlen(name)
rc_pos: t.CSizeT = 0
while rc_pos < total_len and result_found == 0:
# 找到行首
rc_line_start: t.CSizeT = rc_pos
# 找到行尾
while rc_pos < total_len and pyi_buf[rc_pos] != '\n':
rc_pos += 1
rc_line_len: t.CSizeT = rc_pos - rc_line_start
if rc_pos < total_len:
rc_pos += 1
# 检查行是否以 "from ." 开头(相对导入子模块)
if rc_line_len < 12:
continue
if pyi_buf[rc_line_start] != 'f' or pyi_buf[rc_line_start+1] != 'r' or \
pyi_buf[rc_line_start+2] != 'o' or pyi_buf[rc_line_start+3] != 'm' or \
pyi_buf[rc_line_start+4] != ' ' or pyi_buf[rc_line_start+5] != '.':
continue
# 提取子模块名(从 rc_line_start+6 开始,到空格/tab 为止)
rc_sub_start: t.CSizeT = rc_line_start + 6
rc_sub_end: t.CSizeT = rc_sub_start
while rc_sub_end < rc_line_start + rc_line_len and \
pyi_buf[rc_sub_end] != ' ' and pyi_buf[rc_sub_end] != '\t':
rc_sub_end += 1
rc_sub_len: t.CSizeT = rc_sub_end - rc_sub_start
if rc_sub_len == 0:
continue
# 检查行是否包含 "import"
rc_has_import: int = 0
rc_ipos: t.CSizeT = rc_sub_end
while rc_ipos + 6 <= rc_line_start + rc_line_len:
if pyi_buf[rc_ipos] == 'i' and pyi_buf[rc_ipos+1] == 'm' and \
pyi_buf[rc_ipos+2] == 'p' and pyi_buf[rc_ipos+3] == 'o' and \
pyi_buf[rc_ipos+4] == 'r' and pyi_buf[rc_ipos+5] == 't':
rc_has_import = 1
break
rc_ipos += 1
if rc_has_import == 0:
continue
# 检查 NAME 是否在导入列表中,或导入是 *
# 处理多行导入:from .X import (... NAME, ...)
# 从 "import" 之后扫描,如果有 "(" 则扫描到 ")" 为止
rc_imp_start: t.CSizeT = rc_ipos + 6
rc_star: int = 0
rc_name_match: int = 0
# 确定扫描范围:单行或跨行(括号内)
rc_scan_end: t.CSizeT = rc_line_start + rc_line_len
rc_has_paren: int = 0
rc_pp: t.CSizeT = rc_imp_start
while rc_pp < rc_line_start + rc_line_len:
if pyi_buf[rc_pp] == '(':
rc_has_paren = 1
break
if pyi_buf[rc_pp] == '*':
rc_star = 1
rc_pp += 1
if rc_has_paren != 0:
# 多行导入:从 "(" 之后扫描到 ")" 为止
rc_scan_end = rc_pp + 1
rc_paren_closed: int = 0
rc_sp: t.CSizeT = rc_pp + 1
while rc_sp < total_len and rc_paren_closed == 0:
if pyi_buf[rc_sp] == ')':
rc_paren_closed = 1
rc_scan_end = rc_sp
break
rc_sp += 1
if rc_paren_closed == 0:
rc_scan_end = total_len
# 在 rc_imp_start 到 rc_scan_end 范围内查找 NAME 和 *
rc_cpos: t.CSizeT = rc_imp_start
while rc_cpos < rc_scan_end:
rc_ch: t.CChar = pyi_buf[rc_cpos]
if rc_ch == '*':
rc_star = 1
# 检查 NAME 是否匹配(作为完整单词)
if rc_cpos + name_len_rc <= rc_scan_end:
rc_match: int = 1
rc_ki: t.CSizeT
for rc_ki in range(name_len_rc):
if pyi_buf[rc_cpos + rc_ki] != name[rc_ki]:
rc_match = 0
break
if rc_match == 1:
# 检查前字符是否是非标识符字符
rc_before_ok: int = 0
if rc_cpos == rc_imp_start:
rc_before_ok = 1
else:
rc_before_ch: t.CChar = pyi_buf[rc_cpos - 1]
if rc_before_ch == ' ' or rc_before_ch == '\t' or \
rc_before_ch == '(' or rc_before_ch == ',' or \
rc_before_ch == '\n' or rc_before_ch == '\r':
rc_before_ok = 1
# 检查后字符是否是非标识符字符
rc_after_ok: int = 0
rc_after_pos: t.CSizeT = rc_cpos + name_len_rc
if rc_after_pos >= rc_scan_end:
rc_after_ok = 1
else:
rc_after_ch: t.CChar = pyi_buf[rc_after_pos]
if rc_after_ch == ' ' or rc_after_ch == '\t' or \
rc_after_ch == ')' or rc_after_ch == ',' or \
rc_after_ch == '\r' or rc_after_ch == '\n':
rc_after_ok = 1
if rc_before_ok == 1 and rc_after_ok == 1:
rc_name_match = 1
rc_cpos += 1
if rc_star == 0 and rc_name_match == 0:
continue
# 复制子模块名到新缓冲区(pyi_buf + rc_sub_start 不是 NUL 结尾)
rc_sub_buf: str = pool.alloc(rc_sub_len + 1)
if rc_sub_buf is None:
continue
rc_si: t.CSizeT
for rc_si in range(rc_sub_len):
rc_sub_buf[rc_si] = pyi_buf[rc_sub_start + rc_si]
rc_sub_buf[rc_sub_len] = '\0'
# 构建完整子模块名 base_mod.X
rc_full_len: t.CSizeT = string.strlen(base_mod) + 1 + rc_sub_len + 1
rc_full_mod: str = pool.alloc(rc_full_len)
if rc_full_mod is None:
continue
viperlib.snprintf(rc_full_mod, rc_full_len, "%s.%s", base_mod, rc_sub_buf)
# 防循环:若 base_mod 已以 "."+rc_sub_buf 结尾(如 ast.base 再拼
# .base → ast.base.base),说明子模块递归在重复追加同一模块段
# (模块间循环 re-export / 后缀匹配容错命中自身),跳过该次递归
# (NewAstTest CONST_INT 查找 dump:ast.base.base.base 无限增长根因)。
rc_blen: t.CSizeT = string.strlen(base_mod)
if rc_blen > rc_sub_len + 1:
rc_tail: str = base_mod + (rc_blen - rc_sub_len)
if rc_tail[0] == '.' and \
string.strncmp(rc_tail + 1, rc_sub_buf, rc_sub_len) == 0:
continue
# 构建临时 from_imports "name:full_mod"
rc_fi_len: t.CSizeT = name_len_rc + 1 + string.strlen(rc_full_mod) + 1
rc_fi: str = pool.alloc(rc_fi_len)
if rc_fi is None:
continue
viperlib.snprintf(rc_fi, rc_fi_len, "%s:%s", name, rc_full_mod)
# 递归调用查找子模块
rc_sub_val: int = self._lookup_cross_module_cdefine(pool, name, rc_fi, None, depth + 1, node)
if HandlesType.is_cdefine_found() != 0:
result_val = rc_sub_val
result_found = 1
stdlib.free(pyi_buf)
# 9.6 from_imports star fallback 模块中未找到:回退到 imported_modules 遍历
# 当 `from stdint import *` 导致 star fallback 返回 stdint,但目标常量
# (如 SHARE_READ)实际在 `import w32.fileio` 导入的模块中时,
# 需要遍历 imported_modules 逐个查找
if result_found == 0 and imported_modules is not None:
im2_len: t.CSizeT = string.strlen(imported_modules)
im2_ci: t.CSizeT = 0
while im2_ci < im2_len and result_found == 0:
while im2_ci < im2_len and imported_modules[im2_ci] == ' ':
im2_ci += 1
if im2_ci >= im2_len:
break
im2_start: t.CSizeT = im2_ci
while im2_ci < im2_len and imported_modules[im2_ci] != ' ':
im2_ci += 1
im2_end: t.CSizeT = im2_ci
im2_nlen: t.CSizeT = im2_end - im2_start
if im2_nlen > 0 and im2_nlen < 256:
im2_buf: str = pool.alloc(im2_nlen + 1)
if im2_buf is not None:
mk3: t.CSizeT = 0
while mk3 < im2_nlen:
im2_buf[mk3] = imported_modules[im2_start + mk3]
mk3 += 1
im2_buf[im2_nlen] = '\0'
sub_fi2: str = pool.alloc(im2_nlen + 3)
if sub_fi2 is not None:
sub_fi2[0] = '*'
sub_fi2[1] = ':'
mk4: t.CSizeT = 0
while mk4 < im2_nlen:
sub_fi2[2 + mk4] = im2_buf[mk4]
mk4 += 1
sub_fi2[2 + im2_nlen] = '\0'
sub_val2: t.CInt64T = self._lookup_cross_module_cdefine(pool, name, sub_fi2, None, depth + 1, node)
if HandlesType.is_cdefine_found() != 0:
result_val = sub_val2
result_found = 1
# 10. 缓存到当前模块的 CDefine 表中,并写入跨模块 perm_pool 缓存
# 浮点 CDefine 已在匹配循环中 register_cdefine_double 缓存
# (res_is_double=1),此处只缓存整数 CDefine,避免覆盖 double。
if result_found != 0:
if res_is_str != 0:
# 字符串 CDefine:值已在匹配循环 register_cdefine_string(val_buf
# 是命中行的字符串内容,pool 分配跨函数有效),此处只写跨模块缓存
if base_mod is not None:
ck_len_s: t.CSizeT = string.strlen(name) + string.strlen(base_mod) + 2
ckey_s: str = pool.alloc(ck_len_s)
if ckey_s is not None:
viperlib.snprintf(ckey_s, ck_len_s, "%s@%s", name, base_mod)
HandlesType.register_xmod_cdefine(ckey_s, val_buf, 0, 1)
elif res_is_double == 0:
HandlesType.register_cdefine_constant(pool, name, result_val)
# 跨模块查找缓存(perm_pool 永久,跨文件复用):整数
# key 与入口查询一致:"name@源模块"(用截断后的 base_mod,
# 与 mod_name_raw 一致——两者都是去点后的模块名,保证 key 匹配)
if base_mod is not None:
ck_len: t.CSizeT = string.strlen(name) + string.strlen(base_mod) + 2
ckey_buf: str = pool.alloc(ck_len)
if ckey_buf is not None:
viperlib.snprintf(ckey_buf, ck_len, "%s@%s", name, base_mod)
cbuf_i: str = pool.alloc(32)
if cbuf_i is not None:
viperlib.snprintf(cbuf_i, 32, "%lld", result_val)
HandlesType.register_xmod_cdefine(ckey_buf, cbuf_i, 0, 0)
else:
# 浮点:从本地表取回 %.17g 文本,写入跨模块缓存(保精度)
if base_mod is not None:
ck_len2: t.CSizeT = string.strlen(name) + string.strlen(base_mod) + 2
ckey2: str = pool.alloc(ck_len2)
if ckey2 is not None:
viperlib.snprintf(ckey2, ck_len2, "%s@%s", name, base_mod)
dval_cc: double = HandlesType.lookup_cdefine_double(name)
if HandlesType.is_cdefine_double() != 0:
cbuf_d: str = pool.alloc(64)
if cbuf_d is not None:
stdio.snprintf(cbuf_d, 64, "%.17g", dval_cc)
HandlesType.register_xmod_cdefine(ckey2, cbuf_d, 1, 0)
HandlesType.set_cdefine_found(1)
else:
HandlesType.set_cdefine_found(0)
return result_val
# ============================================================
# _is_cdefine_name_pattern - 检查名字是否符合 CDefine 命名约定
#
# CDefine 常量(如 FOREGROUND_GREEN, STD_OUTPUT_HANDLE, SCOPE_MODULE)
# 都遵循 ALL_CAPS 约定:仅含大写字母 A-Z、数字 0-9、下划线 _,
# 且至少含一个大写字母。
#
# Python 关键字(match, if, for)和变量名(self, pool, builder)
# 都含小写字母,不会匹配,从而避免 fail-fast 误报。
#
# 返回: 1=符合 CDefine 命名约定, 0=不符合
# ============================================================
def _is_cdefine_name_pattern(self, name: str) -> int:
"""检查名字是否符合 CDefine 命名约定(全大写+下划线+数字)"""
if name is None:
return 0
has_upper: int = 0
i: t.CSizeT = 0
while name[i] != '\0':
c: t.CChar = name[i]
if c >= 'a' and c <= 'z':
return 0
if c >= 'A' and c <= 'Z':
has_upper = 1
i += 1
return has_upper
# ============================================================
# 翻译变量引用(Name 节点)→ load
# ============================================================
def _const_int_for_cdefine(self, pool: memhub.MemBuddy | t.CPtr,
value: t.CInt64T) -> llvmlite.Value | t.CPtr:
"""CDefine 整数常量 → LLVM 常量(对齐 TPC TypeConvert 的 64 位升级)
规则(与 translate_constant 的 i32 溢出升级一致,但保留 0xFFFFFFFF 语义):
值超 32 位无符号范围(> 0xFFFFFFFF)→ i64 全精度常量
(如 sys.maxsize = 9223372036854775807);
否则 → i32 常量,内部 t.CInt 截断(0xFFFFFFFF → -1,保持既有
INVALID_HANDLE_VALUE / WAIT_FAILED = -1 语义不变,避免 i32 4294967295
生成非法 LLVM IR 文本 "i32 4294967295")。
注意:不能用 `value > 4294967295` 直接比较——TPV 编译自身源码时大数字面量
(> 0x7FFFFFFF)在字面量层回绕成 i32(sext 后等于 -1),阈值恒成立导致所有
正数 CDefine 都被升 i64(INVALID_FILE_ATTRIBUTES 0xFFFFFFFF 从 i32 -1 变
i64 4294967295 → 与 _get_attrs 的 sext(-1) 比较失败,SysLibTest exists FAIL 根因)。
改用截断回比较:t.CUInt32T(value) 回绕后还原,与 value 不等即超 u32 范围。
"""
if value >= 0 and t.CUInt64T(t.CUInt32T(value)) != value:
return llvmlite.const_int64(pool, value)
return llvmlite.const_int32(pool, t.CInt(value))
def translate_name_value(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译变量引用(Name 节点)→ load"""
# mod 必须取自 trans.Module:函数签名无 mod 参数,若用裸名 mod 会被
# 解析为 None → create_global_string 的 module_add_global(None, gv) 被
# add_global 的 `if self is None: return` 静默丢弃 → 字符串 CDefine 的
# global 定义丢失但 _label_counter/IR 引用已生成 → llc 报
# "use of undefined value '@.str.{sha1}.N'(SysLibTest _cleanup 根因)"
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm is None:
return None
nm_id: str = nm.id
if nm_id is None:
return None
# CDefine 编译期常量: 返回常量值(浮点 CDefine → double 常量,整数 → i32)
# NAME: t.CDefine = value 形式定义的常量在编译期已注册到本地表
# 浮点 CDefine(如 vipermath 的 U_M_PI=3.14...)必须返回浮点常量,
# 否则在 double 函数里 return 生成 ret i32 0(llc 报错)。
cdef_val_d: double = HandlesType.lookup_cdefine_double(nm_id)
if HandlesType.is_cdefine_found() != 0:
if HandlesType.is_cdefine_str() != 0:
# 字符串 CDefine(如 ZLIB_VERSION: t.CDefine = "1.3.2"):
# 返回 global string(i8*),供 return ZLIB_VERSION 等使用。
cdef_sv: str = HandlesType.lookup_cdefine_string(nm_id)
if cdef_sv is not None:
return self.create_global_string(builder, pool, mod, cdef_sv, trans)
HandlesType.fatal_error(node, f"CDefine 字符串值缺失: {nm_id}", "InternalCompilerError")
if HandlesType.is_cdefine_double() != 0:
return llvmlite.ConstFloat(pool, llvmlite.Double(pool), cdef_val_d)
# 整数 CDefine:重新查表取 64 位全精度值(double 往返会丢精度,
# 9223372036854775807 → 9.223372036854776e18 舍入错位,生成错常量)
cdef_val_c: t.CInt64T = HandlesType.lookup_cdefine_constant(nm_id)
return self._const_int_for_cdefine(pool, cdef_val_c)
# 本地表未找到:尝试跨模块 .vpi 查找(保证多模块查表存表一致)
# 仅对符合 CDefine 命名约定(ALL_CAPS)的名字触发跨模块查找,
# 避免对普通变量名(mb/name/ptr/pool/self 等)误触发,造成严重性能损耗
if self._is_cdefine_name_pattern(nm_id) != 0:
if trans is not None:
cdef_val: t.CInt64T = self._lookup_cross_module_cdefine(
pool, nm_id, trans._from_imports, trans._imported_modules, 0, node)
if HandlesType.is_cdefine_found() != 0:
if HandlesType.is_cdefine_str() != 0:
cdef_sv2: str = HandlesType.lookup_cdefine_string(nm_id)
if cdef_sv2 is not None:
return self.create_global_string(builder, pool, mod, cdef_sv2, trans)
HandlesType.fatal_error(node, f"CDefine 字符串值缺失: {nm_id}", "InternalCompilerError")
# 跨模块浮点 CDefine 已缓存为 double(is_cdefine_double),
# 需返回浮点常量而非截断的整数
dchk_nm: double = HandlesType.lookup_cdefine_double(nm_id)
if HandlesType.is_cdefine_double() != 0:
return llvmlite.ConstFloat(pool, llvmlite.Double(pool), dchk_nm)
return self._const_int_for_cdefine(pool, cdef_val)
# global 变量:从模块作用域查找(global 声明优先级最高)
if trans is not None:
if HT.is_global_name(trans, nm_id) != 0:
mod_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var(
trans.SymTab, nm_id)
if mod_alloca is not None:
load_ty: llvmlite.LLVMType | t.CPtr = None
if mod_alloca.Ty is not None:
load_ty = mod_alloca.Ty.Pointee
if load_ty is None:
# fail-fast:global 变量存储类型缺失,立即终止编译(暴露上游根因)
HandlesType.fatal_error(
node, f"变量 '{nm_id}' 存储类型缺失", "TypeError")
# 全局数组(如 pyzlib.pyzlib_error_msg: CArray[t.CChar, 512]):
# 按 C 语义衰减为基址返回(对齐下方局部数组分支 L3383-3388)。
# 否则 load [512 x i8] 数组值当 i8* 传 → set_error 的 strncpy
# dest 是数组值(栈拷贝)而非数组基址 → 写入丢失 → get_error
# 为空(test_pyzlib "error message is empty" 根因)。
match load_ty:
case llvmlite.LLVMType.Array(_, _):
return mod_alloca
case _:
pass
return llvmlite.build_load(builder, load_ty, mod_alloca)
# nonlocal 变量:通过闭包 env 访问
if HT.is_nonlocal_name(trans, nm_id) != 0:
return HandlesNonlocal.load_nonlocal_var(trans, nm_id)
# *args va_list:变量名直接解析为指向 24 字节 va_list 缓冲区的 i8* 地址
# (对齐 TPC _direct_values[vararg_name] = va_list_ptr;不 load 缓冲区内容,
# 转发时按值传该地址即可,如 sprintf → vsnprintf(..., args))
if trans is not None and trans._vararg_name is not None:
if nm_id is not None and string.strcmp(nm_id, trans._vararg_name) == 0:
if trans._va_list_ptr is not None:
return trans._va_list_ptr
# 局部变量查找(必须先于模块别名检查)
# 否则与模块同名的局部变量(如 `import t, c` 后的局部变量 c)会被误判为模块别名,
# 导致 SymTab 已注册的局部变量查找不到(rhs_val is None bug 根因)
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm_id)
if alloca is not None:
load_ty: llvmlite.LLVMType | t.CPtr = None
if alloca.Ty is not None:
load_ty = alloca.Ty.Pointee
if load_ty is None:
# fail-fast:局部变量存储类型缺失,立即终止编译(暴露上游根因)
HandlesType.fatal_error(
node, f"变量 '{nm_id}' 存储类型缺失", "TypeError")
# 结构体值变量(alloca 类型为 Struct,如 `x: Cls | t.CPtr`):
# 按"对象即指针"语义返回其地址(alloca 指针),而非加载结构体值。
# 结构体值无法参与 ptrtoint/方法调用等地址相关操作,加载值会导致
# `ptrtoint %Struct ... to i64` 无效指令(llc invalid cast opcode)。
match load_ty:
case llvmlite.LLVMType.Struct(_, _, _):
return alloca
case llvmlite.LLVMType.Array(_, _):
# 数组变量按 C 语义退化为指针(返回数组基址),而非整体
# load 数组值——如 printf("%s", str_arr) 应传数组首地址,
# load [N x i8] 值传给 %s 会读到错误数据崩溃
# (ClassArrayTest TestCharArray 运行时崩溃根因)。
return alloca
return llvmlite.build_load(builder, load_ty, alloca)
# 模块别名检查:SymTab 查找失败后才检查
# 如果 nm_id 是已导入模块名(如 win32file, fileio)且未被声明为局部变量,
# 返回 None 让上层处理(用于属性访问 win32file.CreateFileA)
if trans is not None and trans._imported_modules is not None:
if HandlesImports.is_module_imported(trans._imported_modules, nm_id) != 0:
return None
# 模块级函数引用:函数名作为值传递(如 CreateThread(..., _thread_alloc_free, ...))。
# 之前缺失此查找导致函数名参数翻译返回 None 被跳过,后续参数整体前移
# (lpStartAddress 变成对象地址,线程从数据区执行直接崩溃)。
# 注意:不能直接返回 Function 对象,也不能直接 build_bitcast(Function)——
# Function 布局是 {Next, Name, RetTy, Params, ...},value_get_ty 会错位读到
# Name 字段,TypePrint 输出 "void",生成 `bitcast void ... to i8*` 非法 IR;
# 且 Function.Next 是模块函数链表指针,参数链表构建对其赋值会截断函数链表,
# 最终打印出 `declare void @()` 空名函数。正确做法:按 Function 的
# RetTy/Params 构造真实函数类型,用 new_value 包装成普通 Value 再 bitcast。
if trans is not None and trans.Module is not None:
fn_ref: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(
trans.Module, nm_id)
if fn_ref is not None:
fn_name: str = llvmlite.function_get_name(fn_ref)
if fn_name is None:
return None
# 构造函数类型:ret_ty + 参数 ParamNode 链表
fn_ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(fn_ref)
if fn_ret_ty is None:
# fail-fast:函数返回类型缺失,无法构造函数指针(暴露上游根因)
HandlesType.fatal_error(
node, f"函数 '{nm_id}' 返回类型缺失,无法构造函数指针", "TypeError")
fn_pp: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(fn_ref)
fn_first_node: llvmlite.ParamNode | t.CPtr = None
fn_prev_node: llvmlite.ParamNode | t.CPtr = None
fn_pcount: int = 0
while fn_pp is not None:
fn_pty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(fn_pp)
fn_pn: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, fn_pty)
if fn_pn is not None:
if fn_first_node is None:
fn_first_node = fn_pn
if fn_prev_node is not None:
llvmlite.paramnode_set_next(fn_prev_node, fn_pn)
fn_prev_node = fn_pn
fn_pcount += 1
fn_pp = llvmlite.param_get_next(fn_pp)
fn_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Func(
pool, fn_ret_ty, fn_first_node, fn_pcount)
if fn_ty is None:
return None
# 构造函数指针 Value(new_value 包装,避免触碰 Function.Next)
fn_ref_v: llvmlite.Value | t.CPtr = llvmlite.new_value(pool)
if fn_ref_v is None:
return None
llvmlite.value_set_ty(fn_ref_v, llvmlite.Ptr(pool, fn_ty))
llvmlite.value_set_isconst(fn_ref_v, 1)
fn_ref_name: str = pool.alloc(256)
if fn_ref_name is not None:
fn_ref_name[0] = '\0'
if HandlesClassDef._vtable_name_needs_quote(fn_name) != 0:
viperlib.snprintf(fn_ref_name, 256, "@\"%s\"", fn_name)
else:
viperlib.snprintf(fn_ref_name, 256, "@%s", fn_name)
llvmlite.value_set_name(fn_ref_v, fn_ref_name)
fn_i8ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
pool, llvmlite.Int8(pool))
return llvmlite.build_bitcast(builder, fn_ref_v, fn_i8ptr_ty)
# ============================================================
# 跨模块 from-import 全局变量读取(如 from .tokens import _op_head)
# 根因:模块级全局(非 CDefine/非函数)from-import 后无本模块 SymTab
# 条目,所有查找路径未命中 → translate_name_value 返回 None → 赋值被
# 静默跳过 → 变量未初始化 → 垃圾指针解引用崩溃(AstTest 的
# _read_operator 里 `cur = _op_head` 丢失导致 while 循环读未初始化 %6)。
# 语义对齐模块属性全局(sys._mbuddy):external @{全局名} 引用 + load,
# 定义由源模块提供(COFF 按名链接)。别名导入用源模块内真实名。
# ============================================================
if trans is not None and trans._from_imports is not None:
if trans.Module is not None:
# 是 from-import 名称(禁用 star 回退,避免误归 star 模块)
fi_mod: str = HandlesImports.lookup_from_import(
trans._from_imports, nm_id, 0)
if fi_mod is not None:
# 排除已注册类名/枚举名:这些是类型,不作为全局变量值读取
fi_is_class: int = 0
if HandlesStruct.get_struct_type(nm_id) is not None:
fi_is_class = 1
# 排除 ALL_CAPS CDefine(上方已处理本地+跨模块查找)
if self._is_cdefine_name_pattern(nm_id) != 0:
fi_is_class = 1
if fi_is_class == 0:
fi_gname: str = nm_id
fi_orig: str = HandlesImports.lookup_from_import_original(
trans._from_imports, nm_id)
if fi_orig is not None:
og_len_fi: t.CSizeT = 0
while fi_orig[og_len_fi] != ' ' and fi_orig[og_len_fi] != '\0':
og_len_fi += 1
if og_len_fi > 0:
og_buf_fi: str = pool.alloc(og_len_fi + 1)
if og_buf_fi is not None:
string.strncpy(og_buf_fi, fi_orig, og_len_fi)
og_buf_fi[og_len_fi] = '\0'
fi_gname = og_buf_fi
# stub 已注入全局(精确类型)→ 直接引用;未注入 → fail-fast
# (不再创建 external i8* 声明,否则 llc 报 "use of undefined value",
# 静默回退掩盖 stub 未注入的上游根因)
gv_fi: llvmlite.GlobalVariable | t.CPtr = self.find_global_in_module(
trans.Module, fi_gname)
ref_fi: llvmlite.Value | t.CPtr = None
if gv_fi is not None and gv_fi.Ty is not None:
ref_fi = self.make_global_ref(pool, fi_gname, gv_fi.Ty)
else:
HandlesType.fatal_error(
node,
f"跨模块全局变量 '{fi_gname}' 类型未知(stub 未注入)",
"TypeError")
if ref_fi is not None and ref_fi.Ty is not None:
lty_fi: llvmlite.LLVMType | t.CPtr = ref_fi.Ty.Pointee
if lty_fi is not None:
return llvmlite.build_load(builder, lty_fi, ref_fi)
return ref_fi
# 所有查找路径均失败
# fail-fast:未定义名称立即终止编译(NameError,暴露上游根因),
# 不再静默返回 None(否则下游 store/call 到 None 崩溃,掩盖名称查找失败)。
# 豁免:关键字/内建名(self/pool/true/false/None/match 上下文关键字、_、
# CIf 系等特殊名字)——这些名字不注册 SymTab 属正常,调用方有合法语义
# 处理路径(如 CIf 未定义名特判走 else),保持返回 None 不报错。
if nm_id is None:
return None
if string.strcmp(nm_id, "self") == 0 or string.strcmp(nm_id, "pool") == 0 \
or string.strcmp(nm_id, "true") == 0 or string.strcmp(nm_id, "false") == 0 \
or string.strcmp(nm_id, "None") == 0 or string.strcmp(nm_id, "match") == 0 \
or string.strcmp(nm_id, "_") == 0 \
or string.strcmp(nm_id, "CIf") == 0 or string.strcmp(nm_id, "CIfdef") == 0 \
or string.strcmp(nm_id, "CIfndef") == 0:
return None
HandlesType.fatal_error(node, f"未定义名称 '{nm_id}'", "NameError")
return None
# ============================================================
# list_getitem_inline — 内联生成 list[T] __getitem__ 逻辑
#
# list 结构布局 (6 字段 × 8 字节):
# __data__(0) __count__(8) __capacity__(16) __pool__(24) __elem_size__(32) __iter_index__(40)
# 返回元素地址 (i8*),调用方根据元素类型 load 正确的值
# ============================================================
def list_getitem_inline(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
lm_obj: llvmlite.Value | t.CPtr,
idx_val: llvmlite.Value | t.CPtr,
node: ast.AST | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""内联生成 list __getitem__ 逻辑,返回元素地址 (i8*)"""
if builder is None or pool is None or lm_obj is None or idx_val is None:
return None
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
# 加载 __data__ (偏移 0, index 0)
g_idx0: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
g_dpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, g_idx0)
if g_dpp is None:
return None
g_data: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, g_dpp)
# 加载 __elem_size__ (偏移 32, index 4)
g_idx4: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 4)
g_epp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, g_idx4)
if g_epp is None:
return None
g_esize: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, g_epp)
if g_data is None or g_esize is None:
return None
g_idx_i64: llvmlite.Value | t.CPtr = self.coerce_to_type(builder, idx_val, i64_ty, node)
if g_idx_i64 is None:
return None
g_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, g_idx_i64, g_esize)
if g_offset is None:
return None
# 返回元素地址 (i8*),不 load
return llvmlite.build_gep(builder, i8_ty, g_data, g_offset)
# ============================================================
# _list_elem_type_from_name — 从 list 类型名提取元素 LLVM 类型
#
# 类型名格式: <sha1>.list[<elem_type>]
# 用 strstr 检查完整模式,避免子串提取
# 支持: list[int]→i32, list[str]/list[bytes]→i8*, list[CInt]→i32 等
# 默认: i32
# ============================================================
def _list_elem_type_from_name(self, pool: memhub.MemBuddy | t.CPtr,
struct_name: str) -> llvmlite.LLVMType | t.CPtr:
"""从 list 类型名提取元素 LLVM 类型"""
if pool is None or struct_name is None:
return None
# 用 strstr 检查完整模式
if string.strstr(struct_name, "list[int]") is not None:
return llvmlite.Int32(pool)
if string.strstr(struct_name, "list[CInt]") is not None:
return llvmlite.Int32(pool)
if string.strstr(struct_name, "list[CInt8T]") is not None:
return llvmlite.Int8(pool)
if string.strstr(struct_name, "list[CInt16T]") is not None:
return llvmlite.Int16(pool)
if string.strstr(struct_name, "list[CInt32T]") is not None:
return llvmlite.Int32(pool)
if string.strstr(struct_name, "list[CInt64T]") is not None:
return llvmlite.Int64(pool)
if string.strstr(struct_name, "list[CSizeT]") is not None:
return llvmlite.Int64(pool)
if string.strstr(struct_name, "list[CDouble]") is not None:
return llvmlite.Double(pool)
if string.strstr(struct_name, "list[CFloat]") is not None:
return llvmlite.Float(pool)
if string.strstr(struct_name, "list[CChar]") is not None:
return llvmlite.Int8(pool)
# str/bytes/CPtr 等已知指针元素类型 → i8*(元素存储为 8 字节地址)
i8_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
if string.strstr(struct_name, "list[str]") is not None:
return llvmlite.Ptr(pool, i8_ty_d)
if string.strstr(struct_name, "list[bytes]") is not None:
return llvmlite.Ptr(pool, i8_ty_d)
if string.strstr(struct_name, "list[CPtr]") is not None:
return llvmlite.Ptr(pool, i8_ty_d)
# 未知 list[T] 元素类型(如 list[Foo]、list[AST|t.CPtr]):元素存储为
# 8 字节槽(指针或 8 字节整数),回退 Ptr(i8) 位宽一致(load 8 字节),
# 由 AnnotClassName/特化恢复机制保证语义。禁止 fail-fast(误杀合法
# 自定义类元素列表的下标访问)。
return llvmlite.Ptr(pool, i8_ty_d)
# ============================================================
# list_setitem_inline — 内联生成 list[T] __setitem__ 逻辑
#
# 直接存储 rhs_val 到元素地址 (coerce 为 i8* 后 store)
# ============================================================
def list_setitem_inline(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
lm_obj: llvmlite.Value | t.CPtr,
idx_val: llvmlite.Value | t.CPtr,
rhs_val: llvmlite.Value | t.CPtr,
node: ast.AST | t.CPtr = None) -> int:
"""内联生成 list __setitem__ 逻辑,返回 0"""
if builder is None or pool is None or lm_obj is None or idx_val is None or rhs_val is None:
return 0
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
# 加载 __data__ (偏移 0, index 0)
s_idx0: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
s_dpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, s_idx0)
if s_dpp is None:
return 0
s_data: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, s_dpp)
# 加载 __elem_size__ (偏移 32, index 4)
s_idx4: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 4)
s_epp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, s_idx4)
if s_epp is None:
return 0
s_esize: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, s_epp)
if s_data is None or s_esize is None:
return 0
s_idx_i64: llvmlite.Value | t.CPtr = self.coerce_to_type(builder, idx_val, i64_ty, node)
if s_idx_i64 is None:
return 0
s_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, s_idx_i64, s_esize)
if s_offset is None:
return 0
s_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, s_data, s_offset)
if s_addr is None:
return 0
# 元素类型 T:从 list 特化类型名解析(list[int]→i32、list[str]→i8*)。
# 此前固定 i8*(8 字节)store(inttoptr 值)覆盖相邻元素——与
# CallGenMixin list 内置分支同根因(ListTest pop FAILED expect 30)。
s_sty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type_from_value(lm_obj)
s_name: str = None
if s_sty is not None:
s_name = llvmlite.get_struct_name(s_sty)
s_elem_ty: llvmlite.LLVMType | t.CPtr = None
if s_name is not None:
s_elem_ty = self._list_elem_type_from_name(pool, s_name)
if s_elem_ty is None:
s_elem_ty = i8_ptr_ty
s_val_ptr: llvmlite.Value | t.CPtr = self.coerce_to_type(builder, rhs_val, s_elem_ty, node)
if s_val_ptr is None:
return 0
s_ep: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, s_addr, llvmlite.Ptr(pool, s_elem_ty))
if s_ep is not None:
llvmlite.build_store(builder, s_val_ptr, s_ep)
return 0
# ============================================================
# is_list_subscript — 检查 Subscript 节点是否是 list[T] 类型
#
# 返回 list 对象指针 (Ptr(list[T])) 或 None
# ============================================================
def is_list_subscript(self, node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""检查 Subscript 节点是否是 list[T] 类型,返回 list 对象指针或 None"""
if node is None or trans is None:
return None
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
if sub is None or sub.value is None:
return None
if sub.value.kind() != ast.ASTKind.Name:
return None
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if nm is None or nm.id is None:
return None
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
if alloca is None or alloca.Ty is None:
return None
if self.is_ptr_type(alloca.Ty) == 0:
return None
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is None:
return None
# 检查 pointee 是否是 Ptr(Struct)
inner_ty: llvmlite.LLVMType | t.CPtr = None
match pointee:
case llvmlite.LLVMType.Ptr(it):
inner_ty = it
case _:
return None
if inner_ty is None:
return None
# 检查 inner_ty 是否是 Struct 且名称包含 "list["
struct_name: str = None
match inner_ty:
case llvmlite.LLVMType.Struct(_, _, sname):
struct_name = sname
case _:
return None
if struct_name is None:
return None
if string.strstr(struct_name, "list[") is None:
return None
# 是 list 类型: load 出 list 对象指针
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
return llvmlite.build_load(builder, pointee, alloca)
# ============================================================
# _nonlocal_subscript_ptr - nonlocal 数组/指针的下标元素指针
#
# 嵌套函数(提升函数)体内访问 nonlocal 变量时,变量的实际存储是
# 运行时共享堆(env[ni] 存的堆地址),不能 lookup_var 找外层函数
# alloca(其 SSA 编号属于外层函数,当前函数引用错乱崩溃——
# ClosureTest2 Test 4 fill() 内 arr[i] 崩溃根因)。
# 正确路径:从 env 读堆地址(_load_raw_var_ptr)→ bitcast → GEP。
# ============================================================
def _nonlocal_subscript_ptr(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
nm_id: str,
idx_val: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
"""nonlocal 数组/指针下标:从 env 读堆地址 → bitcast → GEP,返回元素指针"""
if trans is None or nm_id is None or idx_val is None:
return None
nl_ty: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(trans, nm_id)
if nl_ty is None:
return None
raw_nl: llvmlite.Value | t.CPtr = HandlesNonlocal._load_raw_var_ptr(trans, nm_id)
if raw_nl is None:
return None
match nl_ty:
case llvmlite.LLVMType.Array(elem_ty_nl, _cnt_nl):
arr_ptr_nl: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, raw_nl, llvmlite.Ptr(pool, nl_ty))
if arr_ptr_nl is None:
return None
return llvmlite.build_gep_array(
builder, nl_ty, elem_ty_nl, arr_ptr_nl,
self._zext_index_to_i32(builder, idx_val))
case llvmlite.LLVMType.Ptr(inner_nl):
arr_ptr_nl2: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, raw_nl, llvmlite.Ptr(pool, nl_ty))
if arr_ptr_nl2 is None:
return None
return llvmlite.build_gep(builder, inner_nl, arr_ptr_nl2, idx_val)
case _:
arr_ptr_nl3: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, raw_nl, llvmlite.Ptr(pool, nl_ty))
if arr_ptr_nl3 is None:
return None
return llvmlite.build_gep(builder, nl_ty, arr_ptr_nl3, idx_val)
return None
# ============================================================
# 翻译下标表达式 Subscript(value, slice, ctx) — ptr[i] / arr[i]
#
# 指针遍历: ptr 是指针变量,load 出指针值后 GEP + load
# 数组遍历: arr 是数组变量,用 alloca 指针做双索引 GEP [0, i] + load
# list[T]: 泛型类不注册 struct,subscript 走 __getitem__ 内联路径
# ============================================================
def translate_subscript(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:
"""翻译下标表达式,返回加载的元素值"""
if node is None or builder is None:
return None
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
if sub is None:
return None
# 翻译索引
idx_val: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, sub.slice, None, 0, trans)
if idx_val is None:
return None
# 如果 value 是 Name,尝试从 alloca 类型推断
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if nm.id is not None and trans is not None:
# nonlocal 数组/指针:从 env 读共享堆地址(提升函数内不能
# lookup_var 外层 alloca——SSA 编号跨函数错乱崩溃)
# 读路径:元素指针 load 出元素值
if HT.is_nonlocal_name(trans, nm.id) != 0:
nl_ptr_r: llvmlite.Value | t.CPtr = self._nonlocal_subscript_ptr(
builder, pool, trans, nm.id, idx_val)
if nl_ptr_r is not None and nl_ptr_r.Ty is not None:
nl_elem_ty: llvmlite.LLVMType | t.CPtr = nl_ptr_r.Ty.Pointee
if nl_elem_ty is not None:
return llvmlite.build_load(builder, nl_elem_ty, nl_ptr_r)
return None
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
if alloca is not None and alloca.Ty is not None:
if self.is_ptr_type(alloca.Ty) != 0:
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is not None:
# 不调用 pointee.kind() 避免跨模块引用 LLVMType.kind 符号
# 用 match 检查 pointee 类型
match pointee:
case llvmlite.LLVMType.Array(elem_ty, count):
# 数组遍历: getelementptr [N x elem_ty], ... , i32 0, i32 %idx
elem_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, pointee, elem_ty, alloca,
self._zext_index_to_i32(builder, idx_val))
if elem_ptr is not None:
return llvmlite.build_load(builder, elem_ty, elem_ptr)
return None
case llvmlite.LLVMType.Struct(_fs_v, _fc_v, lsn_v):
# list[T] 值变量(无注解赋值 rhs 返回 list 值,
# 如 parts = s1.split(','):split 的 define 返回
# %list[str] 结构体值,alloca 直接存 list 对象)。
# alloca 即 list*,直接走 list 内联 __getitem__
#(对齐 Ptr(Ptr(list)) 变量路径,修复 parts[0] 崩溃)
if lsn_v is not None:
if string.strstr(lsn_v, "list[") is not None:
elem_addr_v: llvmlite.Value | t.CPtr = \
self.list_getitem_inline(
builder, pool, alloca, idx_val, node)
if elem_addr_v is not None:
elem_ty_v: llvmlite.LLVMType | t.CPtr = \
self._list_elem_type_from_name(pool, lsn_v)
if elem_ty_v is not None:
elem_ptr_ty_v: llvmlite.LLVMType | t.CPtr = \
llvmlite.Ptr(pool, elem_ty_v)
casted_v: llvmlite.Value | t.CPtr = \
llvmlite.build_bitcast(
builder, elem_addr_v, elem_ptr_ty_v)
if casted_v is not None:
return llvmlite.build_load(
builder, elem_ty_v, casted_v)
return None
case llvmlite.LLVMType.Ptr(inner_ty):
# 检查 inner_ty 是否是 list[T] 类型(泛型类不注册 struct)
# list 的 subscript 应该走 __getitem__ 内联路径,而非指针遍历
list_struct_name: str = None
match inner_ty:
case llvmlite.LLVMType.Struct(_, _, lsn):
list_struct_name = lsn
case _:
pass
if list_struct_name is not None:
if string.strstr(list_struct_name, "list[") is not None:
# list[T] 类型: 内联生成 __getitem__ 逻辑
ptr_val_list: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val_list is not None:
elem_addr_list: llvmlite.Value | t.CPtr = self.list_getitem_inline(
builder, pool, ptr_val_list, idx_val, node)
if elem_addr_list is not None:
# 根据类型名提取元素类型,bitcast 后 load
elem_ty_list: llvmlite.LLVMType | t.CPtr = self._list_elem_type_from_name(
pool, list_struct_name)
if elem_ty_list is not None:
elem_ptr_ty_list: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
pool, elem_ty_list)
casted_addr_list: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, elem_addr_list, elem_ptr_ty_list)
if casted_addr_list is not None:
return llvmlite.build_load(
builder, elem_ty_list, casted_addr_list)
return None
# inner_ty 是 Ptr 说明 alloca.Ty 是三重指针 Ptr(Ptr(Ptr(...))),
# 即 X|t.CPtr 当 X 本身是指针类型 (如 T=AST|t.CPtr, T|t.CPtr=AST**)。
# 此时 [i] 应该是指针解引用 (步长 sizeof(ptr)=8), 而非调用
# 结构体的 __getitem__ 方法 (会错误生成 AST.__getitem__ 调用)。
# 对比 HashTable|t.CPtr: alloca.Ty=HashTable**, inner_ty=Struct(HashTable)
# (非 Ptr), 仍走下面的方法调用转发路径。
if self.is_ptr_type(inner_ty) != 0:
ptr_val_pp: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val_pp is not None:
elem_ptr_pp: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, inner_ty, ptr_val_pp, idx_val)
if elem_ptr_pp is not None:
return llvmlite.build_load(builder, inner_ty, elem_ptr_pp)
return None
# 自定义结构体 (如 HashTable|t.CPtr, JsonValue|t.CPtr):
# 仅当类定义了 __getitem__(当前模块定义或跨模块 stub declare 注入)
# 才转发方法调用;否则普通结构体指针按下标做 GEP 数组访问
# (对齐 TPC _HandleSubscriptLlvm: if ClassName and has_function(__getitem__))
cls_nm_rd_ptr: str = self._get_custom_struct_cls_nm(pool, inner_ty)
if not (cls_nm_rd_ptr is None):
gi_lookup_rd: str = pool.alloc(128)
if gi_lookup_rd is not None:
viperlib.snprintf(gi_lookup_rd, 128, "%s.__getitem__", cls_nm_rd_ptr)
if HandlesExprCall.find_func_in_module(mod, gi_lookup_rd) is not None:
obj_ptr_rd: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if not (obj_ptr_rd is None):
arg_vals_rd_p: t.CSizeT | t.CPtr = pool.alloc(8)
if not (arg_vals_rd_p is None):
arg_vals_rd_p[0] = t.CSizeT(idx_val)
ret_rd_p: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
pool, builder, mod, cls_nm_rd_ptr, "__getitem__",
obj_ptr_rd, arg_vals_rd_p, 1, trans, sub)
if not (ret_rd_p is None):
return ret_rd_p
# 无 __getitem__:结构体指针数组访问(GEP + load)
ptr_val_rd_g: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val_rd_g is not None:
elem_rd_g: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, inner_ty, ptr_val_rd_g, idx_val)
if elem_rd_g is not None:
return llvmlite.build_load(builder, inner_ty, elem_rd_g)
return None
# bytes|t.CPtr / str|t.CPtr: alloca 是 i8**,
# 直接 GEP 按 i8* 步长(8 字节),load i8*
# 仅当 inner_ty 不是自定义结构体时才走指针遍历
if trans is not None:
ve_pe_ts: HandlesVar.VarEntry | t.CPtr = \
HandlesVar.lookup_var_entry(trans.SymTab, nm.id)
if ve_pe_ts is not None and ve_pe_ts.IsPtrElement == 1:
# bytes|t.CPtr / str|t.CPtr / T|t.CPtr:变量存指针值(i8*),
# elem[i] 语义 = load [指针值 + i*8](8 字节指针元素)。
# 必须先 load alloca 得指针值,再 GEP i8*(步长 8)——
# 否则 GEP base 用 alloca(&elem_ptr)读到栈变量而非数据区,
# list.get 返回"元素地址"而非"元素值"(dump 虚调用读错 vtable)。
ptr_val_pe_ts: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val_pe_ts is not None:
elem_ptr_ts: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, pointee, ptr_val_pe_ts, idx_val)
if elem_ptr_ts is not None:
return llvmlite.build_load(builder, pointee, elem_ptr_ts)
return None
# 普通指针遍历: 先 load 指针值,再 GEP
ptr_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val is not None:
elem_ptr2: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, inner_ty, ptr_val, idx_val)
if elem_ptr2 is not None:
return llvmlite.build_load(builder, inner_ty, elem_ptr2)
return None
case _:
# 联合类型变量(IsPtrElement=1,如 _list.vp 的
# elem_ptr: T | t.CPtr 退化为 i8*):pointee=i8 但
# 元素是 8 字节指针,须按 i8* 步长 GEP + load i8*——
# 否则 list.get 只读 1 字节,指针截断 inttoptr 崩溃。
if trans is not None:
ve_pe_rd: HandlesVar.VarEntry | t.CPtr = \
HandlesVar.lookup_var_entry(trans.SymTab, nm.id)
if ve_pe_rd is not None and ve_pe_rd.IsPtrElement == 1:
match pointee:
case llvmlite.LLVMType.Int8():
i8_ptr_rd: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
pool, llvmlite.Int8(pool))
elem_ptr_rd: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, i8_ptr_rd, alloca, idx_val)
if elem_ptr_rd is not None:
return llvmlite.build_load(
builder, i8_ptr_rd, elem_ptr_rd)
case _:
pass
# 自定义类(结构体类型): 仅当类定义了 __getitem__
# (当前模块定义或跨模块 stub declare 注入)才转发方法调用;
# 否则普通结构体指针按下标做 GEP 数组访问
cls_nm_sub: str = HandlesStruct.get_class_name_by_type(pool, pointee)
if not (cls_nm_sub is None):
gi_lookup_sub: str = pool.alloc(128)
if gi_lookup_sub is not None:
viperlib.snprintf(gi_lookup_sub, 128, "%s.__getitem__", cls_nm_sub)
if HandlesExprCall.find_func_in_module(mod, gi_lookup_sub) is not None:
obj_val_sub: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if not (obj_val_sub is None):
arg_vals_sub: t.CSizeT | t.CPtr = pool.alloc(8)
if not (arg_vals_sub is None):
arg_vals_sub[0] = t.CSizeT(idx_val)
ret_sub: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
pool, builder, mod, cls_nm_sub, "__getitem__",
obj_val_sub, arg_vals_sub, 1, trans, sub)
if not (ret_sub is None):
return ret_sub
# 无 __getitem__:结构体指针数组访问(GEP + load)
ptr_val_sub_g: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val_sub_g is not None:
elem_sub_g: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, pointee, ptr_val_sub_g, idx_val)
if elem_sub_g is not None:
return llvmlite.build_load(builder, pointee, elem_sub_g)
return None
pass
# 通用路径:翻译 value 获取指针
ptr_val: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, sub.value, None, 0, trans)
if ptr_val is None or ptr_val.Ty is None:
return None
# 检查是否是指针类型
if self.is_ptr_type(ptr_val.Ty) != 0:
elem_ty2: llvmlite.LLVMType | t.CPtr = ptr_val.Ty.Pointee
if elem_ty2 is not None:
# 数组类型: 双索引 GEP + load 元素
match elem_ty2:
case llvmlite.LLVMType.Array(arr_elem_ty2, arr_count2):
elem_ptr3: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, elem_ty2, arr_elem_ty2, ptr_val,
self._zext_index_to_i32(builder, idx_val))
if elem_ptr3 is not None:
return llvmlite.build_load(builder, arr_elem_ty2, elem_ptr3)
return None
case _:
pass
# 自定义结构体 (如 self._ht[key]): 仅当类定义了 __getitem__ 才转发方法调用;
# 否则普通结构体指针按下标做 GEP 数组访问
cls_nm_rd_gen: str = self._get_custom_struct_cls_nm(pool, elem_ty2)
if not (cls_nm_rd_gen is None):
gi_lookup_gen: str = pool.alloc(128)
if gi_lookup_gen is not None:
viperlib.snprintf(gi_lookup_gen, 128, "%s.__getitem__", cls_nm_rd_gen)
if HandlesExprCall.find_func_in_module(mod, gi_lookup_gen) is not None:
arg_vals_rd_g: t.CSizeT | t.CPtr = pool.alloc(8)
if not (arg_vals_rd_g is None):
arg_vals_rd_g[0] = t.CSizeT(idx_val)
ret_rd_g: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
pool, builder, mod, cls_nm_rd_gen, "__getitem__",
ptr_val, arg_vals_rd_g, 1, trans, sub)
if not (ret_rd_g is None):
return ret_rd_g
# 无 __getitem__:结构体指针数组访问(GEP + load)
elem_ptr_gen: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, elem_ty2, ptr_val, idx_val)
if elem_ptr_gen is not None:
return llvmlite.build_load(builder, elem_ty2, elem_ptr_gen)
return None
# 指针类型: 单索引 GEP + load
elem_ptr4: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, elem_ty2, ptr_val, idx_val)
if elem_ptr4 is not None:
return llvmlite.build_load(builder, elem_ty2, elem_ptr4)
return None
# ============================================================
# find_global_in_module - 在模块全局变量链表中按名称查找
#
# 查找策略:
# 1. 精确匹配 (如 "_mbuddy" 匹配 @_mbuddy)
# 2. 后缀匹配 (如 "_mbuddy" 匹配 @"sha1._mbuddy")
# ============================================================
def find_global_in_module(self, mod: llvmlite.LLVMModule | t.CPtr,
name: str) -> llvmlite.GlobalVariable | t.CPtr:
"""在模块全局变量链表中按名称查找全局变量"""
if mod is None or name is None:
return None
name_len: t.CSizeT = string.strlen(name)
cur: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead
# 第一遍: 精确匹配
while cur is not None:
if cur.Name is not None:
if string.strcmp(cur.Name, name) == 0:
return cur
cur = cur.Next
# 第二遍: 后缀匹配 (.name)
cur = mod.GlobalHead
while cur is not None:
if cur.Name is not None:
cur_len: t.CSizeT = string.strlen(cur.Name)
if cur_len > name_len + 1:
suffix_start: t.CSizeT = cur_len - name_len
if cur.Name[suffix_start - 1] == '.':
match: int = 1
for i in range(name_len):
if cur.Name[suffix_start + i] != name[i]:
match = 0
break
if match == 1:
return cur
cur = cur.Next
return None
# ============================================================
# make_global_ref - 创建全局变量引用 (@name,类型为 ty*)
#
# 用于跨模块全局变量访问: stub 未注入时,翻译阶段生成 @name 引用,
# 链接时由 stub 提供 external global 声明。
# ============================================================
def make_global_ref(self, pool: memhub.MemBuddy | t.CPtr,
name: str,
ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""创建全局变量引用 (类型为 ty*,名称为 @name)"""
if name is None or ty is None:
return None
ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, ty)
ref_name: str = pool.alloc(64)
if ref_name is None:
return None
viperlib.snprintf(ref_name, 64, "@%s", name)
return llvmlite.SSAValue(pool, ptr_ty, ref_name)
# ============================================================
# _resolve_module_attribute_global - 解析模块属性访问的全局变量引用
#
# 当 obj 是已导入模块名 (如 sys._mbuddy) 时:
# 1. 在当前模块全局变量链表中查找 (stub 已注入的情况)
# 2. 未找到则创建前向引用 (@attr_name, 类型为 ty 参数)
#
# 参数:
# ty_hint: 类型提示 (写路径用 rhs 类型, 读路径用 i8* 回退)
# 返回: Value 指针 (类型为 ty*),未识别为模块属性返回 None
# ============================================================
def _is_t_type_object_name(self, name: str) -> int:
"""检查 name 是否是 t.py 中的类型对象名(CPtr/CInt/CDouble 等)
这些类型对象在运行时作为全局变量 @CPtr/@CInt 等被引用,
但 t.py 不被编译,需要在使用处生成带 null 初值的定义。
"""
if name is None:
return 0
# t.py 中的类型对象名(PascalCase,首字母大写)
if string.strcmp(name, "CPtr") == 0:
return 1
if string.strcmp(name, "CInt") == 0:
return 1
if string.strcmp(name, "CChar") == 0:
return 1
if string.strcmp(name, "CShort") == 0:
return 1
if string.strcmp(name, "CLong") == 0:
return 1
if string.strcmp(name, "CLongLong") == 0:
return 1
if string.strcmp(name, "CDouble") == 0:
return 1
if string.strcmp(name, "CFloat") == 0:
return 1
if string.strcmp(name, "CBool") == 0:
return 1
if string.strcmp(name, "CVoid") == 0:
return 1
if string.strcmp(name, "CInt8T") == 0:
return 1
if string.strcmp(name, "CInt16T") == 0:
return 1
if string.strcmp(name, "CInt32T") == 0:
return 1
if string.strcmp(name, "CInt64T") == 0:
return 1
if string.strcmp(name, "CUInt8T") == 0:
return 1
if string.strcmp(name, "CUInt16T") == 0:
return 1
if string.strcmp(name, "CUInt32T") == 0:
return 1
if string.strcmp(name, "CUInt64T") == 0:
return 1
if string.strcmp(name, "CUnsigned") == 0:
return 1
if string.strcmp(name, "CUnsignedInt") == 0:
return 1
if string.strcmp(name, "CUnsignedChar") == 0:
return 1
if string.strcmp(name, "CUnsignedShort") == 0:
return 1
if string.strcmp(name, "CUnsignedLong") == 0:
return 1
if string.strcmp(name, "CUnsignedLongLong") == 0:
return 1
if string.strcmp(name, "CSizeT") == 0:
return 1
if string.strcmp(name, "CIntPtrT") == 0:
return 1
if string.strcmp(name, "CUIntPtrT") == 0:
return 1
if string.strcmp(name, "CPtrDiffT") == 0:
return 1
if string.strcmp(name, "CType") == 0:
return 1
if string.strcmp(name, "CDefine") == 0:
return 1
if string.strcmp(name, "CStatic") == 0:
return 1
if string.strcmp(name, "CExport") == 0:
return 1
if string.strcmp(name, "CArray") == 0:
return 1
if string.strcmp(name, "CTypedef") == 0:
return 1
if string.strcmp(name, "CNoVTable") == 0:
return 1
if string.strcmp(name, "CNeedPtr") == 0:
return 1
if string.strcmp(name, "CAutoPtr") == 0:
return 1
return 0
def _attr_module_path_str(self, pool: memhub.MemBuddy | t.CPtr,
node: ast.AST | t.CPtr) -> str:
"""把模块引用 AST 转成模块路径字符串(递归拼接 Attribute 链):
Name('sys') → "sys";Attribute(Attribute(Name('os'),'_win32')) → "os._win32"。
非模块引用形态(Call/Subscript 等)返回 None。"""
if node is None:
return None
k: int = node.kind()
if k == ast.ASTKind.Name:
nm_m: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm_m.id is not None:
return nm_m.id
return None
if k == ast.ASTKind.Attribute:
at_m: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at_m.attr is None:
return None
base_m: str = self._attr_module_path_str(pool, at_m.value)
if base_m is None:
return None
blen: t.CSizeT = string.strlen(base_m)
alen: t.CSizeT = string.strlen(at_m.attr)
buf_m: str = pool.alloc(blen + alen + 2)
if buf_m is None:
return None
viperlib.snprintf(buf_m, blen + alen + 2, "%s.%s", base_m, at_m.attr)
return buf_m
return None
def _resolve_module_attribute_global(self, pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
trans: HT.Translator | t.CPtr,
at: ast.Attribute | t.CPtr,
ty_hint: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""解析模块属性访问,返回全局变量引用 (用于跨模块 global 访问)"""
if at is None or at.value is None or at.attr is None:
return None
if trans is None or trans._imported_modules is None:
return None
# 提取模块路径名:Name → "sys";嵌套 Attribute → "os._win32"
# (此前只支持 Name 模块;SysLibTest main.vp L32 `os._win32._mbuddy = v`
# 的 at.value 是 Attribute → 被跳过 → "属性写入失败" 根因)
mod_path_w: str = self._attr_module_path_str(pool, at.value)
if mod_path_w is None:
return None
# 检查是否是已导入模块
if HandlesImports.is_module_imported(trans._imported_modules, mod_path_w) == 0:
return None
# 在当前模块查找全局变量 (stub 可能已注入)
gv: llvmlite.GlobalVariable | t.CPtr = self.find_global_in_module(mod, at.attr)
if gv is not None and gv.Ty is not None:
return self.make_global_ref(pool, at.attr, gv.Ty)
# 未找到: 创建 external global 声明并添加到模块
# 否则只生成 @_argc 引用而无声明,LLVM 报 "use of undefined value '@_argc'"
use_ty: llvmlite.LLVMType | t.CPtr = ty_hint
if use_ty is None:
use_ty = llvmlite.Int8(pool)
# 确保 use_ty 中的跨模块结构体类型有 opaque 声明
llvmlite.module_ensure_opaque_for_type(mod, pool, use_ty)
# 特殊处理: t.py 中的类型对象(CPtr/CInt/CDouble 等)
# t.py 不被编译,但这些类型对象在运行时被引用(如 va_arg(ap, t.CPtr))。
# 创建带 null 初值的 linkonce_odr 全局变量定义,避免链接时 undefined reference。
# LLVM 22+ 不允许 external linkage 全局变量带初值,使用 linkonce_odr 替代。
is_t_type_obj: int = 0
if string.strcmp(mod_path_w, "t") == 0:
is_t_type_obj = self._is_t_type_object_name(at.attr)
ext_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, at.attr, use_ty)
if ext_gv is not None:
if is_t_type_obj != 0:
# t.CPtr 等类型对象: 创建带 null 初值的定义(linkonce_odr)
ext_gv.Linkage = "linkonce_odr"
# i8* null 的初值文本
null_init: bytes = pool.alloc(8)
if null_init is not None:
null_init[0] = 'n'
null_init[1] = 'u'
null_init[2] = 'l'
null_init[3] = 'l'
null_init[4] = '\0'
ext_gv.Initializer = null_init
else:
ext_gv.Initializer = None
else:
# 普通模块属性: external 声明(由其他模块提供定义)
ext_gv.Linkage = "external"
ext_gv.Initializer = None
llvmlite.module_add_global(mod, ext_gv)
return self.make_global_ref(pool, at.attr, use_ty)
# ============================================================
# 翻译属性访问 Attribute(value, attr, ctx) — obj.field
#
# 优先级:
# 1. 枚举成员访问 (State.Idle → 常量值)
# 2. 模块属性访问 (sys._mbuddy → 加载全局变量)
# 3. 结构体字段访问 (obj.field → GEP + load)
#
# 对于 Name 类型的 obj,直接使用 alloca 指针(不 load 结构体)
# ============================================================
# ============================================================
# _get_cderef_attr_base - c.Deref(ptr).attr 属性访问的 base 指针
#
# c.Deref(ptr) 作为属性访问 base 时(如 c.Deref(p).a),需要 ptr
# 指针本身而非 load 后的结构体值(*p.a → p->a),与 TPC
# _handle_attr_call_result 语义一致。否则 c.Deref 返回结构体值,
# 后续 GEP 在非指针上做属性访问会误报"base=非指针类型"。
#
# 返回:
# ptr 指针值(Ptr(Struct) 或 Ptr(Ptr(Struct)),交由 _deref_if_ptr_ptr 解层级)
# 非 c.Deref 模式返回 None
# ============================================================
def _get_cderef_attr_base(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
at_value: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""检测 at.value 是否为 c.Deref(ptr) 调用,返回 ptr 指针(不做 load)"""
if at_value is None or at_value.kind() != ast.ASTKind.Call:
return None
cl_cd: ast.Call | t.CPtr = (ast.Call | t.CPtr)(at_value)
if cl_cd.func is None or cl_cd.func.kind() != ast.ASTKind.Attribute:
return None
f_cd: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl_cd.func)
if f_cd.attr is None or string.strcmp(f_cd.attr, "Deref") != 0:
return None
if f_cd.value is None or f_cd.value.kind() != ast.ASTKind.Name:
return None
nm_cd: ast.Name | t.CPtr = (ast.Name | t.CPtr)(f_cd.value)
if nm_cd.id is None or string.strcmp(nm_cd.id, "c") != 0:
return None
if cl_cd.args is None or len(cl_cd.args) < 1:
return None
inner_cd: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, cl_cd.args.get(0), None, 0, trans)
if inner_cd is not None and self.is_ptr_type(inner_cd.Ty) != 0:
return inner_cd
return None
# ============================================================
# docstring 提取/登记/查找(__doc__ 魔法属性支持)
#
# 预注册阶段(BuildPipeline.TranslateFileGetTrans)扫描所有
# App/includes 文件的 AST,提取函数/类/方法的 docstring 内容
# 登记到全局表(key = "{module_sha1}.{symbol}");__doc__ 属性
# 访问时查表,命中则当前模块内联生成字符串常量(无跨模块 IR
# 符号依赖),未命中返回 null。幂等查重,多遍翻译安全。
# ============================================================
def _extract_docstring_from_body(self, body: list[ast.AST | t.CPtr] | t.CPtr) -> str:
"""从函数/类体提取 docstring:body[0] 为 Expr(Constant str) 时返回内容"""
if body is None or len(body) == 0:
return None
first: ast.AST | t.CPtr = body.get(0)
if first is None or first.kind() != ast.ASTKind.Expr:
return None
ex: ast.Expr | t.CPtr = (ast.Expr | t.CPtr)(first)
if ex is None or ex.value is None or ex.value.kind() != ast.ASTKind.Constant:
return None
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(ex.value)
if cn is None or cn.const_kind != ast.CONST_STR:
return None
return cn.str_val
def register_docstring(self, module_sha1: str, symbol: str, doc: str) -> None:
"""登记 docstring(key="{module_sha1}.{symbol}"),查重幂等"""
if module_sha1 is None or symbol is None or doc is None:
return
if string.strlen(doc) == 0:
return
if self.DocKeys is None:
self.DocKeys = CompilerFactory.get_perm_pool().alloc(DOC_MAX * DOC_KEY_SZ)
self.DocValues = CompilerFactory.get_perm_pool().alloc(DOC_MAX * DOC_VAL_SZ)
if self.DocKeys is None or self.DocValues is None:
return
string.memset(self.DocKeys, 0, DOC_MAX * DOC_KEY_SZ)
string.memset(self.DocValues, 0, DOC_MAX * DOC_VAL_SZ)
if self.DocCount >= DOC_MAX:
return
ml: t.CSizeT = string.strlen(module_sha1)
sl: t.CSizeT = string.strlen(symbol)
key_buf: bytes = stdlib.malloc(ml + sl + 2)
if key_buf is None:
return
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
i: int
for i in range(self.DocCount):
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
stdlib.free(key_buf)
return
string.strcpy(self.DocKeys + self.DocCount * DOC_KEY_SZ, key_buf)
string.strcpy(self.DocValues + self.DocCount * DOC_VAL_SZ, doc)
self.DocCount += 1
stdlib.free(key_buf)
def lookup_docstring(self, module_sha1: str, symbol: str) -> str:
"""查找 docstring 内容(key="{module_sha1}.{symbol}"),未找到返回 None"""
if self.DocKeys is None or module_sha1 is None or symbol is None:
return None
ml: t.CSizeT = string.strlen(module_sha1)
sl: t.CSizeT = string.strlen(symbol)
key_buf: bytes = stdlib.malloc(ml + sl + 2)
if key_buf is None:
return None
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
i: int
for i in range(self.DocCount):
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
stdlib.free(key_buf)
return self.DocValues + i * DOC_VAL_SZ
stdlib.free(key_buf)
return None
def extract_module_docstrings(self, tree: ast.AST | t.CPtr, module_sha1: str) -> None:
"""扫描模块 AST:登记模块级函数/类/方法的 docstring(幂等)"""
if tree is None or module_sha1 is None:
return
ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children
if ch is None:
return
cn: t.CSizeT = len(ch)
for ci in range(cn):
child: ast.AST | t.CPtr = ch.get(ci)
if child is None:
continue
kd: int = child.kind()
if kd == ast.ASTKind.FunctionDef:
fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child)
if fd is not None and fd.name is not None:
fd_body: list[ast.AST | t.CPtr] | t.CPtr = fd.children
fdoc: str = self._extract_docstring_from_body(fd_body)
if fdoc is not None:
self.register_docstring(module_sha1, fd.name, fdoc)
elif kd == ast.ASTKind.ClassDef:
cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child)
if cd is None or cd.name is None:
continue
cd_body0: list[ast.AST | t.CPtr] | t.CPtr = cd.children
cdoc: str = self._extract_docstring_from_body(cd_body0)
if cdoc is not None:
self.register_docstring(module_sha1, cd.name, cdoc)
# 类方法 docstring(key="{ClassName}.{method}")
cd_body: list[ast.AST | t.CPtr] | t.CPtr = cd.children
if cd_body is not None:
cbn: t.CSizeT = len(cd_body)
for mi in range(cbn):
m: ast.AST | t.CPtr = cd_body.get(mi)
if m is None or m.kind() != ast.ASTKind.FunctionDef:
continue
mfd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(m)
if mfd is None or mfd.name is None:
continue
mdoc: str = self._extract_docstring_from_body(mfd.children)
if mdoc is not None:
cl_nl: t.CSizeT = string.strlen(cd.name)
mt_nl: t.CSizeT = string.strlen(mfd.name)
sym_buf: bytes = stdlib.malloc(cl_nl + mt_nl + 2)
if sym_buf is not None:
viperlib.snprintf(sym_buf, cl_nl + mt_nl + 2, "%s.%s", cd.name, mfd.name)
self.register_docstring(module_sha1, sym_buf, mdoc)
stdlib.free(sym_buf)
def _resolve_doc_symbol_name(self, trans: HT.Translator | t.CPtr, name_id: str,
out_mod: str | t.CPtr, out_sym: str | t.CPtr) -> int:
"""解析 Name 符号的 docstring 归属,写入 out_mod/out_sym,成功返回 1
判定顺序:本地变量(函数变量)→ 结构体类(含跨模块预注册)→ 模块名。
"""
if trans is None or name_id is None or out_mod is None or out_sym is None:
return 0
if trans.ModuleSha1 is None:
return 0
# 1. 本地变量(函数变量/全局变量)
if HandlesVar.lookup_var(trans.SymTab, name_id) is not None:
string.strcpy(out_mod, trans.ModuleSha1)
string.strcpy(out_sym, name_id)
return 1
# 2. 结构体类(entry.ModuleSha1 区分跨模块预注册的类)
se: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(name_id)
if se is not None:
if se.ModuleSha1 is not None:
string.strcpy(out_mod, se.ModuleSha1)
else:
string.strcpy(out_mod, trans.ModuleSha1)
string.strcpy(out_sym, name_id)
return 1
# 3. 模块名(模块本身,少见);用 StubMerger 模块级函数(避免
# 跨 Mixin self 调用生成不存在符号)
mod_sha1: str = StubMerger.LookupSha1ByModName(name_id)
if mod_sha1 is None:
mod_sha1 = StubMerger.LookupSha1ByModNameSuffix(name_id)
if mod_sha1 is not None:
string.strcpy(out_mod, mod_sha1)
string.strcpy(out_sym, name_id)
return 1
# 4. 兜底当前模块
string.strcpy(out_mod, trans.ModuleSha1)
string.strcpy(out_sym, name_id)
return 1
def _resolve_doc_symbol_attr(self, trans: HT.Translator | t.CPtr,
at: ast.Attribute | t.CPtr,
out_mod: str | t.CPtr, out_sym: str | t.CPtr) -> int:
"""解析嵌套 Attribute 链的 docstring 归属,写入 out_mod/out_sym
支持形态:remote.remote_func / remote.RemoteClass /
remote.RemoteClass.remote_method / LocalClass.local_method 等。
最左 Name 判定:模块名 → 该模块 sha1;变量/类 → 类所属模块 sha1。
"""
if trans is None or at is None or out_mod is None or out_sym is None:
return 0
if trans.ModuleSha1 is None:
return 0
# 收集 Attribute 链(最外层 attr 在前)
DOC_CHAIN_MAX: t.CInt = 8
items: bytes = stdlib.malloc(DOC_CHAIN_MAX * 128)
if items is None:
return 0
n_items: int = 0
cur: ast.AST | t.CPtr = (ast.AST | t.CPtr)(at)
while cur is not None and cur.kind() == ast.ASTKind.Attribute and n_items < DOC_CHAIN_MAX:
cat: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cur)
if cat.attr is None:
break
string.strcpy(items + n_items * 128, cat.attr)
n_items += 1
cur = cat.value
# 最左应为 Name
head_id: str = None
if cur is not None and cur.kind() == ast.ASTKind.Name:
hnm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(cur)
head_id = hnm.id
if head_id is None:
stdlib.free(items)
return 0
# head 判定:模块名 or 变量/类
is_mod: int = 0
mod_sha1: str = None
if HandlesVar.lookup_var(trans.SymTab, head_id) is None:
mod_sha1 = StubMerger.LookupSha1ByModName(head_id)
if mod_sha1 is None:
mod_sha1 = StubMerger.LookupSha1ByModNameSuffix(head_id)
if mod_sha1 is not None:
is_mod = 1
if is_mod != 0:
# 模块限定:symbol = items 反转(不含 head_id)
if n_items == 0:
string.strcpy(out_mod, mod_sha1)
string.strcpy(out_sym, head_id)
stdlib.free(items)
return 1
total: t.CSizeT = 0
j: int
for j in range(n_items):
total += string.strlen(items + j * 128) + 1
sym_buf: bytes = stdlib.malloc(total + 1)
if sym_buf is None:
stdlib.free(items)
return 0
sym_buf[0] = '\0'
for j in range(n_items):
if j > 0:
string.strcat(sym_buf, ".")
string.strcat(sym_buf, items + (n_items - 1 - j) * 128)
string.strcpy(out_mod, mod_sha1)
string.strcpy(out_sym, sym_buf)
stdlib.free(sym_buf)
stdlib.free(items)
return 1
# 变量/类:symbol = head_id + '.' + items 反转
total2: t.CSizeT = string.strlen(head_id)
for j in range(n_items):
total2 += string.strlen(items + j * 128) + 1
sym2: bytes = stdlib.malloc(total2 + 1)
if sym2 is None:
stdlib.free(items)
return 0
sym2[0] = '\0'
string.strcat(sym2, head_id)
for j in range(n_items):
string.strcat(sym2, ".")
string.strcat(sym2, items + (n_items - 1 - j) * 128)
se2: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(head_id)
if se2 is not None and se2.ModuleSha1 is not None:
string.strcpy(out_mod, se2.ModuleSha1)
else:
string.strcpy(out_mod, trans.ModuleSha1)
string.strcpy(out_sym, sym2)
stdlib.free(sym2)
stdlib.free(items)
return 1
def translate_attribute(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:
"""翻译属性访问,返回加载的字段值"""
if node is None or builder is None:
return None
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at is None or at.value is None or at.attr is None:
# fast-fail: AST 节点字段缺失属解析器 bug,应报告具体缺失字段
diag_buf_an: bytes = stdlib.malloc(256)
if diag_buf_an is not None:
missing_an: str = "?"
if at is not None:
if at.value is None:
missing_an = "value"
elif at.attr is None:
missing_an = "attr"
viperlib.snprintf(diag_buf_an, 256,
"属性访问失败: AST 节点字段缺失 (%s sha1=%s:%d)",
missing_an,
trans.ModuleSha1 if trans is not None and trans.ModuleSha1 is not None else "?",
at.lineno if at is not None and at.lineno is not None else 0)
VLogger.error(diag_buf_an, "ATTR")
return None
# 魔法属性 __doc__:查询 docstring 登记表(预注册阶段从 AST 提取),
# 命中则当前模块内联生成字符串常量返回;未命中(无 docstring 的
# 函数/类/方法)返回 null 指针(i8*)。支持本模块与跨模块符号:
# local_func / LocalClass / LocalClass.method / remote.remote_func /
# remote.RemoteClass / remote.RemoteClass.remote_method
if string.strcmp(at.attr, "__doc__") == 0:
doc_mod_buf: bytes = pool.alloc(64)
doc_sym_buf: bytes = pool.alloc(256)
if doc_mod_buf is None or doc_sym_buf is None:
return None
doc_mod_buf[0] = '\0'
doc_sym_buf[0] = '\0'
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
nm_doc: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_doc.id is not None:
self._resolve_doc_symbol_name(trans, nm_doc.id, doc_mod_buf, doc_sym_buf)
elif at.value is not None and at.value.kind() == ast.ASTKind.Attribute:
self._resolve_doc_symbol_attr(trans, (ast.Attribute | t.CPtr)(at.value), doc_mod_buf, doc_sym_buf)
doc_val: str = None
if doc_mod_buf[0] != '\0' and doc_sym_buf[0] != '\0':
doc_val = self.lookup_docstring(doc_mod_buf, doc_sym_buf)
if doc_val is None:
i8_ty_doc: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
if i8_ty_doc is None:
return None
i8_ptr_ty_doc: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_doc)
if i8_ptr_ty_doc is None:
return None
return llvmlite.ConstNull(pool, i8_ptr_ty_doc, "null")
return self.create_global_string(builder, pool, mod, doc_val, trans)
# 枚举成员访问: EnumName.MemberName → 常量值
# 当 value 是 Name 且 Name.id 是已注册枚举时,查找成员并返回常量
if at.value.kind() == ast.ASTKind.Name:
enum_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if enum_nm.id is not None:
if HandlesEnum.is_enum_class(enum_nm.id) == 1:
member: HandlesEnum.EnumMember | t.CPtr = HandlesEnum.lookup_enum_member(
enum_nm.id, at.attr)
if member is not None:
base_ty: llvmlite.LLVMType | t.CPtr = HandlesEnum.get_enum_base_type(
enum_nm.id)
if base_ty is not None:
# 生成数字字符串作为常量名(LLVM IR 要求常量输出数字值)
name_buf: str = pool.alloc(32)
if name_buf is not None:
viperlib.snprintf(name_buf, 32, "%lld", member.Value)
return llvmlite.ConstInt(pool, base_ty, member.Value, name_buf)
return llvmlite.const_int32(pool, member.Value)
# fast-fail: 枚举成员未找到
VLogger.error(
f"枚举成员未找到: {enum_nm.id}.{at.attr}",
"ATTR")
return None
# 嵌套枚举成员访问: Module.EnumName.MemberName → 常量值
# 当 value 是 Attribute(如 fileio.MODE / w32.fileio.MODE,含三级以上
# 模块路径)且 attr 部分是已注册枚举时。此前限制 inner_at_e.value 必须是
# Name(仅支持两层 Module.EnumName),w32.fileio.MODE.W(三层,value 是
# Attribute)被跳过 → "属性访问失败: .W"(SysLibTest L190 根因)。
# 枚举类名只取最内层 attr(MODE),模块路径形态不影响。
if at.value.kind() == ast.ASTKind.Attribute:
inner_at_e: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(at.value)
if inner_at_e.attr is not None:
if HandlesEnum.is_enum_class(inner_at_e.attr) == 1:
member_e: HandlesEnum.EnumMember | t.CPtr = HandlesEnum.lookup_enum_member(
inner_at_e.attr, at.attr)
if member_e is not None:
base_ty_e: llvmlite.LLVMType | t.CPtr = HandlesEnum.get_enum_base_type(
inner_at_e.attr)
if base_ty_e is not None:
name_buf_e: str = pool.alloc(32)
if name_buf_e is not None:
viperlib.snprintf(name_buf_e, 32, "%lld", member_e.Value)
return llvmlite.ConstInt(pool, base_ty_e, member_e.Value, name_buf_e)
return llvmlite.const_int32(pool, member_e.Value)
# fast-fail: 嵌套枚举成员未找到
dbg_ee: HandlesEnum.EnumEntry | t.CPtr = HandlesEnum.find_enum(
inner_at_e.attr)
dbg_mc: int = -1
if dbg_ee is not None:
dbg_mc = dbg_ee.MemberCount
VLogger.error(
f"嵌套枚举成员未找到: {inner_at_e.attr}.{at.attr} "
f"(enum_members={dbg_mc})", "ATTR")
return None
# FakeDuck: 影子结构体属性读取 (str 变量的 per-instance 字段)
# a.__data__ / a.__mbuddy__ → GEP {a}__meta__[field] 并加载
# 对齐 TPC HandlesExprAttr.py L433-442:对象是 Name 变量且存在
# {var}__meta__ 影子(fd2 创建)时,属性访问改走影子结构体。
if at.value.kind() == ast.ASTKind.Name and trans is not None:
fd_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if fd_nm.id is not None:
# pool 持久缓冲(f-string 栈缓冲在函数返回后失效)
fd_meta_buf: str = pool.alloc(256)
if fd_meta_buf is not None:
viperlib.snprintf(fd_meta_buf, 256, "%s__meta__", fd_nm.id)
fd_meta_v: llvmlite.Value | t.CPtr = HandlesVar.lookup_current(
trans.SymTab, fd_meta_buf)
if fd_meta_v is not None:
fd_sty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(
"_str")
if fd_sty is not None:
fd_fe: HandlesStruct.FieldEntry | t.CPtr = \
HandlesStruct.lookup_field(fd_sty, at.attr)
if fd_fe is not None and fd_fe.Ty is not None:
fd_fp: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, fd_sty, fd_fe.Ty, fd_meta_v, fd_fe.Index)
if fd_fp is not None:
return llvmlite.build_load(builder, fd_fe.Ty, fd_fp)
# 模块属性访问 (读路径): sys._mbuddy → 加载全局变量 @_mbuddy
# 当 obj 是已导入模块名且不是普通变量时,查找/创建全局变量引用并加载
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm_ma: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_ma.id is not None:
# 先检查是否是普通变量 (优先级高于模块属性)
ma_is_var: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
trans.SymTab, nm_ma.id)
if ma_is_var is None:
# 先检查是否是 CDefine 编译期常量 (如 ast.CONST_INT / win32base.INVALID_HANDLE_VALUE)
# 模块属性访问的 CDefine 常量需要跨模块查找源 .vp / .vpi 文件
cdef_ma_val: t.CInt64T = HandlesType.lookup_cdefine_constant(at.attr)
cd_local: int = HandlesType.is_cdefine_found()
if cd_local == 0:
# 确定真实模块名:优先从 _from_imports 解析别名
# from_imports 中条目格式 "local_name:real_module"
# 如 win32base:w32.win32base → 别名 win32base 真实模块 w32.win32base
real_mod_name: str = nm_ma.id
if trans._from_imports is not None:
resolved_mod: str = HandlesImports.lookup_from_import(
trans._from_imports, nm_ma.id, 0)
if resolved_mod is not None:
# 复制模块名到新缓冲区(截断于空格、null、或 ':')
rm_len: t.CSizeT = 0
while resolved_mod[rm_len] != ' ' and resolved_mod[rm_len] != '\0' and resolved_mod[rm_len] != ':':
rm_len += 1
rm_buf: str = pool.alloc(rm_len + 1)
if rm_buf is not None:
for rm_i in range(rm_len):
rm_buf[rm_i] = resolved_mod[rm_i]
rm_buf[rm_len] = '\0'
real_mod_name = rm_buf
# 即使 _from_imports 为 None(如纯 `import ast` 无 from-import),
# 也必须尝试跨模块 CDefine 查找,否则 ast.CONST_INT 会被
# 误解析为全局变量引用 @CONST_INT(未定义符号)。
# 构建临时 from_imports "{attr}:{real_module}" 用于跨模块查找
if real_mod_name is not None:
ma_fi_len: t.CSizeT = string.strlen(at.attr) + string.strlen(real_mod_name) + 2
ma_fi: str = pool.alloc(ma_fi_len)
if ma_fi is not None:
viperlib.snprintf(ma_fi, ma_fi_len, "%s:%s", at.attr, real_mod_name)
cdef_ma_val = self._lookup_cross_module_cdefine(pool, at.attr, ma_fi)
if HandlesType.is_cdefine_found() != 0:
# 跨模块字符串 CDefine(如 pyzlib.ZLIB_VERSION = "1.3.2")→
# 字符串全局常量(test_pyzlib.vp L49 ver = pyzlib.ZLIB_VERSION)
if HandlesType.is_cdefine_str() != 0:
cdef_str_v: str = HandlesType.lookup_cdefine_string(at.attr)
if cdef_str_v is not None:
return self.create_global_string(
builder, pool, mod, cdef_str_v, trans)
# 跨模块浮点 CDefine(如 vipermath.U_M_PI)→ 浮点常量
dchk_ma: double = HandlesType.lookup_cdefine_double(at.attr)
if HandlesType.is_cdefine_double() != 0:
return llvmlite.ConstFloat(pool, llvmlite.Double(pool), dchk_ma)
return self._const_int_for_cdefine(pool, cdef_ma_val)
# 检查 nm_ma.id.at.attr 是否是已导入子模块 (如 w32.win32file)
# 如果是子模块,不应创建全局变量,让嵌套属性访问路径处理
is_submod: int = 0
if trans._imported_modules is not None:
submod_len: t.CSizeT = string.strlen(nm_ma.id) + string.strlen(at.attr) + 2
submod_buf: str = pool.alloc(submod_len)
if submod_buf is not None:
viperlib.snprintf(submod_buf, submod_len, "%s.%s", nm_ma.id, at.attr)
if HandlesImports.is_module_imported(trans._imported_modules, submod_buf) == 1:
is_submod = 1
# also check from_imports: at.attr 别名对应真实模块 w32.win32file
if is_submod == 0 and trans._from_imports is not None:
fi_mod_raw: str = HandlesImports.lookup_from_import(
trans._from_imports, at.attr, 0)
if fi_mod_raw is not None:
# 复制模块名到新缓冲区(截断于空格、null、或 ':')
fm_len: t.CSizeT = 0
while fi_mod_raw[fm_len] != ' ' and fi_mod_raw[fm_len] != '\0' and fi_mod_raw[fm_len] != ':':
fm_len += 1
fi_mod: str = pool.alloc(fm_len + 1)
if fi_mod is not None:
for fm_i in range(fm_len):
fi_mod[fm_i] = fi_mod_raw[fm_i]
fi_mod[fm_len] = '\0'
# 检查 fi_mod 是否以 "nm_ma.id." 开头 (如 w32.win32file 以 w32. 开头)
prefix_len: t.CSizeT = string.strlen(nm_ma.id)
fi_mod_len: t.CSizeT = string.strlen(fi_mod)
if fi_mod_len > prefix_len + 1:
match_prefix: int = 1
for pi in range(prefix_len):
if fi_mod[pi] != nm_ma.id[pi]:
match_prefix = 0
break
if match_prefix == 1 and fi_mod[prefix_len] == '.':
is_submod = 1
if is_submod != 0:
return None # 是子模块,交给嵌套属性访问路径
# 特殊处理: t.ASM_DESCR 不是 CDefine,是内联汇编约束命名空间
# t.ASM_DESCR.XXX 由 HandlesExprCall 的 _asm_resolve_constraint 处理
# 此处返回 None,让上层嵌套属性访问路径处理
if string.strcmp(nm_ma.id, "t") == 0 and string.strcmp(at.attr, "ASM_DESCR") == 0:
return None
# CDefine 未找到且属性名符合 ALL_CAPS 约定: fail-fast 终止编译
# 不再创建未定义的外部全局变量引用(否则 llc 报 "use of undefined value")
if self._is_cdefine_name_pattern(at.attr) != 0:
HandlesType.fatal_error(
at,
f"跨模块 CDefine 未找到: '{nm_ma.id}.{at.attr}'(CDefine cross-module lookup failed)",
"TypeError")
# 非 CDefine: 使用 i8* 作为类型提示 (模块级变量通常存储指针)
i8_ptr_hint: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
mod_gv_ref: llvmlite.Value | t.CPtr = self._resolve_module_attribute_global(
pool, mod, trans, at, i8_ptr_hint)
if mod_gv_ref is not None and mod_gv_ref.Ty is not None:
load_ty_mod: llvmlite.LLVMType | t.CPtr = mod_gv_ref.Ty.Pointee
if load_ty_mod is not None:
# 模块级全局数组(如 zdef.zdeflate_len_base: CArray[T, N]):
# 按 C 语义衰减为基址返回(对齐 translate_name_value 局部
# 数组分支 L3383-3388),load [N x T] 数组值无法作 GEP 基址。
match load_ty_mod:
case llvmlite.LLVMType.Array(_, _):
return mod_gv_ref
case _:
pass
return llvmlite.build_load(builder, load_ty_mod, mod_gv_ref)
# 嵌套模块属性访问 (w32.win32file.GENERIC_READ → 查找全局 @GENERIC_READ)
# 当 at.value 是 Attribute 且解析为已导入子模块时,查找 at.attr 作为全局常量
if at.value.kind() == ast.ASTKind.Attribute and trans is not None:
inner_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(at.value)
if inner_at.value is not None and inner_at.value.kind() == ast.ASTKind.Name:
inner_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(inner_at.value)
if inner_nm.id is not None and inner_at.attr is not None:
if HandlesVar.lookup_var(trans.SymTab, inner_nm.id) is None:
# 构建限定模块名: "w32.win32file"
qual_len: t.CSizeT = string.strlen(inner_nm.id) + string.strlen(inner_at.attr) + 2
qual_buf: str = pool.alloc(qual_len)
if qual_buf is not None:
viperlib.snprintf(qual_buf, qual_len, "%s.%s", inner_nm.id, inner_at.attr)
if HandlesImports.is_module_imported(trans._imported_modules, qual_buf) == 1:
# 查找全局变量 at.attr (stub 已注入的情况)
gv_nested: llvmlite.GlobalVariable | t.CPtr = self.find_global_in_module(mod, at.attr)
if gv_nested is not None and gv_nested.Ty is not None:
gv_ref_nested: llvmlite.Value | t.CPtr = self.make_global_ref(
pool, at.attr, gv_nested.Ty)
if gv_ref_nested is not None and gv_ref_nested.Ty is not None:
load_ty_nested: llvmlite.LLVMType | t.CPtr = gv_ref_nested.Ty.Pointee
if load_ty_nested is not None:
return llvmlite.build_load(builder, load_ty_nested, gv_ref_nested)
# 未找到全局: 尝试 CDefine 常量查找
# 对于 w32.win32base.INVALID_HANDLE_VALUE 等嵌套模块属性访问,
# 先从限定模块的 .vpi 文件中查找 CDefine 常量值并内联
cdef_val_nested: t.CInt64T = HandlesType.lookup_cdefine_constant(at.attr)
if HandlesType.is_cdefine_found() == 0:
# 构建临时 from_imports "attr:module" 用于跨模块查找
tmp_fi_len: t.CSizeT = string.strlen(at.attr) + string.strlen(qual_buf) + 2
tmp_fi: str = pool.alloc(tmp_fi_len)
if tmp_fi is not None:
viperlib.snprintf(tmp_fi, tmp_fi_len, "%s:%s", at.attr, qual_buf)
cdef_val_nested = self._lookup_cross_module_cdefine(pool, at.attr, tmp_fi)
if HandlesType.is_cdefine_found() != 0:
# 跨模块浮点 CDefine → 浮点常量
dchk_nst: double = HandlesType.lookup_cdefine_double(at.attr)
if HandlesType.is_cdefine_double() != 0:
return llvmlite.ConstFloat(pool, llvmlite.Double(pool), dchk_nst)
return self._const_int_for_cdefine(pool, cdef_val_nested)
# CDefine 未找到: fail-fast 终止编译(不再创建未定义的全局变量引用)
# 之前此处创建 @INVALID_HANDLE_VALUE 等前向引用,但全局未定义,
# 导致 llc 报 "use of undefined value" 错误。
# 正确做法是报错终止,让开发者修复跨模块 CDefine 查找失败的根本原因。
if self._is_cdefine_name_pattern(at.attr) != 0:
HandlesType.fatal_error(
at,
f"嵌套属性跨模块 CDefine 未找到: '{qual_buf}.{at.attr}'(CDefine cross-module lookup failed)",
"TypeError")
return None
# 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体);
# nonlocal 变量(提升函数内)从 env 读共享堆值,避免 lookup_var
# 跨函数引用外层 alloca(SSA 编号跨函数错乱崩溃)
obj_ptr: llvmlite.Value | t.CPtr = None
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm.id is not None:
if HT.is_nonlocal_name(trans, nm.id) != 0:
raw_nl_a: llvmlite.Value | t.CPtr = HandlesNonlocal._load_raw_var_ptr(
trans, nm.id)
if raw_nl_a is not None:
nl_ty_a: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(
trans, nm.id)
if nl_ty_a is not None:
# 对象即指针:Struct/Array 类型直接用堆地址(bitcast 后
# 作为对象指针,不 load);指针类型(如 Ptr(Point))堆里
# 存对象指针,需 load 出对象指针
match nl_ty_a:
case llvmlite.LLVMType.Struct(_, _, _):
obj_ptr = llvmlite.build_bitcast(
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
case llvmlite.LLVMType.Array(_, _):
obj_ptr = llvmlite.build_bitcast(
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
case _:
nl_val_ptr_a: llvmlite.Value | t.CPtr = \
llvmlite.build_bitcast(
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
if nl_val_ptr_a is not None:
obj_ptr = llvmlite.build_load(
builder, nl_ty_a, nl_val_ptr_a)
else:
obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)
# 非 Name 路径:翻译对象值(会 load)
if obj_ptr is None:
# c.Deref(ptr).attr: 属性访问 base 取指针本身而非 load 后的结构体值
# (如 c.Deref(p).a 语义是 (*p).a,直接对 p 做 GEP)
cd_base_rd: llvmlite.Value | t.CPtr = self._get_cderef_attr_base(
builder, pool, mod, at.value, trans)
if cd_base_rd is not None:
obj_ptr = cd_base_rd
elif at.value.kind() == ast.ASTKind.Subscript:
# 下标基座属性访问(如 pts[0].x):list[T]/OOP __getitem__ 类走
# translate_value(返回元素值/地址);普通结构体指针数组走
# get_subscript_ptr(GEP 元素地址,避免结构体值 base=非指针 报错)
if self._sub_base_uses_getitem(pool, mod, trans, at.value) != 0:
obj_ptr = self.translate_value(builder, pool, mod, at.value, None, 0, trans)
else:
obj_ptr = self.get_subscript_ptr(builder, pool, mod, at.value, trans)
elif at.value.kind() == ast.ASTKind.Attribute:
# 嵌套属性基座(如 r.origin.x):取内层字段地址(get_attribute_ptr 递归,
# 地址语义),指针字段(X|t.CPtr)由 _deref_if_ptr_ptr load 解引用
obj_ptr = self.get_attribute_ptr(builder, pool, mod, at.value, trans)
if obj_ptr is None:
obj_ptr = self.translate_value(builder, pool, mod, at.value, None, 0, trans)
else:
obj_ptr = self.translate_value(builder, pool, mod, at.value, None, 0, trans)
if obj_ptr is None or obj_ptr.Ty is None:
return None
# 如果 obj_ptr 是 Ptr(Ptr(Struct))(X|t.CPtr 变量的 alloca),
# load 解引用获取 Ptr(Struct)
obj_ptr = self._deref_if_ptr_ptr(builder, obj_ptr)
# 查找结构体类型信息
# obj_ptr 类型应该是 Ptr(Struct(...))
match obj_ptr.Ty:
case llvmlite.LLVMType.Ptr(struct_ty):
# 确保跨模块结构体的完整定义在当前模块中可用(供 getelementptr 访问字段)
HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty)
# 查找字段索引和类型
field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field(
struct_ty, at.attr)
# REnum 变体字段查找: lookup_field 失败时,尝试 REnum 别名
# REnum 结构体字段名是 _p1/_p2/...,但源码用变体字段名访问(如 ty.Pointee)
if field_info is None:
renum_se: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type(
struct_ty)
if renum_se is not None and renum_se.Name is not None:
is_rn: int = HandlesClassDef._is_renum_class_name(renum_se.Name)
if is_rn == 1:
alias_idx: int = HandlesClassDef._lookup_renum_field_alias(
renum_se.Name, at.attr)
if alias_idx >= 1:
alias_fe: HandlesStruct.FieldEntry | t.CPtr = \
HandlesStruct._get_field_entry(renum_se, alias_idx)
if alias_fe is not None and alias_fe.Ty is not None:
alias_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, struct_ty, alias_fe.Ty, obj_ptr, alias_idx)
if alias_ptr is not None:
return llvmlite.build_load(builder, alias_fe.Ty, alias_ptr)
# property getter 回退: obj.attr 是 @property/@x.getter 方法时生成调用
# (字段未找到时先尝试,避免报误导性的"字段未找到"错误)
if field_info is None and at.value is not None and trans is not None:
prop_cls_fb: str = HandlesStruct.get_class_name_by_type(pool, struct_ty)
prop_val: llvmlite.Value | t.CPtr = HandlesExprCall._try_property_getter_attr(
pool, builder, mod, at, obj_ptr, trans, prop_cls_fb)
if prop_val is not None:
return prop_val
# 按需过滤: struct_ty 为 i8(简化联合类型)时,通过 AnnotClassName 按类名查找
# 传递 SHA1 以区分跨模块同名类
# base 注解类名提取:Name → 变量注解类名;Attribute → 属性链递归字段注解类名
if field_info is None:
base_class: str = None
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm_fb: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_fb.id is not None:
var_entry: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, nm_fb.id)
if var_entry is not None:
base_class = var_entry.AnnotClassName
elif at.value.kind() == ast.ASTKind.Attribute:
base_class = self._resolve_attr_field_class(pool, trans, at.value)
if base_class is not None:
cur_sha1: str = trans.ModuleSha1
field_info = HandlesStruct.lookup_field_by_class(
base_class, at.attr, cur_sha1)
# REnum 变体字段别名回退:base_class 是 REnum 类时,变体
# 字段名(如 LLVMType.Pointee → _p2 槽位)不是普通字段,
# lookup_field_by_class 查不到(LLVMType"字段 Pointee 未找到"
# 根因;主路径 L4989-5004 的 REnum 别名查找因 struct_ty 简化
# 为 i8、find_struct_by_type 失败被跳过)
if field_info is None \
and HandlesClassDef._is_renum_class_name(base_class) != 0:
rn_alias: int = HandlesClassDef._lookup_renum_field_alias(
base_class, at.attr)
if rn_alias >= 1:
rn_se: HandlesStruct.StructEntry | t.CPtr = \
HandlesStruct.find_struct(base_class)
if rn_se is not None:
rn_fe: HandlesStruct.FieldEntry | t.CPtr = \
HandlesStruct._get_field_entry(rn_se, rn_alias)
if rn_fe is not None and rn_fe.Ty is not None:
field_info = rn_fe
# 按需过滤成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
# 原始 struct_ty 可能是 i8(X|t.CPtr 简化为 Ptr(i8)),
# 需用实际结构体类型做 GEP,否则 GEP i8 失败
if field_info is not None:
annot_se: HandlesStruct.StructEntry | t.CPtr = \
HandlesStruct.find_struct(base_class)
if annot_se is not None and annot_se.Ty is not None:
annot_ptr_ty: llvmlite.LLVMType | t.CPtr = \
llvmlite.Ptr(pool, annot_se.Ty)
casted_ptr: llvmlite.Value | t.CPtr = \
llvmlite.build_bitcast(builder, obj_ptr, annot_ptr_ty)
if casted_ptr is not None:
obj_ptr = casted_ptr
struct_ty = annot_se.Ty
# 按需过滤更新 struct_ty 后,需重新确保
# 注解类型结构体定义在当前模块可用
# (原始 struct_ty 可能是 i8,ensure 无效)
HandlesStruct.ensure_struct_def_in_module(
pool, mod, annot_se.Ty)
if field_info is not None:
field_ty: llvmlite.LLVMType | t.CPtr = field_info.Ty
# 联合体:bitcast obj_ptr 到 field_ty* 后 load
if HandlesStruct.is_union_by_type(struct_ty) == 1:
field_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, field_ty)
casted: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, obj_ptr, field_ptr_ty)
if casted is not None:
return llvmlite.build_load(builder, field_ty, casted)
return None
# 普通结构体:GEP + load
field_idx: int = field_info.Index
field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, struct_ty, field_ty, obj_ptr, field_idx)
if field_ptr is not None:
# 数组类型字段不能 load 为 SSA value,直接返回字段指针
# 用于后续下标访问: self.state[0] → GEP state 字段 → GEP 数组元素
if self.is_array_type(field_ty) != 0:
return field_ptr
loaded_val: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, field_ty, field_ptr)
# 联合类型字段(如 Token | t.CPtr)被编译为 i8*,
# 若 AnnotClassName 指示了具体结构体类型,bitcast 为正确的结构体指针。
# 注意:仅当字段类型本身是指针时才 bitcast(指针→指针);
# 内嵌结构体字段(field_ty=Struct)load 出的是结构体值,
# bitcast 值→指针是非法 IR(llc: invalid cast opcode)。
if loaded_val is not None and field_info.AnnotClassName is not None \
and self.is_ptr_type(field_ty) != 0:
annot_struct: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(
field_info.AnnotClassName)
if annot_struct is not None and annot_struct.Ty is not None:
annot_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, annot_struct.Ty)
return llvmlite.build_bitcast(builder, loaded_val, annot_ptr_ty)
return loaded_val
# fast-fail: 字段未找到,立即终止编译(不再打印诊断后返回 None)
# Phase A 依赖未就绪(_deps_pending,父类/依赖类未注册致字段缺失):
# 跳过本属性访问交给下遍重译(对齐 get_attribute_ptr L5541-5547),
# 避免 fail-fast 中断多遍收敛;依赖就绪后仍缺失则走下方真实报错。
if trans is not None and trans._deps_pending != 0:
return None
if trans is not None:
diag_annot: str = None
diag_var_nm: str = None
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
dnm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
diag_var_nm = dnm.id
if dnm.id is not None:
dve: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, dnm.id)
if dve is not None:
diag_annot = dve.AnnotClassName
diag_var_txt: str = "?"
if diag_var_nm is not None:
diag_var_txt = diag_var_nm
diag_annot_txt: str = "None"
if diag_annot is not None:
diag_annot_txt = diag_annot
sha1_attr: str = "?"
if trans.ModuleSha1 is not None:
sha1_attr = trans.ModuleSha1
HandlesType.fatal_error(
at,
f"属性访问失败: .{at.attr} (var={diag_var_txt} annot={diag_annot_txt} sha1={sha1_attr})",
"TypeError")
return None
case _:
# fast-fail: 对非指针类型做属性访问(如 i32.x),
# Python traceback 立即终止,不静默返回 None(否则下游 "store to None" 崩溃,丢失错误现场)
attr_nm_ap: str = "?"
if at is not None and at.attr is not None:
attr_nm_ap = at.attr
sha1_ap: str = "?"
if trans is not None and trans.ModuleSha1 is not None:
sha1_ap = trans.ModuleSha1
ln_ap: int = 0
if at is not None and at.lineno is not None:
ln_ap = at.lineno
HandlesType.fatal_error(
at,
f"属性访问失败: .{attr_nm_ap} (base=非指针类型,无法做属性访问 sha1={sha1_ap}:{ln_ap})",
"TypeError")
# ============================================================
# 获取下标表达式的元素指针(不加载值,用于赋值)
# ============================================================
def get_subscript_ptr(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:
"""获取下标表达式的元素指针(用于赋值 lhs)"""
if node is None or builder is None:
return None
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
if sub is None:
return None
# 翻译索引
idx_val: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, sub.slice, None, 0, trans)
if idx_val is None:
return None
# 如果 value 是 Name,尝试从 alloca 类型推断
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if nm.id is not None and trans is not None:
# nonlocal 数组/指针:从 env 读共享堆地址(提升函数内不能
# lookup_var 外层 alloca——SSA 编号跨函数错乱崩溃)
if HT.is_nonlocal_name(trans, nm.id) != 0:
return self._nonlocal_subscript_ptr(
builder, pool, trans, nm.id, idx_val)
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
if alloca is not None and alloca.Ty is not None:
if self.is_ptr_type(alloca.Ty) != 0:
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is not None:
# 用 match 检查 pointee 类型
match pointee:
case llvmlite.LLVMType.Array(elem_ty, count):
# 数组遍历
return llvmlite.build_gep_array(
builder, pointee, elem_ty, alloca,
self._zext_index_to_i32(builder, idx_val))
case llvmlite.LLVMType.Ptr(inner_ty):
# inner_ty 是 Ptr 说明 alloca.Ty 是三重指针,
# 即 X|t.CPtr 当 X 本身是指针类型 (如 T=AST|t.CPtr, T|t.CPtr=AST**)。
# 此时 [i]=val 应该是指针解引用赋值, 而非调用 __setitem__。
if self.is_ptr_type(inner_ty) != 0:
ptr_val_gsp: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val_gsp is not None:
return llvmlite.build_gep(
builder, inner_ty, ptr_val_gsp, idx_val)
return None
# 自定义结构体 (如 HashTable|t.CPtr, JsonValue|t.CPtr):
# 仅当类定义了 __setitem__(当前模块定义或跨模块 stub declare 注入)
# 才返回 None 触发 __setitem__ 重载;否则普通结构体指针按下标
# 做 GEP 数组访问(走下方指针遍历路径)
cls_nm_ptr_chk: str = self._get_custom_struct_cls_nm(pool, inner_ty)
if not (cls_nm_ptr_chk is None):
si_lookup_gsp: str = pool.alloc(128)
if si_lookup_gsp is not None:
viperlib.snprintf(si_lookup_gsp, 128, "%s.__setitem__", cls_nm_ptr_chk)
if HandlesExprCall.find_func_in_module(mod, si_lookup_gsp) is not None:
return None
# bytes|t.CPtr / str|t.CPtr: alloca 是 i8**,
# 直接 GEP 按 i8* 步长(8 字节),不 load
# 仅当 inner_ty 不是自定义结构体时才走指针遍历
if trans is not None:
ve_pe: HandlesVar.VarEntry | t.CPtr = \
HandlesVar.lookup_var_entry(trans.SymTab, nm.id)
if ve_pe is not None and ve_pe.IsPtrElement == 1:
# bytes|t.CPtr / str|t.CPtr / T|t.CPtr:变量存指针值,
# 下标语义 = [指针值 + i*8](8 字节指针元素)。必须先 load
# alloca 得指针值,再 GEP——否则 GEP base 用 alloca(&elem_ptr)
# 读到栈变量而非数据区,赋值写不进 list 数据
# (data 全 0xbaadf00d,dump 虚调用读错 vtable 崩溃)。
ptr_val_pe: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val_pe is not None:
match pointee:
case llvmlite.LLVMType.Int8():
i8_ptr_pe: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
pool, llvmlite.Int8(pool))
return llvmlite.build_gep(
builder, i8_ptr_pe, ptr_val_pe, idx_val)
case _:
pass
return llvmlite.build_gep(
builder, pointee, ptr_val_pe, idx_val)
return None
# 普通指针遍历: 先 load 指针值,再 GEP
ptr_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val is None:
return None
gep_r: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, inner_ty, ptr_val, idx_val)
return gep_r
case _:
# pointee 是普通标量类型 (如 i64, i32, i8):
# alloca 是 Ptr(标量), 直接 GEP 获取第 idx 个元素指针
# 支持 arg_vals[i] = val 这类参数数组下标赋值
# 联合类型变量(IsPtrElement=1,如 _list.vp 的
# elem_ptr: T | t.CPtr 退化为 i8*):pointee=i8 但
# 元素是 8 字节指针,须按 i8* 步长 GEP(防只写 1 字节截断)
if trans is not None:
ve_pe_cs: HandlesVar.VarEntry | t.CPtr = \
HandlesVar.lookup_var_entry(trans.SymTab, nm.id)
if ve_pe_cs is not None and ve_pe_cs.IsPtrElement == 1:
# T|t.CPtr(退化为 i8*,如 _list.vp 的 elem_ptr)变量
# 存数据区指针,元素为 8 字节指针:必须先 load alloca
# 得指针值,再按 i8* 步长 GEP——否则 GEP base 用 alloca
# (&elem_ptr)算到栈地址,赋值写不进 list 数据区
# (append 只增 count、data 全 0xbaadf00d)。
ptr_val_cs: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val_cs is not None:
match pointee:
case llvmlite.LLVMType.Int8():
i8_ptr_cs: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
pool, llvmlite.Int8(pool))
return llvmlite.build_gep(
builder, i8_ptr_cs, ptr_val_cs, idx_val)
case _:
pass
return llvmlite.build_gep(
builder, pointee, ptr_val_cs, idx_val)
return None
return llvmlite.build_gep(builder, pointee, alloca, idx_val)
# 通用路径
ptr_val: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, sub.value, None, 0, trans)
if ptr_val is None or ptr_val.Ty is None:
return None
if self.is_ptr_type(ptr_val.Ty) != 0:
elem_ty2: llvmlite.LLVMType | t.CPtr = ptr_val.Ty.Pointee
if elem_ty2 is not None:
# 数组类型: 双索引 GEP (getelementptr [N x ty], ptr, i32 0, i32 idx)
match elem_ty2:
case llvmlite.LLVMType.Array(arr_elem_ty, arr_count):
return llvmlite.build_gep_array(
builder, elem_ty2, arr_elem_ty, ptr_val,
self._zext_index_to_i32(builder, idx_val))
case _:
pass
# 自定义结构体 (如 self._ht[key]): 仅当类定义了 __setitem__
# 才返回 None 触发 HandlesAssign 的 __setitem__ 重载;
# 否则普通结构体指针按下标做 GEP 数组访问
cls_nm_gen: str = self._get_custom_struct_cls_nm(pool, elem_ty2)
if not (cls_nm_gen is None):
si_lookup_gen: str = pool.alloc(128)
if si_lookup_gen is not None:
viperlib.snprintf(si_lookup_gen, 128, "%s.__setitem__", cls_nm_gen)
if find_func_in_module(mod, si_lookup_gen) is not None:
return None
# 指针类型: 单索引 GEP (getelementptr ty, ptr, idx)
return llvmlite.build_gep(builder, elem_ty2, ptr_val, idx_val)
return None
# ============================================================
# 获取属性访问的字段指针(不加载值,用于赋值)
# 对于 Name 类型的 obj,直接使用 alloca 指针(不 load 结构体)
# ============================================================
def _resolve_attr_field_class(self, pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
attr_node: ast.AST | t.CPtr) -> str:
"""解析属性链(a.b.c)中 c 字段的注解类名。
联合类型字段(X | t.CPtr)被简化为 Ptr(i8),属性链写入时
(如 lx.tokens_tail.next = tok)obj_ptr.Ty 是 Ptr(i8),
lookup_field(i8, ...) 必然失败。此函数沿属性链递归解析
最终字段的 AnnotClassName(如 tokens_tail → 'Token'),
供按需过滤分支 bitcast 回真实结构体类型后 GEP。
"""
if attr_node is None or trans is None:
return None
k: int = attr_node.kind()
if k != ast.ASTKind.Attribute:
return None
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(attr_node)
if at is None or at.attr is None or at.value is None:
return None
# 递归解析基座的注解类名(Name → 变量注解;Attribute → 字段注解)
parent_class: str = None
if at.value.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm.id is None:
return None
var_entry: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, nm.id)
if var_entry is None:
return None
parent_class = var_entry.AnnotClassName
elif at.value.kind() == ast.ASTKind.Attribute:
parent_class = self._resolve_attr_field_class(pool, trans, at.value)
else:
return None
if parent_class is None:
return None
# 在父类中查 at.attr 字段,返回其注解类名
cur_sha1: str = None
if trans is not None:
cur_sha1 = trans.ModuleSha1
fe: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field_by_class(
parent_class, at.attr, cur_sha1)
if fe is None:
return None
return HandlesStruct.get_field_annot_class_name(fe)
def get_attribute_ptr(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:
"""获取属性访问的字段指针(用于赋值 lhs)"""
if node is None or builder is None:
return None
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at is None or at.value is None or at.attr is None:
return None
# 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体);
# nonlocal 变量(提升函数内)从 env 读共享堆值,避免 lookup_var
# 跨函数引用外层 alloca(SSA 编号跨函数错乱崩溃)
obj_ptr: llvmlite.Value | t.CPtr = None
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm.id is not None:
if HT.is_nonlocal_name(trans, nm.id) != 0:
raw_nl_a: llvmlite.Value | t.CPtr = HandlesNonlocal._load_raw_var_ptr(
trans, nm.id)
if raw_nl_a is not None:
nl_ty_a: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(
trans, nm.id)
if nl_ty_a is not None:
# 对象即指针:Struct/Array 类型直接用堆地址(bitcast 后
# 作为对象指针,不 load);指针类型(如 Ptr(Point))堆里
# 存对象指针,需 load 出对象指针
match nl_ty_a:
case llvmlite.LLVMType.Struct(_, _, _):
obj_ptr = llvmlite.build_bitcast(
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
case llvmlite.LLVMType.Array(_, _):
obj_ptr = llvmlite.build_bitcast(
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
case _:
nl_val_ptr_a: llvmlite.Value | t.CPtr = \
llvmlite.build_bitcast(
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
if nl_val_ptr_a is not None:
obj_ptr = llvmlite.build_load(
builder, nl_ty_a, nl_val_ptr_a)
else:
obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)
# 模块属性访问 (写路径): sys._mbuddy = mb / os._win32._mbuddy = v
# → store 到模块全局变量。当 obj 是已导入模块名(Name 或嵌套 Attribute
# 模块路径)且不是普通变量时,查找/创建全局变量引用。
if obj_ptr is None and trans is not None:
mod_path_c: str = self._attr_module_path_str(pool, at.value)
if mod_path_c is not None:
# CDefine 常量不可写: 报 FATAL 错误,不创建未定义全局
# (仅单层 Name 模块可精确判 CDefine)
if at.value.kind() == ast.ASTKind.Name:
nm_wma: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_wma.id is not None:
if self._is_cdefine_name_pattern(at.attr) != 0:
err_buf_wma: str = pool.alloc(256)
if err_buf_wma is not None:
viperlib.snprintf(err_buf_wma, 256,
"[CD] FATAL: Cannot assign to CDefine constant '%s.%s'\n",
nm_wma.id, at.attr)
VLogger.error(err_buf_wma, "CD")
return None
# 使用 i8* 作为类型提示 (模块级变量通常存储指针)
i8_ptr_w: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
wma_ref: llvmlite.Value | t.CPtr = self._resolve_module_attribute_global(
pool, mod, trans, at, i8_ptr_w)
if wma_ref is not None:
return wma_ref
# 非 Name 路径:翻译对象值(会 load)
if obj_ptr is None:
# c.Deref(ptr).attr: 属性写入 base 取指针本身而非 load 后的结构体值
# (如 c.Deref(p).a = val 语义是 (*p).a = val,直接对 p 做 GEP)
cd_base_wr: llvmlite.Value | t.CPtr = self._get_cderef_attr_base(
builder, pool, mod, at.value, trans)
if cd_base_wr is not None:
obj_ptr = cd_base_wr
elif at.value.kind() == ast.ASTKind.Subscript:
# 下标基座属性写入(如 pts[0].x = val):list[T]/OOP __getitem__ 类走
# translate_value(返回元素值/地址);普通结构体指针数组走
# get_subscript_ptr(GEP 元素地址,避免结构体值 base=非指针 报错)
if self._sub_base_uses_getitem(pool, mod, trans, at.value) != 0:
obj_ptr = self.translate_value(builder, pool, mod, at.value, None, 0, trans)
else:
obj_ptr = self.get_subscript_ptr(builder, pool, mod, at.value, trans)
elif at.value.kind() == ast.ASTKind.Attribute:
# 嵌套属性基座写入(如 r.origin.x = val):取内层字段地址
# (get_attribute_ptr 递归,地址语义),指针字段由 _deref_if_ptr_ptr 解引用
obj_ptr = self.get_attribute_ptr(builder, pool, mod, at.value, trans)
if obj_ptr is None:
obj_ptr = self.translate_value(builder, pool, mod, at.value, None, 0, trans)
else:
obj_ptr = self.translate_value(builder, pool, mod, at.value, None, 0, trans)
if obj_ptr is None or obj_ptr.Ty is None:
return None
# 如果 obj_ptr 是 Ptr(Ptr(Struct))(X|t.CPtr 变量的 alloca),
# load 解引用获取 Ptr(Struct)
obj_ptr = self._deref_if_ptr_ptr(builder, obj_ptr)
match obj_ptr.Ty:
case llvmlite.LLVMType.Ptr(struct_ty):
# 确保跨模块结构体的完整定义在当前模块中可用(供 getelementptr 访问字段)
# 与 translate_attribute 一致,避免 opaque 类型导致 GEP 报错
HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty)
field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field(
struct_ty, at.attr)
# 按需过滤: struct_ty 为 i8(简化联合类型)时,通过 AnnotClassName 按类名查找
# 传递 SHA1 以区分跨模块同名类
# base 注解类名提取:Name → 变量注解类名;Attribute → 属性链递归字段注解类名
# (如 lx.tokens_tail.next = tok:lx 的注解 Lexer → tokens_tail 字段注解 Token)
if field_info is None:
base_class: str = None
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm_fb: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_fb.id is not None:
var_entry: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, nm_fb.id)
if var_entry is not None:
base_class = var_entry.AnnotClassName
elif at.value.kind() == ast.ASTKind.Attribute:
base_class = self._resolve_attr_field_class(pool, trans, at.value)
if base_class is not None:
cur_sha1: str = None
if trans is not None:
cur_sha1 = trans.ModuleSha1
field_info = HandlesStruct.lookup_field_by_class(
base_class, at.attr, cur_sha1)
# REnum 变体字段别名回退(同 translate_attribute L5046-5062):
# get_attribute_ptr 赋值路径的按需过滤同样需要——struct_ty 简化
# i8 时主路径 REnum 别名查找被跳过(LLVMType.Pointee 报错根因)
if field_info is None \
and HandlesClassDef._is_renum_class_name(base_class) != 0:
rn_alias_g: int = HandlesClassDef._lookup_renum_field_alias(
base_class, at.attr)
if rn_alias_g >= 1:
rn_se_g: HandlesStruct.StructEntry | t.CPtr = \
HandlesStruct.find_struct(base_class)
if rn_se_g is not None:
rn_fe_g: HandlesStruct.FieldEntry | t.CPtr = \
HandlesStruct._get_field_entry(rn_se_g, rn_alias_g)
if rn_fe_g is not None and rn_fe_g.Ty is not None:
field_info = rn_fe_g
# 按需过滤成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
# 原始 struct_ty 可能是 i8(X|t.CPtr 简化为 Ptr(i8)),
# 需用实际结构体类型做 GEP,否则 GEP i8 失败
if field_info is not None:
annot_se: HandlesStruct.StructEntry | t.CPtr = \
HandlesStruct.find_struct(base_class)
if annot_se is not None and annot_se.Ty is not None:
annot_ptr_ty: llvmlite.LLVMType | t.CPtr = \
llvmlite.Ptr(pool, annot_se.Ty)
casted_ptr: llvmlite.Value | t.CPtr = \
llvmlite.build_bitcast(builder, obj_ptr, annot_ptr_ty)
if casted_ptr is not None:
obj_ptr = casted_ptr
struct_ty = annot_se.Ty
# 按需过滤更新 struct_ty 后,需重新确保
# 注解类型结构体定义在当前模块可用
HandlesStruct.ensure_struct_def_in_module(
pool, mod, annot_se.Ty)
if field_info is not None:
# 联合体:bitcast obj_ptr 到 field_ty*(字段指针用于 store)
if HandlesStruct.is_union_by_type(struct_ty) == 1:
field_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, field_info.Ty)
return llvmlite.build_bitcast(builder, obj_ptr, field_ptr_ty)
# 普通结构体:GEP 获取字段指针
return llvmlite.build_gep_struct(
builder, struct_ty, field_info.Ty, obj_ptr, field_info.Index)
# fast-fail: 字段未找到,立即终止编译(不再打印诊断后返回 None)
# Phase A 依赖未就绪(_deps_pending,父类/依赖类未注册致字段缺失,
# 如 Generator.vp 先于 TypeGenMixin.vp 编译时 self.TraceFrames):
# 跳过本属性写入交给下遍重译(对齐 HandlesAssign L69-78),
# 避免 fail-fast 中断多遍收敛;依赖就绪后仍缺失则走下方真实报错。
if trans is not None and trans._deps_pending != 0:
return None
if trans is not None:
diag_annot_g: str = None
diag_var_nm_g: str = None
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
dnm_g: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
diag_var_nm_g = dnm_g.id
if dnm_g.id is not None:
dve_g: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, dnm_g.id)
if dve_g is not None:
diag_annot_g = dve_g.AnnotClassName
diag_var_txt_g: str = "?"
if diag_var_nm_g is not None:
diag_var_txt_g = diag_var_nm_g
diag_annot_txt_g: str = "None"
if diag_annot_g is not None:
diag_annot_txt_g = diag_annot_g
sha1_attr_g: str = "?"
if trans.ModuleSha1 is not None:
sha1_attr_g = trans.ModuleSha1
HandlesType.fatal_error(
at,
f"属性写入失败: .{at.attr} (var={diag_var_txt_g} annot={diag_annot_txt_g} sha1={sha1_attr_g})",
"TypeError")
return None
case _:
# fast-fail: 对非指针类型做属性写入(如 i32.x = ...),
# Python traceback 立即终止,不静默返回 None(否则下游 "store to None" 崩溃)
attr_nm_gp: str = "?"
if at is not None and at.attr is not None:
attr_nm_gp = at.attr
sha1_gp: str = "?"
if trans is not None and trans.ModuleSha1 is not None:
sha1_gp = trans.ModuleSha1
ln_gp: int = 0
if at is not None and at.lineno is not None:
ln_gp = at.lineno
HandlesType.fatal_error(
at,
f"属性写入失败: .{attr_nm_gp} (base=非指针类型,无法做属性写入 sha1={sha1_gp}:{ln_gp})",
"TypeError")
# ============================================================
# P3 迁移方法(来自 HandlesExprOps.vp:运算符重载 + 二元运算)
# ============================================================
# ============================================================
# infer_expr_class - 从表达式推断结构体类名(共享推断)
#
# 无注解赋值(HandlesAssign)与方法调用分派(CallGenMixin)共用:
# 联合类型 X|t.CPtr 简化为 Ptr(i8),不含类名,需按表达式结构推断:
# Call → 类实例化(func 为已注册结构体名);func 为 Attribute
# 时是方法调用,经 vpi 返回注解缓存解析返回类名
# Name → 源变量的 AnnotClassName
# BinOp/UnaryOp → lhs 类名 + 存在对应 dunder 方法(返回同类对象)
# Attribute → 字段注解类名(_resolve_attr_field_class)
# 仅采用已注册结构体;无法精确推断返回 None(fail-fast 由调用方暴露)。
# ============================================================
def infer_expr_class(self, pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> str:
"""从表达式推断结构体类名,无法推断返回 None"""
if node is None or trans is None:
return None
k: int = node.kind()
# Call: 类实例化 / 方法调用
if k == ast.ASTKind.Call:
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
if cl is None or cl.func is None:
return None
if cl.func.kind() == ast.ASTKind.Name:
cand: str = ((ast.Name | t.CPtr)(cl.func)).id
if cand is None:
return None
if HandlesStruct.find_struct(cand) is not None:
return cand
return None
if cl.func.kind() == ast.ASTKind.Attribute:
# 方法调用 a.cross(b):vpi 返回注解缓存精确解析返回类名
# (方法可返回非同类对象,如 Transform.world_position → 'Vec3')。
ca: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if ca is None or ca.attr is None or ca.value is None:
return None
obj_cls: str = self.infer_expr_class(pool, mod, ca.value, trans)
if obj_cls is None:
return None
if self._cls_has_dunder(pool, mod, obj_cls, ca.attr) == 0:
return None
cls_sha1: str = HandlesStruct.get_struct_sha1(obj_cls)
if cls_sha1 is None and trans.ModuleSha1 is not None:
cls_sha1 = trans.ModuleSha1
ret_cls: str = None
if cls_sha1 is not None:
import lib.StubGen.VpiReader as VpiReader
temp_dir: str = None
# 跨 Mixin 不能 self 访问 Generator 的 TempDir 字段(字段按
# 声明类隔离),经全局单例获取(同 HandlesAssign 模式)
import lib.core.Gen.Generator as Generator
g2: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(
t.CVoid(HandlesExpr._g_gen, t.CPtr))
if g2 is not None and g2.TempDir is not None:
temp_dir = g2.TempDir
if temp_dir is not None:
VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, temp_dir)
annot_ret: str = VpiReader._lookup_vpi_ret_annot(
cls_sha1, obj_cls, ca.attr)
if annot_ret is not None:
ret_cls = self._extract_annot_class_name(pool, annot_ret)
return ret_cls
return None
# Name: 源变量注解
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm is None or nm.id is None:
return None
ve: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, nm.id)
if ve is None:
return None
acn_n: str = ve.AnnotClassName
if acn_n is None:
return None
# 去模块前缀:json.JsonValue → JsonValue。AnnotClassName 可能带
# 模块限定(extract_class_name_from_annotation 的 Attribute 分支返回
# "memhub.MemBuddy" 形式),而 find_struct/_cls_has_dunder/
# get_struct_sha1 查找都用裸类名(struct 注册名)。泛型名
# (list[str] 含 '[')不截断(方括号内可能有点号)。
acn_dot_n: str = string.strrchr(acn_n, 46)
if acn_dot_n is not None and string.strchr(acn_n, 91) is None:
acn_n = acn_dot_n + 1
return acn_n
# BinOp/UnaryOp: 运算符重载返回同类对象
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
if bop is None or bop.left is None:
return None
dunder: str = self._binop_to_dunder(bop.op)
if dunder is None:
return None
lhs_cls: str = self.infer_expr_class(pool, mod, bop.left, trans)
if lhs_cls is None or self._cls_has_dunder(pool, mod, lhs_cls, dunder) == 0:
return None
return lhs_cls
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
if uop is None or uop.operand is None:
return None
dunder: str = self._unaryop_to_dunder(uop.op)
if dunder is None:
return None
opr_cls: str = self.infer_expr_class(pool, mod, uop.operand, trans)
if opr_cls is None or self._cls_has_dunder(pool, mod, opr_cls, dunder) == 0:
return None
return opr_cls
# Attribute: 字段注解类名(属性链递归,如 p = r.origin)
if k == ast.ASTKind.Attribute:
return self._resolve_attr_field_class(pool, trans, node)
return None
# ============================================================
# _cls_has_dunder - 检查类(含继承链)是否存在 dunder/方法
#
# 与 try_operator_overload 的查找一致:当前模块内定义或跨模块类有
# SHA1(stub 中声明)即视为存在。仅在确实存在时采用推断类名,
# 避免把原生运算误标为类类型。
# ============================================================
def _cls_has_dunder(self, pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
class_name: str, dunder: str) -> int:
"""类及其 MRO 祖先链中是否存在 dunder 方法,存在返回 1"""
if class_name is None or dunder is None or mod is None:
return 0
lookup_name: str = pool.alloc(128)
if lookup_name is None:
return 0
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
if HandlesExprCall.find_func_in_module(mod, lookup_name) is not None:
return 1
# 继承链祖先查找
mro_ptr: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, class_name)
mi: int = 1
while mro_ptr is not None and mro_ptr[mi] != 0:
anc: str = (str | t.CPtr)(t.CVoid(mro_ptr[mi], t.CPtr))
if anc is not None:
anc_lookup: str = pool.alloc(128)
if anc_lookup is not None:
viperlib.snprintf(anc_lookup, 128, "%s.%s", anc, dunder)
if HandlesExprCall.find_func_in_module(mod, anc_lookup) is not None:
return 1
mi += 1
# 跨模块:类有 SHA1(stub 中声明该方法)视为存在
if HandlesStruct.get_struct_sha1(class_name) is not None:
return 1
return 0
# ============================================================
# _extract_annot_class_name - 从返回注解文本提取结构体类名
#
# vpi 返回注解文本(如 "'Vec3' | CPtr")按 '|' 分段,每段去除
# 空格/引号后查结构体注册表:命中即类名(内置类型 str/int/
# CFloat64T/t.CPtr 等均不注册 struct,天然过滤)。"A | B" 联合
# 多候选时取首个命中段。
# ============================================================
def _extract_annot_class_name(self, pool: memhub.MemBuddy | t.CPtr,
annot: str) -> str:
"""从返回注解文本提取已注册结构体类名,无命中返回 None"""
if pool is None or annot is None:
return None
alen: t.CSizeT = string.strlen(annot)
if alen == 0:
return None
seg: str = pool.alloc(256)
if seg is None:
return None
start: t.CSizeT = 0
i: t.CSizeT
for i in range(alen):
if annot[i] == '|':
s: t.CSizeT = start
e: t.CSizeT = i
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'):
s += 1
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'):
e -= 1
seglen: t.CSizeT = e - s
if seglen > 0 and seglen < 256:
string.strncpy(seg, annot + s, seglen)
seg[seglen] = '\0'
if HandlesStruct.find_struct(seg) is not None:
return seg
start = i + 1
# 最后一段(无 '|' 后缀)
s: t.CSizeT = start
e: t.CSizeT = alen
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'):
s += 1
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'):
e -= 1
seglen: t.CSizeT = e - s
if seglen > 0 and seglen < 256:
string.strncpy(seg, annot + s, seglen)
seg[seglen] = '\0'
if HandlesStruct.find_struct(seg) is not None:
return seg
return None
# ============================================================
# BinOp 运算符 → dunder 方法名
# ============================================================
def _binop_to_dunder(self, op: int) -> str:
"""将 BinOp 运算符映射到 dunder 方法名,无映射时返回 None
Python 3 语义:/ 映射 __truediv__(Python 2 的 __div__ 已废弃)。
"""
if op == ast.OpKind.Add: return "__add__"
if op == ast.OpKind.Sub: return "__sub__"
if op == ast.OpKind.Mult: return "__mul__"
if op == ast.OpKind.Div: return "__truediv__"
if op == ast.OpKind.Mod: return "__mod__"
if op == ast.OpKind.BitAnd: return "__and__"
if op == ast.OpKind.BitOr: return "__or__"
if op == ast.OpKind.BitXor: return "__xor__"
if op == ast.OpKind.LShift: return "__lshift__"
if op == ast.OpKind.RShift: return "__rshift__"
if op == ast.OpKind.FloorDiv: return "__floordiv__"
return None
# ============================================================
# Compare 运算符 → dunder 方法名
# ============================================================
def _cmpop_to_dunder(self, op: int) -> str:
"""将 Compare 运算符映射到 dunder 方法名,无映射时返回 None"""
if op == ast.OpKind.Eq: return "__eq__"
if op == ast.OpKind.Ne: return "__ne__"
if op == ast.OpKind.Lt: return "__lt__"
if op == ast.OpKind.Le: return "__le__"
if op == ast.OpKind.Gt: return "__gt__"
if op == ast.OpKind.Ge: return "__ge__"
return None
# ============================================================
# UnaryOp 运算符 → dunder 方法名
# ============================================================
def _unaryop_to_dunder(self, op: int) -> str:
"""将 UnaryOp 运算符映射到 dunder 方法名,无映射时返回 None"""
if op == ast.OpKind.USub: return "__neg__"
if op == ast.OpKind.UAdd: return "__pos__"
if op == ast.OpKind.Invert: return "__invert__"
return None
# ============================================================
# try_operator_overload - 尝试运算符重载
#
# 检查 lhs 是否为结构体指针,并在该类(含继承链)中查找对应的
# dunder 方法。找到则生成方法调用 lhs.__dunder__(rhs),返回
# 调用结果;未找到返回 None,调用方回退到原生运算。
# ============================================================
def try_operator_overload(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
lhs: llvmlite.Value | t.CPtr,
rhs: llvmlite.Value | t.CPtr,
op: int,
trans: HT.Translator | t.CPtr,
is_compare: int) -> llvmlite.Value | t.CPtr:
"""尝试运算符重载:lhs 是结构体指针时调用 dunder 方法,否则返回 None"""
if lhs is None or rhs is None:
return None
# 延迟导入避免循环依赖
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.Handles.HandlesExprCall as HandlesExprCall
# 映射运算符到 dunder 方法名
dunder: str = None
if is_compare != 0:
dunder = self._cmpop_to_dunder(op)
else:
dunder = self._binop_to_dunder(op)
if dunder is None:
return None
# 检查 lhs 是否为结构体指针
struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type_from_value(lhs)
if struct_ty is None:
return None
# 获取类名
class_name: str = HandlesStruct.get_class_name_by_type(pool, struct_ty)
if class_name is None:
return None
# 在继承链中查找 dunder 方法
lookup_name: str = pool.alloc(128)
if lookup_name is None:
return None
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
found_func: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, lookup_name)
search_class: str = class_name
if found_func is None:
# 多继承:沿 MRO 祖先链查找(先声明父类优先)
mro_ptr: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, class_name)
mi: int = 1
while mro_ptr is not None and mro_ptr[mi] != 0:
anc: str = (str | t.CPtr)(t.CVoid(mro_ptr[mi], t.CPtr))
if anc is not None:
parent_lookup: str = pool.alloc(128)
if parent_lookup is not None:
viperlib.snprintf(parent_lookup, 128, "%s.%s", anc, dunder)
parent_func: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, parent_lookup)
if parent_func is not None:
found_func = parent_func
search_class = anc
break
mi += 1
# 方法不在当前模块中时,检查类(含父类)是否有 SHA1(stub 可能未注入)
# 优先用类型指针定位 entry 获取 SHA1(规避跨模块同名 find_struct 找错)
cls_sha1: str = None
op_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type(struct_ty)
if op_entry is not None:
cls_sha1 = op_entry.ModuleSha1
if cls_sha1 is None:
cls_sha1 = HandlesStruct.get_struct_sha1(search_class)
if cls_sha1 is None:
mro_p: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, search_class)
mj: int = 1
while mro_p is not None and mro_p[mj] != 0 and cls_sha1 is None:
anc_p: str = (str | t.CPtr)(t.CVoid(mro_p[mj], t.CPtr))
if anc_p is not None:
cls_sha1 = HandlesStruct.get_struct_sha1(anc_p)
mj += 1
# 既无函数定义也无 SHA1 → 该类没有此 dunder 方法,返回 None 回退原生运算
if found_func is None and cls_sha1 is None:
return None
# 方法在"定义它的模块"中确实不存在(同模块类但模块内无此 dunder):
# 返回 None 回退原生运算,绝不发出幽灵 call——否则 IR 残留
# `call void @...__div__`(副作用)且调用方回退原生 sdiv 双重发射,
# llc 报 invalid operand type for instruction(CPythonTest PyFloatObj 根因)
if found_func is None and trans is not None and trans.ModuleSha1 is not None:
if cls_sha1 is not None and string.strcmp(cls_sha1, trans.ModuleSha1) == 0:
return None
# 跨模块类:stub 可能未注入,方法存在性以依赖模块 vpi stub 为准。
# vpi 未命中(依赖模块确实无此 dunder)→ 回退原生比较(icmp),
# 绝不发幽灵 call——`call @{cls_sha1}.{Class}.{dunder}` 链接 undefined
# (MemBuddy 无 __eq__ 时 os._mbuddy == sys._mbuddy,SysLibTest 根因)。
vpi_dup: llvmlite.LLVMType | t.CPtr = HandlesExprCall._lookup_method_ret_ty_via_vpi(
pool, cls_sha1, search_class, dunder)
if vpi_dup is None:
return None
# 构建 extra_args 数组(仅含 rhs 一个参数)
extra_args: t.CSizeT | t.CPtr = pool.alloc(8)
if extra_args is None:
return None
extra_args[0] = t.CSizeT(rhs)
# 调用方法: lhs.__dunder__(rhs)
return HandlesExprCall._call_method_on_ptr(
pool, builder, mod, search_class, dunder,
lhs, extra_args, 1, trans)
# ============================================================
# try_unary_operator_overload - 尝试一元运算符重载
#
# 检查 operand 是否为结构体指针,并在该类(含继承链)中查找对应的
# dunder 方法(__neg__/__pos__/__invert__)。找到则生成方法调用
# operand.__dunder__(),返回调用结果;未找到返回 None,调用方回退到原生运算。
# ============================================================
def try_unary_operator_overload(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
operand: llvmlite.Value | t.CPtr,
op: int,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""尝试一元运算符重载:operand 是结构体指针时调用 dunder 方法,否则返回 None"""
if operand is None:
return None
# 延迟导入避免循环依赖
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.Handles.HandlesExprCall as HandlesExprCall
dunder: str = self._unaryop_to_dunder(op)
if dunder is None:
return None
# 检查 operand 是否为结构体指针
struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type_from_value(operand)
if struct_ty is None:
return None
# 获取类名
class_name: str = HandlesStruct.get_class_name_by_type(pool, struct_ty)
if class_name is None:
return None
# 在继承链中查找 dunder 方法
lookup_name: str = pool.alloc(128)
if lookup_name is None:
return None
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
found_func: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, lookup_name)
search_class: str = class_name
if found_func is None:
# 多继承:沿 MRO 祖先链查找(先声明父类优先)
mro_ptr: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, class_name)
mi: int = 1
while mro_ptr is not None and mro_ptr[mi] != 0:
anc: str = (str | t.CPtr)(t.CVoid(mro_ptr[mi], t.CPtr))
if anc is not None:
parent_lookup: str = pool.alloc(128)
if parent_lookup is not None:
viperlib.snprintf(parent_lookup, 128, "%s.%s", anc, dunder)
parent_func: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, parent_lookup)
if parent_func is not None:
found_func = parent_func
search_class = anc
break
mi += 1
# 方法不在当前模块中时,检查类(含父类)是否有 SHA1(stub 可能未注入)
cls_sha1: str = None
uop_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type(struct_ty)
if uop_entry is not None:
cls_sha1 = uop_entry.ModuleSha1
if cls_sha1 is None:
cls_sha1 = HandlesStruct.get_struct_sha1(search_class)
if cls_sha1 is None:
mro_p: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, search_class)
mj: int = 1
while mro_p is not None and mro_p[mj] != 0 and cls_sha1 is None:
anc_p: str = (str | t.CPtr)(t.CVoid(mro_p[mj], t.CPtr))
if anc_p is not None:
cls_sha1 = HandlesStruct.get_struct_sha1(anc_p)
mj += 1
# 既无函数定义也无 SHA1 → 该类没有此 dunder 方法,返回 None 回退原生运算
if found_func is None and cls_sha1 is None:
return None
# 同模块类但模块内无此 dunder → 返回 None 回退原生运算,不发幽灵 call
# (与 try_operator_overload 一致,防 IR 残留 call void @...__neg__)
if found_func is None and trans is not None and trans.ModuleSha1 is not None:
if cls_sha1 is not None and string.strcmp(cls_sha1, trans.ModuleSha1) == 0:
return None
# 一元方法无额外参数(extra_args 为空)
return HandlesExprCall._call_method_on_ptr(
pool, builder, mod, search_class, dunder,
operand, None, 0, trans)
# ============================================================
# 翻译二元运算(自动类型提升)
# ============================================================
def translate_binop(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译二元运算(自动类型提升)"""
binop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
if binop is None:
return None
lhs_node: ast.AST | t.CPtr = binop.left
rhs_node: ast.AST | t.CPtr = binop.right
op: int = binop.op
# 先翻译 rhs(只翻译一次,避免副作用重复)
rhs: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, rhs_node, None, 0, trans)
if rhs is None:
return None
# === 运算符重载路径 1: lhs 是 Name 且对应结构体变量 ===
# 对于值类型变量(如 cnt: Counter),translate_value 会 load 返回 Struct 值,
# 但 dunder 方法需要 Ptr(Struct) 作为 self。直接用 alloca 指针尝试重载。
if lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(lhs_node)
if nm is not None and nm.id is not None:
lhs_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
trans.SymTab, nm.id)
if lhs_alloca is not None:
ovl_self1: llvmlite.Value | t.CPtr = lhs_alloca
# alloca 是 Ptr(Ptr(Struct))(X|t.CPtr 对象存储槽)时,
# load 出对象指针作为 self——若把槽地址当 self,dunder 内
# load 槽内容(对象指针值)当字段读 → 垃圾(MultiInheritTest
# Vec2Both __add__ 输出 (1.0,2.0) 根因;get_struct_type_from_value
# 递归解指针后路径 1 能识别 alloca,需同步修正 self 语义)
if lhs_alloca.Ty is not None and lhs_alloca.Ty.Pointee is not None \
and HandlesExpr.is_ptr_type(lhs_alloca.Ty.Pointee) != 0:
load_self1: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, lhs_alloca.Ty.Pointee, lhs_alloca)
if load_self1 is not None:
ovl_self1 = load_self1
ovl_result: llvmlite.Value | t.CPtr = self.try_operator_overload(
pool, builder, mod, ovl_self1, rhs, op, trans, 0)
if ovl_result is not None:
return ovl_result
# 正常翻译 lhs
lhs: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, lhs_node, None, 0, trans)
if lhs is None:
return None
# === 运算符重载路径 2: lhs 是 Ptr(Struct)(如 Counter|t.CPtr 变量 load 后)===
ovl_result2: llvmlite.Value | t.CPtr = self.try_operator_overload(
pool, builder, mod, lhs, rhs, op, trans, 0)
if ovl_result2 is not None:
return ovl_result2
# 获取操作数类型信息
lhs_bits: int = self.get_llvm_type_bits(lhs.Ty)
rhs_bits: int = self.get_llvm_type_bits(rhs.Ty)
lhs_fbits: int = self.get_llvm_float_bits(lhs.Ty)
rhs_fbits: int = self.get_llvm_float_bits(rhs.Ty)
# 浮点运算: 任一操作数为浮点时,使用浮点指令(必须在指针算术之前检查,
# 因为 float 的 int bits 为 0,会被误认为指针)
if lhs_fbits != 0 or rhs_fbits != 0:
# 确定目标浮点类型(使用较大的位宽)
target_fbits: int = lhs_fbits
if rhs_fbits > target_fbits:
target_fbits = rhs_fbits
target_float_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
if target_fbits == 32:
target_float_ty = llvmlite.Float(pool)
# 将两个操作数转换为目标浮点类型(int→float via si2fp, float→float via fpext/fptrunc)
lhs = self.coerce_to_type(builder, lhs, target_float_ty, node)
rhs = self.coerce_to_type(builder, rhs, target_float_ty, node)
if op == ast.OpKind.Add:
return llvmlite.build_fadd(builder, lhs, rhs)
if op == ast.OpKind.Sub:
return llvmlite.build_fsub(builder, lhs, rhs)
if op == ast.OpKind.Mult:
return llvmlite.build_fmul(builder, lhs, rhs)
if op == ast.OpKind.Div:
return llvmlite.build_fdiv(builder, lhs, rhs)
if op == ast.OpKind.Mod:
return llvmlite.build_frem(builder, lhs, rhs)
return None
# 指针算术: ptr + int / ptr - int / int + ptr → getelementptr(按元素大小缩放)
# 对齐 TPC ExprGen.emit_binary_op L50-67;int - ptr 无 GEP 形式(C 语义非法)
if (lhs_bits == 0 and rhs_bits != 0) or (lhs_bits != 0 and rhs_bits == 0):
if op == ast.OpKind.Add:
if lhs_bits == 0:
return self.translate_ptr_arith(pool, builder, lhs, rhs, op)
# int + ptr(交换)
return self.translate_ptr_arith(pool, builder, rhs, lhs, op)
if op == ast.OpKind.Sub and lhs_bits == 0:
return self.translate_ptr_arith(pool, builder, lhs, rhs, op)
if lhs_bits > rhs_bits:
if self.should_zext_widen(pool, rhs, lhs, rhs_node) != 0:
rhs = llvmlite.build_zext(builder, rhs, lhs.Ty)
else:
rhs = self.coerce_to_type(builder, rhs, lhs.Ty, node)
elif rhs_bits > lhs_bits:
if self.should_zext_widen(pool, lhs, rhs, lhs_node) != 0:
lhs = llvmlite.build_zext(builder, lhs, rhs.Ty)
else:
lhs = self.coerce_to_type(builder, lhs, rhs.Ty, node)
# 位运算类型安全检查:两边都是指针(lhs_bits==0 and rhs_bits==0)时,
# 位运算(BitOr/BitAnd/BitXor/LShift/RShift)非法,LLVM 要求整数操作数。
# 常见触发场景:CDefine 常量查找失败返回指针 null,与另一指针做 `|`。
if lhs_bits == 0 and rhs_bits == 0:
if op == ast.OpKind.BitOr or op == ast.OpKind.BitAnd or op == ast.OpKind.BitXor or op == ast.OpKind.LShift or op == ast.OpKind.RShift:
# fail-fast:位运算操作数类型非法,立即终止编译(暴露上游根因)
HandlesType.fatal_error(
node,
"位运算操作数类型非法: lhs/rhs 均为非整数类型(指针或结构体),无法执行位运算",
"TypeError")
if op == ast.OpKind.Add:
return llvmlite.build_add(builder, lhs, rhs)
elif op == ast.OpKind.Sub:
return llvmlite.build_sub(builder, lhs, rhs)
elif op == ast.OpKind.Mult:
return llvmlite.build_mul(builder, lhs, rhs)
elif op == ast.OpKind.Div:
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.FloorDiv:
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.Mod:
return llvmlite.build_srem(builder, lhs, rhs)
elif op == ast.OpKind.BitAnd:
return llvmlite.build_and(builder, lhs, rhs)
elif op == ast.OpKind.BitOr:
return llvmlite.build_or(builder, lhs, rhs)
elif op == ast.OpKind.BitXor:
return llvmlite.build_xor(builder, lhs, rhs)
elif op == ast.OpKind.LShift:
return llvmlite.build_shl(builder, lhs, rhs)
elif op == ast.OpKind.RShift:
# 无符号 >> → lshr(逻辑右移),有符号 >> → ashr(算术右移)
# 对齐 TPC ExprGen.emit_binary_op:i8/i16 默认无符号;其余查操作数无符号性
if lhs_bits == 8 or lhs_bits == 16:
return llvmlite.build_lshr(builder, lhs, rhs)
if self._node_type_unsigned(pool, lhs_node, trans) != 0:
return llvmlite.build_lshr(builder, lhs, rhs)
return llvmlite.build_ashr(builder, lhs, rhs)
return None