import t, c
from stdint import *
import ast
import memhub
import string
import llvmlite
import stdlib
import stdio
import viperlib
import w32.fileio as fileio
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesVar as HandlesVar
import lib.core.Handles.HandlesExprOps as HandlesExprOps
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
import lib.core.Handles.HandlesType as HandlesType
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.Handles.HandlesEnum as HandlesEnum
import lib.core.Handles.HandlesClassDef as HandlesClassDef
import lib.core.Handles.HandlesImports as HandlesImports
import lib.core.StubMerger as StubMerger
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config


# ============================================================
# HandlesExpr - 表达式处理（Mixin 继承模式）
#
# 工具函数保留为模块级（供外部调用），ExprHandle.HandleValue 提供 trans 接口
# ============================================================


# ============================================================
# 从 AST 节点提取函数名
# ============================================================
def get_func_name(func_node: ast.AST | t.CPtr) -> str:
    """从函数节点提取函数名"""
    if func_node is None:
        return None
    k: int = func_node.kind()
    if k == ast.ASTKind.Name:
        nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(func_node)
        return nm.id
    elif k == ast.ASTKind.Attribute:
        at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(func_node)
        return at.attr
    return None


# ============================================================
# 转义 LLVM IR 字符串字面量
# ============================================================
def escape_llvm_string(pool: memhub.MemBuddy | t.CPtr,
                       src: t.CChar | t.CPtr) -> t.CChar | t.CPtr:
    """将字符串转换为 LLVM IR 字符串字面量 c"...\\00" """
    if src is None:
        return None
    slen: t.CSizeT = string.strlen(src)
    alloc_size: t.CSizeT = 4 * slen + 8
    buf: t.CChar | t.CPtr = pool.alloc(alloc_size)
    if buf is None:
        return None
    pos: t.CSizeT = 0
    buf[pos] = 'c'
    pos += 1
    buf[pos] = '"'
    pos += 1
    for i in range(slen):
        ch: t.CChar = src[i]
        if ch == '\0':
            break
        if ch == '\"' or ch == '\\' or ch < 0x20:
            buf[pos] = '\\'
            pos += 1
            hi: int = (ch >> 4) & 0xF
            lo: int = ch & 0xF
            if hi < 10:
                buf[pos] = '0' + hi
            else:
                buf[pos] = 'A' + (hi - 10)
            pos += 1
            if lo < 10:
                buf[pos] = '0' + lo
            else:
                buf[pos] = 'A' + (lo - 10)
            pos += 1
        else:
            buf[pos] = ch
            pos += 1
    buf[pos] = '\\'
    pos += 1
    buf[pos] = '0'
    pos += 1
    buf[pos] = '0'
    pos += 1
    buf[pos] = '"'
    pos += 1
    buf[pos] = '\0'
    return buf


# ============================================================
# 获取 LLVM 整数类型的位宽
# ============================================================
def get_llvm_type_bits(ty: llvmlite.LLVMType | t.CPtr) -> int:
    """获取 LLVM 整数类型的位宽，非整数类型返回 0"""
    if ty is None:
        return 0
    match ty:
        case llvmlite.LLVMType.Int(bits):
            return bits
        case _:
            return 0


# ============================================================
# 获取 LLVM 浮点类型的位宽
# ============================================================
def get_llvm_float_bits(ty: llvmlite.LLVMType | t.CPtr) -> int:
    """获取 LLVM 浮点类型的位宽，非浮点类型返回 0"""
    if ty is None:
        return 0
    match ty:
        case llvmlite.LLVMType.Float(bits):
            return bits
        case _:
            return 0


# ============================================================
# 检查类型是否为 Ptr（独立函数，避免嵌套 match 的编译器 BUG）
#
# 嵌套 match 的 REnum 检测在宿主编译器中有 BUG：当 match 嵌套在
# 另一个 match 的 case 块中时，内层 match 走非 REnum 路径，
# 导致 case 列表为空。提取为独立函数可规避此问题。
# ============================================================
def is_ptr_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
    """检查 ty 是否是 Ptr 类型，返回 1=是, 0=否"""
    if ty is None:
        return 0
    match ty:
        case llvmlite.LLVMType.Ptr(pointee):
            return 1
        case _:
            return 0


# ============================================================
# 检查类型是否为 Array（独立函数，避免嵌套 match 的编译器 BUG）
# ============================================================
def is_array_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
    """检查 ty 是否是 Array 类型，返回 1=是, 0=否"""
    if ty is None:
        return 0
    match ty:
        case llvmlite.LLVMType.Array(_, _):
            return 1
        case _:
            return 0


# ============================================================
# _get_custom_struct_cls_nm - 检查类型是否为已注册的自定义结构体
#
# 处理两种情况:
#   - ty 直接是 Struct → 检查是否注册
#   - ty 是 Ptr(Struct) → 检查 pointee 是否注册
#
# 返回类名字符串或 None。用于 subscript 操作中判断是否需要
# 转发到 __getitem__/__setitem__ 而非生成 GEP。
# ============================================================
def _get_custom_struct_cls_nm(pool: memhub.MemBuddy | t.CPtr,
                              ty: llvmlite.LLVMType | t.CPtr) -> str:
    """检查 ty 或 ty 的 pointee 是否是已注册的自定义结构体，返回类名或 None"""
    if ty is None:
        return None
    # 直接检查 ty 是否是注册的结构体
    cls_nm_direct: str = HandlesStruct.get_class_name_by_type(pool, ty)
    if cls_nm_direct is not None:
        return cls_nm_direct
    # 如果 ty 是 Ptr，检查 pointee
    if is_ptr_type(ty) != 0:
        pointee_cs: llvmlite.LLVMType | t.CPtr = ty.Pointee
        if pointee_cs is not None:
            return HandlesStruct.get_class_name_by_type(pool, pointee_cs)
    return None


# ============================================================
# _deref_if_ptr_ptr - 如果 obj_ptr 是 Ptr(Ptr(...))，load 解引用
#
# 用于支持 X|t.CPtr 类型变量的属性访问：
#   - 值类型变量 (cnt: Counter): alloca 类型是 Ptr(Struct) → 不解引用
#   - 指针类型变量 (r: Vec2|t.CPtr): alloca 类型是 Ptr(Ptr(Struct)) → load 解引用
#
# 用 is_ptr_type 避免嵌套 match 的编译器 BUG
# ============================================================
def _deref_if_ptr_ptr(builder: llvmlite.IRBuilder | t.CPtr,
                      obj_ptr: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
    """如果 obj_ptr 是 Ptr(Ptr(...))，load 解引用获取内层指针"""
    if obj_ptr is None or obj_ptr.Ty is None:
        return obj_ptr
    if is_ptr_type(obj_ptr.Ty) == 0:
        return obj_ptr
    pointee: llvmlite.LLVMType | t.CPtr = obj_ptr.Ty.Pointee
    if pointee is None:
        return obj_ptr
    if is_ptr_type(pointee) == 0:
        return obj_ptr
    # obj_ptr 是 Ptr(Ptr(...))，load 解引用
    loaded: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, pointee, obj_ptr)
    if loaded is None:
        return obj_ptr
    return loaded


# ============================================================
# 类型强制转换：整数 sext/trunc，浮点 fpext/fptrunc，int↔float si2fp/fp2si
# ============================================================
def coerce_to_type(builder: llvmlite.IRBuilder | t.CPtr,
                   val: llvmlite.Value | t.CPtr,
                   target_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
    """将 val 强制转换为 target_ty

    整数: sext（扩展）/ trunc（截断）
    浮点: fpext（扩展）/ fptrunc（截断）
    int→float: sitofp
    float→int: fptosi
    """
    if val is None or target_ty is None:
        return val
    # 整数转换
    val_bits: int = get_llvm_type_bits(val.Ty)
    target_bits: int = get_llvm_type_bits(target_ty)
    # 整数 → 指针: inttoptr（必须在纯整数转换之前检查，因为 ptr 的 bits 非零会误入 sext/trunc 路径）
    # 适用于: 跨模块方法返回 i64/i32（默认推断），目标变量是指针类型
    if val_bits != 0 and is_ptr_type(target_ty) != 0:
        return llvmlite.build_inttoptr(builder, val, target_ty)
    # 指针 → 整数: ptrtoint（当目标明确是整数而非指针时）
    # 注意: target_bits == 8 时跳过 ptrtoint，走后面的 build_load 解引用首字符
    # 修复: buf[idx] = '\0' 中 '\0' 是 CONST_STR → i8* 指针，
    #   ptrtoint 会截断地址低 8 位（非 0），应 load 解引用取首字符 0
    if is_ptr_type(val.Ty) != 0 and target_bits != 0 and target_bits != 8 and is_ptr_type(target_ty) == 0:
        return llvmlite.build_ptrtoint(builder, val, target_ty)
    if val_bits != 0 and target_bits != 0:
        if val_bits == target_bits:
            return val
        if val_bits < target_bits:
            return llvmlite.build_sext(builder, val, target_ty)
        return llvmlite.build_trunc(builder, val, target_ty)
    # 浮点转换
    val_fbits: int = get_llvm_float_bits(val.Ty)
    target_fbits: int = get_llvm_float_bits(target_ty)
    if val_fbits != 0 and target_fbits != 0:
        if val_fbits == target_fbits:
            return val
        if val_fbits < target_fbits:
            return llvmlite.build_fpext(builder, val, target_ty)
        return llvmlite.build_fptrunc(builder, val, target_ty)
    # int → float
    if val_bits != 0 and target_fbits != 0:
        return llvmlite.build_si2fp(builder, val, target_ty)
    # float → int
    if val_fbits != 0 and target_bits != 0:
        return llvmlite.build_fp2si(builder, val, target_ty)
    # 指针 → 非指针值: build_load 解引用
    # 适用于: 指针 → 整数 (如 i8* → i8), 指针 → 结构体值
    # 当构造器返回 Ptr(Struct) 但目标变量是 Struct 值类型时，需要 load
    # 注意：使用独立函数 is_ptr_type 检查，避免嵌套 match 的编译器 BUG
    if is_ptr_type(val.Ty) != 0 and is_ptr_type(target_ty) == 0:
        return llvmlite.build_load(builder, target_ty, val)
    # 指针 → 指针: bitcast (如 i8* → i8** 当目标是全局变量存储指针)
    if is_ptr_type(val.Ty) != 0 and is_ptr_type(target_ty) != 0:
        return llvmlite.build_bitcast(builder, val, target_ty)
    return val


# ============================================================
# 创建全局字符串常量
# ============================================================
def create_global_string(builder: llvmlite.IRBuilder | t.CPtr,
                         pool: memhub.MemBuddy | t.CPtr,
                         mod: llvmlite.LLVMModule | t.CPtr,
                         str_val: str,
                         trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
    """创建全局字符串常量并返回 i8* bitcast"""
    escaped: t.CChar | t.CPtr = escape_llvm_string(pool, str_val)
    if escaped is None:
        return None

    slen: t.CSizeT = string.strlen(str_val)
    count: int = slen + 1

    i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
    arr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty, count)

    # 使用模块级计数器（trans._label_counter）避免跨函数命名冲突
    str_idx: int = trans._label_counter
    trans._label_counter += 1

    # 字符串名加 SHA1 前缀，和函数导出规则一致，避免跨模块重名
    gv_name: t.CChar | t.CPtr = pool.alloc(48)
    if gv_name is None:
        return None
    if trans.ModuleSha1 is not None:
        viperlib.snprintf(gv_name, 48, ".str.%s.%d", trans.ModuleSha1, str_idx)
    else:
        viperlib.snprintf(gv_name, 48, ".str.%d", str_idx)

    gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gv_name, arr_ty)
    if gv is None:
        return None
    llvmlite.module_add_global(mod, gv)

    # 去掉 private 链接，和函数一致：stub 中 declare，text 中 define
    llvmlite.global_set_unnamed_addr(gv, 1)
    llvmlite.global_set_constant(gv, 1)
    gv.Initializer = escaped

    arr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, arr_ty)
    gv_ref_name: t.CChar | t.CPtr = pool.alloc(64)
    if gv_ref_name is None:
        return None
    viperlib.snprintf(gv_ref_name, 64, "@%s", gv.Name)
    gv_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, arr_ptr_ty, gv_ref_name)

    i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)

    bc: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, gv_ref, i8_ptr_ty)
    return bc


# ============================================================
# 翻译常量表达式
# ============================================================
def translate_constant(builder: llvmlite.IRBuilder | t.CPtr,
                       pool: memhub.MemBuddy | t.CPtr,
                       mod: llvmlite.LLVMModule | t.CPtr,
                       node: ast.AST | t.CPtr,
                       trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
    """翻译常量（int/str/bool）"""
    cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
    if cn is None:
        return None
    ck: int = cn.const_kind
    if cn.const_kind == ast.CONST_INT:
        # 超出 i32 范围则用 i64（避免常量创建时被截断）
        iv: t.CInt64T = cn.int_val
        if iv > 2147483647 or iv < -2147483648:
            return llvmlite.const_int64(pool, iv)
        return llvmlite.const_int32(pool, iv)
    elif cn.const_kind == ast.CONST_FLOAT:
        # 浮点常量默认创建为 double（64 位），赋值时由 coerce_to_type 自动 fptrunc
        double_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
        return llvmlite.ConstFloat(pool, double_ty, cn.float_val)
    elif cn.const_kind == ast.CONST_CHAR:
        # 单引号单字符 → i32 值（字符 ASCII 码），coerce_to_type 会 trunc 为 i8
        cv: t.CInt64T = cn.int_val
        return llvmlite.const_int32(pool, cv)
    elif cn.const_kind == ast.CONST_STR:
        sv: str = cn.str_val
        if sv is None:
            return None
        r: llvmlite.Value | t.CPtr = create_global_string(builder, pool, mod, sv, trans)
        return r
    elif cn.const_kind == ast.CONST_BOOL:
        if cn.int_val != 0:
            return llvmlite.const_int32(pool, 1)
        return llvmlite.const_int32(pool, 0)
    elif cn.const_kind == ast.CONST_NONE:
        # None → i8* null（空指针常量），用于 `p is None` / `p is not None` 比较
        i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
        if i8_ty is None:
            return None
        i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
        if i8_ptr_ty is None:
            return None
        rv_none: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, i8_ptr_ty, "null")
        return rv_none
    return None


# ============================================================
# _infer_elem_type_from_value — 从 LLVM Value 推断 list 元素类型名
#
# 用于 list 字面量 [a, b, c] 的元素类型推断
# ============================================================
def _infer_elem_type_from_value(pool: memhub.MemBuddy | t.CPtr,
                                value: llvmlite.Value | t.CPtr) -> str:
    """从 LLVM Value 的类型推断 list 元素类型名"""
    if value is None or value.Ty is None:
        return "str"
    ty: llvmlite.LLVMType | t.CPtr = value.Ty
    bits: int = get_llvm_type_bits(ty)
    if bits != 0:
        if bits == 32:
            return "int"
        if bits == 64:
            return "CSizeT"
        if bits == 8:
            return "CInt8T"
        if bits == 16:
            return "CInt16T"
        return "int"
    if is_ptr_type(ty) != 0:
        return "str"
    fbits: int = get_llvm_float_bits(ty)
    if fbits != 0:
        if fbits == 64:
            return "CDouble"
        if fbits == 32:
            return "CFloat"
    return "str"


# ============================================================
# _find_pool_var — 查找上下文中的 pool 变量
#
# 依次尝试 "pool", "_mbuddy", "mbuddy", "mb"
# ============================================================
def _find_pool_var(trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
    """查找上下文中的 pool 变量，返回 alloca 或 None

    查找顺序: pool → _mbuddy → mbuddy → mb
    兜底: 若都未找到，注册外部全局 @_mbuddy (i8*) 供函数内动态 list 创建使用。
    注意: 模块级 t.CArray 初始化不走此路径（由 _init_global_array_from_list 处理），
    此兜底仅用于函数内的动态 list 字面量（如 op=[...]）。
    @_mbuddy 由入口模块（如 Phase2.py）定义并初始化，其他模块以 external 引用。
    """
    if trans is None:
        return None
    alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, "pool")
    if alloca is not None:
        return alloca
    alloca = HandlesVar.lookup_var(trans.SymTab, "_mbuddy")
    if alloca is not None:
        return alloca
    alloca = HandlesVar.lookup_var(trans.SymTab, "mbuddy")
    if alloca is not None:
        return alloca
    alloca = HandlesVar.lookup_var(trans.SymTab, "mb")
    if alloca is not None:
        return alloca
    # 兜底: 创建外部全局 @_mbuddy 引用（i8** 类型）
    # 仅用于函数内动态 list 字面量，@_mbuddy 由入口模块定义并初始化
    pool: memhub.MemBuddy | t.CPtr = trans.Pool
    if pool is None or trans.Module is None or trans.SymTab is None:
        return None
    # 先检查是否已注册到模块作用域（避免重复创建全局变量）
    existing: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var(trans.SymTab, "_mbuddy")
    if existing is not None:
        return existing
    # 创建 i8* 类型（t.CVoid | t.CPtr = i8*）
    i8_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
    i8_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_fb)
    # 创建外部全局 @_mbuddy（external linkage，无初始值）
    gv_ext: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, "_mbuddy", i8_ptr_ty_fb)
    if gv_ext is not None:
        gv_ext.Linkage = "external"
        llvmlite.module_add_global(trans.Module, gv_ext)
    # 创建 Value 引用（@_mbuddy, 类型为 i8**）
    i8_ptr_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty_fb)
    ref_name_fb: t.CChar | t.CPtr = pool.alloc(16)
    if ref_name_fb is not None:
        string.strcpy(ref_name_fb, "@_mbuddy")
    gv_ref_fb: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb)
    # 注册到模块作用域，避免重复创建
    HandlesVar.define_module_var(trans.SymTab, "_mbuddy", gv_ref_fb)
    return gv_ref_fb


# ============================================================
# translate_list_literal — 翻译 list 字面量 [a, b, c]
#
# 将 list 字面量翻译为 list[T](pool) 构造 + append 调用序列
#
# Args:
#   builder: IRBuilder
#   pool: 编译器内存池（MemBuddy）
#   mod: LLVMModule
#   node: ast.List 节点
#   elem_type_name: 元素类型名（如 "str", "int"），None 表示从元素推断
#   trans: Translator 对象
#
# Returns:
#   list 对象指针（Ptr(list[T] struct)），None 失败
# ============================================================
def translate_list_literal(builder: llvmlite.IRBuilder | t.CPtr,
                           pool: memhub.MemBuddy | t.CPtr,
                           mod: llvmlite.LLVMModule | t.CPtr,
                           node: ast.AST | t.CPtr,
                           elem_type_name: str,
                           trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
    """翻译 list 字面量 [a, b, c] → list[T](pool) + append 序列"""
    if node is None or trans is None:
        return None

    list_node: ast.List | t.CPtr = (ast.List | t.CPtr)(node)
    if list_node is None:
        return None

    elts: list[ast.AST | t.CPtr] | t.CPtr = list_node.elts
    elts_count: t.CSizeT = 0
    if elts is not None:
        elts_count = elts.__len__()

    # 推断元素类型
    inferred_type_name: str = elem_type_name
    first_elem_val: llvmlite.Value | t.CPtr = None
    if inferred_type_name is None:
        inferred_type_name = "str"
        if elts_count > 0:
            first_elem_node: ast.AST | t.CPtr = elts.get(0)
            first_elem_val = translate_value(builder, pool, mod, first_elem_node, None, 0, trans)
            if first_elem_val is not None:
                inferred_type_name = _infer_elem_type_from_value(pool, first_elem_val)

    # 查找 pool 变量
    pool_alloca: llvmlite.Value | t.CPtr = _find_pool_var(trans)
    if pool_alloca is None:
        HandlesType.fatal_error(node, "list literal requires pool variable in context")
        return None

    # load pool 变量值（i8*）
    i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
    i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
    pool_val: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, pool_alloca)
    if pool_val is None:
        return None

    # 特化 list[T] 类
    type_args: list[str] | t.CPtr = list[str](pool, 4)
    type_args.append(inferred_type_name)
    spec_name: str = HandlesClassDef._specialize_generic_class(trans, "list", type_args)
    if spec_name is None:
        HandlesType.fatal_error(node, "list literal generic specialization failed")
        return None

    # 查找 struct entry
    struct_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(spec_name)
    if struct_entry is None:
        HandlesType.fatal_error(node, "list literal specialized class not registered")
        return None
    struct_ty: llvmlite.LLVMType | t.CPtr = struct_entry.Ty
    if struct_ty is None:
        return None

    # alloca 临时 list 空间
    # 确保跨模块结构体的完整定义在当前模块中可用（供 alloca 分配空间）
    HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty)
    tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty)
    if tmp is None:
        return None

    # 存储指针：默认用 alloca，如果有 __new__ 则用 __new__ 返回的指针
    # __new__ 是可选的，仅当泛型类模板定义了 __new__ 方法时才调用
    # （与 _translate_struct_ctor/_translate_generic_ctor_call 保持一致，
    #   避免为无 __new__ 的类生成过度声明）
    storage_ptr: llvmlite.Value | t.CPtr = tmp
    has_new_flag: int = 0
    if struct_entry is not None:
        has_new_flag = struct_entry.HasNew
    if has_new_flag != 0:
        new_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
        if new_args is None:
            return None
        string.memset(new_args, 0, 8 * 32)
        new_args[0] = t.CSizeT(pool_val)
        new_ptr: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
            pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans)
        if new_ptr is not None:
            storage_ptr = new_ptr

    # 调用 __before_init__(storage_ptr)
    HandlesExprCall._call_method_on_ptr(
        pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans)

    # 调用 __init__(storage_ptr, pool_val, elem_size=0)
    init_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
    if init_args is None:
        return None
    string.memset(init_args, 0, 8 * 32)
    init_args[0] = t.CSizeT(pool_val)
    elem_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
    init_args[1] = t.CSizeT(elem_size_val)
    HandlesExprCall._call_method_on_ptr(
        pool, builder, mod, spec_name, "__init__", storage_ptr, init_args, 2, trans)

    # 对每个元素调用 append(storage_ptr, elem_val)
    if elts_count > 0:
        # 第一个元素可能已经翻译过
        if first_elem_val is not None:
            append_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
            if append_args is not None:
                string.memset(append_args, 0, 8 * 32)
                append_args[0] = t.CSizeT(first_elem_val)
                HandlesExprCall._call_method_on_ptr(
                    pool, builder, mod, spec_name, "append", storage_ptr, append_args, 1, trans)

        # 翻译剩余元素
        i: t.CSizeT = 1
        while i < elts_count:
            elem_node_i: ast.AST | t.CPtr = elts.get(i)
            elem_val_i: llvmlite.Value | t.CPtr = translate_value(builder, pool, mod, elem_node_i, None, 0, trans)
            if elem_val_i is not None:
                ap_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
                if ap_args is not None:
                    string.memset(ap_args, 0, 8 * 32)
                    ap_args[0] = t.CSizeT(elem_val_i)
                    HandlesExprCall._call_method_on_ptr(
                        pool, builder, mod, spec_name, "append", storage_ptr, ap_args, 1, trans)
            i += 1

    return storage_ptr


# ============================================================
# 翻译值表达式（RHS 分派）— 模块级版本
# ============================================================
def translate_value(builder: llvmlite.IRBuilder | t.CPtr,
                    pool: memhub.MemBuddy | t.CPtr,
                    mod: llvmlite.LLVMModule | t.CPtr,
                    node: ast.AST | t.CPtr,
                    funcs_ptr: t.CPtr = None,
                    func_count: int = 0,
                    trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
    """翻译值表达式为 LLVM Value"""
    if node is None:
        return None
    k: int = node.kind()
    if k == ast.ASTKind.Constant:
        rv: llvmlite.Value | t.CPtr = translate_constant(builder, pool, mod, node, trans)
        return rv
    elif k == ast.ASTKind.Name:
        return translate_name_value(builder, pool, node, trans)
    elif k == ast.ASTKind.BinOp:
        return HandlesExprOps.translate_binop(pool, builder, mod, node, trans)
    elif k == ast.ASTKind.Call:
        return HandlesExprCall.translate_call(pool, builder, mod, node,
                                              funcs_ptr, func_count, trans)
    elif k == ast.ASTKind.Compare:
        return translate_compare(builder, pool, mod, node, trans)
    elif k == ast.ASTKind.UnaryOp:
        return translate_unaryop(builder, pool, mod, node, trans)
    elif k == ast.ASTKind.BoolOp:
        return translate_boolop(builder, pool, mod, node,
                                funcs_ptr, func_count, trans)
    elif k == ast.ASTKind.Subscript:
        return translate_subscript(builder, pool, mod, node, trans)
    elif k == ast.ASTKind.Attribute:
        return translate_attribute(builder, pool, mod, node, trans)
    elif k == ast.ASTKind.List:
        return translate_list_literal(builder, pool, mod, node, None, trans)
    elif k == ast.ASTKind.IfExp:
        return translate_ifexp(builder, pool, mod, node, trans)
    return None


# ============================================================
# 翻译三元表达式 IfExp(test, body, orelse)
#
# 生成: cond ? body : orelse
#   then_blk:  翻译 body → br merge
#   else_blk:  翻译 orelse → br merge
#   merge_blk: phi [body_val, then_blk], [orelse_val, else_blk]
# ============================================================
def translate_ifexp(builder: llvmlite.IRBuilder | t.CPtr,
                    pool: memhub.MemBuddy | t.CPtr,
                    mod: llvmlite.LLVMModule | t.CPtr,
                    node: ast.AST | t.CPtr,
                    trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
    """翻译三元表达式 (body if test else orelse)，返回 phi 结果 Value"""
    if node is None or builder is None or trans is None:
        return None
    ie: ast.IfExp | t.CPtr = (ast.IfExp | t.CPtr)(node)
    if ie is None:
        return None

    func: llvmlite.Function | t.CPtr = trans._cur_func
    if func is None:
        return None

    # 1. 求值条件
    cond_val: llvmlite.Value | t.CPtr = translate_value(
        builder, pool, mod, ie.test, None, 0, trans)
    if cond_val is None:
        return None
    # 转换为 i1
    cond_bits: int = get_llvm_type_bits(cond_val.Ty)
    if cond_bits == 1:
        cond_i1: llvmlite.Value | t.CPtr = cond_val
    else:
        if is_ptr_type(cond_val.Ty) != 0:
            # 指针类型：与 null 比较
            null_cond: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, cond_val.Ty, "null")
            cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, null_cond)
        else:
            zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
            cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, zero)

    # 2. 创建三个基本块
    cnt: int = trans._label_counter
    trans._label_counter = cnt + 1
    name_buf: t.CChar | t.CPtr = pool.alloc(32)
    if name_buf is None:
        return None
    viperlib.snprintf(name_buf, 32, "ifexp.then.%d", cnt)
    then_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
    viperlib.snprintf(name_buf, 32, "ifexp.else.%d", cnt)
    else_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
    viperlib.snprintf(name_buf, 32, "ifexp.merge.%d", cnt)
    merge_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)

    if then_bb is None or else_bb is None or merge_bb is None:
        return None

    # 3. 条件跳转
    llvmlite.build_cond_br(builder, cond_i1, then_bb, else_bb)

    # 4. then 分支
    llvmlite.position_at_end(builder, then_bb)
    body_val: llvmlite.Value | t.CPtr = translate_value(
        builder, pool, mod, ie.body, None, 0, trans)
    then_blk_for_phi: llvmlite.BasicBlock | t.CPtr = trans._cur_builder.CurBlock
    if body_val is None:
        body_val = llvmlite.const_int32(pool, 0)
    # 暂不 br，等类型对齐后再 br

    # 5. else 分支
    llvmlite.position_at_end(builder, else_bb)
    orelse_val: llvmlite.Value | t.CPtr = translate_value(
        builder, pool, mod, ie.orelse, None, 0, trans)
    else_blk_for_phi: llvmlite.BasicBlock | t.CPtr = trans._cur_builder.CurBlock
    if orelse_val is None:
        orelse_val = llvmlite.const_int32(pool, 0)
    # 暂不 br，等类型对齐后再 br

    # 类型对齐 (两分支类型可能不同)
    # 必须在各自分支中执行 coerce_to_type，确保 merge 块中 PHI 是第一条指令
    # （否则 coerce_to_type 生成的 sext/zext/ptrtoint/inttoptr 指令在 PHI 之前，违反 LLVM 规则）
    body_bits: int = get_llvm_type_bits(body_val.Ty)
    orelse_bits: int = get_llvm_type_bits(orelse_val.Ty)
    body_is_ptr: int = is_ptr_type(body_val.Ty)
    orelse_is_ptr: int = is_ptr_type(orelse_val.Ty)
    # 情况0: 一侧是指针，另一侧是整数（如 i64 vs i8* null）
    # 必须优先处理，因为 Ptr 的 bits=0 会导致下面三个分支全部跳过
    # 选择 body_val.Ty 作为 phi_ty（then 分支优先），orelse_val 对齐到它
    # coerce_to_type 内部会调用 ptrtoint（指针→整数）或 inttoptr（整数→指针）
    if body_is_ptr != orelse_is_ptr:
        llvmlite.position_at_end(builder, else_blk_for_phi)
        orelse_val = coerce_to_type(builder, orelse_val, body_val.Ty)
    elif body_bits > orelse_bits and orelse_bits > 0:
        # 在 else 块中转换 orelse_val
        llvmlite.position_at_end(builder, else_blk_for_phi)
        orelse_val = coerce_to_type(builder, orelse_val, body_val.Ty)
    elif orelse_bits > body_bits and body_bits > 0:
        # 回到 then 块转换 body_val
        llvmlite.position_at_end(builder, then_blk_for_phi)
        body_val = coerce_to_type(builder, body_val, orelse_val.Ty)
    elif body_bits == orelse_bits and body_bits > 0:
        # bits 相同但类型不同（如 i64 vs Ptr）：null 不能用于 i64 PHI
        # 此分支理论上不会触发（已被情况0覆盖），保留作为防御
        if is_ptr_type(body_val.Ty) != is_ptr_type(orelse_val.Ty):
            llvmlite.position_at_end(builder, else_blk_for_phi)
            orelse_val = coerce_to_type(builder, orelse_val, body_val.Ty)

    # 现在分别在 then/else 块中 br 到 merge（在类型转换之后）
    llvmlite.position_at_end(builder, then_blk_for_phi)
    llvmlite.build_br(builder, merge_bb)
    llvmlite.position_at_end(builder, else_blk_for_phi)
    llvmlite.build_br(builder, merge_bb)

    # 6. merge 块: phi（PHI 是 merge 块的第一条指令）
    llvmlite.position_at_end(builder, merge_bb)

    phi_ty: llvmlite.LLVMType | t.CPtr = body_val.Ty
    if phi_ty is None:
        phi_ty = llvmlite.Int32(pool)

    inc1: llvmlite.PhiIncoming | t.CPtr = llvmlite.new_phi_incoming(
        pool, body_val, then_blk_for_phi)
    inc2: llvmlite.PhiIncoming | t.CPtr = llvmlite.new_phi_incoming(
        pool, orelse_val, else_blk_for_phi)
    if inc1 is None or inc2 is None:
        return None
    # 链表顺序: inc1 (head) -> inc2 (tail)
    # 之前错误写成 inc2.Next = inc1，导致 build_phi 从 inc1 遍历时
    # inc1.Next 为 None，只输出一个 phi 条目，引发 LLVM 验证错误：
    # "PHINode should have one entry for each predecessor"
    inc1.Next = inc2
    return llvmlite.build_phi(builder, phi_ty, inc1, 2)


# ============================================================
# 翻译比较表达式 Compare(left, ops, comparators)
# ============================================================
def translate_compare(builder: llvmlite.IRBuilder | t.CPtr,
                      pool: memhub.MemBuddy | t.CPtr,
                      mod: llvmlite.LLVMModule | t.CPtr,
                      node: ast.AST | t.CPtr,
                      trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
    """翻译比较表达式，返回 i1 结果"""
    cmp: ast.Compare | t.CPtr = (ast.Compare | t.CPtr)(node)
    if cmp is None:
        return None

    ops_list: list[t.CInt] | t.CPtr = (list[t.CInt] | t.CPtr)(cmp.ops)
    if ops_list is None or ops_list.__len__() == 0:
        return None
    op: int = ops_list.get(0)

    comparators: list[ast.AST | t.CPtr] | t.CPtr = cmp.comparators
    if comparators is None or comparators.__len__() == 0:
        return None
    rhs: llvmlite.Value | t.CPtr = translate_value(
        builder, pool, mod, comparators.get(0), None, 0, trans)
    if rhs is None:
        return None

    # === 比较运算符重载路径 1: lhs 是 Name 且对应结构体变量 ===
    # 对于值类型变量（如 cnt: Counter），用 alloca 指针尝试重载
    lhs_node: ast.AST | t.CPtr = cmp.left
    if lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None:
        nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(lhs_node)
        if nm is not None and nm.id is not None:
            lhs_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
                trans.SymTab, nm.id)
            if lhs_alloca is not None:
                ovl_result: llvmlite.Value | t.CPtr = HandlesExprOps.try_operator_overload(
                    pool, builder, mod, lhs_alloca, rhs, op, trans, 1)
                if ovl_result is not None:
                    return ovl_result

    # 正常翻译 lhs
    lhs: llvmlite.Value | t.CPtr = translate_value(
        builder, pool, mod, cmp.left, None, 0, trans)
    if lhs is None:
        return None

    # === 比较运算符重载路径 2: lhs 是 Ptr(Struct) ===
    # Is/IsNot 不在 _cmpop_to_dunder 映射中，会自动返回 None 回退原生比较
    ovl_result2: llvmlite.Value | t.CPtr = HandlesExprOps.try_operator_overload(
        pool, builder, mod, lhs, rhs, op, trans, 1)
    if ovl_result2 is not None:
        return ovl_result2

    predicate: int = llvmlite.ICMP_SLT
    if op == ast.OpKind.Lt:
        predicate = llvmlite.ICMP_SLT
    elif op == ast.OpKind.Le:
        predicate = llvmlite.ICMP_SLE
    elif op == ast.OpKind.Gt:
        predicate = llvmlite.ICMP_SGT
    elif op == ast.OpKind.Ge:
        predicate = llvmlite.ICMP_SGE
    elif op == ast.OpKind.Eq:
        predicate = llvmlite.ICMP_EQ
    elif op == ast.OpKind.Ne:
        predicate = llvmlite.ICMP_NE
    elif op == ast.OpKind.Is:
        # `x is y` → 指针/值相等比较
        predicate = llvmlite.ICMP_EQ
    elif op == ast.OpKind.IsNot:
        # `x is not y` → 指针/值不等比较
        predicate = llvmlite.ICMP_NE

    # 隐式类型转换：当一侧是指针(i8*)，另一侧是整数时，
    # 从指针加载第一个字节，使两侧类型一致
    lhs_is_ptr: int = is_ptr_type(lhs.Ty)
    rhs_is_ptr: int = is_ptr_type(rhs.Ty)
    if lhs_is_ptr != 0 and rhs_is_ptr == 0:
        i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
        lhs = llvmlite.build_load(builder, i8_ty, lhs)
    elif rhs_is_ptr != 0 and lhs_is_ptr == 0:
        i8_ty2: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
        rhs = llvmlite.build_load(builder, i8_ty2, rhs)

    # 类型对齐：icmp 要求两边类型相同，将较窄的整数 sext 到较宽的类型
    lhs_bits: int = get_llvm_type_bits(lhs.Ty)
    rhs_bits: int = get_llvm_type_bits(rhs.Ty)
    if lhs_bits != 0 and rhs_bits != 0 and lhs_bits != rhs_bits:
        if lhs_bits < rhs_bits:
            lhs = llvmlite.build_sext(builder, lhs, rhs.Ty)
        else:
            rhs = llvmlite.build_sext(builder, rhs, lhs.Ty)

    return llvmlite.build_icmp(builder, predicate, lhs, rhs)


# ============================================================
# 翻译一元运算表达式 UnaryOp(op, operand)
# ============================================================
def translate_unaryop(builder: llvmlite.IRBuilder | t.CPtr,
                      pool: memhub.MemBuddy | t.CPtr,
                      mod: llvmlite.LLVMModule | t.CPtr,
                      node: ast.AST | t.CPtr,
                      trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
    """翻译一元运算（-x, +x, not x, ~x）"""
    uo: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
    if uo is None:
        return None

    operand: llvmlite.Value | t.CPtr = translate_value(
        builder, pool, mod, uo.operand, None, 0, trans)
    if operand is None:
        return None

    if uo.op == ast.OpKind.USub:
        # -x = 0 - x
        # 根据 operand 类型选择零值常量（避免 i32 与 i64 类型不匹配）
        operand_bits: int = get_llvm_type_bits(operand.Ty)
        zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
        if operand_bits == 64:
            zero = llvmlite.const_int64(pool, 0)
        elif operand_bits > 0 and operand_bits != 32:
            # 其他位宽（i8/i16 等）：用 i32 零值，operand 会被 coerce
            zero = llvmlite.const_int32(pool, 0)
        return llvmlite.build_sub(builder, zero, operand)
    elif uo.op == ast.OpKind.UAdd:
        # +x = x
        return operand
    elif uo.op == ast.OpKind.Not:
        # not x = (x == 0/null)，返回 i1
        if is_ptr_type(operand.Ty) != 0:
            # 指针类型：与 null 比较
            null_op: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, operand.Ty, "null")
            return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, operand, null_op)
        operand_bits_not: int = get_llvm_type_bits(operand.Ty)
        zero = llvmlite.const_int32(pool, 0)
        if operand_bits_not == 64:
            zero = llvmlite.const_int64(pool, 0)
        return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, operand, zero)
    elif uo.op == ast.OpKind.Invert:
        # ~x = x ^ -1
        operand_bits_inv: int = get_llvm_type_bits(operand.Ty)
        neg1: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, -1)
        if operand_bits_inv == 64:
            neg1 = llvmlite.const_int64(pool, -1)
        return llvmlite.build_xor(builder, operand, neg1)
    return operand


# ============================================================
# 翻译布尔运算 BoolOp(op, values) — 短路求值，返回 i1
#
# and: a and b and c → 若 a 为假短路到 merge(返回 a 的 i1)，否则求 b，
#     最后一个值直接求值并跳 merge
# or:  a or b or c  → 若 a 为真短路到 merge(返回 a 的 i1)，否则求 b，
#     最后一个值直接求值并跳 merge
# merge 块用 phi 合并所有入边的 i1
# ============================================================
def translate_boolop(builder: llvmlite.IRBuilder | t.CPtr,
                     pool: memhub.MemBuddy | t.CPtr,
                     mod: llvmlite.LLVMModule | t.CPtr,
                     node: ast.AST | t.CPtr,
                     funcs_ptr: t.CPtr = None,
                     func_count: int = 0,
                     trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
    """翻译布尔运算（and/or）的短路求值，返回 i1"""
    bo: ast.BoolOp | t.CPtr = (ast.BoolOp | t.CPtr)(node)
    if bo is None:
        return None

    op: int = bo.op
    values: list[ast.AST | t.CPtr] | t.CPtr = bo.values
    if values is None:
        return None
    count: t.CSizeT = values.__len__()
    if count == 0:
        return None
    if count == 1:
        return translate_value(builder, pool, mod, values.get(0),
                               funcs_ptr, func_count, trans)

    func: llvmlite.Function | t.CPtr = builder.Func
    if func is None:
        return None

    # 创建 merge BB（用 builder.Counter 生成唯一标签名，position_at_end 的
    # move_to_end 保证 BB 文本顺序与控制流顺序一致，标签名用 counter 安全）
    cnt: int = builder.Counter
    builder.Counter = cnt + 1
    name_buf: t.CChar | t.CPtr = pool.alloc(32)
    if name_buf is None:
        return None
    viperlib.snprintf(name_buf, 32, "bool.merge.%d", cnt)
    merge_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)

    # phi 入边链表头/尾
    phi_head: llvmlite.PhiIncoming | t.CPtr = None
    phi_tail: llvmlite.PhiIncoming | t.CPtr = None
    phi_count: int = count - 1 # 循环次数

    # 对每个值（除最后一个）求值并短路
    for i in range(count - 1):
        val_node: ast.AST | t.CPtr = values.get(i)
        val: llvmlite.Value | t.CPtr = translate_value(
            builder, pool, mod, val_node,
            funcs_ptr, func_count, trans)
        if val is None:
            return None

        # 转为 i1（已经是 i1 的直接用，否则与 0/null 比较）
        val_bits: int = get_llvm_type_bits(val.Ty)
        val_i1: llvmlite.Value | t.CPtr = val
        if val_bits != 1:
            if is_ptr_type(val.Ty) != 0:
                # 指针类型：与 null 比较（避免 i8* 与 i32 类型不匹配）
                null_val: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, val.Ty, "null")
                val_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, null_val)
            else:
                zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
                val_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, zero)

        # 创建 next BB（求值下一个值）
        cnt = builder.Counter
        builder.Counter = cnt + 1
        viperlib.snprintf(name_buf, 32, "bool.next.%d", cnt)
        next_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)

        # and: 短路假→merge，真→next；or: 短路真→merge，假→next
        if op == ast.OpKind.And:
            llvmlite.build_cond_br(builder, val_i1, next_bb, merge_bb)
        else:
            llvmlite.build_cond_br(builder, val_i1, merge_bb, next_bb)

        # 添加 phi 入边：(val_i1, cond_br 所在 BB)
        cur_bb: llvmlite.BasicBlock | t.CPtr = builder.CurBlock
        inc: llvmlite.PhiIncoming | t.CPtr = llvmlite.new_phi_incoming(pool, val_i1, cur_bb)
        if phi_head is None:
            phi_head = inc
            phi_tail = inc
        else:
            phi_tail.Next = inc
            phi_tail = inc

        # 定位到 next BB，求值下一个值
        llvmlite.position_at_end(builder, next_bb)

    # 最后一个值：求值后直接跳 merge
    last_node: ast.AST | t.CPtr = values.get(count - 1)
    last_val: llvmlite.Value | t.CPtr = translate_value(
        builder, pool, mod, last_node,
        funcs_ptr, func_count, trans)
    if last_val is None:
        return None
    last_bits: int = get_llvm_type_bits(last_val.Ty)
    last_i1: llvmlite.Value | t.CPtr = last_val
    if last_bits != 1:
        if is_ptr_type(last_val.Ty) != 0:
            # 指针类型：与 null 比较（避免 i8* 与 i32 类型不匹配）
            null_val2: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, last_val.Ty, "null")
            last_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, last_val, null_val2)
        else:
            zero = llvmlite.const_int32(pool, 0)
            last_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, last_val, zero)

    llvmlite.build_br(builder, merge_bb)

    cur_bb = builder.CurBlock
    inc = llvmlite.new_phi_incoming(pool, last_i1, cur_bb)
    if phi_head is None:
        phi_head = inc
    else:
        phi_tail.Next = inc
    phi_count += 1

    # 定位到 merge，创建 phi 合并所有入边
    llvmlite.position_at_end(builder, merge_bb)
    i1_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool)
    return llvmlite.build_phi(builder, i1_ty, phi_head, phi_count)


# ============================================================
# _build_source_path_from_sha1 - 通过 SHA1 从存储器构建源 .py 文件路径
#
# .pyi 文件可能未生成（Phase1 未输出 pyi），此时回退读取源 .py 文件。
# 从 StubMerger 全局存储器查找 SHA1 对应的相对路径，
# 根据 rel_path 前缀选择基础目录：
#   - rel_path 以 "lib/" 开头 → App 源文件，用 {Config.SourceDir}/{rel_path}
#   - 其他 → includes 文件，用 {Config.IncludesDir}/{rel_path}
#
# Returns:
#   stdlib.malloc 分配的路径字符串（调用者负责 free），None 失败
# ============================================================
def _build_source_path_from_sha1(sha1: str) -> str:
    """通过 SHA1 从全局存储器构建源 .py 文件路径"""
    if sha1 is None:
        return None
    if Config.IncludesDir is None:
        return None
    # 从 StubMerger 全局存储器获取相对路径数组
    rel_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr()
    count: int = StubMerger.GetSha1StoreCount()
    if rel_arr_ptr is None or count <= 0:
        return None
    sha1_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr()
    if sha1_arr_ptr is None:
        return None
    # 遍历查找匹配的 SHA1
    idx_s: int = -1
    for i in range(count):
        sidx: t.CSizeT = t.CSizeT(i) * 17
        cur_sha1: str = sha1_arr_ptr + sidx
        if cur_sha1[0] == '\0':
            continue
        if string.strcmp(cur_sha1, sha1) == 0:
            idx_s = i
            break
    if idx_s < 0:
        return None
    # 获取相对路径（MAX_REL_PATH_LEN=256，避免跨模块 CDefine 解析问题）
    MAX_RPL: t.CSizeT = 256
    ridx: t.CSizeT = t.CSizeT(idx_s) * MAX_RPL
    rel_path: str = rel_arr_ptr + ridx
    if rel_path[0] == '\0':
        return None
    # 选择基础目录：rel_path 以 "lib/" 开头 → App 源文件（SourceDir），否则 includes 文件
    base_dir: str = Config.IncludesDir
    is_app: int = 0
    if string.strlen(rel_path) >= 4:
        if (rel_path[0] == 'l' and rel_path[1] == 'i' and
            rel_path[2] == 'b' and rel_path[3] == '/'):
            is_app = 1
    if is_app != 0 and Config.SourceDir is not None:
        base_dir = Config.SourceDir
    # 构建完整路径: {base_dir}/{rel_path}
    base_len: t.CSizeT = string.strlen(base_dir)
    rel_len: t.CSizeT = string.strlen(rel_path)
    path_len: t.CSizeT = base_len + rel_len + 2
    buf: str = stdlib.malloc(path_len)
    if buf is None:
        return None
    viperlib.snprintf(buf, path_len, "%s/%s", base_dir, rel_path)
    return buf


# ============================================================
# _lookup_cross_module_cdefine - 跨模块查找 CDefine 常量
#
# 当当前模块的 CDefine 表中找不到某个 Name 时，从 from_imports
# 解析出源模块，再从该模块的 pyi 文件中解析 CDefine 常量值。
# 找到后缓存到当前模块的 CDefine 表中，避免重复查找。
#
# pyi 文件格式: "NAME: t.CDefine = value"
# ============================================================
def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
                                  name: str,
                                  from_imports: str,
                                  imported_modules: str = None) -> int:
    """跨模块查找 CDefine 常量，返回值或 -1（未找到）

    查找顺序:
      1. from_imports 精确匹配（from X import NAME）
      2. from_imports star 回退（from X import *）
      3. imported_modules 遍历（import X）：当通过 import X 导入模块时，
         模块中定义的 CDefine 常量可通过此路径找到（如方法签名默认参数引用）
    """
    if name is None:
        return -1
    if from_imports is None:
        from_imports = ""

    # 1. 从 from_imports 查找名称对应的模块名
    # 先尝试精确匹配（allow_star_fallback=0），避免 CDefine 常量被 star import 误导
    mod_name_raw: str = HandlesImports.lookup_from_import(from_imports, name, 0)
    if mod_name_raw is None:
        # 精确匹配失败：尝试 star import 回退
        mod_name_raw = HandlesImports.lookup_from_import(from_imports, name, 1)

    # 2. from_imports 查找失败：直接回退到 imported_modules 遍历（步骤 9.6）
    # 注意：star fallback 可能返回错误模块（如 stdint），常量实际不在其中，
    # 步骤 9.6 会在该模块查找失败后继续遍历 imported_modules
    if mod_name_raw is None:
        # from_imports 完全没有匹配（精确和 star 都失败）
        # 直接进入 imported_modules 遍历（步骤 9.6）
        if imported_modules is None:
            return -1
        # 跳过 from_imports 模块读取，直接走步骤 9.6 的 imported_modules 遍历
        # 设置 result_found=0 跳过步骤 3-9，直接到 9.6
        HandlesType.set_cdefine_found(0)
        # 直接遍历 imported_modules
        im_len: t.CSizeT = string.strlen(imported_modules)
        im_ci: t.CSizeT = 0
        while im_ci < im_len:
            while im_ci < im_len and imported_modules[im_ci] == ' ':
                im_ci += 1
            if im_ci >= im_len:
                break
            im_start: t.CSizeT = im_ci
            while im_ci < im_len and imported_modules[im_ci] != ' ':
                im_ci += 1
            im_end: t.CSizeT = im_ci
            im_nlen: t.CSizeT = im_end - im_start
            if im_nlen > 0 and im_nlen < 256:
                im_buf: str = pool.alloc(im_nlen + 1)
                if im_buf is not None:
                    mk: t.CSizeT = 0
                    while mk < im_nlen:
                        im_buf[mk] = imported_modules[im_start + mk]
                        mk += 1
                    im_buf[im_nlen] = '\0'
                    sub_fi: str = pool.alloc(im_nlen + 3)
                    if sub_fi is not None:
                        sub_fi[0] = '*'
                        sub_fi[1] = ':'
                        mk2: t.CSizeT = 0
                        while mk2 < im_nlen:
                            sub_fi[2 + mk2] = im_buf[mk2]
                            mk2 += 1
                        sub_fi[2 + im_nlen] = '\0'
                        sub_val: int = _lookup_cross_module_cdefine(pool, name, sub_fi, None)
                        if HandlesType.is_cdefine_found() != 0:
                            return sub_val
        return -1

    # 2. 复制模块名到新缓冲区（lookup_from_import 返回的是内部指针）
    # 截断于空格、null、或 ':'（别名格式的分隔符）
    mod_len: t.CSizeT = 0
    while mod_name_raw[mod_len] != ' ' and mod_name_raw[mod_len] != '\0' and mod_name_raw[mod_len] != ':':
        mod_len += 1
    mod_name_buf: str = pool.alloc(mod_len + 1)
    if mod_name_buf is None:
        return -1
    for mi in range(mod_len):
        mod_name_buf[mi] = mod_name_raw[mi]
    mod_name_buf[mod_len] = '\0'

    # 3. 去掉相对导入的前导点
    base_mod: str = mod_name_buf
    while base_mod[0] == '.':
        base_mod = base_mod + 1

    # 4. 查找模块的 SHA1
    sha1: str = HandlesExprCall._lookup_module_sha1(base_mod)
    if sha1 is None:
        sha1 = HandlesExprCall._lookup_module_sha1_suffix(base_mod)
    if sha1 is None:
        return -1

    # 5. 获取 temp_dir
    temp_dir: str = HandlesType.get_temp_dir()
    if temp_dir is None:
        return -1

    # 6. 构建 pyi 文件路径
    td_len: t.CSizeT = string.strlen(temp_dir)
    pyi_path: str = StubMerger._sliced_path(temp_dir, td_len, sha1, "pyi")
    if pyi_path is None:
        return -1

    # 7. 读取 pyi 文件；若不存在或内容过短（空 stub）则回退读取源 .py 文件
    PYI_READ_BUF_SIZE: t.CSizeT = 65536
    pyi_buf: bytes = stdlib.malloc(PYI_READ_BUF_SIZE)
    if pyi_buf is None:
        stdlib.free(pyi_path)
        return -1
    pf: fileio.File | t.CPtr = fileio.File(pyi_path, fileio.MODE.R)
    need_py_fallback: int = 0
    bytes_read: LONG = 0
    if pf.closed:
        # .pyi 文件不存在：回退读取源 .py 文件
        need_py_fallback = 1
    else:
        bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
        pf.close()
        # pyi 内容过短（< 10 字节）视为空 stub，回退 .py
        if bytes_read < 10:
            need_py_fallback = 1
        else:
            if bytes_read < PYI_READ_BUF_SIZE:
                pyi_buf[bytes_read] = 0
            else:
                pyi_buf[PYI_READ_BUF_SIZE - 1] = 0
    stdlib.free(pyi_path)

    if need_py_fallback != 0:
        # 从 SHA1 存储器查找相对路径，构建 {base_dir}/{rel_path}
        src_path: str = _build_source_path_from_sha1(sha1)
        if src_path is None:
            # 存储器中找不到 SHA1 = 编译器 bug（Phase1/Phase2 未填充存储器）
            # 报错终止，不静默返回 -1（避免后续生成错误的 @UNDEFINED 全局变量引用）
            err_buf: str = pool.alloc(512)
            if err_buf is not None:
                viperlib.snprintf(err_buf, 512,
                    "跨模块 CDefine 查找失败：SHA1 %s 不在存储器中（base_mod=%s name=%s）。"
                    "Phase1/Phase2 未正确填充 PopulateSha1MapStore/AppendToSha1MapStore",
                    sha1, base_mod, name)
                VLogger.error(err_buf, "XMOD-CD")
            stdlib.free(pyi_buf)
            return -1
        pf = fileio.File(src_path, fileio.MODE.R)
        stdlib.free(src_path)
        if pf.closed:
            stdlib.free(pyi_buf)
            return -1
        bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
        pf.close()
        if bytes_read <= 0:
            stdlib.free(pyi_buf)
            return -1
        if bytes_read < PYI_READ_BUF_SIZE:
            pyi_buf[bytes_read] = 0
        else:
            pyi_buf[PYI_READ_BUF_SIZE - 1] = 0

    # 8. 在 pyi_buf 中查找 "name: t.CDefine = value" 行
    #    格式: NAME: t.CDefine = value
    name_len: t.CSizeT = string.strlen(name)
    total_len: t.CSizeT = string.strlen(pyi_buf)
    pos: t.CSizeT = 0
    result_val: int = 0
    result_found: int = 0
    while pos < total_len:
        # 找到行首
        line_start: t.CSizeT = pos
        # 找到行尾
        while pos < total_len and pyi_buf[pos] != '\n':
            pos += 1
        line_len: t.CSizeT = pos - line_start
        if pos < total_len:
            pos += 1  # 跳过 '\n'

        # 检查行是否以 "name:" 开头
        if line_len < name_len + 1:
            continue
        match_ok: int = 1
        for ki in range(name_len):
            if pyi_buf[line_start + ki] != name[ki]:
                match_ok = 0
                break
        if match_ok == 0:
            continue
        if pyi_buf[line_start + name_len] != ':':
            continue

        # 检查行是否包含 "t.CDefine"
        # 在行中查找 "t.CDefine"
        has_cdefine: int = 0
        ci: t.CSizeT = line_start + name_len + 1
        while ci + 8 <= line_start + line_len:
            if (pyi_buf[ci] == 't' and pyi_buf[ci + 1] == '.' and
                pyi_buf[ci + 2] == 'C' and pyi_buf[ci + 3] == 'D' and
                pyi_buf[ci + 4] == 'e' and pyi_buf[ci + 5] == 'f' and
                pyi_buf[ci + 6] == 'i' and pyi_buf[ci + 7] == 'n' and
                pyi_buf[ci + 8] == 'e'):
                has_cdefine = 1
                break
            ci += 1
        if has_cdefine == 0:
            continue

        # 找到 "=" 后面的值
        eq_pos: t.CSizeT = line_start + name_len + 1
        while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '=':
            eq_pos += 1
        if eq_pos >= line_start + line_len:
            continue
        eq_pos += 1  # 跳过 '='

        # 跳过空格
        while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
            eq_pos += 1

        # 检查是否是 t.CUnsignedLong(...) / t.CLong(...) / t.CInt(...) 等类型构造函数
        # 格式: t.CUnsignedLong(-11) / t.CUnsignedLong(0xFFFFFFFF)
        # 如果是，跳过 "t.CXxx(" 前缀，解析括号内的值，忽略结尾 ')'
        is_type_ctor: int = 0
        if eq_pos + 2 < line_start + line_len:
            if pyi_buf[eq_pos] == 't' and pyi_buf[eq_pos + 1] == '.':
                # 找到 '(' 的位置
                paren_pos: t.CSizeT = eq_pos + 2
                while paren_pos < line_start + line_len and pyi_buf[paren_pos] != '(':
                    paren_pos += 1
                if paren_pos < line_start + line_len and pyi_buf[paren_pos] == '(':
                    is_type_ctor = 1
                    eq_pos = paren_pos + 1  # 跳过 '('
                    # 跳过括号内可能的前导空格
                    while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
                        eq_pos += 1

        # 解析整数值（支持十六进制 0x 前缀和负号）
        # 负号标记
        is_neg: int = 0
        if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-':
            is_neg = 1
            eq_pos += 1
        val_str_start: t.CSizeT = eq_pos
        val_str_len: t.CSizeT = 0
        is_hex: int = 0
        if eq_pos + 1 < line_start + line_len:
            if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'):
                is_hex = 1
                val_str_len = 2
                eq_pos += 2
        while eq_pos < line_start + line_len:
            ch: t.CChar = pyi_buf[eq_pos]
            if is_hex == 1:
                if ((ch >= '0' and ch <= '9') or
                    (ch >= 'a' and ch <= 'f') or
                    (ch >= 'A' and ch <= 'F')):
                    val_str_len += 1
                    eq_pos += 1
                else:
                    break
            else:
                if ch >= '0' and ch <= '9':
                    val_str_len += 1
                    eq_pos += 1
                else:
                    break

        if val_str_len == 0:
            continue

        # 复制值字符串到新缓冲区并解析
        val_buf: str = pool.alloc(val_str_len + 1)
        if val_buf is None:
            continue
        for vi in range(val_str_len):
            val_buf[vi] = pyi_buf[val_str_start + vi]
        val_buf[val_str_len] = '\0'

        if is_hex == 1:
            # 手动解析十六进制（跳过 0x 前缀）
            hex_result: int = 0
            hex_buf: str = val_buf + 2  # 跳过 "0x"
            for hi in range(val_str_len - 2):
                hc: t.CChar = hex_buf[hi]
                hd: int = 0
                if hc >= '0' and hc <= '9':
                    hd = hc - '0'
                elif hc >= 'a' and hc <= 'f':
                    hd = hc - 'a' + 10
                elif hc >= 'A' and hc <= 'F':
                    hd = hc - 'A' + 10
                else:
                    break
                hex_result = hex_result * 16 + hd
            result_val = hex_result
            # 十六进制负值（如 0xFFFFFFFF）保持原样，由 32 位截断处理
            result_found = 1
        else:
            result_val = string.atoi(val_buf)
            # 应用负号（如 t.CUnsignedLong(-11) → -11）
            if is_neg != 0:
                result_val = -result_val
            result_found = 1
        break

    # 9.5 如果当前模块文件中没找到，递归查找子模块
    # 处理 from .X import NAME 和 from .X import * 的情况
    # 例如 ast.__init__.py 中 from .base import CONST_INT, ...
    # 需要递归查找 ast.base 子模块中的 CONST_INT: t.CDefine = 1
    if result_found == 0:
        name_len_rc: t.CSizeT = string.strlen(name)
        rc_pos: t.CSizeT = 0
        while rc_pos < total_len and result_found == 0:
            # 找到行首
            rc_line_start: t.CSizeT = rc_pos
            # 找到行尾
            while rc_pos < total_len and pyi_buf[rc_pos] != '\n':
                rc_pos += 1
            rc_line_len: t.CSizeT = rc_pos - rc_line_start
            if rc_pos < total_len:
                rc_pos += 1

            # 检查行是否以 "from ." 开头（相对导入子模块）
            if rc_line_len < 12:
                continue
            if pyi_buf[rc_line_start] != 'f' or pyi_buf[rc_line_start+1] != 'r' or \
               pyi_buf[rc_line_start+2] != 'o' or pyi_buf[rc_line_start+3] != 'm' or \
               pyi_buf[rc_line_start+4] != ' ' or pyi_buf[rc_line_start+5] != '.':
                continue

            # 提取子模块名（从 rc_line_start+6 开始，到空格/tab 为止）
            rc_sub_start: t.CSizeT = rc_line_start + 6
            rc_sub_end: t.CSizeT = rc_sub_start
            while rc_sub_end < rc_line_start + rc_line_len and \
                  pyi_buf[rc_sub_end] != ' ' and pyi_buf[rc_sub_end] != '\t':
                rc_sub_end += 1
            rc_sub_len: t.CSizeT = rc_sub_end - rc_sub_start
            if rc_sub_len == 0:
                continue

            # 检查行是否包含 "import"
            rc_has_import: int = 0
            rc_ipos: t.CSizeT = rc_sub_end
            while rc_ipos + 6 <= rc_line_start + rc_line_len:
                if pyi_buf[rc_ipos] == 'i' and pyi_buf[rc_ipos+1] == 'm' and \
                   pyi_buf[rc_ipos+2] == 'p' and pyi_buf[rc_ipos+3] == 'o' and \
                   pyi_buf[rc_ipos+4] == 'r' and pyi_buf[rc_ipos+5] == 't':
                    rc_has_import = 1
                    break
                rc_ipos += 1
            if rc_has_import == 0:
                continue

            # 检查 NAME 是否在导入列表中，或导入是 *
            # 处理多行导入：from .X import (... NAME, ...)
            # 从 "import" 之后扫描，如果有 "(" 则扫描到 ")" 为止
            rc_imp_start: t.CSizeT = rc_ipos + 6
            rc_star: int = 0
            rc_name_match: int = 0

            # 确定扫描范围：单行或跨行（括号内）
            rc_scan_end: t.CSizeT = rc_line_start + rc_line_len
            rc_has_paren: int = 0
            rc_pp: t.CSizeT = rc_imp_start
            while rc_pp < rc_line_start + rc_line_len:
                if pyi_buf[rc_pp] == '(':
                    rc_has_paren = 1
                    break
                if pyi_buf[rc_pp] == '*':
                    rc_star = 1
                rc_pp += 1

            if rc_has_paren != 0:
                # 多行导入：从 "(" 之后扫描到 ")" 为止
                rc_scan_end = rc_pp + 1
                rc_paren_closed: int = 0
                rc_sp: t.CSizeT = rc_pp + 1
                while rc_sp < total_len and rc_paren_closed == 0:
                    if pyi_buf[rc_sp] == ')':
                        rc_paren_closed = 1
                        rc_scan_end = rc_sp
                        break
                    rc_sp += 1
                if rc_paren_closed == 0:
                    rc_scan_end = total_len

            # 在 rc_imp_start 到 rc_scan_end 范围内查找 NAME 和 *
            rc_cpos: t.CSizeT = rc_imp_start
            while rc_cpos < rc_scan_end:
                rc_ch: t.CChar = pyi_buf[rc_cpos]
                if rc_ch == '*':
                    rc_star = 1
                # 检查 NAME 是否匹配（作为完整单词）
                if rc_cpos + name_len_rc <= rc_scan_end:
                    rc_match: int = 1
                    rc_ki: t.CSizeT
                    for rc_ki in range(name_len_rc):
                        if pyi_buf[rc_cpos + rc_ki] != name[rc_ki]:
                            rc_match = 0
                            break
                    if rc_match == 1:
                        # 检查前字符是否是非标识符字符
                        rc_before_ok: int = 0
                        if rc_cpos == rc_imp_start:
                            rc_before_ok = 1
                        else:
                            rc_before_ch: t.CChar = pyi_buf[rc_cpos - 1]
                            if rc_before_ch == ' ' or rc_before_ch == '\t' or \
                               rc_before_ch == '(' or rc_before_ch == ',' or \
                               rc_before_ch == '\n' or rc_before_ch == '\r':
                                rc_before_ok = 1
                        # 检查后字符是否是非标识符字符
                        rc_after_ok: int = 0
                        rc_after_pos: t.CSizeT = rc_cpos + name_len_rc
                        if rc_after_pos >= rc_scan_end:
                            rc_after_ok = 1
                        else:
                            rc_after_ch: t.CChar = pyi_buf[rc_after_pos]
                            if rc_after_ch == ' ' or rc_after_ch == '\t' or \
                               rc_after_ch == ')' or rc_after_ch == ',' or \
                               rc_after_ch == '\r' or rc_after_ch == '\n':
                                rc_after_ok = 1
                        if rc_before_ok == 1 and rc_after_ok == 1:
                            rc_name_match = 1
                rc_cpos += 1

            if rc_star == 0 and rc_name_match == 0:
                continue

            # 复制子模块名到新缓冲区（pyi_buf + rc_sub_start 不是 NUL 结尾）
            rc_sub_buf: str = pool.alloc(rc_sub_len + 1)
            if rc_sub_buf is None:
                continue
            rc_si: t.CSizeT
            for rc_si in range(rc_sub_len):
                rc_sub_buf[rc_si] = pyi_buf[rc_sub_start + rc_si]
            rc_sub_buf[rc_sub_len] = '\0'

            # 构建完整子模块名 base_mod.X
            rc_full_len: t.CSizeT = string.strlen(base_mod) + 1 + rc_sub_len + 1
            rc_full_mod: str = pool.alloc(rc_full_len)
            if rc_full_mod is None:
                continue
            viperlib.snprintf(rc_full_mod, rc_full_len, "%s.%s", base_mod, rc_sub_buf)

            # 构建临时 from_imports "name:full_mod"
            rc_fi_len: t.CSizeT = name_len_rc + 1 + string.strlen(rc_full_mod) + 1
            rc_fi: str = pool.alloc(rc_fi_len)
            if rc_fi is None:
                continue
            viperlib.snprintf(rc_fi, rc_fi_len, "%s:%s", name, rc_full_mod)

            # 递归调用查找子模块
            rc_sub_val: int = _lookup_cross_module_cdefine(pool, name, rc_fi)
            if HandlesType.is_cdefine_found() != 0:
                result_val = rc_sub_val
                result_found = 1

    stdlib.free(pyi_buf)

    # 9.6 from_imports star fallback 模块中未找到：回退到 imported_modules 遍历
    # 当 `from stdint import *` 导致 star fallback 返回 stdint，但目标常量
    # （如 SHARE_READ）实际在 `import w32.fileio` 导入的模块中时，
    # 需要遍历 imported_modules 逐个查找
    if result_found == 0 and imported_modules is not None:
        im2_len: t.CSizeT = string.strlen(imported_modules)
        im2_ci: t.CSizeT = 0
        while im2_ci < im2_len and result_found == 0:
            while im2_ci < im2_len and imported_modules[im2_ci] == ' ':
                im2_ci += 1
            if im2_ci >= im2_len:
                break
            im2_start: t.CSizeT = im2_ci
            while im2_ci < im2_len and imported_modules[im2_ci] != ' ':
                im2_ci += 1
            im2_end: t.CSizeT = im2_ci
            im2_nlen: t.CSizeT = im2_end - im2_start
            if im2_nlen > 0 and im2_nlen < 256:
                im2_buf: str = pool.alloc(im2_nlen + 1)
                if im2_buf is not None:
                    mk3: t.CSizeT = 0
                    while mk3 < im2_nlen:
                        im2_buf[mk3] = imported_modules[im2_start + mk3]
                        mk3 += 1
                    im2_buf[im2_nlen] = '\0'
                    sub_fi2: str = pool.alloc(im2_nlen + 3)
                    if sub_fi2 is not None:
                        sub_fi2[0] = '*'
                        sub_fi2[1] = ':'
                        mk4: t.CSizeT = 0
                        while mk4 < im2_nlen:
                            sub_fi2[2 + mk4] = im2_buf[mk4]
                            mk4 += 1
                        sub_fi2[2 + im2_nlen] = '\0'
                        sub_val2: int = _lookup_cross_module_cdefine(pool, name, sub_fi2, None)
                        if HandlesType.is_cdefine_found() != 0:
                            result_val = sub_val2
                            result_found = 1

    # 10. 缓存到当前模块的 CDefine 表中
    if result_found != 0:
        HandlesType.register_cdefine_constant(pool, name, result_val)
        HandlesType.set_cdefine_found(1)
    else:
        HandlesType.set_cdefine_found(0)

    return result_val


# ============================================================
# _is_cdefine_name_pattern - 检查名字是否符合 CDefine 命名约定
#
# CDefine 常量（如 FOREGROUND_GREEN, STD_OUTPUT_HANDLE, SCOPE_MODULE）
# 都遵循 ALL_CAPS 约定：仅含大写字母 A-Z、数字 0-9、下划线 _，
# 且至少含一个大写字母。
#
# Python 关键字（match, if, for）和变量名（self, pool, builder）
# 都含小写字母，不会匹配，从而避免 fail-fast 误报。
#
# 返回: 1=符合 CDefine 命名约定, 0=不符合
# ============================================================
def _is_cdefine_name_pattern(name: str) -> int:
    """检查名字是否符合 CDefine 命名约定（全大写+下划线+数字）"""
    if name is None:
        return 0
    has_upper: int = 0
    i: t.CSizeT = 0
    while name[i] != '\0':
        c: t.CChar = name[i]
        if c >= 'a' and c <= 'z':
            return 0
        if c >= 'A' and c <= 'Z':
            has_upper = 1
        i += 1
    return has_upper


# ============================================================
# 翻译变量引用（Name 节点）→ load
# ============================================================
def translate_name_value(builder: llvmlite.IRBuilder | t.CPtr,
                         pool: memhub.MemBuddy | t.CPtr,
                         node: ast.AST | t.CPtr,
                         trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
    """翻译变量引用（Name 节点）→ load"""
    nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
    if nm is None:
        return None
    nm_id: str = nm.id
    if nm_id is None:
        return None

    # CDefine 编译期常量: 直接返回整数常量值（不生成运行时代码）
    # NAME: t.CDefine = value 形式定义的常量在编译期已注册到本地表
    cdef_val: int = HandlesType.lookup_cdefine_constant(nm_id)
    if HandlesType.is_cdefine_found() != 0:
        return llvmlite.const_int32(pool, cdef_val)

    # 本地表未找到：尝试跨模块 .pyi 查找（保证多模块查表存表一致）
    # 仅对符合 CDefine 命名约定（ALL_CAPS）的名字触发跨模块查找，
    # 避免对普通变量名（mb/name/ptr/pool/self 等）误触发，造成严重性能损耗
    if _is_cdefine_name_pattern(nm_id) != 0:
        if trans is not None:
            cdef_val = _lookup_cross_module_cdefine(
                pool, nm_id, trans._from_imports, trans._imported_modules)
            if HandlesType.is_cdefine_found() != 0:
                return llvmlite.const_int32(pool, cdef_val)

    # global 变量：从模块作用域查找（global 声明优先级最高）
    if trans is not None:
        if HT.is_global_name(trans, nm_id) != 0:
            mod_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var(
                trans.SymTab, nm_id)
            if mod_alloca is not None:
                load_ty: llvmlite.LLVMType | t.CPtr = None
                if mod_alloca.Ty is not None:
                    load_ty = mod_alloca.Ty.Pointee
                if load_ty is None:
                    load_ty = llvmlite.Int32(pool)
                return llvmlite.build_load(builder, load_ty, mod_alloca)

        # nonlocal 变量：通过闭包 env 访问
        if HT.is_nonlocal_name(trans, nm_id) != 0:
            return HandlesNonlocal.load_nonlocal_var(trans, nm_id)

    # 局部变量查找（必须先于模块别名检查）
    # 否则与模块同名的局部变量（如 `import t, c` 后的局部变量 c）会被误判为模块别名，
    # 导致 SymTab 已注册的局部变量查找不到（rhs_val is None bug 根因）
    alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm_id)
    if alloca is not None:
        load_ty: llvmlite.LLVMType | t.CPtr = None
        if alloca.Ty is not None:
            load_ty = alloca.Ty.Pointee
        if load_ty is None:
            load_ty = llvmlite.Int32(pool)
        return llvmlite.build_load(builder, load_ty, alloca)

    # 模块别名检查：SymTab 查找失败后才检查
    # 如果 nm_id 是已导入模块名（如 win32file, fileio）且未被声明为局部变量，
    # 返回 None 让上层处理（用于属性访问 win32file.CreateFileA）
    if trans is not None and trans._imported_modules is not None:
        if HandlesImports.is_module_imported(trans._imported_modules, nm_id) != 0:
            return None

    # 所有查找路径均失败
    # fail-fast: 仅对符合 CDefine 命名约定（ALL_CAPS）的名字报错
    # 避免对 match/self/pool 等关键字和变量名误报
    # 跨模块 CDefine 查找已在上方完成，此处不重复
    if _is_cdefine_name_pattern(nm_id) != 0:
        err_buf2: str = pool.alloc(256)
        if err_buf2 is not None:
            cur_sha1: str = "(unknown)"
            if trans is not None and trans.ModuleSha1 is not None:
                cur_sha1 = trans.ModuleSha1
            viperlib.snprintf(err_buf2, 256,
                "[CD] FATAL: Name '%s' NOT found (CDefine local+cross + global + nonlocal + alloca + module alias) in module sha1=%s\n",
                nm_id, cur_sha1)
            VLogger.error(err_buf2, "CD")
    return None


# ============================================================
# ExprHandle - 表达式处理器（Mixin 继承模式）
#
# HandleValue 提供 trans 接口，委托到模块级 translate_value
# ============================================================
@t.NoVTable
class ExprHandle(HandlesBase.Mixin):
    """表达式处理器：继承 Mixin 获得 Trans 回指针"""

    def __init__(self, trans: HT.Translator | t.CPtr):
        self.Trans = trans

    # ============================================================
    # HandleValue - 翻译值表达式为 LLVM Value
    # ============================================================
    def HandleValue(self, node: ast.AST | t.CPtr) -> llvmlite.Value | t.CPtr:
        """翻译值表达式，从 self.Trans 获取共享状态"""
        return translate_value(
            self.Trans._cur_builder, self.Trans.Pool, self.Trans.Module,
            node,
            self.Trans._funcs, self.Trans._func_count, self.Trans)


# ============================================================
# NewExprHandle - 工厂函数
# ============================================================
def NewExprHandle(pool: memhub.MemBuddy | t.CPtr,
                  trans: HT.Translator | t.CPtr) -> ExprHandle | t.CPtr:
    h: ExprHandle | t.CPtr = pool.alloc(ExprHandle.__sizeof__())
    if h is None:
        return None
    string.memset(h, 0, ExprHandle.__sizeof__())
    h.Trans = trans
    return h


# ============================================================
# 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(builder: llvmlite.IRBuilder | t.CPtr,
                        pool: memhub.MemBuddy | t.CPtr,
                        lm_obj: llvmlite.Value | t.CPtr,
                        idx_val: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
    """内联生成 list __getitem__ 逻辑，返回元素地址 (i8*)"""
    if builder is None or pool is None or lm_obj is None or idx_val is None:
        return None
    i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
    i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
    i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
    # 加载 __data__ (偏移 0, index 0)
    g_idx0: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
    g_dpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, g_idx0)
    if g_dpp is None:
        return None
    g_data: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, g_dpp)
    # 加载 __elem_size__ (偏移 32, index 4)
    g_idx4: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 4)
    g_epp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, g_idx4)
    if g_epp is None:
        return None
    g_esize: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, g_epp)
    if g_data is None or g_esize is None:
        return None
    g_idx_i64: llvmlite.Value | t.CPtr = coerce_to_type(builder, idx_val, i64_ty)
    if g_idx_i64 is None:
        return None
    g_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, g_idx_i64, g_esize)
    if g_offset is None:
        return None
    # 返回元素地址 (i8*)，不 load
    return llvmlite.build_gep(builder, i8_ty, g_data, g_offset)


# ============================================================
# _list_elem_type_from_name — 从 list 类型名提取元素 LLVM 类型
#
# 类型名格式: <sha1>.list[<elem_type>]
# 用 strstr 检查完整模式，避免子串提取
# 支持: list[int]→i32, list[str]/list[bytes]→i8*, list[CInt]→i32 等
# 默认: i32
# ============================================================
def _list_elem_type_from_name(pool: memhub.MemBuddy | t.CPtr,
                              struct_name: str) -> llvmlite.LLVMType | t.CPtr:
    """从 list 类型名提取元素 LLVM 类型"""
    if pool is None or struct_name is None:
        return None
    # 用 strstr 检查完整模式
    if string.strstr(struct_name, "list[int]") is not None:
        return llvmlite.Int32(pool)
    if string.strstr(struct_name, "list[CInt]") is not None:
        return llvmlite.Int32(pool)
    if string.strstr(struct_name, "list[CInt8T]") is not None:
        return llvmlite.Int8(pool)
    if string.strstr(struct_name, "list[CInt16T]") is not None:
        return llvmlite.Int16(pool)
    if string.strstr(struct_name, "list[CInt32T]") is not None:
        return llvmlite.Int32(pool)
    if string.strstr(struct_name, "list[CInt64T]") is not None:
        return llvmlite.Int64(pool)
    if string.strstr(struct_name, "list[CSizeT]") is not None:
        return llvmlite.Int64(pool)
    if string.strstr(struct_name, "list[CDouble]") is not None:
        return llvmlite.Double(pool)
    if string.strstr(struct_name, "list[CFloat]") is not None:
        return llvmlite.Float(pool)
    if string.strstr(struct_name, "list[CChar]") is not None:
        return llvmlite.Int8(pool)
    # str/bytes/CPtr/其他指针类型 → i8*
    i8_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
    return llvmlite.Ptr(pool, i8_ty_d)


# ============================================================
# list_setitem_inline — 内联生成 list[T] __setitem__ 逻辑
#
# 直接存储 rhs_val 到元素地址 (coerce 为 i8* 后 store)
# ============================================================
def list_setitem_inline(builder: llvmlite.IRBuilder | t.CPtr,
                        pool: memhub.MemBuddy | t.CPtr,
                        lm_obj: llvmlite.Value | t.CPtr,
                        idx_val: llvmlite.Value | t.CPtr,
                        rhs_val: llvmlite.Value | t.CPtr) -> int:
    """内联生成 list __setitem__ 逻辑，返回 0"""
    if builder is None or pool is None or lm_obj is None or idx_val is None or rhs_val is None:
        return 0
    i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
    i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
    i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
    # 加载 __data__ (偏移 0, index 0)
    s_idx0: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
    s_dpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, s_idx0)
    if s_dpp is None:
        return 0
    s_data: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, s_dpp)
    # 加载 __elem_size__ (偏移 32, index 4)
    s_idx4: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 4)
    s_epp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, s_idx4)
    if s_epp is None:
        return 0
    s_esize: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, s_epp)
    if s_data is None or s_esize is None:
        return 0
    s_idx_i64: llvmlite.Value | t.CPtr = coerce_to_type(builder, idx_val, i64_ty)
    if s_idx_i64 is None:
        return 0
    s_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, s_idx_i64, s_esize)
    if s_offset is None:
        return 0
    s_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, s_data, s_offset)
    if s_addr is None:
        return 0
    s_val_ptr: llvmlite.Value | t.CPtr = coerce_to_type(builder, rhs_val, i8_ptr_ty)
    if s_val_ptr is None:
        return 0
    llvmlite.build_store(builder, s_val_ptr, s_addr)
    return 0


# ============================================================
# is_list_subscript — 检查 Subscript 节点是否是 list[T] 类型
#
# 返回 list 对象指针 (Ptr(list[T])) 或 None
# ============================================================
def is_list_subscript(node: ast.AST | t.CPtr,
                      trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
    """检查 Subscript 节点是否是 list[T] 类型，返回 list 对象指针或 None"""
    if node is None or trans is None:
        return None
    sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
    if sub is None or sub.value is None:
        return None
    if sub.value.kind() != ast.ASTKind.Name:
        return None
    nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
    if nm is None or nm.id is None:
        return None
    alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
    if alloca is None or alloca.Ty is None:
        return None
    if is_ptr_type(alloca.Ty) == 0:
        return None
    pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
    if pointee is None:
        return None
    # 检查 pointee 是否是 Ptr(Struct)
    inner_ty: llvmlite.LLVMType | t.CPtr = None
    match pointee:
        case llvmlite.LLVMType.Ptr(it):
            inner_ty = it
        case _:
            return None
    if inner_ty is None:
        return None
    # 检查 inner_ty 是否是 Struct 且名称包含 "list["
    struct_name: str = None
    match inner_ty:
        case llvmlite.LLVMType.Struct(_, _, sname):
            struct_name = sname
        case _:
            return None
    if struct_name is None:
        return None
    if string.strstr(struct_name, "list[") is None:
        return None
    # 是 list 类型: load 出 list 对象指针
    builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
    return llvmlite.build_load(builder, pointee, alloca)


# ============================================================
# 翻译下标表达式 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(builder: llvmlite.IRBuilder | t.CPtr,
                        pool: memhub.MemBuddy | t.CPtr,
                        mod: llvmlite.LLVMModule | t.CPtr,
                        node: ast.AST | t.CPtr,
                        trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
    """翻译下标表达式，返回加载的元素值"""
    if node is None or builder is None:
        return None
    sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
    if sub is None:
        return None

    # 翻译索引
    idx_val: llvmlite.Value | t.CPtr = translate_value(
        builder, pool, mod, sub.slice, None, 0, trans)
    if idx_val is None:
        return None

    # 如果 value 是 Name，尝试从 alloca 类型推断
    if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
        nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
        if nm.id is not None and trans is not None:
            alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
            if alloca is not None and alloca.Ty is not None:
                if is_ptr_type(alloca.Ty) != 0:
                    pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
                    if pointee is not None:
                        # 不调用 pointee.kind() 避免跨模块引用 LLVMType.kind 符号
                        # 单层 match（避免嵌套 match 的编译器 bug）
                        match pointee:
                            case llvmlite.LLVMType.Array(elem_ty, count):
                                # 数组遍历: getelementptr [N x elem_ty], ... , i32 0, i32 %idx
                                elem_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
                                    builder, pointee, elem_ty, alloca, idx_val)
                                if elem_ptr is not None:
                                    return llvmlite.build_load(builder, elem_ty, elem_ptr)
                                return None
                            case llvmlite.LLVMType.Ptr(inner_ty):
                                # 检查 inner_ty 是否是 list[T] 类型（泛型类不注册 struct）
                                # list 的 subscript 应该走 __getitem__ 内联路径，而非指针遍历
                                list_struct_name: str = None
                                match inner_ty:
                                    case llvmlite.LLVMType.Struct(_, _, lsn):
                                        list_struct_name = lsn
                                    case _:
                                        pass
                                if list_struct_name is not None:
                                    if string.strstr(list_struct_name, "list[") is not None:
                                        # list[T] 类型: 内联生成 __getitem__ 逻辑
                                        ptr_val_list: llvmlite.Value | t.CPtr = llvmlite.build_load(
                                            builder, pointee, alloca)
                                        if ptr_val_list is not None:
                                            elem_addr_list: llvmlite.Value | t.CPtr = list_getitem_inline(
                                                builder, pool, ptr_val_list, idx_val)
                                            if elem_addr_list is not None:
                                                # 根据类型名提取元素类型，bitcast 后 load
                                                elem_ty_list: llvmlite.LLVMType | t.CPtr = _list_elem_type_from_name(
                                                    pool, list_struct_name)
                                                if elem_ty_list is not None:
                                                    elem_ptr_ty_list: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
                                                        pool, elem_ty_list)
                                                    casted_addr_list: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
                                                        builder, elem_addr_list, elem_ptr_ty_list)
                                                    if casted_addr_list is not None:
                                                        return llvmlite.build_load(
                                                            builder, elem_ty_list, casted_addr_list)
                                        return None
                                # inner_ty 是 Ptr 说明 alloca.Ty 是三重指针 Ptr(Ptr(Ptr(...))),
                                # 即 X|t.CPtr 当 X 本身是指针类型 (如 T=AST|t.CPtr, T|t.CPtr=AST**)。
                                # 此时 [i] 应该是指针解引用 (步长 sizeof(ptr)=8), 而非调用
                                # 结构体的 __getitem__ 方法 (会错误生成 AST.__getitem__ 调用)。
                                # 对比 HashTable|t.CPtr: alloca.Ty=HashTable**, inner_ty=Struct(HashTable)
                                # (非 Ptr), 仍走下面的方法调用转发路径。
                                if is_ptr_type(inner_ty) != 0:
                                    ptr_val_pp: llvmlite.Value | t.CPtr = llvmlite.build_load(
                                        builder, pointee, alloca)
                                    if ptr_val_pp is not None:
                                        elem_ptr_pp: llvmlite.Value | t.CPtr = llvmlite.build_gep(
                                            builder, inner_ty, ptr_val_pp, idx_val)
                                        if elem_ptr_pp is not None:
                                            return llvmlite.build_load(builder, inner_ty, elem_ptr_pp)
                                    return None
                                # 自定义结构体 (如 HashTable|t.CPtr, JsonValue|t.CPtr):
                                # 优先转发到 __getitem__，避免 IsPtrElement 误判为指针遍历
                                cls_nm_rd_ptr: str = _get_custom_struct_cls_nm(pool, inner_ty)
                                if not (cls_nm_rd_ptr is None):
                                    obj_ptr_rd: llvmlite.Value | t.CPtr = llvmlite.build_load(
                                        builder, pointee, alloca)
                                    if not (obj_ptr_rd is None):
                                        arg_vals_rd_p: t.CSizeT | t.CPtr = pool.alloc(8)
                                        if not (arg_vals_rd_p is None):
                                            arg_vals_rd_p[0] = t.CSizeT(idx_val)
                                            ret_rd_p: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
                                                pool, builder, mod, cls_nm_rd_ptr, "__getitem__",
                                                obj_ptr_rd, arg_vals_rd_p, 1, trans)
                                            if not (ret_rd_p is None):
                                                return ret_rd_p
                                    return None
                                # bytes|t.CPtr / str|t.CPtr: alloca 是 i8**，
                                # 直接 GEP 按 i8* 步长（8 字节），load i8*
                                # 仅当 inner_ty 不是自定义结构体时才走指针遍历
                                if trans is not None:
                                    ve_pe_ts: HandlesVar.VarEntry | t.CPtr = \
                                        HandlesVar.lookup_var_entry(trans.SymTab, nm.id)
                                    if ve_pe_ts is not None and ve_pe_ts.IsPtrElement == 1:
                                        elem_ptr_ts: llvmlite.Value | t.CPtr = llvmlite.build_gep(
                                            builder, pointee, alloca, idx_val)
                                        if elem_ptr_ts is not None:
                                            return llvmlite.build_load(builder, pointee, elem_ptr_ts)
                                        return None
                                # 普通指针遍历: 先 load 指针值，再 GEP
                                ptr_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
                                    builder, pointee, alloca)
                                if ptr_val is not None:
                                    elem_ptr2: llvmlite.Value | t.CPtr = llvmlite.build_gep(
                                        builder, inner_ty, ptr_val, idx_val)
                                    if elem_ptr2 is not None:
                                        return llvmlite.build_load(builder, inner_ty, elem_ptr2)
                                return None
                            case _:
                                # 自定义类（结构体类型）: 转发到 __getitem__ 方法调用
                                # 如 hashtable[key] → hashtable.__getitem__(key)
                                cls_nm_sub: str = HandlesStruct.get_class_name_by_type(pool, pointee)
                                if cls_nm_sub is None:
                                    pass
                                if not (cls_nm_sub is None):
                                    obj_val_sub: llvmlite.Value | t.CPtr = llvmlite.build_load(
                                        builder, pointee, alloca)
                                    if not (obj_val_sub is None):
                                        arg_vals_sub: t.CSizeT | t.CPtr = pool.alloc(8)
                                        if not (arg_vals_sub is None):
                                            arg_vals_sub[0] = t.CSizeT(idx_val)
                                            ret_sub: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
                                                pool, builder, mod, cls_nm_sub, "__getitem__",
                                                obj_val_sub, arg_vals_sub, 1, trans)
                                            if not (ret_sub is None):
                                                return ret_sub
                                pass

    # 通用路径：翻译 value 获取指针
    ptr_val: llvmlite.Value | t.CPtr = translate_value(
        builder, pool, mod, sub.value, None, 0, trans)
    if ptr_val is None or ptr_val.Ty is None:
        return None

    # 检查是否是指针类型
    if is_ptr_type(ptr_val.Ty) != 0:
        elem_ty2: llvmlite.LLVMType | t.CPtr = ptr_val.Ty.Pointee
        if elem_ty2 is not None:
            # 数组类型: 双索引 GEP + load 元素
            match elem_ty2:
                case llvmlite.LLVMType.Array(arr_elem_ty2, arr_count2):
                    elem_ptr3: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
                        builder, elem_ty2, arr_elem_ty2, ptr_val, idx_val)
                    if elem_ptr3 is not None:
                        return llvmlite.build_load(builder, arr_elem_ty2, elem_ptr3)
                    return None
                case _:
                    pass
            # 自定义结构体 (如 self._ht[key]): 转发到 __getitem__ 方法调用
            cls_nm_rd_gen: str = _get_custom_struct_cls_nm(pool, elem_ty2)
            if cls_nm_rd_gen is None:
                pass
            if not (cls_nm_rd_gen is None):
                arg_vals_rd_g: t.CSizeT | t.CPtr = pool.alloc(8)
                if not (arg_vals_rd_g is None):
                    arg_vals_rd_g[0] = t.CSizeT(idx_val)
                    ret_rd_g: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
                        pool, builder, mod, cls_nm_rd_gen, "__getitem__",
                        ptr_val, arg_vals_rd_g, 1, trans)
                    if not (ret_rd_g is None):
                        return ret_rd_g
                return None
            # 指针类型: 单索引 GEP + load
            elem_ptr4: llvmlite.Value | t.CPtr = llvmlite.build_gep(
                builder, elem_ty2, ptr_val, idx_val)
            if elem_ptr4 is not None:
                return llvmlite.build_load(builder, elem_ty2, elem_ptr4)
    return None


# ============================================================
# find_global_in_module - 在模块全局变量链表中按名称查找
#
# 查找策略:
# 1. 精确匹配 (如 "_mbuddy" 匹配 @_mbuddy)
# 2. 后缀匹配 (如 "_mbuddy" 匹配 @"sha1._mbuddy")
# ============================================================
def find_global_in_module(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(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: t.CChar | t.CPtr = pool.alloc(64)
    if ref_name is None:
        return None
    viperlib.snprintf(ref_name, 64, "@%s", name)
    return llvmlite.SSAValue(pool, ptr_ty, ref_name)


# ============================================================
# _resolve_module_attribute_global - 解析模块属性访问的全局变量引用
#
# 当 obj 是已导入模块名 (如 sys._mbuddy) 时:
# 1. 在当前模块全局变量链表中查找 (stub 已注入的情况)
# 2. 未找到则创建前向引用 (@attr_name, 类型为 ty 参数)
#
# 参数:
#   ty_hint: 类型提示 (写路径用 rhs 类型, 读路径用 i8* 回退)
# 返回: Value 指针 (类型为 ty*)，未识别为模块属性返回 None
# ============================================================
def _is_t_type_object_name(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 _resolve_module_attribute_global(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 at.value.kind() != ast.ASTKind.Name:
        return None
    if trans is None or trans._imported_modules is None:
        return None
    mod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
    if mod_nm.id is None:
        return None
    # 检查是否是已导入模块
    if HandlesImports.is_module_imported(trans._imported_modules, mod_nm.id) == 0:
        return None
    # 在当前模块查找全局变量 (stub 可能已注入)
    gv: llvmlite.GlobalVariable | t.CPtr = find_global_in_module(mod, at.attr)
    if gv is not None and gv.Ty is not None:
        return make_global_ref(pool, at.attr, gv.Ty)
    # 未找到: 创建 external global 声明并添加到模块
    # 否则只生成 @_argc 引用而无声明，LLVM 报 "use of undefined value '@_argc'"
    use_ty: llvmlite.LLVMType | t.CPtr = ty_hint
    if use_ty is None:
        use_ty = llvmlite.Int8(pool)
    # 确保 use_ty 中的跨模块结构体类型有 opaque 声明
    llvmlite.module_ensure_opaque_for_type(mod, pool, use_ty)
    # 特殊处理: t.py 中的类型对象（CPtr/CInt/CDouble 等）
    # t.py 不被编译，但这些类型对象在运行时被引用（如 va_arg(ap, t.CPtr)）。
    # 创建带 null 初值的 linkonce_odr 全局变量定义，避免链接时 undefined reference。
    # LLVM 22+ 不允许 external linkage 全局变量带初值，使用 linkonce_odr 替代。
    is_t_type_obj: int = 0
    if string.strcmp(mod_nm.id, "t") == 0:
        is_t_type_obj = _is_t_type_object_name(at.attr)
    ext_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, at.attr, use_ty)
    if ext_gv is not None:
        if is_t_type_obj != 0:
            # t.CPtr 等类型对象: 创建带 null 初值的定义（linkonce_odr）
            ext_gv.Linkage = "linkonce_odr"
            # i8* null 的初值文本
            null_init: bytes = pool.alloc(8)
            if null_init is not None:
                null_init[0] = 'n'
                null_init[1] = 'u'
                null_init[2] = 'l'
                null_init[3] = 'l'
                null_init[4] = '\0'
                ext_gv.Initializer = null_init
            else:
                ext_gv.Initializer = None
        else:
            # 普通模块属性: external 声明（由其他模块提供定义）
            ext_gv.Linkage = "external"
            ext_gv.Initializer = None
        llvmlite.module_add_global(mod, ext_gv)
    return make_global_ref(pool, at.attr, use_ty)


# ============================================================
# 翻译属性访问 Attribute(value, attr, ctx) — obj.field
#
# 优先级:
# 1. 枚举成员访问 (State.Idle → 常量值)
# 2. 模块属性访问 (sys._mbuddy → 加载全局变量)
# 3. 结构体字段访问 (obj.field → GEP + load)
#
# 对于 Name 类型的 obj，直接使用 alloca 指针（不 load 结构体）
# ============================================================
def translate_attribute(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:
        return None

    # 枚举成员访问: 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: t.CChar | t.CPtr = 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)
                return None

    # 嵌套枚举成员访问: Module.EnumName.MemberName → 常量值
    # 当 value 是 Attribute（如 fileio.MODE）且 attr 部分是已注册枚举时
    if at.value.kind() == ast.ASTKind.Attribute:
        inner_at_e: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(at.value)
        if inner_at_e.value is not None and inner_at_e.value.kind() == ast.ASTKind.Name:
            inner_nm_e: ast.Name | t.CPtr = (ast.Name | t.CPtr)(inner_at_e.value)
            if inner_nm_e.id is not None and 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: t.CChar | t.CPtr = 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)
                    return None

    # 模块属性访问 (读路径): 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 常量需要跨模块查找源 .py / .pyi 文件
                cdef_ma_val: int = 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 = _lookup_cross_module_cdefine(pool, at.attr, ma_fi)
                if HandlesType.is_cdefine_found() != 0:
                    return llvmlite.const_int32(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: t.CChar | t.CPtr = pool.alloc(submod_len)
                    if submod_buf is not None:
                        viperlib.snprintf(submod_buf, submod_len, "%s.%s", nm_ma.id, at.attr)
                        if HandlesImports.is_module_imported(trans._imported_modules, submod_buf) == 1:
                            is_submod = 1
                # also check from_imports: at.attr 别名对应真实模块 w32.win32file
                if is_submod == 0 and trans._from_imports is not None:
                    fi_mod_raw: str = HandlesImports.lookup_from_import(
                        trans._from_imports, at.attr, 0)
                    if fi_mod_raw is not None:
                        # 复制模块名到新缓冲区（截断于空格、null、或 ':'）
                        fm_len: t.CSizeT = 0
                        while fi_mod_raw[fm_len] != ' ' and fi_mod_raw[fm_len] != '\0' and fi_mod_raw[fm_len] != ':':
                            fm_len += 1
                        fi_mod: str = pool.alloc(fm_len + 1)
                        if fi_mod is not None:
                            for fm_i in range(fm_len):
                                fi_mod[fm_i] = fi_mod_raw[fm_i]
                            fi_mod[fm_len] = '\0'
                            # 检查 fi_mod 是否以 "nm_ma.id." 开头 (如 w32.win32file 以 w32. 开头)
                            prefix_len: t.CSizeT = string.strlen(nm_ma.id)
                            fi_mod_len: t.CSizeT = string.strlen(fi_mod)
                            if fi_mod_len > prefix_len + 1:
                                match_prefix: int = 1
                                for pi in range(prefix_len):
                                    if fi_mod[pi] != nm_ma.id[pi]:
                                        match_prefix = 0
                                        break
                                if match_prefix == 1 and fi_mod[prefix_len] == '.':
                                    is_submod = 1
                if is_submod != 0:
                    return None  # 是子模块，交给嵌套属性访问路径
                # 特殊处理: t.ASM_DESCR 不是 CDefine，是内联汇编约束命名空间
                # t.ASM_DESCR.XXX 由 HandlesExprCall 的 _asm_resolve_constraint 处理
                # 此处返回 None，让上层嵌套属性访问路径处理
                if string.strcmp(nm_ma.id, "t") == 0 and string.strcmp(at.attr, "ASM_DESCR") == 0:
                    return None
                # CDefine 未找到且属性名符合 ALL_CAPS 约定: 报 FATAL 错误
                # 不再创建未定义的外部全局变量引用（否则 llc 报 "use of undefined value"）
                if _is_cdefine_name_pattern(at.attr) != 0:
                    err_buf_sma: str = pool.alloc(256)
                    if err_buf_sma is not None:
                        cur_sha1_sma: str = "(unknown)"
                        if trans is not None and trans.ModuleSha1 is not None:
                            cur_sha1_sma = trans.ModuleSha1
                        viperlib.snprintf(err_buf_sma, 256,
                            "[CD] FATAL: Module attr '%s.%s' NOT found (CDefine cross-module lookup failed) in module sha1=%s\n",
                            nm_ma.id, at.attr, cur_sha1_sma)
                        VLogger.error(err_buf_sma, "CD")
                    return None
                # 非 CDefine: 使用 i8* 作为类型提示 (模块级变量通常存储指针)
                i8_ptr_hint: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
                mod_gv_ref: llvmlite.Value | t.CPtr = _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:
                        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: t.CChar | t.CPtr = pool.alloc(qual_len)
                    if qual_buf is not None:
                        viperlib.snprintf(qual_buf, qual_len, "%s.%s", inner_nm.id, inner_at.attr)
                        if HandlesImports.is_module_imported(trans._imported_modules, qual_buf) == 1:
                            # 查找全局变量 at.attr (stub 已注入的情况)
                            gv_nested: llvmlite.GlobalVariable | t.CPtr = find_global_in_module(mod, at.attr)
                            if gv_nested is not None and gv_nested.Ty is not None:
                                gv_ref_nested: llvmlite.Value | t.CPtr = make_global_ref(
                                    pool, at.attr, gv_nested.Ty)
                                if gv_ref_nested is not None and gv_ref_nested.Ty is not None:
                                    load_ty_nested: llvmlite.LLVMType | t.CPtr = gv_ref_nested.Ty.Pointee
                                    if load_ty_nested is not None:
                                        return llvmlite.build_load(builder, load_ty_nested, gv_ref_nested)
                            # 未找到全局: 尝试 CDefine 常量查找
                            # 对于 w32.win32base.INVALID_HANDLE_VALUE 等嵌套模块属性访问，
                            # 先从限定模块的 .pyi 文件中查找 CDefine 常量值并内联
                            cdef_val_nested: int = 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 = _lookup_cross_module_cdefine(pool, at.attr, tmp_fi)
                            if HandlesType.is_cdefine_found() != 0:
                                return llvmlite.const_int32(pool, cdef_val_nested)
                            # CDefine 未找到: 报 FATAL 错误（不再创建未定义的全局变量引用）
                            # 之前此处创建 @INVALID_HANDLE_VALUE 等前向引用，但全局未定义，
                            # 导致 llc 报 "use of undefined value" 错误。
                            # 正确做法是报错终止，让开发者修复跨模块 CDefine 查找失败的根本原因。
                            if _is_cdefine_name_pattern(at.attr) != 0:
                                err_buf_ma: str = pool.alloc(256)
                                if err_buf_ma is not None:
                                    cur_sha1_ma: str = "(unknown)"
                                    if trans is not None and trans.ModuleSha1 is not None:
                                        cur_sha1_ma = trans.ModuleSha1
                                    viperlib.snprintf(err_buf_ma, 256,
                                        "[CD] FATAL: Nested attr '%s.%s' NOT found (CDefine cross-module lookup failed) in module sha1=%s\n",
                                        qual_buf, at.attr, cur_sha1_ma)
                                    VLogger.error(err_buf_ma, "CD")
                            return None

    # 对于 Name 类型的 obj，直接查找 alloca（不 load 结构体）
    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:
            obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)

    # 非 Name 路径：翻译对象值（会 load）
    if obj_ptr is None:
        obj_ptr = 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 = _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)
            # 按需过滤: struct_ty 为 i8（简化联合类型）时，通过 AnnotClassName 按类名查找
            # 传递 SHA1 以区分跨模块同名类
            if field_info is 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 and var_entry.AnnotClassName is not None:
                            cur_sha1: str = trans.ModuleSha1
                            field_info = HandlesStruct.lookup_field_by_class(
                                var_entry.AnnotClassName, at.attr, cur_sha1)
                            # 按需过滤成功：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(var_entry.AnnotClassName)
                                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 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 为正确的结构体指针
                    if loaded_val is not None and field_info.AnnotClassName is not None:
                        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: 字段未找到，打印诊断信息
            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
                fb_attr: t.CChar | t.CPtr = VLogger.fmt_buf()
                if fb_attr is not None:
                    viperlib.snprintf(fb_attr, 1024,
                        "属性访问失败: .%s (var=%s annot=%s sha1=%s:%d)",
                        at.attr,
                        diag_var_nm if diag_var_nm is not None else "?",
                        diag_annot if diag_annot is not None else "None",
                        trans.ModuleSha1 if trans.ModuleSha1 is not None else "?",
                        at.lineno if at.lineno is not None else 0)
                    VLogger.error(fb_attr, "ATTR")
            return None
        case _:
            return None

# ============================================================
# 获取下标表达式的元素指针（不加载值，用于赋值）
# ============================================================
def get_subscript_ptr(builder: llvmlite.IRBuilder | t.CPtr,
                      pool: memhub.MemBuddy | t.CPtr,
                      mod: llvmlite.LLVMModule | t.CPtr,
                      node: ast.AST | t.CPtr,
                      trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
    """获取下标表达式的元素指针（用于赋值 lhs）"""
    if node is None or builder is None:
        return None
    sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
    if sub is None:
        return None

    # 翻译索引
    idx_val: llvmlite.Value | t.CPtr = translate_value(
        builder, pool, mod, sub.slice, None, 0, trans)
    if idx_val is None:
        return None

    # 如果 value 是 Name，尝试从 alloca 类型推断
    if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
        nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
        if nm.id is not None and trans is not None:
            alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
            if alloca is not None and alloca.Ty is not None:
                if is_ptr_type(alloca.Ty) != 0:
                    pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
                    if pointee is not None:
                        # 单层 match（避免嵌套 match 的编译器 bug）
                        match pointee:
                            case llvmlite.LLVMType.Array(elem_ty, count):
                                # 数组遍历
                                return llvmlite.build_gep_array(
                                    builder, pointee, elem_ty, alloca, 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 is_ptr_type(inner_ty) != 0:
                                    ptr_val_gsp: llvmlite.Value | t.CPtr = llvmlite.build_load(
                                        builder, pointee, alloca)
                                    if ptr_val_gsp is not None:
                                        return llvmlite.build_gep(
                                            builder, inner_ty, ptr_val_gsp, idx_val)
                                    return None
                                # 自定义结构体 (如 HashTable|t.CPtr, JsonValue|t.CPtr):
                                # 优先返回 None 触发 __setitem__ 重载，
                                # 避免 IsPtrElement 误判为指针遍历
                                cls_nm_ptr_chk: str = _get_custom_struct_cls_nm(pool, inner_ty)
                                if cls_nm_ptr_chk is None:
                                    pass
                                if not (cls_nm_ptr_chk is None):
                                    return None
                                # bytes|t.CPtr / str|t.CPtr: alloca 是 i8**，
                                # 直接 GEP 按 i8* 步长（8 字节），不 load
                                # 仅当 inner_ty 不是自定义结构体时才走指针遍历
                                if trans is not None:
                                    ve_pe: HandlesVar.VarEntry | t.CPtr = \
                                        HandlesVar.lookup_var_entry(trans.SymTab, nm.id)
                                    if ve_pe is not None and ve_pe.IsPtrElement == 1:
                                        return llvmlite.build_gep(
                                            builder, pointee, alloca, idx_val)
                                # 普通指针遍历: 先 load 指针值，再 GEP
                                ptr_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
                                    builder, pointee, alloca)
                                if ptr_val is None:
                                    return None
                                gep_r: llvmlite.Value | t.CPtr = llvmlite.build_gep(
                                    builder, inner_ty, ptr_val, idx_val)
                                return gep_r
                            case _:
                                # pointee 是普通标量类型 (如 i64, i32, i8):
                                # alloca 是 Ptr(标量), 直接 GEP 获取第 idx 个元素指针
                                # 支持 arg_vals[i] = val 这类参数数组下标赋值
                                return llvmlite.build_gep(builder, pointee, alloca, idx_val)

    # 通用路径
    ptr_val: llvmlite.Value | t.CPtr = translate_value(
        builder, pool, mod, sub.value, None, 0, trans)
    if ptr_val is None or ptr_val.Ty is None:
        return None
    if is_ptr_type(ptr_val.Ty) != 0:
        elem_ty2: llvmlite.LLVMType | t.CPtr = ptr_val.Ty.Pointee
        if elem_ty2 is not None:
            # 数组类型: 双索引 GEP (getelementptr [N x ty], ptr, i32 0, i32 idx)
            match elem_ty2:
                case llvmlite.LLVMType.Array(arr_elem_ty, arr_count):
                    return llvmlite.build_gep_array(
                        builder, elem_ty2, arr_elem_ty, ptr_val, idx_val)
                case _:
                    pass
            # 自定义结构体 (如 self._ht[key]): 返回 None,
            # 触发 HandlesAssign 的 __setitem__ 重载
            cls_nm_gen: str = _get_custom_struct_cls_nm(pool, elem_ty2)
            if cls_nm_gen is None:
                pass
            if not (cls_nm_gen is None):
                return None
            # 指针类型: 单索引 GEP (getelementptr ty, ptr, idx)
            return llvmlite.build_gep(builder, elem_ty2, ptr_val, idx_val)
    return None


# ============================================================
# 获取属性访问的字段指针（不加载值，用于赋值）
# 对于 Name 类型的 obj，直接使用 alloca 指针（不 load 结构体）
# ============================================================
def get_attribute_ptr(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 结构体）
    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:
            obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)

    # 模块属性访问 (写路径): sys._mbuddy = mb → store 到全局变量 @_mbuddy
    # 当 obj 是已导入模块名且不是普通变量时，查找/创建全局变量引用
    if obj_ptr is None and at.value.kind() == ast.ASTKind.Name and trans is not None:
        nm_wma: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
        if nm_wma.id is not None:
            # CDefine 常量不可写: 报 FATAL 错误，不创建未定义全局
            if _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 = _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:
        obj_ptr = 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 = _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 以区分跨模块同名类
            if field_info is 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 and var_entry.AnnotClassName is not None:
                            cur_sha1: str = None
                            if trans is not None:
                                cur_sha1 = trans.ModuleSha1
                            field_info = HandlesStruct.lookup_field_by_class(
                                var_entry.AnnotClassName, at.attr, cur_sha1)
                            # 按需过滤成功：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(var_entry.AnnotClassName)
                                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: 字段未找到，打印诊断信息
            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
                fb_attr_g: t.CChar | t.CPtr = VLogger.fmt_buf()
                if fb_attr_g is not None:
                    viperlib.snprintf(fb_attr_g, 1024,
                        "属性写入失败: .%s (var=%s annot=%s sha1=%s:%d)",
                        at.attr,
                        diag_var_nm_g if diag_var_nm_g is not None else "?",
                        diag_annot_g if diag_annot_g is not None else "None",
                        trans.ModuleSha1 if trans.ModuleSha1 is not None else "?",
                        at.lineno if at.lineno is not None else 0)
                    VLogger.error(fb_attr_g, "ATTR")
            return None
        case _:
            return None
