Files
ViperC/App/lib/core/Handles/HandlesAssign.vp
T
TermiNexus a0edc3d961 fix(gen): 泛型 type_params 生命周期与无注解赋值 annot 推断
- ClassGenMixin: list[str](pool, 8) elem_size 修正(原 4 致 Pair[T1,T2]
  重叠写),新增 GenericClassTpParams stdlib 深拷贝表,特化统一取深拷贝
- Generator: 初始化 GenericClassTpParams 防 0xBEBEBEBEBEBEBEBE
- TypeGenMixin: 泛型上下文栈式管理,支持嵌套特化
- CallGenMixin: sz_builtin 补 double/float/char,修复 T.__sizeof__ NameError
- StubMerger: _CommentOutGlobalInBuf 循环注释同名全局,修复 @_mbuddy 重复定义
- HandlesAssign: 无注解赋值按 RHS 推断 AnnotClassName(类实例化/源变量/
  运算符重载/字段注解/方法返回注解 vpi),修复联合类型 Ptr(i8) 属性访问失败
2026-08-13 17:31:54 +08:00

730 lines
42 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesVar as HandlesVar
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.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.StubMerger as StubMerger
# ============================================================
# HandlesAssign - Assign 语句处理(Mixin 继承模式)
#
# 对应 TransPyC 的 class AssignHandle(BaseHandle):
# @t.NoVTable 继承 Mixin 获得 Trans 字段(展平嵌入,无 vtable)
# 通过 self.Trans 访问共享状态(Pool/Module/_cur_builder/SymTab/...)
# 通过 self.Trans.ExprH / self.Trans.IfH 等访问其他 Handle
# ============================================================
@t.NoVTable
class AssignHandle(HandlesBase.Mixin):
"""Assign 语句处理器:继承 Mixin 获得 Trans 回指针 + 共享方法"""
_CurrentClass: str # 模块私有状态
def __init__(self, trans: HT.Translator | t.CPtr):
self.Trans = trans
self._CurrentClass = None
# ============================================================
# Handle - 处理 Assign 语句,返回新增变量数(0 或 1)
#
# 对应 TransPyC AssignHandle._HandleAssignLlvm
# 共享状态从 self.Trans 获取,无需 11 个参数
# ============================================================
def Handle(self, node: ast.AST | t.CPtr) -> int:
asgn: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node)
if asgn is None:
VLogger.error("cast failed", "ASGN")
return 0
targets: list[ast.AST | t.CPtr] | t.CPtr = asgn.targets
if targets is None:
VLogger.error("targets is None", "ASGN")
return 0
# 从 self.Trans 取共享状态(替代 11 个参数)
pool: memhub.MemBuddy | t.CPtr = self.Trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = self.Trans._cur_builder
mod: llvmlite.LLVMModule | t.CPtr = self.Trans.Module
# 翻译 RHS 值
# 无注解赋值无目标类型可推断,清空 CurAssignAnnot/CurAssignNode
# (防残留误判 c.Deref 宽度)
self.Trans.CurAssignAnnot = None
self.Trans.CurAssignNode = None
rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, asgn.value, None, 0, self.Trans)
if rhs_val is None:
# 增强错误信息:包含文件名 + lineno + AST 节点详情,便于定位
fb: str = VLogger.fmt_buf()
if fb is not None and asgn.value is not None:
sha1: str = self.Trans.ModuleSha1
val_kind: int = asgn.value.kind()
val_line: t.CInt = asgn.value.lineno
# 通过 sha1 查找文件名(人类可读)
rel_path: str = None
if sha1 is not None:
rel_path = StubMerger.LookupSha1RelPath(sha1)
# 根据 AST 节点类型提取详情
if val_kind == ast.ASTKind.Attribute:
at_nv: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(asgn.value)
attr_nm: str = at_nv.attr
target_nm: str = "?"
if at_nv.value is not None:
if at_nv.value.kind() == ast.ASTKind.Name:
tn_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_nv.value)
target_nm = tn_nm.id
if rel_path is not None:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: %s:%d '%s.%s' 属性访问失败",
rel_path, val_line, target_nm, attr_nm)
else:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: sha1=%s:%d '%s.%s' 属性访问失败",
sha1, val_line, target_nm, attr_nm)
elif val_kind == ast.ASTKind.Name:
nm_nv: ast.Name | t.CPtr = (ast.Name | t.CPtr)(asgn.value)
if rel_path is not None:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: %s:%d 变量 '%s' 未找到",
rel_path, val_line, nm_nv.id)
else:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: sha1=%s:%d 变量 '%s' 未找到",
sha1, val_line, nm_nv.id)
elif val_kind == ast.ASTKind.Call:
cl_nv: ast.Call | t.CPtr = (ast.Call | t.CPtr)(asgn.value)
func_nm: str = "?"
if cl_nv.func is not None:
if cl_nv.func.kind() == ast.ASTKind.Name:
fn_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(cl_nv.func)
func_nm = fn_nm.id
if rel_path is not None:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: %s:%d 调用 '%s(...)' 返回 None",
rel_path, val_line, func_nm)
else:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: sha1=%s:%d 调用 '%s(...)' 返回 None",
sha1, val_line, func_nm)
else:
if rel_path is not None:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: %s:%d [kind=%d]",
rel_path, val_line, val_kind)
else:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: sha1=%s:%d [kind=%d]",
sha1, val_line, val_kind)
VLogger.error(fb, "ASGN")
else:
VLogger.error("赋值右侧为 None (asgn.value 为空)", "ASGN")
return 0
new_vars: int = 0
tn: t.CSizeT = len(targets)
for ti in range(tn):
target: ast.AST | t.CPtr = targets.get(ti)
if target is None:
continue
tk: int = target.kind()
# Tuple 赋值: a, b = f() 多返回值解包
# rhs_val 是匿名 struct 值(函数返回 tuple[T1,T2] 打包)→ alloca +
# store + GEP 逐字段 load → store 到各 Name 目标。
if tk == ast.ASTKind.Tuple:
tgt_tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(target)
tgt_elts: list[ast.AST | t.CPtr] | t.CPtr = tgt_tup.elts
# 守卫:rhs 不是 struct(tuple 打包)时跳过本 target
if tgt_elts is None or rhs_val is None or rhs_val.Ty is None:
continue
match rhs_val.Ty:
case llvmlite.LLVMType.Struct(_, _, _):
tgt_n: t.CSizeT = len(tgt_elts)
tmp_s: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, rhs_val.Ty)
if tmp_s is not None:
llvmlite.build_store(builder, rhs_val, tmp_s)
fchain: llvmlite.ParamNode | t.CPtr = llvmlite.get_struct_fields(rhs_val.Ty)
fcur: llvmlite.ParamNode | t.CPtr = fchain
fti: t.CSizeT
for fti in range(tgt_n):
tgt_elt: ast.AST | t.CPtr = tgt_elts.get(fti)
if tgt_elt is None or fcur is None:
break
fty: llvmlite.LLVMType | t.CPtr = llvmlite.paramnode_get_ty(fcur)
fcur = llvmlite.paramnode_get_next(fcur)
if fty is None:
# fail-fast: 字段类型缺失,立即终止
HandlesType.fatal_error(target, "元组解包字段类型缺失", "TypeError")
# 守卫:非 Name 元素无法解包,跳过
if tgt_elt.kind() != ast.ASTKind.Name:
continue
tgt_nm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(tgt_elt)
if tgt_nm2.id is None:
continue
fp2: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, rhs_val.Ty, fty, tmp_s, fti)
if fp2 is None:
continue
fv2: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, fty, fp2)
tgt_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
self.Trans.SymTab, tgt_nm2.id)
if tgt_alloca is None:
tgt_alloca = HandlesVar.get_or_create_sym(
self.Trans.SymTab, pool, builder, tgt_nm2.id, fty)
if tgt_alloca is None:
continue
sval: llvmlite.Value | t.CPtr = fv2
if tgt_alloca.Ty is not None and tgt_alloca.Ty.Pointee is not None:
sval = HandlesExpr.coerce_to_type(
builder, fv2, tgt_alloca.Ty.Pointee, node)
llvmlite.build_store(builder, sval, tgt_alloca)
case _:
pass
continue
# Subscript 赋值: arr[i] = val / ptr[i] = val / list[i] = val
if tk == ast.ASTKind.Subscript:
# 检查是否是 list[T] 类型的 Subscript(泛型类不注册 struct)
# list 的 subscript 赋值走 __setitem__ 内联路径
list_obj: llvmlite.Value | t.CPtr = HandlesExpr.is_list_subscript(
target, self.Trans)
if list_obj is not None:
# list[T] 类型: 内联生成 __setitem__ 逻辑
sub_node: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(target)
if sub_node is not None and sub_node.slice is not None:
list_idx_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, sub_node.slice, None, 0, self.Trans)
if list_idx_val is None:
# fail-fast: list 下标翻译失败,立即终止
HandlesType.fatal_error(target, "list 下标翻译失败", "TypeError")
HandlesExpr.list_setitem_inline(
builder, pool, list_obj, list_idx_val, rhs_val, target)
continue
# 普通 Subscript 赋值
elem_ptr: llvmlite.Value | t.CPtr = HandlesExpr.get_subscript_ptr(
builder, pool, mod, target, self.Trans)
if elem_ptr is not None:
store_val: llvmlite.Value | t.CPtr = rhs_val
# 守卫短路:elem 类型完整才做类型转换
if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None:
store_val = HandlesExpr.coerce_to_type(
builder, rhs_val, elem_ptr.Ty.Pointee, node)
llvmlite.build_store(builder, store_val, elem_ptr)
continue
# get_subscript_ptr 返回 None: 尝试 __setitem__ 运算符重载
# 适用于自定义类(如 hashtable[key]=val → hashtable.__setitem__(key, val))
sub_asgn: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(target)
setitem_done: int = 0
if sub_asgn is not None and sub_asgn.value is not None:
if sub_asgn.value.kind() == ast.ASTKind.Name:
sub_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub_asgn.value)
if sub_nm.id is not None:
sub_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
self.Trans.SymTab, sub_nm.id)
if sub_alloca is not None and sub_alloca.Ty is not None \
and sub_alloca.Ty.Pointee is not None \
and HandlesExpr.is_ptr_type(sub_alloca.Ty) != 0:
sub_pointee: llvmlite.LLVMType | t.CPtr = sub_alloca.Ty.Pointee
cls_nm_set: str = HandlesStruct.get_class_name_by_type(pool, sub_pointee)
obj_val_set: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, sub_pointee, sub_alloca)
# 指针类型变量 (X|t.CPtr): sub_pointee 是 Ptr(Struct),
# 需 load 获取 Ptr(Struct) 再检查 inner struct
if cls_nm_set is None and obj_val_set is not None \
and HandlesExpr.is_ptr_type(sub_pointee) != 0 \
and sub_pointee.Pointee is not None:
cls_nm_set = HandlesStruct.get_class_name_by_type(
pool, sub_pointee.Pointee)
if cls_nm_set is not None and obj_val_set is not None:
key_val_set: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, sub_asgn.slice, None, 0, self.Trans)
if key_val_set is not None:
arg_vals_set: t.CSizeT | t.CPtr = pool.alloc(16)
if arg_vals_set is not None:
arg_vals_set[0] = t.CSizeT(key_val_set)
arg_vals_set[1] = t.CSizeT(rhs_val)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, cls_nm_set, "__setitem__",
obj_val_set, arg_vals_set, 2, self.Trans)
setitem_done = 1
elif sub_asgn.value.kind() == ast.ASTKind.Attribute:
# self._ht[key] = val → self._ht.__setitem__(key, val)
# 通过 get_attribute_ptr 获取字段指针,再调用 __setitem__
field_ptr_set: llvmlite.Value | t.CPtr = HandlesExpr.get_attribute_ptr(
builder, pool, mod, sub_asgn.value, self.Trans)
if field_ptr_set is not None and field_ptr_set.Ty is not None \
and field_ptr_set.Ty.Pointee is not None \
and HandlesExpr.is_ptr_type(field_ptr_set.Ty) != 0:
field_pointee_set: llvmlite.LLVMType | t.CPtr = field_ptr_set.Ty.Pointee
cls_nm_attr: str = HandlesStruct.get_class_name_by_type(pool, field_pointee_set)
obj_val_attr: llvmlite.Value | t.CPtr = field_ptr_set
# field_ptr 是 Ptr(Ptr(Struct)) (X|t.CPtr 字段):
# load 解引用获取 Ptr(Struct)
if cls_nm_attr is None \
and HandlesExpr.is_ptr_type(field_pointee_set) != 0 \
and field_pointee_set.Pointee is not None:
cls_nm_attr = HandlesStruct.get_class_name_by_type(
pool, field_pointee_set.Pointee)
if cls_nm_attr is not None:
obj_val_attr = llvmlite.build_load(
builder, field_pointee_set, field_ptr_set)
if cls_nm_attr is not None and obj_val_attr is not None:
key_val_attr: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, sub_asgn.slice, None, 0, self.Trans)
if key_val_attr is not None:
arg_vals_attr: t.CSizeT | t.CPtr = pool.alloc(16)
if arg_vals_attr is not None:
arg_vals_attr[0] = t.CSizeT(key_val_attr)
arg_vals_attr[1] = t.CSizeT(rhs_val)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, cls_nm_attr, "__setitem__",
obj_val_attr, arg_vals_attr, 2, self.Trans)
setitem_done = 1
if setitem_done == 0:
# 通用 fallback: 翻译 sub.value 并尝试 __setitem__ 或直接 GEP+store
# 处理 Name/Attribute 之外的节点以及 SymTab 查找失败的情况
if sub_asgn is not None and sub_asgn.value is not None:
obj_val_gen: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, sub_asgn.value, None, 0, self.Trans)
key_val_gen: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, sub_asgn.slice, None, 0, self.Trans)
if obj_val_gen is not None and key_val_gen is not None \
and obj_val_gen.Ty is not None:
# 内联 _get_custom_struct_cls_nm 逻辑(避免依赖新函数)
cls_nm_gen: str = HandlesStruct.get_class_name_by_type(
pool, obj_val_gen.Ty)
if cls_nm_gen is None and HandlesExpr.is_ptr_type(obj_val_gen.Ty) != 0 \
and obj_val_gen.Ty.Pointee is not None:
cls_nm_gen = HandlesStruct.get_class_name_by_type(
pool, obj_val_gen.Ty.Pointee)
if cls_nm_gen is not None:
# 自定义结构体: 调用 __setitem__
arg_vals_gen: t.CSizeT | t.CPtr = pool.alloc(16)
if arg_vals_gen is not None:
arg_vals_gen[0] = t.CSizeT(key_val_gen)
arg_vals_gen[1] = t.CSizeT(rhs_val)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, cls_nm_gen, "__setitem__",
obj_val_gen, arg_vals_gen, 2, self.Trans)
setitem_done = 1
elif HandlesExpr.is_ptr_type(obj_val_gen.Ty) != 0 \
and obj_val_gen.Ty.Pointee is not None:
# 非自定义结构体: 直接 GEP + store
# 处理 get_subscript_ptr 因边界情况返回 None 的指针下标
elem_gen: llvmlite.LLVMType | t.CPtr = obj_val_gen.Ty.Pointee
store_val_gen: llvmlite.Value | t.CPtr = rhs_val
elem_ptr_gen: llvmlite.Value | t.CPtr = None
match elem_gen:
case llvmlite.LLVMType.Array(arr_elem_gen, _):
elem_ptr_gen = llvmlite.build_gep_array(
builder, elem_gen, arr_elem_gen,
obj_val_gen, key_val_gen)
case _:
elem_ptr_gen = llvmlite.build_gep(
builder, elem_gen, obj_val_gen, key_val_gen)
if elem_ptr_gen is not None and elem_ptr_gen.Ty is not None:
if elem_ptr_gen.Ty.Pointee is not None:
store_val_gen = HandlesExpr.coerce_to_type(
builder, rhs_val, elem_ptr_gen.Ty.Pointee, node)
llvmlite.build_store(builder, store_val_gen, elem_ptr_gen)
setitem_done = 1
if setitem_done == 0:
HandlesType.fatal_error(target, "subscript ptr is None")
continue
# Attribute 赋值: obj.field = val
if tk == ast.ASTKind.Attribute:
# property setter 拦截: obj.attr = v 且 attr 是 @x.setter 方法时,
# 生成 call Class.attr$set(obj, v),跳过字段存储
# 注意参数顺序:_try_property_setter(pool, builder, mod, ...)——
# 旧代码传 (builder, pool, ...) 导致 pool/builder 错位,pool 参数
# 实际是 IRBuilder 对象,pool.alloc 虚调用读 IRBuilder[0] 当 vtable
# 跳到垃圾地址崩溃(BuiltinDecoratorTest Phase A 崩溃根因)。
if HandlesExprCall._try_property_setter(
pool, builder, mod, target, rhs_val, self.Trans) == 1:
continue
field_ptr: llvmlite.Value | t.CPtr = HandlesExpr.get_attribute_ptr(
builder, pool, mod, target, self.Trans)
if field_ptr is not None:
# 获取字段类型,对 rhs_val 进行类型转换(如 i32 → i64)
store_val: llvmlite.Value | t.CPtr = rhs_val
if field_ptr.Ty is not None and field_ptr.Ty.Pointee is not None:
store_val = HandlesExpr.coerce_to_type(
builder, rhs_val, field_ptr.Ty.Pointee, node)
llvmlite.build_store(builder, store_val, field_ptr)
continue
# 守卫:field_ptr 为 None → 构造详细错误信息
attr_node: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(target)
attr_name: str = "(unknown)"
obj_name: str = "(unknown)"
if attr_node is not None:
attr_name = attr_node.attr
if attr_node.value is not None and attr_node.value.kind() == ast.ASTKind.Name:
obj_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(attr_node.value)
obj_name = obj_nm.id
err_buf: str = pool.alloc(256)
if err_buf is not None:
viperlib.snprintf(err_buf, 256,
"attribute ptr is None: %s.%s",
obj_name, attr_name)
HandlesType.fatal_error(target, err_buf)
else:
HandlesType.fatal_error(target, "attribute ptr is None")
continue
# Name 赋值: var = val
if tk == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(target)
# 守卫:target 名缺失时跳过
if nm.id is None:
continue
# global 变量:写入模块作用域中的全局变量
if HT.is_global_name(self.Trans, nm.id) != 0:
mod_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var(
self.Trans.SymTab, nm.id)
if mod_alloca is None:
# fail-fast: global 变量未注册,无法赋值
HandlesType.fatal_error(
target,
f"global 变量 '{nm.id}' 未注册,无法赋值",
"NameError")
target_ty: llvmlite.LLVMType | t.CPtr = None
if mod_alloca.Ty is not None:
target_ty = mod_alloca.Ty.Pointee
if target_ty is not None:
rhs_val = HandlesExpr.coerce_to_type(builder, rhs_val, target_ty, node)
llvmlite.build_store(builder, rhs_val, mod_alloca)
continue
# nonlocal 变量:通过闭包 env 写入
if HT.is_nonlocal_name(self.Trans, nm.id) != 0:
nl_ptr: llvmlite.Value | t.CPtr = HandlesNonlocal.get_nonlocal_var_ptr(
self.Trans, nm.id)
if nl_ptr is None:
# fail-fast: nonlocal 变量指针解析失败
HandlesType.fatal_error(
target,
f"nonlocal 变量 '{nm.id}' 指针解析失败",
"NameError")
# 从 nl_ptr 的类型推断 nonlocal 变量实际类型,
# 不再强制 i32(会截断指针/结构体/i64 导致闭包数据损坏)
nl_target_ty: llvmlite.LLVMType | t.CPtr = None
if nl_ptr.Ty is not None:
nl_target_ty = nl_ptr.Ty.Pointee
if nl_target_ty is None:
# fail-fast: nl_ptr 类型信息丢失,无法安全 store
HandlesType.fatal_error(
target,
f"nonlocal 变量 '{nm.id}' 类型信息丢失,无法写入",
"TypeError")
rhs_coerced: llvmlite.Value | t.CPtr = HandlesExpr.coerce_to_type(
builder, rhs_val, nl_target_ty, node)
llvmlite.build_store(builder, rhs_coerced, nl_ptr)
continue
# 拦截:函数内对模块级全局变量赋值但没 global 声明
# 否则静默创建局部变量遮蔽全局,全局保持 null 导致运行时崩溃
# (_mbuddy = mb 缺 global 声明即此类 bug:Int32 解引用 null 崩溃)
if HandlesVar.is_module_scope(self.Trans.SymTab) == 0:
mod_gv: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var(
self.Trans.SymTab, nm.id)
if mod_gv is not None:
fb_g: str = VLogger.fmt_buf()
msg_g: str = "global 声明缺失"
if fb_g is not None:
viperlib.snprintf(fb_g, 1024, "变量 '%s' 是模块级全局变量,函数内赋值需用 global 声明(否则创建局部变量遮蔽全局,导致全局保持 null 崩溃)", nm.id)
msg_g = fb_g
HandlesType.fatal_error(target, msg_g, "SyntaxError")
# 普通局部变量
alloca: llvmlite.Value | t.CPtr = HandlesVar.get_or_create_sym(
self.Trans.SymTab, pool, builder, nm.id, rhs_val.Ty)
if alloca is not None:
# 无注解赋值:从 RHS 表达式推断类名设置变量注解
# (联合类型 X|t.CPtr 简化为 Ptr(i8),属性访问按需过滤需要
# AnnotClassName 才能定位结构体字段)
cls_inf: str = self._infer_rhs_annot(pool, mod, asgn.value, self.Trans, rhs_val)
if cls_inf is not None:
HandlesVar.set_var_annot_class_name(
self.Trans.SymTab, nm.id, cls_inf)
# 按 alloca 类型对值进行转换(如 double → float)
store_val: llvmlite.Value | t.CPtr = rhs_val
if alloca.Ty is not None and alloca.Ty.Pointee is not None:
store_val = HandlesExpr.coerce_to_type(
builder, rhs_val, alloca.Ty.Pointee, node)
llvmlite.build_store(builder, store_val, alloca)
# lambda 表达式赋给变量:注册为闭包变量名,
# 调用分派(is_closure_name)才走闭包间接调用路径;
# 否则 i8** alloca 被 _is_func_ptr_alloca 误判为
# 函数指针,无参 call 直接跳闭包数据崩溃
if asgn.value is not None \
and asgn.value.kind() == ast.ASTKind.Lambda:
HandlesExprCall.register_closure_name(nm.id)
existing: llvmlite.Value | t.CPtr = HandlesVar.lookup_current(
self.Trans.SymTab, nm.id)
if existing is None:
new_vars += 1
else:
# fail-fast: 变量存储创建失败,立即终止
HandlesType.fatal_error(
target,
f"变量 '{nm.id}' 存储创建失败",
"TypeError")
return new_vars
# ============================================================
# _infer_rhs_annot - 从 RHS 表达式推断变量注解类名
#
# 无注解赋值(如 v = Vec2(...) / v3 = v1 + v2)时,变量 AnnotClassName
# 缺失会导致后续属性访问(.x)无法"按需过滤":联合类型 X|t.CPtr 在 LLVM
# 中简化为 Ptr(i8),不携带类名,无法定位结构体字段(字段未找到 → fast-fail)。
# 此函数按 RHS 结构推断类名:
# Call → 类实例化(func 为已注册结构体名才采用,避免普通函数误标);
# func 为 Attribute 时是方法调用(a.cross()),obj 类名 +
# 方法存在 + rhs_val 为指针类型(联合对象)三条件判定
# Name → 源变量的 AnnotClassName
# BinOp/UnaryOp → lhs 类名 + 该类存在对应 dunder 方法(运算符重载返回同类对象)
# Attribute → 字段注解类名(委托 Generator 单例 _resolve_attr_field_class)
# 仅当目标为已注册结构体时采用,与"按需过滤,找不到 fast-fail"原则一致。
# ============================================================
def _infer_rhs_annot(self, pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr,
rhs_val: llvmlite.Value | t.CPtr = None) -> str:
"""从 RHS 表达式推断类名,无法推断返回 None"""
if node is None or trans is None:
return None
k: int = node.kind()
# Call: 类实例化 / 方法调用
if k == ast.ASTKind.Call:
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
if cl is None or cl.func is None:
return None
if cl.func.kind() == ast.ASTKind.Name:
# 类实例化:func 为已注册结构体名才采用
cand: str = ((ast.Name | t.CPtr)(cl.func)).id
if cand is None:
return None
se: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(cand)
if se is not None:
return cand
return None
if cl.func.kind() == ast.ASTKind.Attribute:
# 方法调用 a.cross(b):从 vpi 返回注解缓存精确解析返回类名
# (方法可返回非同类对象,如 Transform.world_position → 'Vec3',
# 启发式取 obj 类名会 GEP 错字段)。vpi 未命中时返回 None,
# 由后续属性访问 fast-fail 暴露 vpi 缺失根因(不静默回退)。
ca: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if ca is None or ca.attr is None or ca.value is None:
return None
obj_cls: str = self._infer_rhs_annot(pool, mod, ca.value, trans)
if obj_cls is None:
return None
if self._cls_has_dunder(pool, mod, obj_cls, ca.attr) == 0:
return None
# 方法定义所在模块 SHA1(优先 struct entry,回退当前模块 sha1)
cls_sha1: str = HandlesStruct.get_struct_sha1(obj_cls)
if cls_sha1 is None and trans.ModuleSha1 is not None:
cls_sha1 = trans.ModuleSha1
ret_cls: str = None
if cls_sha1 is not None:
import lib.StubGen.VpiReader as VpiReader
temp_dir: str = None
import lib.core.Gen.Generator as Generator
g2: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(
t.CVoid(HandlesExpr._g_gen, t.CPtr))
if g2 is not None and g2.TempDir is not None:
temp_dir = g2.TempDir
if temp_dir is not None:
VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, temp_dir)
annot_ret: str = VpiReader._lookup_vpi_ret_annot(
cls_sha1, obj_cls, ca.attr)
if annot_ret is not None:
ret_cls = self._extract_annot_class_name(pool, annot_ret)
return ret_cls
return None
# Name: 源变量注解
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm is None or nm.id is None:
return None
ve: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, nm.id)
if ve is None:
return None
return ve.AnnotClassName
# BinOp/UnaryOp: 运算符重载返回同类对象
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
if bop is None or bop.left is None:
return None
dunder: str = self._op_to_dunder(bop.op)
if dunder is None:
return None
lhs_cls: str = self._infer_rhs_annot(pool, mod, bop.left, trans)
if lhs_cls is None or self._cls_has_dunder(pool, mod, lhs_cls, dunder) == 0:
return None
return lhs_cls
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
if uop is None or uop.operand is None:
return None
dunder: str = self._op_to_dunder(uop.op)
if dunder is None:
return None
opr_cls: str = self._infer_rhs_annot(pool, mod, uop.operand, trans)
if opr_cls is None or self._cls_has_dunder(pool, mod, opr_cls, dunder) == 0:
return None
return opr_cls
# Attribute: 字段注解类名(属性链递归,如 p = r.origin)
if k == ast.ASTKind.Attribute:
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(
t.CVoid(HandlesExpr._g_gen, t.CPtr))
if g is not None:
return g._resolve_attr_field_class(pool, trans, node)
return None
return None
# ============================================================
# _extract_annot_class_name - 从返回注解文本提取结构体类名
#
# vpi 返回注解文本(如 "'Vec3' | CPtr")按 '|' 分段,每段去除
# 空格/引号后查结构体注册表:命中即类名(内置类型 str/int/
# CFloat64T/t.CPtr 等均不注册 struct,天然过滤)。"A | B" 联合
# 多候选时取首个命中段。
# ============================================================
def _extract_annot_class_name(self, pool: memhub.MemBuddy | t.CPtr,
annot: str) -> str:
"""从返回注解文本提取已注册结构体类名,无命中返回 None"""
if pool is None or annot is None:
return None
alen: t.CSizeT = string.strlen(annot)
if alen == 0:
return None
seg: str = pool.alloc(256)
if seg is None:
return None
start: t.CSizeT = 0
i: t.CSizeT
for i in range(alen):
if annot[i] == '|':
seglen: t.CSizeT = i - start
# 段 [start, i):去除首尾空格与引号后查注册表
s: t.CSizeT = start
e: t.CSizeT = i
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'):
s += 1
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'):
e -= 1
seglen = e - s
if seglen > 0 and seglen < 256:
string.strncpy(seg, annot + s, seglen)
seg[seglen] = '\0'
se: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(seg)
if se is not None:
return seg
start = i + 1
# 最后一段(无 '|' 后缀)
s: t.CSizeT = start
e: t.CSizeT = alen
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'):
s += 1
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'):
e -= 1
seglen: t.CSizeT = e - s
if seglen > 0 and seglen < 256:
string.strncpy(seg, annot + s, seglen)
seg[seglen] = '\0'
se: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(seg)
if se is not None:
return seg
return None
# ============================================================
# _op_to_dunder - 运算符 → dunder 方法名(本地副本,避免跨 Mixin 调用)
# ============================================================
def _op_to_dunder(self, op: int) -> str:
"""BinOp/UnaryOp 运算符 → dunder 方法名,无映射返回 None"""
if op == ast.OpKind.Add: return "__add__"
if op == ast.OpKind.Sub: return "__sub__"
if op == ast.OpKind.Mult: return "__mul__"
if op == ast.OpKind.Div: return "__truediv__"
if op == ast.OpKind.FloorDiv: return "__floordiv__"
if op == ast.OpKind.Mod: return "__mod__"
if op == ast.OpKind.Pow: return "__pow__"
if op == ast.OpKind.BitAnd: return "__and__"
if op == ast.OpKind.BitOr: return "__or__"
if op == ast.OpKind.BitXor: return "__xor__"
if op == ast.OpKind.LShift: return "__lshift__"
if op == ast.OpKind.RShift: return "__rshift__"
if op == ast.OpKind.USub: return "__neg__"
if op == ast.OpKind.UAdd: return "__pos__"
if op == ast.OpKind.Invert: return "__invert__"
return None
# ============================================================
# _cls_has_dunder - 检查类(含继承链)是否存在 dunder 方法
#
# 与 ExprGenMixin.try_operator_overload 的查找一致:当前模块内定义
# 或跨模块类有 SHA1(stub 中声明)即视为存在。仅在确实存在时
# 才采用运算符重载推断的类名,避免把原生运算误标为类类型。
# ============================================================
def _cls_has_dunder(self, pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
class_name: str, dunder: str) -> int:
"""类及其 MRO 祖先链中是否存在 dunder 方法,存在返回 1"""
if class_name is None or dunder is None or mod is None:
return 0
lookup_name: str = pool.alloc(128)
if lookup_name is None:
return 0
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
if HandlesExprCall.find_func_in_module(mod, lookup_name) is not None:
return 1
# 继承链祖先查找
mro_ptr: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, class_name)
mi: int = 1
while mro_ptr is not None and mro_ptr[mi] != 0:
anc: str = (str | t.CPtr)(t.CVoid(mro_ptr[mi], t.CPtr))
if anc is not None:
anc_lookup: str = pool.alloc(128)
if anc_lookup is not None:
viperlib.snprintf(anc_lookup, 128, "%s.%s", anc, dunder)
if HandlesExprCall.find_func_in_module(mod, anc_lookup) is not None:
return 1
mi += 1
# 跨模块:类有 SHA1(stub 中声明该方法)视为存在
if HandlesStruct.get_struct_sha1(class_name) is not None:
return 1
return 0
# ============================================================
# NewAssignHandle - 工厂函数:分配并初始化 AssignHandle
# ============================================================
def NewAssignHandle(pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr) -> AssignHandle | t.CPtr:
h: AssignHandle | t.CPtr = pool.alloc(AssignHandle.__sizeof__())
if h is None:
return None
string.memset(h, 0, AssignHandle.__sizeof__())
h.Trans = trans
return h