Files
ViperC/App/lib/core/Gen/Expr/ExprGenName.vp
T
TermiNexus d979cc8c1f fix(TPV/ExprGenName): 结构体值局部变量在使用处要求同型结构体值时 load 出值
现场:Test/WebTest 编译 includes/socket.vp:121 报
  CastError: 非基本类型的值/指针隐式转换已禁止(构造物化除外):
  不能把指针类型隐式转为非基本类型值(Foo* → Foo)
  源类型 %"56d0ee99ecf0a0b5.SocketAddr"* → 目标值类型 %"56d0ee99ecf0a0b5.SocketAddr"

现场代码:
  class SocketAddr:  family: u16 / port: u16 / addr: u32 / zero: u64   # 纯结构体,无 __new__、无方法
  def _MakeAddr(host: str, port: INT) -> SocketAddr:
      addr: SocketAddr                     # 结构体值局部变量
      string.memset(c.Addr(addr), 0, 16)
      ...
      return addr                          # ← 第 121 行报错

根因:ExprGenName.translate_name_value 对"结构体值局部变量"的右值**恒返回 alloca 地址**
(原实现,注释解释为"对象即指针"语义,避免 ptrtoint %Struct 非法指令);而 _MakeAddr
的返回类型经 resolve_annotation_type 解析为**结构体值**。translate_return 会把函数返回
类型作为 target_ty 下传给 translate_value(StmtGenCifMixin.vp:557-558),于是 `addr`
求值出 %SocketAddr* → coerce_to_type 的"指针 → 非基本类型值"分支
(ExprGenBase.vp:954-970)只对"构造物化"豁免 → 报错。
标量局部变量无此问题(同函数内非 Struct 分支会 build_load 出值),属结构体值局部变量
独有的表示差异。

改动:ExprGenName.translate_name_value 的 Struct 分支增加 target_ty 判据——使用处
**明确要求同一非基本类型(非指针)**时(_same_agg_ty 判据:与"构造物化豁免"同一类型
匹配真源 AssignGen._stg_ty_equal),像标量一样 build_load 出值;否则维持既有"返回地址"。
- target_ty 为指针时不命中(裸类形参 lowering 为 Ptr(struct),见 FuncGenDef)⇒
  Test/StructTest 的 point_sum(p) 这类实参传址行为不变。
- 字段访问基座(ExprGenAttr.vp:743-744,直接 lookup_var 取地址)、c.Addr 取址
  (CallGenCBuiltin.vp:293)、方法接收者均不经本函数取地址,不受影响。
- 同时修正该处已失真的注释(原文举例 `x: Cls | t.CPtr` 实际解析为 Ptr(struct),
  并非 Struct 值局部变量)。

待验证:重编译 TransPyV(自举两遍)→ WebTest / StructTest / ClassArrayTest / JsonTest。
2026-10-01 14:26:18 +08:00

1236 lines
85 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import viperlib
import lib.core.Gen.Translator as HT
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
import lib.core.Gen.GenApi as GenApi
# ============================================================
# ExprGenName - ExprGenMixin 拆分(阶段 D):Name/Subscript/下标访问
# ============================================================
novirtual class ExprGenName(ExprGenBase.ExprGenBase):
"""Name/Subscript/下标访问"""
def _cut_from_import_mod(self, pool: memhub.MemBuddy*,
raw: str) -> str:
"""截断 lookup_from_import 返回的模块名到独立缓冲
lookup_from_import 返回指向 from_imports 内部的指针(非 NUL 结尾,
后随空格分隔的其他条目,如 "ast.tokens TokenType:ast.tokens ...")。
不截断则 LookupSha1ByModName 收到整串查不到 → mangle 退化裸名 →
全局引用不带 SHA1 前缀 → 链接 undefined symbol(_op_head 根因,
对齐 ExprGenCdef._lookup_cross_module_cdefine 的截断逻辑)。
截断到空格/冒号/NUL;raw 无效返回 None。
"""
if raw is None: return None
mbl_c: sizet = 0
while raw[mbl_c] != ' ' and raw[mbl_c] != '\0' and raw[mbl_c] != ':':
mbl_c += 1
if mbl_c == 0: return None
buf_c: str = pool.alloc(mbl_c + 1)
if buf_c is None: return None
string.strncpy(buf_c, raw, mbl_c)
buf_c[mbl_c] = '\0'
return buf_c
def translate_name_value(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
node: ast.AST*,
trans: HT.Translator* = None,
target_ty: llvmlite.LLVMType* = None,
use_ctx: str = None) -> llvmlite.Value*:
"""翻译变量引用(Name 节点)→ load
target_ty:调用方(translate_value)下传的**使用处类型**(赋值目标 /
形参类型 / 返回类型)。被两处读取:① 下方 CDefine 整数常量物化点
(决定物化宽度);② **结构体值局部变量**在使用处明确要求同型结构体值时
改为 load 出值(见下方 Struct 分支说明)。其余变量 load 逐字沿用原有逻辑。
use_ctx:使用处**真实站点种类**标签,仅透传给宽度不足报文(见
cdefine_use_width_value),不参与任何判定。
"""
# 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* = trans.Module
nm: ast.Name* = ast.Name*(node)
if nm is None: return None
nm_id: str = nm.id
if nm_id is None: return None
# CDefine 编译期常量:返回常量值(浮点 CDefine → double 常量,整数 → i32;否则 double 函数里 return 会生成 ret i32 0 报错)。注意同名变量优先——当前作用域已存在变量时不得被 CDefine 表条目遮蔽(vpi stub 注入会把依赖模块的 CDefine 注册进导入方表,可能误解析局部变量为常量),守卫与下方跨模块 CDefine 查找的 lookup_var 检查一致
cdef_val_d: double = self.Gen.TypeGenCDef.lookup_cdefine_double(nm_id)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
if trans is None or self.Gen.VarGen.lookup_var(trans.SymTab, nm_id) is None:
if self.Gen.TypeGenCDef.is_cdefine_str() != 0:
# 字符串 CDefine(如 ZLIB_VERSION: t.CDefine = "1.3.2"):
# 返回 global string(i8*),供 return ZLIB_VERSION 等使用。
cdef_sv: str = self.Gen.TypeGenCDef.lookup_cdefine_string(nm_id)
if cdef_sv is not None:
return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, cdef_sv, trans)
self.Gen.TypeGenErr.fatal_error(node, f"CDefine 字符串值缺失: {nm_id}", "InternalCompilerError")
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
return llvmlite.ConstFloat(pool, llvmlite.Double(pool), cdef_val_d)
# 整数 CDefine:重新查表取 64 位全精度值(double 往返会丢精度,
# 9223372036854775807 → 9.223372036854776e18 舍入错位,生成错常量)
cdef_val_c: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(nm_id)
# 使用处宽度(经 translate_value 的 target_ty 下传;站点种类由
# use_ctx 携带,仅供报文使用):
# 更宽 ⇒ 就地升宽物化;更窄 ⇒ fail-fast;取不到 ⇒ 既有默认 32/64。
uw_val: llvmlite.Value* = self.Gen.ExprGenCdef.cdefine_use_width_value(pool, node, nm_id, cdef_val_c, target_ty, use_ctx)
if uw_val is not None:
return uw_val
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, cdef_val_c)
# 本地表未找到:尝试跨模块 .vpi 查找(保证多模块查表存表一致)
# 仅对符合 CDefine 命名约定(ALL_CAPS)的名字触发跨模块查找,
# 避免对普通变量名(mb/name/ptr/pool/self 等)误触发,造成严重性能损耗
if self.Gen.ExprGenCdef._is_cdefine_name_pattern(nm_id) != 0:
if trans is not None:
# ALL_CAPS 也可能是普通变量(如 Gargantua 的
# CAM/FWD/RIGHT/UP: V3|t.CPtr 模块级全局)。若当前作用域
# (含模块级 Root,lookup_var 逐级上溯)已存在同名变量,
# 优先按变量处理,跳过跨模块 CDefine 查找——否则 ALL_CAPS
# 命中 star-import 模块(from stdint import *)→ 无谓读源
# 文件,且多 includes 目录下还可能触发 XMOD-CD 误报
# (ExprGenCdef._build_source_path_from_sha1 拼错路径)。
if self.Gen.VarGen.lookup_var(trans.SymTab, nm_id) is None:
cdef_val: int64 = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, nm_id, trans._from_imports, trans._imported_modules, 0, node)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
if self.Gen.TypeGenCDef.is_cdefine_str() != 0:
cdef_sv2: str = self.Gen.TypeGenCDef.lookup_cdefine_string(nm_id)
if cdef_sv2 is not None:
return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, cdef_sv2, trans)
self.Gen.TypeGenErr.fatal_error(node, f"CDefine 字符串值缺失: {nm_id}", "InternalCompilerError")
# 跨模块浮点 CDefine 已缓存为 double(is_cdefine_double),
# 需返回浮点常量而非截断的整数
dchk_nm: double = self.Gen.TypeGenCDef.lookup_cdefine_double(nm_id)
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
return llvmlite.ConstFloat(pool, llvmlite.Double(pool), dchk_nm)
# 使用处宽度同口径(跨模块 CDefine)
uw_x: llvmlite.Value* = self.Gen.ExprGenCdef.cdefine_use_width_value(pool, node, nm_id, cdef_val, target_ty, use_ctx)
if uw_x is not None:
return uw_x
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, cdef_val)
# CDefine 查找失败:from-import 的模块级全局变量(如
# from Phase1 import PYI_BUF_SIZE;PYI_BUF_SIZE: sizet = 262144 是
# i64 全局、非宏,语义同 C 的 extern size_t PYI_BUF_SIZE)。from-import
# 确认名称确实来自某模块后,按后缀匹配查找该全局并加载。
if trans is not None and trans._from_imports is not None:
fi_mod_raw: str = self.Gen.ImportsGen.lookup_from_import(trans._from_imports, nm_id, 0)
if fi_mod_raw is not None:
# 带源模块 SHA1 前缀精确查找,避免多模块同名全局后缀匹配串扰。
# fi_mod_raw 非 NUL 结尾,先截断再查 SHA1(否则整串查不到退化裸名)
fi_mod_cut: str = self._cut_from_import_mod(pool, fi_mod_raw)
if fi_mod_cut is None:
fi_mod_cut = fi_mod_raw
fi_gv_q: str = self.Gen.FuncGenBase._mangle_name_with_sha1(StubMergerStore.LookupSha1ByModName(fi_mod_cut), nm_id)
fi_gv: llvmlite.GlobalVariable* = self.Gen.ExprGenAttr.find_global_in_module(trans.Module, fi_gv_q)
if fi_gv is not None and fi_gv.Ty is not None:
fi_ref: llvmlite.Value* = self.Gen.ExprGenAttr.make_global_ref(pool, fi_gv.Name, fi_gv.Ty)
if fi_ref is not None and fi_ref.Ty is not None:
fi_pty: llvmlite.LLVMType* = fi_ref.Ty.Pointee
if fi_pty is not None:
match fi_pty:
case llvmlite.LLVMType.Array(_, _):
# 数组全局按 C 语义退化为基址(对齐 L3383-3388)
return fi_ref
case _:
pass
return llvmlite.build_load(builder, fi_pty, fi_ref)
# global 变量:从模块作用域查找(global 声明优先级最高)
if trans is not None:
if GenApi.is_global_name(trans, nm_id) != 0:
mod_alloca: llvmlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, nm_id)
if mod_alloca is not None:
load_ty: llvmlite.LLVMType* = None
if mod_alloca.Ty is not None:
load_ty = mod_alloca.Ty.Pointee
if load_ty is None:
# fail-fast:global 变量存储类型缺失,立即终止编译(暴露上游根因)
self.Gen.TypeGenErr.fatal_error(node, f"变量 '{nm_id}' 存储类型缺失", "TypeError")
# 全局数组(如 pyzlib.pyzlib_error_msg: CArray[char, 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
# t.CVolatile 全局:load 带 volatile 限定符(禁优化器消除重复读)
vol_g: int = 0
if trans is not None:
ve_g: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_id)
vol_g = self.Gen.VarGen.var_is_volatile(ve_g)
return llvmlite.build_load(builder, load_ty, mod_alloca, vol_g)
# nonlocal 变量:通过闭包 env 访问
if GenApi.is_nonlocal_name(trans, nm_id) != 0:
return self.Gen.StmtGenDecl.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* = self.Gen.VarGen.lookup_var(trans.SymTab, nm_id)
if alloca is not None:
load_ty: llvmlite.LLVMType* = None
if alloca.Ty is not None:
load_ty = alloca.Ty.Pointee
if load_ty is None:
# fail-fast:局部变量存储类型缺失,立即终止编译(暴露上游根因)
self.Gen.TypeGenErr.fatal_error(node, f"变量 '{nm_id}' 存储类型缺失", "TypeError")
# 结构体值变量(alloca 类型为 Struct,如 `addr: SocketAddr`):
# 右值默认按"对象即指针"语义返回其地址(alloca 指针),而非加载结构体值——
# 加载值后无法参与 ptrtoint/方法接收者等地址相关操作
# (会造成 `ptrtoint %Struct … to i64` 无效指令,llc invalid cast opcode)。
# 使用处**明确要求同型结构体值**时才 load 出值,见下方 Struct 分支。
match load_ty:
case llvmlite.LLVMType.Struct(_, _, _):
# 结构体值局部变量的右值默认返回 alloca 地址("对象即指针"语义;
# 字段访问基座 / c.Addr 取址 / 方法接收者都在**别的**路径直接取地址,
# 不经此处)。但使用处**明确要求同型的非指针结构体值**时必须像标量
# 一样 load 出值,否则调用方 coerce_to_type 会以
# 「非基本类型的值/指针隐式转换已禁止(构造物化除外)」拒绝:
# 现场 includes/socket.vp:121 `return addr` —— _MakeAddr 的返回类型
# 是结构体值 SocketAddr,而 addr 是结构体值局部变量,
# 源 %"…SocketAddr"* → 目标 %"…SocketAddr"。
# 判据用 _same_agg_ty(同一非基本类型),保证 load 后类型与使用处
# 完全一致;target_ty 为指针时(裸类形参 = Ptr(struct))不命中,
# 故 point_sum(p) 这类实参传址行为不变。
if target_ty is not None:
if self.Gen.ExprGenBase._same_agg_ty(pool, load_ty, target_ty) != 0:
vol_s: int = 0
if trans is not None:
ve_s: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_id)
vol_s = self.Gen.VarGen.var_is_volatile(ve_s)
return llvmlite.build_load(builder, load_ty, alloca, vol_s)
return alloca
case llvmlite.LLVMType.Array(_, _):
# 数组变量按 C 语义退化为指针(返回数组基址),而非整体
# load 数组值——如 printf("%s", str_arr) 应传数组首地址,
# load [N x i8] 值传给 %s 会读到错误数据崩溃
# (ClassArrayTest TestCharArray 运行时崩溃根因)。
return alloca
# t.CVolatile 局部变量:load 带 volatile 限定符
vol_l: int = 0
if trans is not None:
ve_l: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_id)
vol_l = self.Gen.VarGen.var_is_volatile(ve_l)
return llvmlite.build_load(builder, load_ty, alloca, vol_l)
# REPL 模式(M2)会话变量:局部表未命中的名字若是会话变量,从 ctx 槽按类型读回。
# 位置在两处:① 局部查找之后(片段自己声明的局部名/参数优先,走原路径);
# ② 模块别名/模块函数引用之前(会话名不得被 printf 等本模块 declare 抢走)。
# 非 REPL(trans.ReplCtx is None)时 repl_try_read 立即返回 None ⇒ 原路径逐字不变。
if trans is not None and trans.ReplCtx is not None:
sess_val: llvmlite.Value* = self.Gen.VarGen.repl_try_read(trans, nm_id, node)
if sess_val is not None:
return sess_val
# 模块别名检查:SymTab 查找失败后才检查
# 如果 nm_id 是已导入模块名(如 win32file, fileio)且未被声明为局部变量,
# 返回 None 让上层处理(用于属性访问 win32file.CreateFileA)
if trans is not None and trans._imported_modules is not None:
if self.Gen.ImportsGen.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* = self.Gen.CallGenBase.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* = llvmlite.function_get_ret_ty(fn_ref)
if fn_ret_ty is None:
# fail-fast:函数返回类型缺失,无法构造函数指针(暴露上游根因)
self.Gen.TypeGenErr.fatal_error(node, f"函数 '{nm_id}' 返回类型缺失,无法构造函数指针", "TypeError")
fn_pp: llvmlite.Param* = llvmlite.function_get_param_head(fn_ref)
fn_first_node: llvmlite.ParamNode* = None
fn_prev_node: llvmlite.ParamNode* = None
fn_pcount: int = 0
while fn_pp is not None:
fn_pty: llvmlite.LLVMType* = llvmlite.param_get_ty(fn_pp)
fn_pn: llvmlite.ParamNode* = 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* = 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* = 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 self.Gen.ClassGenVTable._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* = 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 = self.Gen.ImportsGen.lookup_from_import(trans._from_imports, nm_id, 0)
if fi_mod is not None:
# 排除已注册类名/枚举名:这些是类型,不作为全局变量值读取
fi_is_class: int = 0
if self.Gen.StructGen.get_struct_type(nm_id) is not None:
fi_is_class = 1
# 排除 ALL_CAPS CDefine(上方已处理本地+跨模块查找)
if self.Gen.ExprGenCdef._is_cdefine_name_pattern(nm_id) != 0:
fi_is_class = 1
if fi_is_class == 0:
fi_gname: str = nm_id
fi_orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, nm_id)
if fi_orig is not None:
og_len_fi: sizet = 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 未注入的上游根因)
# 带源模块 SHA1 前缀精确查找(避免多模块同名全局后缀匹配串扰);
# fi_mod 非 NUL 结尾,先截断再查 SHA1(否则退化裸名 → undefined)
fi_mod_cut2: str = self._cut_from_import_mod(pool, fi_mod)
if fi_mod_cut2 is None:
fi_mod_cut2 = fi_mod
gv_fi_q: str = self.Gen.FuncGenBase._mangle_name_with_sha1(StubMergerStore.LookupSha1ByModName(fi_mod_cut2), fi_gname)
gv_fi: llvmlite.GlobalVariable* = self.Gen.ExprGenAttr.find_global_in_module(trans.Module, gv_fi_q)
ref_fi: llvmlite.Value* = None
if gv_fi is not None and gv_fi.Ty is not None:
ref_fi = self.Gen.ExprGenAttr.make_global_ref(pool, gv_fi.Name, gv_fi.Ty)
else:
# 依赖模块 vpi/stub 未注入:Phase A 按文件序翻译时依赖模块
# (如 lexer 先于 ast.tokens)可能晚于本文件,其 .vpi 尚未生成
# (LoadDepVpiIntoModule 无从注入)→ 返回 None,不在此
# fail-fast——否则清缓存后 lexer 先于 tokens 翻译必然误报
# "stub 未注入"。
return None
if ref_fi is not None and ref_fi.Ty is not None:
lty_fi: llvmlite.LLVMType* = 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
self.Gen.TypeGenErr.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*,
pool: memhub.MemBuddy*,
lm_obj: llvmlite.Value*,
idx_val: llvmlite.Value*,
node: ast.AST* = None) -> llvmlite.Value*:
"""内联生成 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* = llvmlite.Int64(pool)
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
# 加载 __data__ (偏移 0, index 0)
g_idx0: llvmlite.Value* = llvmlite.const_int64(pool, 0)
g_dpp: llvmlite.Value* = llvmlite.build_gep(builder, i64_ty, lm_obj, g_idx0)
if g_dpp is None:
return None
g_data: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty, g_dpp)
# 加载 __elem_size__ (偏移 32, index 4)
g_idx4: llvmlite.Value* = llvmlite.const_int64(pool, 4)
g_epp: llvmlite.Value* = llvmlite.build_gep(builder, i64_ty, lm_obj, g_idx4)
if g_epp is None:
return None
g_esize: llvmlite.Value* = 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* = self.coerce_to_type(builder, idx_val, i64_ty, node)
if g_idx_i64 is None:
return None
g_offset: llvmlite.Value* = 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*,
struct_name: str) -> llvmlite.LLVMType*:
"""从 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* = 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[rawptr[7565e081d88517ac.AST]] / list[sha1.Foo|t.CPtr]):
# 元素槽存对象指针(8 字节)。从元素类型文本还原对象指针 LLVM 类型
# Ptr(%Struct),使 list.get/__getitem__/pop 内联返回值的 LLVM 类型
# 携带元素原型类——链式方法调用(mod.children.get(0).kind())的接收者
# 才能经 LLVM 类型解析出类名 AST(T29 修复);否则统一 Ptr(i8) 丢失
# 类名,.kind() 被当未绑定外部函数(AstTest NewAstTest fatal: 'kind'
# 根因:list 元素类名没有沿 get(0) 调用结果值类型传播到后续分派)。
elem_obj_ty_l: llvmlite.LLVMType* = \
self._list_elem_obj_ty_from_struct_name(pool, struct_name)
if elem_obj_ty_l is not None:
return elem_obj_ty_l
# 结构体未注册(依赖模块未就绪等):回退 Ptr(i8)(8 字节槽位宽一致,
# load 值正确),由 AnnotClassName/方法分派回退机制保证语义。
# 禁止 fail-fast(误杀合法自定义类元素列表的访问)。
return llvmlite.Ptr(pool, i8_ty_d)
# ============================================================
# _list_elem_obj_ty_from_struct_name — 从 list 特化类型名还原元素对象指针类型
#
# struct_name 形如 "{_list_sha1}.list[rawptr[{sha1}.AST]]" 或
# "{_list_sha1}.list[{sha1}.AST|t.CPtr]"(联合元素)。取 '[' 与最后 ']'
# 之间的元素描述文本:剥 rawptr[...] 包装、按 '|' 联合分段、去首尾空白,
# 逐段按已注册结构体还原(元素类带模块 SHA1 前缀时 find_struct 走
# find_struct_by_module 精确匹配,规避跨模块同名歧义);命中返回
# llvmlite.Ptr(entry.Ty)(list 元素槽 8 字节存对象指针)。全部未命中
# 返回 None(调用方回退 Ptr(i8) 旧行为,不影响已支持的内置元素)。
# ============================================================
def _list_elem_obj_ty_from_struct_name(self, pool: memhub.MemBuddy*,
struct_name: str) -> llvmlite.LLVMType*:
"""从 list 特化类型名还原元素对象指针 LLVM 类型,失败返回 None"""
if pool is None or struct_name is None:
return None
lb_e: str = string.strchr(struct_name, '[')
if lb_e is None:
return None
rb_e: str = string.strrchr(struct_name, ']')
if rb_e is None:
return None
elen_e: sizet = sizet(uint64(rb_e) - uint64(lb_e)) - 1
if elen_e == 0 or elen_e > 512:
return None
elem_buf_e: str = pool.alloc(elen_e + 1)
if elem_buf_e is None:
return None
string.strncpy(elem_buf_e, lb_e + 1, elen_e)
elem_buf_e[elen_e] = '\0'
# 剥 rawptr[...] 包装(元素声明为对象指针,如 rawptr[sha1.AST])
cur_e: str = elem_buf_e
if string.strncmp(cur_e, "rawptr[", 7) == 0:
cur_e = cur_e + 7
# 按 '|' 联合分段(A|B),段内去首尾空白/尾部 ']'/引号;段文本直接
# 交给 find_struct(内置名 CPtr/str/int 等不注册 struct → 天然返回
# None 跳过;带 "sha1.Class" 前缀走模块归属精确匹配)
total_e: sizet = string.strlen(cur_e)
seg_s_e: sizet = 0
i_e: sizet = 0
while i_e <= total_e:
hit_e: int = 0
if i_e == total_e:
hit_e = 1
elif cur_e[i_e] == '|':
hit_e = 1
if hit_e == 0:
i_e += 1
continue
b_e: sizet = i_e
a_e: sizet = seg_s_e
# 尾部另需剥 '*':实参归一后元素文本是 `T*` 形(如 "{sha}.AST*",
# 见 TypeGenResolve._norm_type_arg_name 的指针书写归一),而
# find_struct 只认类名本身;不剥则对象元素还原失败、静默退回 Ptr(i8)
# 丢元素类名(链式 .get(0).kind() 分派回退根因)。
while b_e > a_e and (cur_e[b_e - 1] == ' ' or cur_e[b_e - 1] == ']' \
or cur_e[b_e - 1] == '\'' or cur_e[b_e - 1] == '"' \
or cur_e[b_e - 1] == '*'):
b_e -= 1
while a_e < b_e and (cur_e[a_e] == ' ' or cur_e[a_e] == '\'' \
or cur_e[a_e] == '"'):
a_e += 1
if b_e > a_e:
seg_len_e: sizet = b_e - a_e
if seg_len_e < 256:
seg_buf_e: str = pool.alloc(seg_len_e + 1)
if seg_buf_e is not None:
string.strncpy(seg_buf_e, cur_e + a_e, seg_len_e)
seg_buf_e[seg_len_e] = '\0'
s_entry_e: GenTypes.StructEntry* = \
self.Gen.StructGen.find_struct(seg_buf_e)
if s_entry_e is not None and s_entry_e.Ty is not None:
return llvmlite.Ptr(pool, s_entry_e.Ty)
if i_e == total_e:
break
seg_s_e = i_e + 1
i_e += 1
return None
# ============================================================
# list_setitem_inline — 内联生成 list[T] __setitem__ 逻辑
#
# 直接存储 rhs_val 到元素地址 (coerce 为 i8* 后 store)
# ============================================================
def list_setitem_inline(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
lm_obj: llvmlite.Value*,
idx_val: llvmlite.Value*,
rhs_val: llvmlite.Value*,
node: ast.AST* = 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* = llvmlite.Int64(pool)
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
# 加载 __data__ (偏移 0, index 0)
s_idx0: llvmlite.Value* = llvmlite.const_int64(pool, 0)
s_dpp: llvmlite.Value* = llvmlite.build_gep(builder, i64_ty, lm_obj, s_idx0)
if s_dpp is None:
return 0
s_data: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty, s_dpp)
# 加载 __elem_size__ (偏移 32, index 4)
s_idx4: llvmlite.Value* = llvmlite.const_int64(pool, 4)
s_epp: llvmlite.Value* = llvmlite.build_gep(builder, i64_ty, lm_obj, s_idx4)
if s_epp is None:
return 0
s_esize: llvmlite.Value* = 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* = self.coerce_to_type(builder, idx_val, i64_ty, node)
if s_idx_i64 is None:
return 0
s_offset: llvmlite.Value* = llvmlite.build_mul(builder, s_idx_i64, s_esize)
if s_offset is None:
return 0
s_addr: llvmlite.Value* = 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* = self.Gen.StructGen.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* = 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* = self.coerce_to_type(builder, rhs_val, s_elem_ty, node)
if s_val_ptr is None:
return 0
s_ep: llvmlite.Value* = 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*,
trans: HT.Translator*) -> llvmlite.Value*:
"""检查 Subscript 节点是否是 list[T] 类型,返回 list 对象指针或 None"""
if node is None or trans is None:
return None
sub: ast.Subscript* = ast.Subscript*(node)
if sub is None or sub.value is None:
return None
if sub.value.kind() != ast.ASTKind.Name:
return None
nm: ast.Name* = ast.Name*(sub.value)
if nm is None or nm.id is None:
return None
alloca: llvmlite.Value* = self.Gen.VarGen.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* = alloca.Ty.Pointee
if pointee is None:
return None
# 检查 pointee 是否是 Ptr(Struct)
inner_ty: llvmlite.LLVMType* = 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* = trans._cur_builder
return llvmlite.build_load(builder, pointee, alloca)
# _nonlocal_subscript_ptr:nonlocal 数组/指针的下标元素指针——嵌套(提升)函数体内访问 nonlocal 变量时,其实际存储是运行时共享堆(env[ni] 存堆地址),不能 lookup_var 找外层函数 alloca(其 SSA 编号属外层函数,当前函数引用会错乱崩溃)。正确路径:从 env 读堆地址(_load_raw_var_ptr)→ bitcast → GEP
def _nonlocal_subscript_ptr(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
trans: HT.Translator*,
nm_id: str,
idx_val: llvmlite.Value*) -> llvmlite.Value*:
"""nonlocal 数组/指针下标:从 env 读堆地址 → bitcast → GEP,返回元素指针"""
if trans is None or nm_id is None or idx_val is None:
return None
nl_ty: llvmlite.LLVMType* = GenApi.get_nonlocal_type(trans, nm_id)
if nl_ty is None:
return None
raw_nl: llvmlite.Value* = self.Gen.StmtGenDecl._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* = 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* = 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* = 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*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""翻译下标表达式,返回加载的元素值"""
if node is None or builder is None:
return None
sub: ast.Subscript* = ast.Subscript*(node)
if sub is None:
return None
# 切片形式 a[start:end](如 path[n:]):返回 i8* 基指针 + start 偏移。
# 此前 Slice AST 被当作单索引 translate_value → 无 Slice 分支返回 None →
# 作为函数实参时整个参数被丢弃(spawn_elf 的
# create_process(path[n:], entry) 丢 name → entry 错位成第一参数 →
# p.entry 垃圾 → 用户进程 iretq 到垃圾地址 → PF)。
if sub.slice is not None and sub.slice.kind() == ast.ASTKind.Slice:
slc: rawptr[ast.Slice] = rawptr[ast.Slice](sub.slice)
base_v: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, sub.value, None, 0, trans)
if base_v is None: return None
if slc.lower is None: return base_v
start_v: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, slc.lower, None, 0, trans)
if start_v is None: return None
i64_sc: llvmlite.LLVMType* = llvmlite.Int64(pool)
start_w: llvmlite.Value* = start_v
if start_v.Ty is not None:
bt_sc: int = self.Gen.ExprGenBase.get_llvm_type_bits(start_v.Ty)
if bt_sc != 0 and bt_sc < 64:
start_w = llvmlite.build_sext(builder, start_v, i64_sc)
if start_w is None:
return None
i8_sc: llvmlite.LLVMType* = llvmlite.Int8(pool)
return llvmlite.build_gep(builder, i8_sc, base_v, start_w)
# 翻译索引
idx_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, sub.slice, None, 0, trans)
if idx_val is None: return None
# 嵌套下标读取: a[i][j] —— 递归取内层指针后 load
# (与 get_subscript_ptr 嵌套分支对称;如 gfx.vp 的
# a.m[i][k] * b.m[k][j],内层 translate_value 会把行加载为
# Array 值(非指针)→ 通用路径失败 → "rhs 翻译失败")
if sub.value is not None and sub.value.kind() == ast.ASTKind.Subscript:
inner_ptr_r: llvmlite.Value* = self.get_subscript_ptr(builder, pool, mod, sub.value, trans)
if inner_ptr_r is None or inner_ptr_r.Ty is None: return None
inner_elem_r: llvmlite.LLVMType* = inner_ptr_r.Ty.Pointee
if inner_elem_r is None: return None
match inner_elem_r:
case llvmlite.LLVMType.Array(arr_ie_r, _):
elem_ptr_r: llvmlite.Value* = llvmlite.build_gep_array(builder, inner_elem_r, arr_ie_r, inner_ptr_r, self._zext_index_to_i32(builder, idx_val))
if elem_ptr_r is not None:
return llvmlite.build_load(builder, arr_ie_r, elem_ptr_r)
return None
case _:
elem_ptr_r2: llvmlite.Value* = llvmlite.build_gep(builder, inner_elem_r, inner_ptr_r, idx_val)
if elem_ptr_r2 is not None:
return llvmlite.build_load(builder, inner_elem_r, elem_ptr_r2)
return None
# 如果 value 是 Name,尝试从 alloca 类型推断
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(sub.value)
if nm.id is not None and trans is not None:
# rawptr[t.CVolatile | T] 变量(volatile 指针指向物):本分支内
# 的**元素 load**(p[i] 解引用)带 volatile;仅读取指针值自身的
# load(alloca → 指针值)不带(那是指针变量而非所指物)。
vol_sub_rd: int = self.Gen.VarGen.var_pointee_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
# nonlocal 数组/指针:从 env 读共享堆地址(提升函数内不能
# lookup_var 外层 alloca——SSA 编号跨函数错乱崩溃)
# 读路径:元素指针 load 出元素值
if GenApi.is_nonlocal_name(trans, nm.id) != 0:
nl_ptr_r: llvmlite.Value* = 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* = 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* = self.Gen.VarGen.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* = 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* = 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* = self.list_getitem_inline(builder, pool, alloca, idx_val, node)
if elem_addr_v is not None:
elem_ty_v: llvmlite.LLVMType* = self._list_elem_type_from_name(pool, lsn_v)
if elem_ty_v is not None:
elem_ptr_ty_v: llvmlite.LLVMType* = llvmlite.Ptr(pool, elem_ty_v)
casted_v: llvmlite.Value* = \
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* = llvmlite.build_load(builder, pointee, alloca)
if ptr_val_list is not None:
elem_addr_list: llvmlite.Value* = 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* = self._list_elem_type_from_name(pool, list_struct_name)
if elem_ty_list is not None:
elem_ptr_ty_list: llvmlite.LLVMType* = llvmlite.Ptr(pool, elem_ty_list)
casted_addr_list: llvmlite.Value* = 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* = llvmlite.build_load(builder, pointee, alloca)
if ptr_val_pp is not None:
elem_ptr_pp: llvmlite.Value* = llvmlite.build_gep(builder, inner_ty, ptr_val_pp, idx_val)
if elem_ptr_pp is not None:
# volatile 指针指向物:解引用 load 带 volatile
return llvmlite.build_load(builder, inner_ty, elem_ptr_pp, vol_sub_rd)
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 self.Gen.CallGenBase.find_func_in_module(mod, gi_lookup_rd) is not None:
obj_ptr_rd: llvmlite.Value* = llvmlite.build_load(builder, pointee, alloca)
if not (obj_ptr_rd is None):
arg_vals_rd_p: sizet* = pool.alloc(8)
if not (arg_vals_rd_p is None):
arg_vals_rd_p[0] = sizet(idx_val)
ret_rd_p: llvmlite.Value* = self.Gen.CallGenCall._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* = llvmlite.build_load(builder, pointee, alloca)
if ptr_val_rd_g is not None:
elem_rd_g: llvmlite.Value* = llvmlite.build_gep(builder, inner_ty, ptr_val_rd_g, idx_val)
if elem_rd_g is not None:
# volatile 指针指向物:解引用 load 带 volatile
return llvmlite.build_load(builder, inner_ty, elem_rd_g, vol_sub_rd)
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: GenTypes.VarEntry* = \
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
if self.Gen.VarGen.var_is_ptr_element(ve_pe_ts) != 0:
# 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* = llvmlite.build_load(builder, pointee, alloca)
if ptr_val_pe_ts is not None:
elem_ptr_ts: llvmlite.Value* = llvmlite.build_gep(builder, pointee, ptr_val_pe_ts, idx_val)
if elem_ptr_ts is not None:
# volatile 指针指向物:解引用 load 带 volatile
return llvmlite.build_load(builder, pointee, elem_ptr_ts, vol_sub_rd)
return None
# 普通指针遍历: 先 load 指针值,再 GEP
ptr_val: llvmlite.Value* = llvmlite.build_load(builder, pointee, alloca)
if ptr_val is not None:
elem_ptr2: llvmlite.Value* = llvmlite.build_gep(builder, inner_ty, ptr_val, idx_val)
if elem_ptr2 is not None:
# volatile 指针指向物:解引用 load 带 volatile
return llvmlite.build_load(builder, inner_ty, elem_ptr2, vol_sub_rd)
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: GenTypes.VarEntry* = \
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
if self.Gen.VarGen.var_is_ptr_element(ve_pe_rd) != 0:
match pointee:
case llvmlite.LLVMType.Int(bits_rd):
if bits_rd == 8:
i8_ptr_rd: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
elem_ptr_rd: llvmlite.Value* = 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 = self.Gen.StructGen.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 self.Gen.CallGenBase.find_func_in_module(mod, gi_lookup_sub) is not None:
obj_val_sub: llvmlite.Value* = llvmlite.build_load(builder, pointee, alloca)
if not (obj_val_sub is None):
arg_vals_sub: sizet* = pool.alloc(8)
if not (arg_vals_sub is None):
arg_vals_sub[0] = sizet(idx_val)
ret_sub: llvmlite.Value* = self.Gen.CallGenCall._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* = llvmlite.build_load(builder, pointee, alloca)
if ptr_val_sub_g is not None:
elem_sub_g: llvmlite.Value* = llvmlite.build_gep(builder, pointee, ptr_val_sub_g, idx_val)
if elem_sub_g is not None:
# volatile 指针指向物:解引用 load 带 volatile
return llvmlite.build_load(builder, pointee, elem_sub_g, vol_sub_rd)
return None
pass
# 通用路径:翻译 value 获取指针
ptr_val: llvmlite.Value* = self.Gen.ExprGen.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:
# 通用路径基址(如 self.ptr / 复杂表达式):仅当基址可解析到
# VarEntry/FieldEntry 且其为 rawptr[t.CVolatile|T] 时,元素 load 带 volatile;
# 其余基址解析返回 0(明确不支持,见限制清单)。守卫为 0 时文本不变。
vol_sub_gen: int = self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, sub.value)
elem_ty2: llvmlite.LLVMType* = 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* = 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, vol_sub_gen)
return None
case _:
pass
# 64-bit scalar pointer fields (e.g. `sizet*`, `uint64*`, written as
# `t.CUInt64T | t.CPtr`) keep the declared element type here: the slot is
# a u64, so it is read as i64 -- same element type the write path uses
# (see get_subscript_ptr). Loading it as i8* fabricated a pointer value
# out of an integer slot and forced the caller through an implicit
# ptrtoint / inttoptr, which the language forbids (it also made
# `MemBuddy.free_lists[i]` mis-type its element). Sources that store or
# read a pointer through such a slot must cast explicitly.
# 自定义结构体 (如 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 self.Gen.CallGenBase.find_func_in_module(mod, gi_lookup_gen) is not None:
arg_vals_rd_g: sizet* = pool.alloc(8)
if not (arg_vals_rd_g is None):
arg_vals_rd_g[0] = sizet(idx_val)
ret_rd_g: llvmlite.Value* = self.Gen.CallGenCall._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* = 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, vol_sub_gen)
return None
# 指针类型: 单索引 GEP + load
elem_ptr4: llvmlite.Value* = 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, vol_sub_gen)
return None
# ============================================================
# 获取下标表达式的元素指针(不加载值,用于赋值)
# ============================================================
def _annot_is_64bit_scalar(self, annot: str) -> int:
"""注解是否为 64 位标量 | t.CPtr 联合(AnnotClassName 为提取出的
非 CPtr 侧类型名,如 int64|t.CPtr → "CInt64T")。此类联合的
8 字节单元格存 i64 标量,下标元素类型应为 Int64 而非指针。"""
if annot is None: return 0
if string.strstr(annot, "CInt64T") is not None: return 1
if string.strstr(annot, "CUInt64T") is not None: return 1
if string.strstr(annot, "CSizeT") is not None: return 1
return 0
# ============================================================
# subscript_base_store_kind - 下标基座的**存储形态**(只读,不生成 IR)
#
# 用于区分「数组槽」与「指针所指对象」,只有前者才允许"整对象赋值":
# 1 = 数组存储:t.CArray[T,N] / 内建 Array 形态。元素槽按 sizeof(T) 布局,
# `v[i] = <指向 T 的指针>`(如构造函数返回的临时对象地址)是合法的
# 整对象复制,不拦。
# 0 = 指针存储:rawptr[T] 形态(基座元素类型是 Ptr(T),T 为对象)。
# 元素即所指对象,`p[i] = <指向 T 的指针>` 会静默整对象拷贝
# (写 sizeof(T) 字节而非 8 字节指针)——可能写穿按 8 字节排布的
# 缓冲/空闲链表,由调用方 fail-fast。
# -1 = 无法判定(基座不是可静态定型的 Name / 单层 Attribute):不作为判据,
# 调用方保持既有行为(不误伤,也不新增静默降级)。
# 分派与 get_subscript_ptr 同源:Name 取变量/全局存储的元素类型,
# Attribute 取字段类型。
# ============================================================
def subscript_base_store_kind(self, pool: memhub.MemBuddy*,
trans: HT.Translator*,
node: ast.AST*) -> int:
"""下标基座存储形态:1=数组 / 0=指针 / -1=无法判定"""
if node is None or trans is None: return -1
sub_bk: ast.Subscript* = ast.Subscript*(node)
if sub_bk is None or sub_bk.value is None: return -1
base_ty_bk: llvmlite.LLVMType* = None
if sub_bk.value.kind() == ast.ASTKind.Name:
nm_bk: ast.Name* = ast.Name*(sub_bk.value)
if nm_bk.id is None: return -1
alloca_bk: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm_bk.id)
if alloca_bk is None:
alloca_bk = self.Gen.VarGen.lookup_module_var(trans.SymTab, nm_bk.id)
if alloca_bk is None or alloca_bk.Ty is None: return -1
if self.is_ptr_type(alloca_bk.Ty) == 0: return -1
base_ty_bk = alloca_bk.Ty.Pointee
elif sub_bk.value.kind() == ast.ASTKind.Attribute:
# 单层 obj.field(如 self.arr):非 fatal 只读解析字段类型
fe_bk: GenTypes.FieldEntry* = self.Gen.ExprGenAttr._resolve_attr_field_entry(pool, trans, sub_bk.value)
if fe_bk is None or fe_bk.Ty is None: return -1
base_ty_bk = fe_bk.Ty
else:
return -1
if base_ty_bk is None: return -1
# 基座自身是数组值 → 数组存储
if self.is_array_type(base_ty_bk) != 0: return 1
if self.is_ptr_type(base_ty_bk) != 0:
inner_bk: llvmlite.LLVMType* = base_ty_bk.Pointee
if inner_bk is None: return -1
# Ptr(Array) → t.CArray[T,N] 局部/字段(数组存储)
if self.is_array_type(inner_bk) != 0: return 1
# Ptr(Struct) → rawptr[T](指针存储)
if inner_bk.kind() == llvmlite.LLVMType.Struct: return 0
return -1
def get_subscript_ptr(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
node: ast.AST*,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""获取下标表达式的元素指针(用于赋值 lhs)"""
if node is None or builder is None: return None
sub: ast.Subscript* = ast.Subscript*(node)
if sub is None: return None
# 翻译索引
idx_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, sub.slice, None, 0, trans)
if idx_val is None: return None
# 嵌套下标: a[i][j] = v —— 递归解析内层下标指针后 GEP
# (如 gfx.vp Mat4.m: t.CArray[t.CArray[float, 4], 4] 的 m.m[0][0] = 1;
# 外层 Subscript 的 value 是内层 Subscript,通用路径 translate_value
# 会把内层元素加载为值(Array 类型)→ is_ptr_type 失败 → 返回 None →
# "subscript ptr is None" fail-fast。递归取内层指针再 GEP 根治。)
if sub.value is not None and sub.value.kind() == ast.ASTKind.Subscript:
inner_ptr: llvmlite.Value* = self.get_subscript_ptr(builder, pool, mod, sub.value, trans)
if inner_ptr is None or inner_ptr.Ty is None: return None
inner_elem: llvmlite.LLVMType* = inner_ptr.Ty.Pointee
if inner_elem is None: return None
match inner_elem:
case llvmlite.LLVMType.Array(arr_ie, _):
return llvmlite.build_gep_array(builder, inner_elem, arr_ie, inner_ptr, self._zext_index_to_i32(builder, idx_val))
case _:
return llvmlite.build_gep(builder, inner_elem, inner_ptr, idx_val)
# 如果 value 是 Name,尝试从 alloca 类型推断
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(sub.value)
if nm.id is not None and trans is not None:
# nonlocal 数组/指针:从 env 读共享堆地址(提升函数内不能
# lookup_var 外层 alloca——SSA 编号跨函数错乱崩溃)
if GenApi.is_nonlocal_name(trans, nm.id) != 0:
return self._nonlocal_subscript_ptr(builder, pool, trans, nm.id, idx_val)
alloca: llvmlite.Value* = self.Gen.VarGen.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* = 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* = 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 self.Gen.CallGenBase.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: GenTypes.VarEntry* = \
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
if self.Gen.VarGen.var_is_ptr_element(ve_pe) != 0:
# 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* = llvmlite.build_load(builder, pointee, alloca)
if ptr_val_pe is not None:
# [FIX 2026-08-30] 64 位标量联合(int64|t.CPtr /
# uint64|t.CPtr / sizet|t.CPtr):8 字节单元格存
# i64 标量(viperlib %n 的 np[0]=write_count 是解引用写入,
# 语义 *np=(i64)x)。GEP 元素必须用 Int64(标量)而非指针,
# 否则元素类型为指针 → i64 存储触发整数隐式转指针
# fail-fast(把解引用写入误判为 int→ptr)。
pe_annot: str = self.Gen.VarGen.var_annot_class_name(ve_pe)
if pe_annot is not None and self._annot_is_64bit_scalar(pe_annot) != 0:
return llvmlite.build_gep(builder, llvmlite.Int64(pool), ptr_val_pe, idx_val)
match pointee:
case llvmlite.LLVMType.Int(bits_pe):
if bits_pe == 8:
i8_ptr_pe: llvmlite.LLVMType* = 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* = llvmlite.build_load(builder, pointee, alloca)
if ptr_val is None:
return None
gep_r: llvmlite.Value* = 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: GenTypes.VarEntry* = \
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
if self.Gen.VarGen.var_is_ptr_element(ve_pe_cs) != 0:
# 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* = llvmlite.build_load(builder, pointee, alloca)
if ptr_val_cs is not None:
match pointee:
case llvmlite.LLVMType.Int(bits_cs):
if bits_cs == 8:
i8_ptr_cs: llvmlite.LLVMType* = 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* = self.Gen.ExprGen.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* = 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 self.Gen.CallGenBase.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