undefined symbol: _op_head 根因:lookup_from_import 返回指向 from_imports 内部的指针(非 NUL 结尾,后随空格分隔的其他条目,如 "ast.tokens TokenType:ast.tokens ...")。ExprGenName 的 from-import 全局查找直接用 该值 LookupSha1ByModName → 整串查不到 → _mangle_name_with_sha1(None) 退化裸名 → 引用 @_op_head 而非 @<tokens_sha1>._op_head → 链接 undefined (tokens 定义端已带 SHA1 前缀)。ExprGenAttr 别名解析同病。 修复:ExprGenName 新增 _cut_from_import_mod(截断到空格/冒号/NUL), 两处 from-import 分支截断后再查 SHA1;ExprGenAttr 别名解析内联截断。 对齐 CallGenBase._lookup_sha1_from_modname_raw / ExprGenCdef 既有截断逻辑。
1204 lines
73 KiB
Plaintext
1204 lines
73 KiB
Plaintext
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.Handles.HandlesFunctions as HandlesFunctions
|
||
import lib.core.StubMerger as StubMerger
|
||
import lib.core.StubMergerStore as StubMergerStore
|
||
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.ExprCallCtor as ExprCallCtor
|
||
|
||
# ============================================================
|
||
# ExprGenAttr - ExprGenMixin 拆分(阶段 D):Attribute/属性访问/全局引用
|
||
#
|
||
# 继承 ExprGenBase(1 级,TPV self 调用仅支持 1 级父类)。
|
||
# 跨 Mixin 调用统一走 HandlesExpr 委托(经 _g_gen)。
|
||
# @t.NoVTable:静态绑定。
|
||
# ============================================================
|
||
@t.NoVTable
|
||
class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||
"""Attribute/属性访问/全局引用"""
|
||
|
||
# ============================================================
|
||
# 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(96)
|
||
if ref_name is None:
|
||
return None
|
||
# LLVM 无引号标识符不能以数字开头:带 SHA1 前缀的全局名("sha1.name")
|
||
# 必命中 needs_quote(数字开头 + '.'),引用必须加引号 @\"sha1.name\",
|
||
# 否则 llc "expected instruction opcode"。对齐函数引用路径
|
||
# (ExprGenName L252-255 的 needs_quote 判断)。
|
||
if HandlesClassDef._vtable_name_needs_quote(name) != 0:
|
||
viperlib.snprintf(ref_name, 96, "@\"%s\"", name)
|
||
else:
|
||
viperlib.snprintf(ref_name, 96, "@%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
|
||
# 别名解析:import lib.core.Gen.Generator as GenGenerator → 查 from_imports
|
||
# 得原始模块名。否则 _lookup_module_sha1("GenGenerator") 查不到(Sha1Store
|
||
# 用原始模块名)→ 退化裸名 → 全局带 SHA1 前缀后 undefined symbol。
|
||
# 别名属性访问(GenGenerator._g_gen = gen)依赖此解析才能命中目标模块定义。
|
||
# 注意:lookup_from_import 返回指针非 NUL 结尾,须截断到空格/冒号/NUL
|
||
# 再查 SHA1(否则整串 "lib.core.Gen.Generator Config:lib... " 查不到)。
|
||
if trans._from_imports is not None:
|
||
orig_mw: str = HandlesImports.lookup_from_import(
|
||
trans._from_imports, mod_path_w, 0)
|
||
if orig_mw is not None:
|
||
oml_a: t.CSizeT = 0
|
||
while orig_mw[oml_a] != ' ' and orig_mw[oml_a] != '\0' and orig_mw[oml_a] != ':':
|
||
oml_a += 1
|
||
if oml_a > 0:
|
||
om_buf_a: str = pool.alloc(oml_a + 1)
|
||
if om_buf_a is not None:
|
||
string.strncpy(om_buf_a, orig_mw, oml_a)
|
||
om_buf_a[oml_a] = '\0'
|
||
mod_path_w = om_buf_a
|
||
# 检查是否是已导入模块
|
||
if HandlesImports.is_module_imported(trans._imported_modules, mod_path_w) == 0:
|
||
return None
|
||
# 在当前模块查找全局变量:优先按"目标模块 SHA1 前缀 + 属性名"精确匹配,
|
||
# 避免多模块同名全局(@<A>._mbuddy / @<B>._mbuddy 同时注入)时裸名后缀
|
||
# 匹配返回错误模块的全局(串扰)。目标 sha1 未知时退化裸名(后缀匹配兜底)
|
||
gv_q: str = HandlesFunctions._mangle_name_with_sha1(
|
||
self._lookup_module_sha1(mod_path_w), at.attr)
|
||
gv: llvmlite.GlobalVariable | t.CPtr = self.find_global_in_module(mod, gv_q)
|
||
if gv is not None and gv.Ty is not None:
|
||
# 用 gv.Name(完整名,带 SHA1 前缀时与定义端一致)构造引用,
|
||
# 避免裸名 @_mbuddy 在多模块各自带前缀定义下 undefined symbol
|
||
return self.make_global_ref(pool, gv.Name, 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)
|
||
# 兜底声明名:t 类型对象(t.py 不编译、无 sha1)保持裸名 linkonce_odr;
|
||
# 其他模块属性带目标模块 SHA1 前缀(对齐定义端 mangling,跨模块同名
|
||
# 全局独立命名,消除 duplicate symbol / 串扰)
|
||
gv_name_ba: str = at.attr
|
||
if is_t_type_obj == 0:
|
||
gv_name_ba = HandlesFunctions._mangle_name_with_sha1(
|
||
self._lookup_module_sha1(mod_path_w), at.attr)
|
||
ext_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gv_name_ba, 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, gv_name_ba, use_ty)
|
||
def _lookup_module_sha1(self, mod_name: str) -> str:
|
||
"""查找模块名对应的 SHA1(委托 StubMergerStore,支持包名回退;
|
||
None=未找到,调用方退化为裸名)"""
|
||
return StubMergerStore.LookupSha1ByModName(mod_name)
|
||
# ============================================================
|
||
# 翻译属性访问 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 = HandlesExpr.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
|
||
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:
|
||
HandlesExpr._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:
|
||
HandlesExpr._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 = HandlesExpr.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 HandlesExpr.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 = HandlesExpr._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 HandlesExpr.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 HandlesExpr._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
|
||
# ALL_CAPS 模块属性但非 CDefine:先检查模块是否定义了同名全局
|
||
# 变量(如 Phase1.PYI_BUF_SIZE: t.CSizeT = 262144 是 i64 全局、
|
||
# 非宏,语义同 C 的 extern size_t PYI_BUF_SIZE)→ 按跨模块全局
|
||
# 引用加载。仅当全局变量也不存在时才 fail-fast(防 llc
|
||
# "use of undefined value")。
|
||
if HandlesExpr._is_cdefine_name_pattern(at.attr) != 0:
|
||
if self.find_global_in_module(mod, at.attr) is None:
|
||
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(带限定模块 SHA1 前缀精确匹配,
|
||
# 避免多模块同名全局后缀匹配串扰;sha1 未知退化裸名)
|
||
gv_nested_q: str = HandlesFunctions._mangle_name_with_sha1(
|
||
self._lookup_module_sha1(qual_buf), at.attr)
|
||
gv_nested: llvmlite.GlobalVariable | t.CPtr = self.find_global_in_module(mod, gv_nested_q)
|
||
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, gv_nested.Name, 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 = HandlesExpr._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 HandlesExpr._const_int_for_cdefine(pool, cdef_val_nested)
|
||
# CDefine 未找到: fail-fast 终止编译(不再创建未定义的全局变量引用)
|
||
# 之前此处创建 @INVALID_HANDLE_VALUE 等前向引用,但全局未定义,
|
||
# 导致 llc 报 "use of undefined value" 错误。
|
||
# 正确做法是报错终止,让开发者修复跨模块 CDefine 查找失败的根本原因。
|
||
if HandlesExpr._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 = HandlesExpr.translate_value(builder, pool, mod, at.value, None, 0, trans)
|
||
else:
|
||
obj_ptr = HandlesExpr.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 = HandlesExpr.translate_value(builder, pool, mod, at.value, None, 0, trans)
|
||
else:
|
||
obj_ptr = HandlesExpr.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 = ExprCallCtor._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")
|
||
# fatal_error 后补显式 return 样板:match 末 arm 块必须 terminated,
|
||
# 否则掉出末尾被函数收尾 fail-fast 误判(块设计约定)
|
||
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 HandlesExpr._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 = HandlesExpr.translate_value(builder, pool, mod, at.value, None, 0, trans)
|
||
else:
|
||
obj_ptr = HandlesExpr.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 = HandlesExpr.translate_value(builder, pool, mod, at.value, None, 0, trans)
|
||
else:
|
||
obj_ptr = HandlesExpr.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")
|
||
# fatal_error 后补显式 return 样板:match 末 arm 块必须 terminated
|
||
return None |