Files
ViperC/App/lib/core/Gen/ExprGenName.vp
T
TermiNexus 285f4c25e2 fix(handles): ExprCallHandle 类方法裸调用补模块前缀(translate_call/printf/print)
拆分后主文件保留的 ExprCallHandle.HandleCall/HandlePrintfCall/
HandlePrintCall 仍裸调 translate_call/translate_printf_call/
translate_print_call → TPC 报 Undefined function: 'translate_call'。
改为 ExprCallCall. 前缀并补 import;ExprGenName.find_func_in_module
裸调用同步补 ExprCallBase. 前缀。全 App 裸调用扫描清零。
2026-08-17 08:27:07 +08:00

987 lines
64 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 llvmlite
import memhub
import string
import stdio
import stdlib
import viperlib
import w32.fileio as fileio
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.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
import lib.core.Gen.ExprGenBase as ExprGenBase
import lib.core.Handles.ExprCallBase as ExprCallBase
import lib.core.Handles.ExprCallCtor as ExprCallCtor
# ============================================================
# ExprGenName - ExprGenMixin 拆分(阶段 D):Name/Subscript/下标访问
#
# 继承 ExprGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesExpr 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class ExprGenName(ExprGenBase.ExprGenBase):
"""Name/Subscript/下标访问"""
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 HandlesExpr.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 HandlesExpr._const_int_for_cdefine(pool, cdef_val_c)
# 本地表未找到:尝试跨模块 .vpi 查找(保证多模块查表存表一致)
# 仅对符合 CDefine 命名约定(ALL_CAPS)的名字触发跨模块查找,
# 避免对普通变量名(mb/name/ptr/pool/self 等)误触发,造成严重性能损耗
if HandlesExpr._is_cdefine_name_pattern(nm_id) != 0:
if trans is not None:
cdef_val: t.CInt64T = HandlesExpr._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 HandlesExpr.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 HandlesExpr._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 = ExprCallBase.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 HandlesExpr._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 = HandlesExpr.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 = HandlesExpr.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 = HandlesExpr.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 ExprCallBase.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 = ExprCallCtor._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 ExprCallBase.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 = ExprCallCtor._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 = HandlesExpr.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 ExprCallBase.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 = ExprCallCtor._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
# ============================================================
# 获取下标表达式的元素指针(不加载值,用于赋值)
# ============================================================
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 = HandlesExpr.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 ExprCallBase.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 = HandlesExpr.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 ExprCallBase.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