Files
TransPyC/lib/core/Handles/HandlesAssign.py
T
TermiNexus 2e80424d67 refactor(handles): 删除 CSpecialCallHandle/TSpecialCallHandle
EmitRawCopyLlvm 改为模块级函数;删除 c./t. 语法糖处理类及 BaseTranslator 实例化,全仓零引用。
2026-10-18 21:51:10 +08:00

2464 lines
170 KiB
Python
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.
from __future__ import annotations
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from lib.core.translator import Translator
import vast as ast
import sys
import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
from lib.core.Handles.HandlesSpecialCall import EmitRawCopyLlvm
from lib.core.VLogger import get_logger as _vlog
from lib.core.ConstEvaluator import ConstEvaluator, EvalContext
from lib.constants.config import mode as _config_mode
from lib.core import TypeSystem
from lib.core.TypeSystem import CTypeRegistry
from lib.core.SymbolUtils import ParseListAnnotation
class AssignHandle(BaseHandle):
def __init__(self, translator: "Translator") -> None:
super().__init__(translator)
self._CurrentCpythonObjectClass: str | None = None
@staticmethod
def _DeclaredClassBlocksOverride(Gen: Any, VarName: str) -> bool:
"""该变量是否**已由类型注解声明**了类名(是 ⇒ 本次赋值不得改掉它的类)。
判据(正向事实):`Gen.var_annotated_class[VarName]` 由 HandlesAnnAssign 在
**带注解的声明完成时**登记(取注解解析的终值)。有它就意味着这个变量的类型是
**声明**出来的 ⇒ 赋值只能把右值**转换进**声明类型,不能反过来把变量重新定型。
为什么要这条:`node: rawptr[AST] = Name(...)` 里右值构造调用推出的是 `Name`,
若覆盖声明的 `AST`,下一处 `node = <AST*>`(函数返回类型 AST)就变成被禁止的
**向下转型** —— 现场 includes/ast/parser.vp:1919,VPC 报
"指针隐式转指针已禁止:源类型 %AST* → 目标类型 %Name*"(本判据是两侧同源的唯一真源)。
"""
if not VarName:
return False
return VarName in (getattr(Gen, 'var_annotated_class', None) or {})
@staticmethod
def _contains_identified_struct(var_type: ir.Type) -> bool:
if isinstance(var_type, ir.IdentifiedStructType):
return True
if isinstance(var_type, ir.ArrayType):
return AssignHandle._contains_identified_struct(var_type.element)
if isinstance(var_type, ir.PointerType):
return AssignHandle._contains_identified_struct(var_type.pointee)
if isinstance(var_type, ir.BaseStructType):
if var_type.elements:
return any(AssignHandle._contains_identified_struct(e) for e in var_type.elements)
return False
def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None,
ConstWidthHint: int | None = None) -> ir.Value | None:
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint)
def _HandleAssignLlvm(self, Node: ast.Assign) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
Gen._set_node_info(Node, "Assign")
if len(Node.targets) == 1:
Target: ast.expr = Node.targets[0]
if isinstance(Target, ast.Name):
VarName: str = Target.id
if Gen._IsVarConst(VarName):
_vlog().error(Gen._ConstAssignMessage("赋值", VarName))
sys.exit(1)
if isinstance(Node.value, ast.Attribute) and isinstance(Node.value.value, ast.Name) :
AttrName: str = Node.value.attr
Gen.var_type_info[VarName] = {'type': 't_type', 'name': AttrName}
Gen.var_type_assignments.setdefault(VarName, []).append({'type': 't_type', 'name': AttrName})
c_type_name: str = ''
resolved: tuple[type, int] | None = CTypeRegistry.ResolveName(AttrName)
if resolved is not None:
ctype_cls: type = resolved[0]
ptr_level: int = resolved[1]
c_type_name = CTypeRegistry.CTypeToName(ctype_cls)
if ptr_level > 0 and c_type_name:
c_type_name = c_type_name + ' *'
if not c_type_name:
if AttrName == 'CPtr':
c_type_name = 'void *'
is_unsigned: bool = Gen._is_type_unsigned(c_type_name) if c_type_name else False
fmt_str: str = "%u\n" if is_unsigned else "%d\n"
fmt_ptr: ir.Value = Gen._create_string_global(fmt_str)
fmt_var_name: str = f"__{VarName}_fmt"
if fmt_var_name not in Gen.variables:
fmt_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=fmt_var_name)
Gen.variables[fmt_var_name] = fmt_alloca
Gen.builder.store(fmt_ptr, Gen.variables[fmt_var_name])
kind_var_name: str = f"__{VarName}_kind"
if kind_var_name not in Gen.variables:
kind_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name=kind_var_name)
Gen.variables[kind_var_name] = kind_alloca
Gen.builder.store(ir.Constant(ir.IntType(32), 0), Gen.variables[kind_var_name])
if VarName not in Gen.variables:
dummy_var: ir.AllocaInstr = Gen._alloca(ir.IntType(8), name=VarName)
Gen.variables[VarName] = dummy_var
return
if isinstance(Node.value, ast.Name) and Node.value.id in Gen.structs:
ClassName: str = Node.value.id
Gen.var_type_info[VarName] = {'type': 'class_cast', 'name': ClassName}
Gen.var_type_assignments.setdefault(VarName, []).append({'type': 'class_cast', 'name': ClassName})
fmt_str: str = "%p\n"
fmt_ptr: ir.Value = Gen._create_string_global(fmt_str)
fmt_var_name: str = f"__{VarName}_fmt"
if fmt_var_name not in Gen.variables:
fmt_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=fmt_var_name)
Gen.variables[fmt_var_name] = fmt_alloca
Gen.builder.store(fmt_ptr, Gen.variables[fmt_var_name])
kind_var_name: str = f"__{VarName}_kind"
if kind_var_name not in Gen.variables:
kind_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name=kind_var_name)
Gen.variables[kind_var_name] = kind_alloca
Gen.builder.store(ir.Constant(ir.IntType(32), 1), Gen.variables[kind_var_name])
if VarName not in Gen.variables:
dummy_var: ir.AllocaInstr = Gen._alloca(ir.IntType(8), name=VarName)
Gen.variables[VarName] = dummy_var
return
if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name) and Node.value.func.id == 'va_list':
va_list_ptr: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(8).as_pointer(), name="va_list")
Gen.variables[VarName] = va_list_ptr
return
IsStructCtor: bool = (isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name)
and Node.value.func.id in Gen.structs)
if IsStructCtor:
ClassName: str = Node.value.func.id
StructType: ir.Type = Gen.structs[ClassName]
IsUnion: bool = False
TypeInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(ClassName)
if TypeInfo and TypeInfo.IsUnion:
IsUnion = True
if IsUnion:
Value: ir.Value | None = self.HandleExprLlvm(Node.value)
if Value:
var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName)
Gen._store(Value, var)
Gen.variables[VarName] = var
return
ExistingPtr: ir.Value | None = None
# BUG-8 修复:清除读取全局变量时缓存的 GlobalVariable(非显式 global 声明)
if (VarName in Gen.variables and VarName not in Gen.global_vars
and isinstance(Gen.variables[VarName], ir.GlobalVariable)):
del Gen.variables[VarName]
if VarName in Gen.variables and Gen.variables[VarName] is not None:
VarPtr: ir.Value = Gen.variables[VarName]
if isinstance(VarPtr.type.pointee, ir.PointerType):
ExistingPtr = VarPtr
elif VarName in Gen._reg_values:
OldVal: ir.Value = Gen._reg_values[VarName]
if isinstance(OldVal.type, ir.PointerType):
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName)
Gen._store(OldVal, var)
Gen.variables[VarName] = var
del Gen._reg_values[VarName]
ExistingPtr = var
elif VarName in Gen._direct_values:
OldVal: ir.Value = Gen._direct_values.pop(VarName)
if isinstance(OldVal.type, ir.PointerType):
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName)
Gen._store(OldVal, var)
Gen.variables[VarName] = var
ExistingPtr = var
if ExistingPtr:
pointee: ir.Type = ExistingPtr.type.pointee
# 仅当 alloca 直接存储结构体值(pointee 不是指针)时才 in-place 初始化。
# 当 pointee 是 PointerType 时,alloca 存储的是堆对象指针:
# - 若 AnnAssign 分支未执行,alloca 内容是未初始化的栈垃圾
# (可能恰好等于 pool 地址),_InitStructAtPtr 会向垃圾地址写字段 → 内存损坏
# - 即使指针有效,node = Class(...) 的语义也是创建新对象并更新指针,
# 而非在旧对象位置 in-place 重初始化
# 此时必须 fall through 到通用路径 HandleExprLlvm → _HandleClassNewLlvm
# 以正确调用 __new__/__before_init__/__init__ 构造新对象。
if not isinstance(pointee, ir.PointerType) and pointee == StructType:
self._InitStructAtPtr(Node.value, ClassName, ExistingPtr)
return
if isinstance(Node.value, ast.Attribute):
enum_type_name: str | None = self._CheckEnumMemberAssignment(Node.value)
if enum_type_name:
Gen.var_type_info[VarName] = {'type': 'enum', 'name': enum_type_name}
var_type_for_list: ir.Type | None = None
if isinstance(Node.value, ast.List) and VarName in Gen.var_type_info:
type_info: dict[str, str] = Gen.var_type_info[VarName]
if type_info.get('type') == 'CArray':
var_type_for_list = type_info.get('full_type')
Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=var_type_for_list)
# 闭包变量追踪:当赋值右侧是返回闭包的函数调用时,记录变量为闭包类型
if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name):
CalledName: str = Node.value.func.id
if CalledName in Gen._closure_return_types:
Gen._closure_var_types[VarName] = Gen._closure_return_types[CalledName]
if not Value:
# 防静默:右侧是属性链(如 blk = func.Blocks.Head)却求值失败时
# 必须报错,禁止跳过 store 留下未初始化变量(随机崩溃)。
if isinstance(Node.value, ast.Attribute):
self._RaiseUnresolvedExpr("赋值", Node.value, Gen)
return
if isinstance(Value.type, ir.VoidType):
return
Gen._UnregisterTempPtr(Value)
# 【右值形态判定】只有"取地址形态"的右值才需要自动解一层引用:Name/Attribute/
# Subscript 的求值结果是**存储地址**(变量 alloca / 字段地址 /"对象即指针"),
# 赋给指针变量时必须 load 出内层指针。而 Call(显式 cast 如 `str*(addr)`、
# `CPtr(x)`,以及构造/普通函数调用)与其它表达式给出的是**值本身**,
# 再 load 会多解一层 → 静默取到野指针(现场 includes/argparse.vp 的
# `entry = str*(uint64(argv) + idx*8)`:i8** 右值被多解一次,entry 变成
# argv[idx] 本身,随后 entry[0] 去读字符串头 8 字节 → ASan access-violation)。
_rhs_addr_form: bool = isinstance(Node.value, (ast.Name, ast.Attribute, ast.Subscript))
# 大端局部变量:赋值时 bswap
Value = Gen._apply_bswap_if_big(Value, Gen.local_var_byteorders.get(VarName, ""), f"bswap_assign_{VarName}")
if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
found_class: str | None = None
found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(Value.type.pointee)
if found:
CN: str = found[0]
ST: ir.Type = found[1]
found_class = CN
# 同类特化覆盖保护:简单赋值(如 dbody = dbody2)会经
# find_struct_by_pointee 拿到带 sha1 前缀的 IR 特化名
# (list[f467...ASTptr]),覆盖声明注解解析出的裸名
# (list[ast.ASTptr])→ 方法调用(dbody.get)按该名查
# SymbolTable/functions 时 miss → fallback 到 list[int].get
# → i32 返回值被 zext+inttoptr 当指针 → 崩溃。因此变量已有
# 同 base 的声明类型时不覆盖(保留声明名,方法解析走声明名)。
_prev_cls: str | None = Gen.var_struct_class.get(VarName)
if self._DeclaredClassBlocksOverride(Gen, VarName):
# 注解声明的类是权威类型(见 _DeclaredClassBlocksOverride)
pass
elif _prev_cls and '[' in _prev_cls and '[' in CN and _prev_cls.split('[')[0] == CN.split('[')[0]:
pass
else:
Gen.var_struct_class[VarName] = CN
Gen._var_to_heap_ptr[VarName] = Value
if found_class is None and isinstance(Value.type.pointee, ir.IdentifiedStructType):
pointee_name: str = Value.type.pointee.name
pointee_short: str = Gen._extract_short_name(pointee_name)
best_elem_count: int = 0
for CN, ST in Gen.structs.items():
if isinstance(ST, ir.IdentifiedStructType):
st_short: str = Gen._extract_short_name(ST.name)
if st_short == pointee_short:
elem_count: int = len(ST.elements) if ST.elements else 0
if elem_count > best_elem_count:
found_class = CN
best_elem_count = elem_count
if found_class:
# 同类特化覆盖保护(见上方 L189 注释):短名匹配的
# sha1 前缀变体不覆盖声明注解类型
_prev_cls2: str | None = Gen.var_struct_class.get(VarName)
if self._DeclaredClassBlocksOverride(Gen, VarName):
# 注解声明的类是权威类型(见 _DeclaredClassBlocksOverride)
pass
elif _prev_cls2 and '[' in _prev_cls2 and '[' in found_class and _prev_cls2.split('[')[0] == found_class.split('[')[0]:
pass
else:
Gen.var_struct_class[VarName] = found_class
Gen._var_to_heap_ptr[VarName] = Value
# 泛型类特化触发:返回类型是泛型类特化(如 list[str])但结构体尚未创建,
# 或虽然找到了结构体但它是 opaque(来自 stub 声明,尚未发射方法体)
_fd_struct_opaque: bool = False
if found_class and found_class in Gen.structs:
_fd_st: ir.IdentifiedStructType = Gen.structs[found_class]
if isinstance(_fd_st, ir.IdentifiedStructType) and (_fd_st.elements is None or len(_fd_st.elements) == 0):
_fd_struct_opaque = True
if (found_class is None or _fd_struct_opaque) and isinstance(Value.type.pointee, ir.IdentifiedStructType):
pn: str = Value.type.pointee.name
short_pn: str = pn
if '.' in pn:
pn_parts: list[str] = pn.split('.', 1)
if len(pn_parts[0]) >= 8 and all(c in '0123456789abcdef' for c in pn_parts[0]):
short_pn = pn_parts[1]
if '[' in short_pn and short_pn.endswith(']'):
gc_base: str = short_pn.split('[')[0]
gc_args_raw: str = short_pn[len(gc_base):]
gc_type_args: list[str] = []
for gc_arg_part in gc_args_raw.split(']['):
gc_arg_part = gc_arg_part.strip('[]')
if gc_arg_part:
gc_type_args.append(gc_arg_part)
if (gc_type_args
and hasattr(self.Trans.ClassHandler, '_generic_class_templates')
and gc_base in self.Trans.ClassHandler._generic_class_templates):
gc_type_names: list[str] = []
for gc_ta in gc_type_args:
gc_tn: str = gc_ta
if gc_ta == 'int':
gc_tn = 'CInt'
elif gc_ta == 'double':
gc_tn = 'CDouble'
elif gc_ta == 'float':
gc_tn = 'CFloat'
elif gc_ta == 'char':
gc_tn = 'CChar'
elif gc_ta == 'bool':
gc_tn = 'CBool'
gc_type_names.append(gc_tn)
gc_spec: str | None = self.Trans.ClassHandler._specialize_generic_class(
gc_base, gc_type_args, Gen, type_names=gc_type_names)
if gc_spec and gc_spec in Gen.structs:
found_class = gc_spec
# 同类特化覆盖保护(见上方 L189 注释):
# gc_spec 与声明注解类型同 base 时不覆盖
_prev_cls3: str | None = Gen.var_struct_class.get(VarName)
if _prev_cls3 and '[' in _prev_cls3 and '[' in gc_spec and _prev_cls3.split('[')[0] == gc_spec.split('[')[0]:
pass
else:
Gen.var_struct_class[VarName] = gc_spec
Gen._var_to_heap_ptr[VarName] = Value
else:
if VarName in ('mctx', 's1ctx', 's2ctx', 's5ctx'):
pass
# 【指针返回注解的被指类恢复】RHS 是调用、且值的 LLVM 类型是**通用指针**
# (i8*/i8**,pointee 不是结构体)时,上面那块类名推断整块失效 —— 现场
# `parts = s1.split(',')`:`split` 的返回注解 `list[str]*` ≡
# `CPtr[list[str]]` 被映射成 i8*
# ⇒ 类名丢失 ⇒ 随后 `parts[0]` 不走 `list[str].__getitem__` 而按通用
# 指针/字节索引 ⇒ 得到整数 ⇒ 赋给 `str` 时报"整数隐式转指针已禁止"
# (真因在 4 行之前,报文完全指不到)。此处从被调者的**返回注解**
# 取回被指类名(判据与 HandlesAnnAssign 的同类恢复同源)。
if (not Gen.var_struct_class.get(VarName)
and isinstance(Node.value, ast.Call)
and isinstance(Value.type, ir.PointerType)
and not isinstance(Value.type.pointee,
(ir.IdentifiedStructType, ir.LiteralStructType))):
_rc: str | None = self.Trans.ExprCallHandle._CallReturnClass(Node.value, Gen)
if _rc:
Gen.var_struct_class[VarName] = _rc
if getattr(Gen, 'global_struct_class', None) is not None:
Gen.global_struct_class[VarName] = _rc
Gen._var_to_heap_ptr[VarName] = Value
if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
# BUG-8 修复:清除读取全局变量时缓存的 GlobalVariable(非显式 global 声明)
if (VarName in Gen.variables and VarName not in Gen.global_vars
and isinstance(Gen.variables[VarName], ir.GlobalVariable)):
del Gen.variables[VarName]
if VarName in Gen.variables and Gen.variables[VarName] is not None:
Gen._store(Value, Gen.variables[VarName])
else:
var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName)
Gen._store(Value, var)
Gen.variables[VarName] = var
if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name):
call_func_name: str = Node.value.func.id
if call_func_name in Gen.var_type_info:
type_info: dict[str, str] = Gen.var_type_info[call_func_name]
if type_info['type'] in ('t_type', 'class_cast'):
src_fmt_var: str = f"__{call_func_name}_fmt"
dst_fmt_var: str = f"__{VarName}_fmt"
if src_fmt_var in Gen.variables:
if dst_fmt_var not in Gen.variables:
dst_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=dst_fmt_var)
Gen.variables[dst_fmt_var] = dst_alloca
src_fmt_ptr: ir.Value = Gen._load(Gen.variables[src_fmt_var], name=f"Load_{call_func_name}_fmt_for_{VarName}")
Gen.builder.store(src_fmt_ptr, Gen.variables[dst_fmt_var])
src_kind_var: str = f"__{call_func_name}_kind"
dst_kind_var: str = f"__{VarName}_kind"
if src_kind_var in Gen.variables:
if dst_kind_var not in Gen.variables:
dst_kind_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name=dst_kind_var)
Gen.variables[dst_kind_var] = dst_kind_alloca
src_kind_val: ir.Value = Gen._load(Gen.variables[src_kind_var], name=f"Load_{call_func_name}_kind_for_{VarName}")
Gen.builder.store(src_kind_val, Gen.variables[dst_kind_var])
Gen.var_type_info[VarName] = {'type': 't_type_runtime', 'source': call_func_name}
return
if VarName in Gen.global_vars:
# 治本修复:global 声明的变量若未被模块级 AnnAssign 创建为 LLVM 全局
# (注解无初始值的全局变量),此处自动创建零初始化全局变量
GVar: ir.GlobalVariable
if VarName not in Gen.module.globals:
_gv_type: ir.Type = Value.type if isinstance(Value.type, (ir.IntType, ir.PointerType)) else ir.IntType(64)
GVar = ir.GlobalVariable(Gen.module, _gv_type, name=VarName)
GVar.initializer = ir.Constant(_gv_type, None) if isinstance(_gv_type, ir.PointerType) else ir.Constant(_gv_type, 0)
else:
GVar = Gen.module.globals[VarName]
# 栈地址逃逸:`G = &local`,或 `G = arr`(G 为指针时的数组退让)——禁止把本帧栈
# 地址写入模块级全局(函数返回后悬垂);目标为同型聚合时按值拷贝,不在此列。
TgtTy1: Any = GVar.type.pointee if isinstance(GVar.type, ir.PointerType) else None
if Gen._IsFrameStackAddr(Node.value, Gen, TgtTy1):
from lib.core.LLVMCG.FuncGen import LifetimeError
raise LifetimeError(
f"栈地址逃逸:不能把本帧栈上存储的地址写入模块级全局变量 {VarName},"
f"函数返回后该指针悬垂;请返回值本身(按值拷贝),或改用堆分配"
)
Gen._store(Value, GVar)
Gen.variables[VarName] = GVar
if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(Value.type.pointee)
if found and not self._DeclaredClassBlocksOverride(Gen, VarName):
CN: str = found[0]
ST: ir.Type = found[1]
Gen.var_struct_class[VarName] = CN
return
if VarName in Gen._reg_values:
OldVal: ir.Value = Gen._reg_values[VarName]
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName)
Gen._store(OldVal, var)
Gen.variables[VarName] = var
del Gen._reg_values[VarName]
if VarName in Gen._direct_values:
OldVal: ir.Value = Gen._direct_values.pop(VarName)
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName)
Gen._store(OldVal, var)
Gen.variables[VarName] = var
# BUG-8 修复:清除读取全局变量时缓存的 GlobalVariable(非显式 global 声明)。
# Python 语义:函数内无 global 声明时赋值应创建局部变量,
# 而非修改被读取缓存的全局变量。清除后走 L339 的正确逻辑。
if (VarName in Gen.variables and VarName not in Gen.global_vars
and isinstance(Gen.variables[VarName], ir.GlobalVariable)):
del Gen.variables[VarName]
if VarName in Gen.variables and Gen.variables[VarName] is not None:
VarPtr: ir.Value = Gen.variables[VarName]
TargetType: ir.Type | None = VarPtr.type.pointee if isinstance(VarPtr.type, ir.PointerType) else None
types_differ: bool
try:
types_differ = TargetType and Value.type != TargetType
except AssertionError:
types_differ = False
if _rhs_addr_form and isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, ir.PointerType):
LoadedValue: ir.Value = Gen._load(Value, name=f"Load_{VarName}")
Loaded_differ: bool
try:
Loaded_differ = TargetType and LoadedValue.type != TargetType
except AssertionError:
Loaded_differ = False
if Loaded_differ:
try:
Coerced: ir.Value | None = Gen._coerce_value(LoadedValue, TargetType, Node.value)
if Coerced is not None:
LoadedValue = Coerced
except AssertionError:
pass
Gen._store(LoadedValue, VarPtr, Node=Node.value)
elif types_differ:
try:
Coerced: ir.Value | None = Gen._coerce_value(Value, TargetType, Node.value)
if Coerced is not None:
Gen._store(Coerced, VarPtr, Node=Node.value)
else:
Gen._store(Value, VarPtr, Node=Node.value)
except AssertionError:
Gen._store(Value, VarPtr, Node=Node.value)
else:
Gen._store(Value, VarPtr, Node=Node.value)
if VarName not in Gen.variables:
# 检查是否为模块级全局变量
# main 函数代表模块级作用域,直接修改全局变量
# 其他函数中,未声明 global 且未先读取的赋值创建局部变量(Python 作用域语义)
# 先读取过的变量已通过 HandleExprLlvm 加入 Gen.variables,会走 L289 分支存储到全局
CurrentFuncName: str = getattr(Gen.func, 'name', '') if Gen.func else ''
IsMainScope: bool = CurrentFuncName == 'main' or CurrentFuncName.endswith('.main')
if VarName in Gen.module.globals and (VarName in Gen.global_vars or IsMainScope):
GVar: ir.GlobalVariable = Gen.module.globals[VarName]
# 栈地址逃逸:main 作用域下的全局写入同口径(含 `G = arr` 的数组退让)。
TgtTy2: Any = GVar.type.pointee if isinstance(GVar.type, ir.PointerType) else None
if Gen._IsFrameStackAddr(Node.value, Gen, TgtTy2):
from lib.core.LLVMCG.FuncGen import LifetimeError
raise LifetimeError(
f"栈地址逃逸:不能把本帧栈上存储的地址写入模块级全局变量 {VarName},"
f"函数返回后该指针悬垂;请返回值本身(按值拷贝),或改用堆分配"
)
Gen._store(Value, GVar, Node=Node.value)
Gen.variables[VarName] = GVar
elif _rhs_addr_form and isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, ir.PointerType):
LoadedValue: ir.Value = Gen._load(Value, name=f"Load_{VarName}")
var: ir.AllocaInstr = Gen._allocaEntry(LoadedValue.type, name=VarName)
Gen._store(LoadedValue, var, Node=Node.value)
Gen.variables[VarName] = var
else:
var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName)
Gen._store(Value, var)
Gen.variables[VarName] = var
is_u: bool = Gen._check_node_unsigned(Node.value)
Gen._record_var_signedness(VarName, 'unsigned int' if is_u else 'int')
if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name):
call_func_name: str = Node.value.func.id
if call_func_name in Gen.var_type_info:
type_info: dict[str, str] = Gen.var_type_info[call_func_name]
if type_info['type'] in ('t_type', 'class_cast'):
src_fmt_var: str = f"__{call_func_name}_fmt"
dst_fmt_var: str = f"__{VarName}_fmt"
if src_fmt_var in Gen.variables:
if dst_fmt_var not in Gen.variables:
dst_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name=dst_fmt_var)
Gen.variables[dst_fmt_var] = dst_alloca
src_fmt_ptr: ir.Value = Gen._load(Gen.variables[src_fmt_var], name=f"Load_{call_func_name}_fmt_for_{VarName}")
Gen.builder.store(src_fmt_ptr, Gen.variables[dst_fmt_var])
src_kind_var: str = f"__{call_func_name}_kind"
dst_kind_var: str = f"__{VarName}_kind"
if src_kind_var in Gen.variables:
if dst_kind_var not in Gen.variables:
dst_kind_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name=dst_kind_var)
Gen.variables[dst_kind_var] = dst_kind_alloca
src_kind_val: ir.Value = Gen._load(Gen.variables[src_kind_var], name=f"Load_{call_func_name}_kind_for_{VarName}")
Gen.builder.store(src_kind_val, Gen.variables[dst_kind_var])
Gen.var_type_info[VarName] = {'type': 't_type_runtime', 'source': call_func_name}
else:
Gen.var_type_info[VarName] = {'type': 't_type_runtime', 'source': call_func_name}
elif isinstance(Target, ast.Attribute):
TargetType: ir.Type | None = None
AttrName: str = Target.attr
VarName: str | None = None
if isinstance(Target.value, ast.Name):
VarName = Target.value.id
if VarName:
ClassName: str | None = Gen.var_struct_class.get(VarName)
if not ClassName and Gen.global_struct_class:
ClassName = Gen.global_struct_class.get(VarName)
if ClassName:
Gen.var_struct_class[VarName] = ClassName
if not ClassName:
for CN in Gen.structs:
if VarName == CN.lower() or VarName.startswith(CN):
ClassName = CN
break
if ClassName and ClassName in Gen.class_members:
for m_name, m_type in Gen.class_members[ClassName]:
if m_name == AttrName:
TargetType = m_type
break
Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=TargetType)
if not Value:
if isinstance(Node.value, ast.Attribute):
self._RaiseUnresolvedExpr("属性写入", Node.value, Gen)
return
self._HandleAttributeStoreLlvm(Target, Value)
elif isinstance(Target, ast.Subscript):
TargetType: ir.Type | None = None
SubVarName: str | None = None
if isinstance(Target.value, ast.Name):
SubVarName = Target.value.id
if SubVarName in Gen.var_struct_class:
ClassName: str = Gen.var_struct_class[SubVarName]
if ClassName in Gen.class_members:
for m_name, m_type in Gen.class_members[ClassName]:
if m_name == SubVarName:
if isinstance(m_type, ir.PointerType):
pointee: ir.Type = m_type.pointee
if isinstance(pointee, ir.ArrayType):
TargetType = pointee.element
elif isinstance(m_type, ir.ArrayType):
TargetType = m_type.element
break
if not TargetType and isinstance(Target.value, ast.Name):
SubVarName = Target.value.id
var_val: ir.Value | None = Gen.variables.get(SubVarName)
if var_val is None and SubVarName in Gen._direct_values:
var_val = Gen._direct_values[SubVarName]
if var_val is not None:
var_type: ir.Type = var_val.type
if isinstance(var_type, ir.PointerType):
pointee: ir.Type = var_type.pointee
if isinstance(pointee, ir.ArrayType):
TargetType = pointee.element
elif isinstance(var_type, ir.ArrayType):
TargetType = var_type.element
Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=TargetType)
if not Value:
if isinstance(Node.value, ast.Attribute):
self._RaiseUnresolvedExpr("下标写入", Node.value, Gen)
return
self._HandleSubscriptStoreLlvm(Target, Value)
elif isinstance(Target, ast.UnaryOp):
# 解引用写入 `*p = v`(与读侧同一节点形态)。
# 解析链:语句级 Assign 走 vast_c.c ParseSimpleStmt 的
# ParseExprList + SetStoreContext;`*p` 由 ParseFactor 产出
# UnaryOp(op=Deref)(与 `*p` 读侧完全同形),而 SetStoreContext 只改
# Attribute/Subscript/Name/Tuple/List/Starred 的 ctx,**不改 UnaryOp**
# ⇒ 赋值目标就是 UnaryOp(Deref)。
# 此前本分派(Name/Attribute/Subscript/Tuple)无 UnaryOp 分支且无兜底
# ⇒ 该语句被整条静默丢弃(既不落盘也不报错):memhub 的块头
# `(order<<1)|1`、空闲链 next、canary 三处写入全不发射 ⇒ 块头恒为 0
# (free 被拒)、free_lists 链读到未初始化 0xBE、池单调漏满 ⇒ 1 GiB OOM。
# `&x`/`-x`/`~x` 作目标不是合法左值,故除 Deref 外一律 fail-fast。
if not isinstance(Target.op, ast.Deref):
BadOp: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Target.op)
raise Exception(
f"赋值目标只支持单目 '*' 解引用('{BadOp}' 不是合法左值)"
f"{Gen._get_node_info()}"
)
Ptr: ir.Value | None = self.HandleExprLlvm(Target.operand)
if Ptr is None:
self._RaiseUnresolvedExpr("解引用写入", Target.operand, Gen)
return
if not isinstance(Ptr.type, ir.PointerType):
raise Exception(
f"解引用赋值 '*' 的操作数必须是指针类型(类型推断退化,"
f"实际 {Ptr.type}){Gen._get_node_info()}"
)
# `*a = *b` is the Load form: copy the raw bytes (width = target
# pointee), never convert the value. Without it a bit-pun such as
# `*&bits = *&dbl` degrades to fptosi/trunc and loses the IEEE bits.
if isinstance(Node.value, ast.UnaryOp) and isinstance(Node.value.op, ast.Deref):
SrcPtr: ir.Value | None = self.HandleExprLlvm(Node.value.operand)
if SrcPtr is None:
self._RaiseUnresolvedExpr("解引用写入", Node.value.operand, Gen)
return
if not isinstance(SrcPtr.type, ir.PointerType):
raise Exception(
f"解引用赋值右侧 '*' 的操作数必须是指针类型(类型推断退化,"
f"实际 {SrcPtr.type}){Gen._get_node_info()}"
)
if EmitRawCopyLlvm(Ptr, SrcPtr, Gen):
return
DerefValue: ir.Value | None = self.HandleExprLlvm(Node.value)
if DerefValue is None:
self._RaiseUnresolvedExpr("解引用写入", Node.value, Gen)
return
# 方案1(收窄版):`*p = v` 仅当【p 的 pointee 是 8 位原始指针目标(i8/rawptr)】
# 且 v 是指针时,才 bitcast p 成 typeof(v)* 后按值存 v(`*(T*(p)) = v`)——
# 即"把指针位写进原始字节缓冲"这类合法位写。
# **不得**对结构槽(pointee 为 renum/struct)生效:那类 `*X = <构造调用>` 必须
# 交给 _StoreWithCoerce 的"构造物化"路径做 load+store 整对象拷贝,否则会把
# 栈 alloca 的地址当指针存进去(栈逃逸 → 变体损坏,SpecTypeBits/TypePrint 崩)。
PtrPointee: ir.Type | None = Ptr.type.pointee if isinstance(Ptr.type, ir.PointerType) else None
if (isinstance(DerefValue.type, ir.PointerType) and PtrPointee is not None
and PtrPointee != DerefValue.type
and isinstance(PtrPointee, ir.IntType) and PtrPointee.width == 8):
Ptr = Gen.builder.bitcast(Ptr, ir.PointerType(DerefValue.type), name="deref_store_as")
self._StoreWithCoerce(DerefValue, Ptr, Node.value)
elif isinstance(Target, (ast.Tuple, ast.List)):
# ast.List 与 ast.Tuple 字段表逐字相同(Nodes.py:119-120 均为
# ('elts', 'ctx')),故 `[a, b] = f()` 与 `a, b = f()` 共用本分支。
# 此前只认 Tuple ⇒ List 目标落入无分支区被静默丢弃(不落盘、不报错)。
if isinstance(Node.value, ast.Call):
FuncName: str | None = None
ModulePath: str | None = None
if isinstance(Node.value.func, ast.Name):
FuncName = Node.value.func.id
elif isinstance(Node.value.func, ast.Attribute):
FuncName = Node.value.func.attr
ModulePath = self.Trans.ExprCallHandle._get_ModulePath(Node.value.func.value)
MultiRetTypes: list[ast.expr] = []
FuncDef: ast.FunctionDef | None = self.Trans.FunctionDefCache.get(FuncName) if FuncName else None
if FuncDef:
if FuncDef.returns and isinstance(FuncDef.returns, ast.Subscript):
if isinstance(FuncDef.returns.value, ast.Name) and FuncDef.returns.value.id == 'tuple':
slice_node: ast.expr = FuncDef.returns.slice
if isinstance(slice_node, ast.Tuple):
MultiRetTypes = slice_node.elts
else:
MultiRetTypes = [slice_node]
if FuncDef.decorator_list:
for decorator in FuncDef.decorator_list:
if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Attribute):
# fail-fast:`@CReturn` 已移除,扫到即报错。
if decorator.func.attr == 'CReturn':
raise SyntaxError(
f"函数 '{FuncName}':`@CReturn` 已移除:请改用返回注解 "
f"`-> tuple[ty1, ty2, ...]`(多返回值打包为 struct)"
f"{Gen._get_node_info()}"
)
if not MultiRetTypes and FuncName:
func: ir.Function | None = Gen.functions.get(FuncName)
if func:
func_ft: ir.FunctionType | None = getattr(func, 'ftype', None)
if func_ft:
ret_type: ir.Type = func_ft.return_type
if isinstance(ret_type, ir.LiteralStructType):
MultiRetTypes = [None] * len(ret_type.elements)
if not MultiRetTypes:
sym_key: str = FuncName
if not self.Trans.SymbolTable.has(sym_key):
if ModulePath:
sym_key = f"{ModulePath}.{FuncName}"
sym_info: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(sym_key)
if sym_info and sym_info.IsFunction:
ret_type_info: CTypeInfo | None = sym_info.FuncPtrReturn
param_type_infos: list[tuple[str, CTypeInfo]] = [pt for _, pt in (sym_info.FuncPtrParams or [])]
if ret_type_info:
if isinstance(ret_type_info, CTypeInfo) and ret_type_info.BaseType:
ret_type: ir.Type = ret_type_info.ToLLVM(Gen)
else:
ret_type = Gen._type_str_to_llvm(str(ret_type_info) if ret_type_info else 'i32', False)
if isinstance(ret_type, ir.LiteralStructType):
MultiRetTypes = [None] * len(ret_type.elements)
if FuncName not in Gen.functions:
if isinstance(ret_type, ir.VoidType):
ret_type = ir.IntType(32)
llvm_param_types: list[ir.Type] = []
for pt in param_type_infos:
if isinstance(pt, CTypeInfo) and pt.BaseType:
lp: ir.Type = pt.ToLLVM(Gen)
else:
lp = Gen._type_str_to_llvm(str(pt) if pt else 'i32', '*' in str(pt) if isinstance(pt, str) else False)
if isinstance(lp, ir.VoidType):
lp = ir.IntType(8).as_pointer()
llvm_param_types.append(lp)
func_type: ir.FunctionType = ir.FunctionType(ret_type, llvm_param_types)
MangledName: str = Gen._mangle_func_name(FuncName)
# 走唯一幂等入口(module.globals 已有同名声明复用 /
# 类型冲突 fail-fast),避免裸建抛 DuplicatedNameError。
func_decl: ir.Function = Gen._CreateOrReuseFuncDecl(MangledName, func_type)
Gen.functions[FuncName] = func_decl
if not MultiRetTypes and FuncName:
func: ir.Function | None = Gen.functions.get(FuncName)
if func:
func_ft: ir.FunctionType | None = getattr(func, 'ftype', None)
if func_ft:
ret_type: ir.Type = func_ft.return_type
if isinstance(ret_type, ir.LiteralStructType):
MultiRetTypes = [None] * len(ret_type.elements)
if not MultiRetTypes:
# 跨模块多返回值兜底:短名未注册时(函数以 ModulePath.名 /
# sha1.名 全名注册),从其 LiteralStructType 返回类型恢复 MultiRetTypes。
# 否则 MultiRetTypes 为空会走下方静默 return,目标变量未初始化,
# 运行期 free(垃圾指针) 触发堆损坏(HashLibTest 0xC0000374)。
func = None
if ModulePath:
func = Gen.functions.get(f"{ModulePath}.{FuncName}")
if func is None:
for key in Gen.functions:
if key.endswith(f".{FuncName}"):
func = Gen.functions[key]
break
if func:
func_ft: ir.FunctionType | None = getattr(func, 'ftype', None)
if func_ft:
ret_type: ir.Type = func_ft.return_type
if isinstance(ret_type, ir.LiteralStructType):
MultiRetTypes = [None] * len(ret_type.elements)
if MultiRetTypes and len(Target.elts) == len(MultiRetTypes):
# 注:此处原有两行 `if func is None: func = ...`,但 func 只在上面
# 「not MultiRetTypes」分支里绑定;当 MultiRetTypes 来自返回注解
# (`-> tuple[T1, T2]`)时该分支未执行 ⇒ UnboundLocalError 崩溃。
# 且紧接着的 `func = Gen.functions.get(FuncName)` 本就覆盖它,
# 故直接删除(`a, b = f()` 因此从未真正跑通)。
CallArgs: list[ir.Value | None] = []
for arg in Node.value.args:
ArgVal: ir.Value | None = self.HandleExprLlvm(arg)
if ArgVal:
CallArgs.append(ArgVal)
for kw in Node.value.keywords:
pass
func: ir.Function | None = Gen.functions.get(FuncName)
func_ft: ir.FunctionType | None = getattr(func, 'ftype', None) if func else None
func_params: list[ir.Type] = list(getattr(func_ft, 'args', []) or []) if func_ft else []
while len(CallArgs) < len(func_params):
FuncDef: ast.FunctionDef | None = self.Trans.FunctionDefCache.get(FuncName)
if FuncDef and len(CallArgs) < len(FuncDef.args.args):
arg_def: list[ast.expr] = FuncDef.args.defaults
arg_idx: int = len(CallArgs)
total_args: int = len(FuncDef.args.args)
default_start: int = total_args - len(arg_def)
if arg_idx >= default_start:
def_idx: int = arg_idx - default_start
if def_idx < len(arg_def):
DefVal: ir.Value | None = self.HandleExprLlvm(arg_def[def_idx])
if DefVal:
expected_type: ir.Type = func_params[arg_idx]
if DefVal.type != expected_type:
if isinstance(expected_type, ir.IntType) and isinstance(DefVal.type, ir.IntType):
if DefVal.type.width < expected_type.width:
DefVal = Gen.builder.zext(DefVal, expected_type, name=f"zext_default_{arg_idx}")
elif DefVal.type.width > expected_type.width:
DefVal = Gen.builder.trunc(DefVal, expected_type, name=f"trunc_default_{arg_idx}")
CallArgs.append(DefVal)
continue
if len(CallArgs) < len(func_params):
CallArgs.append(ir.Constant(func_params[len(CallArgs)], 0))
else:
break
adjusted: list[ir.Value] = Gen._adjust_args(CallArgs, func)
call_result: ir.Value = Gen.builder.call(func, adjusted, name=f"call_{FuncName}")
func_ft: ir.FunctionType | None = getattr(func, 'ftype', None)
ret_type: ir.Type | None = getattr(func_ft, 'return_type', None) if func_ft else None
for j, elt in enumerate(Target.elts):
if isinstance(elt, ast.Name):
VarName: str = elt.id
field_val: ir.Value = Gen.builder.extract_value(call_result, j, name=f"extract_{FuncName}_{j}")
VarType: ir.Type
if MultiRetTypes[j] is not None:
ReturnTypeInfo: CTypeInfo | None = CTypeInfo.FromNode(MultiRetTypes[j], self.Trans.SymbolTable)
if ReturnTypeInfo is None:
ReturnTypeInfo = CTypeInfo()
ReturnTypeInfo.BaseType = TypeSystem.CInt()
VarType = Gen._ctype_to_llvm(ReturnTypeInfo)
elif ret_type and isinstance(ret_type, ir.LiteralStructType) and j < len(ret_type.elements):
VarType = ret_type.elements[j]
else:
VarType = field_val.type
if isinstance(VarType, ir.VoidType):
VarType = ir.IntType(32)
var: ir.AllocaInstr = Gen._alloca(VarType, name=VarName)
Gen.variables[VarName] = var
if MultiRetTypes[j] is not None:
Gen._record_var_signedness(VarName, str(ReturnTypeInfo) if ReturnTypeInfo else '')
elif ret_type and isinstance(ret_type, ir.LiteralStructType) and j < len(ret_type.elements):
elem_type: ir.Type = ret_type.elements[j]
if isinstance(elem_type, ir.IntType):
if elem_type.width == 16:
Gen._record_var_signedness(VarName, 'unsigned short')
elif elem_type.width == 32:
Gen._record_var_signedness(VarName, 'unsigned int')
elif elem_type.width == 64:
Gen._record_var_signedness(VarName, 'unsigned long long')
Gen._store(field_val, var)
elif isinstance(Node.value, ast.Call):
# fail-fast:多返回值赋值但 MultiRetTypes 解析失败(跨模块签名丢失),
# 绝不能静默跳过——目标变量不会被初始化,运行期 free(垃圾指针)
# 触发堆损坏(HashLibTest 0xC0000374)。
target_names: list[str] = [
e.id if isinstance(e, ast.Name) else '<expr>' for e in Target.elts
]
raise Exception(
f"多返回值赋值解析失败: '{FuncName}' 的返回类型无法解析为"
f"返回注解 `-> tuple[...]`(ModulePath={ModulePath}),目标 {target_names} 未被初始化"
f"{Gen._get_node_info()}"
)
return
def _InitStructAtPtr(self, call_node: ast.Call, ClassName: str, VarPtr: ir.Value) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if ClassName not in Gen.structs:
return
StructType: ir.Type = Gen.structs[ClassName]
StructPtrType: ir.PointerType = ir.PointerType(StructType)
RawPtr: ir.Value = Gen._load(VarPtr, name=getattr(VarPtr, 'name', 'ptr'))
StructPtr: ir.Value = Gen.builder.bitcast(RawPtr, StructPtrType, name=ClassName)
members: list[tuple[str, ir.Type]] = Gen.class_members.get(ClassName, [])
defaults: dict[str, ir.Value] = Gen.class_member_defaults.get(ClassName, {})
member_values: dict[str, ir.Value] = {}
for member_name, member_type in members:
if member_name in defaults:
member_values[member_name] = defaults[member_name]
if call_node.args:
for i, arg in enumerate(call_node.args):
if i < len(members):
member_name: str = members[i][0]
val: ir.Value | None = self.HandleExprLlvm(arg)
if val:
member_values[member_name] = val
if call_node.keywords:
for kw in call_node.keywords:
val: ir.Value | None = self.HandleExprLlvm(kw.value)
if val:
member_values[kw.arg] = val
# base(vtable 占位槽数)统一走 Gen._member_base(唯一真源),
# 不再直查 class_vtable/_cross_module_vtable_classes(会漏掉跨模块 AST 节点类)。
base: int = Gen._member_base(ClassName)
for i, (member_name, member_type) in enumerate(members):
if isinstance(member_type, ir.VoidType):
continue
if member_name in member_values:
ElemPtr: ir.Value = Gen.builder.gep(StructPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), base + i)], name=f"{ClassName}_{member_name}")
try:
Gen._store(member_values[member_name], ElemPtr)
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
InitFuncName: str = f'{ClassName}.__init__'
if Gen._has_function(InitFuncName):
Args: list[ir.Value | None] = [self.HandleExprLlvm(arg) for arg in call_node.args]
Args = [a for a in Args if a]
Gen.builder.call(Gen._get_function(InitFuncName), [StructPtr] + Args, name=f"call_{InitFuncName}")
def _HandleAttributeStoreLlvm(self, Target: ast.Attribute, Value: ir.Value) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
AttrName: str = Target.attr
# const 类字段:给 `obj.字段 = ...` / `self.字段 = ...` 直接赋值同样拒绝
# (判据 = 字段自身 const,见 BaseGen._IsFieldConst / Debugger/type-descriptors.md)。
# 类名能解析时按 `类名.字段名` 精确判定;解析不到时退化裸名(保守报错)。
_TargetCN: str | None = None
if isinstance(Target.value, ast.Name):
_TargetCN = self.Trans.ExprHandler._get_var_class(Target.value, Gen)
if Gen._IsFieldConst(_TargetCN, AttrName):
_vlog().error(Gen._ConstAssignMessage("字段赋值", AttrName))
sys.exit(1)
if isinstance(Target.value, ast.Name) and Target.value.id == 'self':
ClassName: str | None = self.Trans._CurrentCpythonObjectClass
if ClassName and ClassName in Gen.structs:
PropKey: str = f'{ClassName}.{AttrName}'
PropInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(PropKey)
if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_SETTER in PropInfo.MetaList:
SelfVar: ir.Value | None = Gen._get_var_ptr('self')
if SelfVar:
SelfPtr: ir.Value = Gen._load(SelfVar, name="self")
SetterFunc: ir.Function | None = Gen._get_function(PropKey + '$set')
if SetterFunc and SelfPtr:
Gen.builder.call(SetterFunc, [SelfPtr, Value], name=f"prop_set_{PropKey}")
return
SelfVar: ir.Value | None = Gen._get_var_ptr('self')
if not SelfVar:
return
if SelfVar:
SelfPtr: ir.Value = Gen._load(SelfVar, name="self")
is_vtable_method: bool = False
method_idx: int = 0
if ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes:
if ClassName in Gen.class_methods:
for mi, mn in enumerate(Gen.class_methods[ClassName]):
method_short: str = mn.split('.')[-1] if '.' in mn else mn
if method_short == AttrName or mn == AttrName:
is_vtable_method = True
method_idx = mi
break
if is_vtable_method:
VtableSlotPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"vtable_slot_{ClassName}")
VtablePtr: ir.Value = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}")
methods: list[str] = Gen.class_methods[ClassName]
VtableArrayType: ir.ArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods))
ClassVtable: ir.GlobalVariable | None = Gen.Vtables.get(ClassName)
if ClassVtable:
ClassVtableAddr: ir.Value = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"class_vtable_addr_{ClassName}")
IsSameVtable: ir.Value = Gen.builder.icmp_unsigned('==', VtablePtr, ClassVtableAddr, name=f"is_same_vtable_{ClassName}")
CopyBlock: ir.Block = Gen.func.append_basic_block(name=f"vtable_copy_self_{AttrName}")
SkipBlock: ir.Block = Gen.func.append_basic_block(name=f"vtable_skip_self_{AttrName}")
Gen.builder.cbranch(IsSameVtable, CopyBlock, SkipBlock)
Gen.builder.position_at_end(CopyBlock)
Gen._vtable_copy_counter += 1
CopyName: str = f"{Gen._mangle_name(ClassName)}_vtable_copy_self_{Gen._vtable_copy_counter}"
VtableCopyGlobal: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VtableArrayType, name=CopyName)
VtableCopyGlobal.linkage = 'internal'
VtableCopyGlobal.initializer = ir.Constant(VtableArrayType, [ir.Constant(ir.PointerType(ir.IntType(8)), None)] * len(methods))
VtableCopyTyped: ir.Value = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_typed")
SrcTyped: ir.Value = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"src_vtable_typed")
VtableSize: int = len(methods) * Gen.ptr_size
MemcpyFunc: ir.Function | None = Gen._find_function('llvm.memcpy.p0i8.p0i8.i64')
if not MemcpyFunc:
MemcpyType: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(1)])
MemcpyFunc = Gen._CreateOrReuseFuncDecl('llvm.memcpy.p0i8.p0i8.i64', MemcpyType)
Gen.functions['llvm.memcpy.p0i8.p0i8.i64'] = MemcpyFunc
Gen.builder.call(MemcpyFunc, [VtableCopyTyped, SrcTyped, ir.Constant(ir.IntType(64), VtableSize), ir.Constant(ir.IntType(1), 0)])
VtableCopyGlobalAddr: ir.Value = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_addr")
Gen._store(VtableCopyGlobalAddr, VtableSlotPtr)
Gen.builder.branch(SkipBlock)
Gen.builder.position_at_end(SkipBlock)
VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}_after_copy")
VtableTyped: ir.Value = Gen.builder.bitcast(VtablePtr, ir.PointerType(VtableArrayType), name=f"vtable_typed_{ClassName}")
MethodPtrAddr: ir.Value = Gen.builder.gep(VtableTyped, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), method_idx)], name=f"vmethod_slot_{AttrName}")
FuncPtrI8: ir.Value = Gen.builder.bitcast(Value, ir.PointerType(ir.IntType(8)), name=f"func_ptr_i8_{AttrName}")
Gen._store(FuncPtrI8, MethodPtrAddr)
return
# 自指针基址收口:与**读路径同一判据源**(HandlesExprAttr
# `_ConcretizeGepBase` / `_ConcretePointeeFor`),三步同源:
# ① 类键那份 opaque ⇒ 先 `_TryLoadStructFromStub` 补 body;
# ② 仍不可用 ⇒ 按同短名取一份已具化实例(`_FindConcreteTwin`);
# ③ 都拿不到 ⇒ 原样返回,由下方 opaque fail-fast 报根因。
# 写路径此前是**内联**版本、缺 ①:类键那份仍 opaque 且 `Gen.structs`
# 里也还没有孪生体时(body 只在 stub 文件里待加载),内联版会一路走到
# GEP 撞 opaque(现场 2026-09-30:includes/ast/astaux.vp:165
# `self.arg = _copy_str(pool, arg)` 的基址 pointee 是 opaque 的
# %"8312ce86a2fd82c0.Keyword"*,而同短名 'Keyword' 键那份已具化 10 槽)。
# 偏移与 GEP 必须同源:返回的实例同时作为 `_get_member_offset` 的 StructHint。
SelfPtr, _SelfPointee = self.Trans.ExprAttrHandle._ConcretizeGepBase(
Gen, SelfPtr, ClassName, f"concrete_self_{ClassName}")
struct_type: ir.Type = (_SelfPointee
if isinstance(_SelfPointee, ir.IdentifiedStructType)
else Gen.structs[ClassName])
if (isinstance(_SelfPointee, ir.IdentifiedStructType)
and _SelfPointee.elements is None):
# 收口失败:类键那份、同短名孪生体、以及 `_TryLoadStructFromStub`
# 三条路都没拿到 body。**不得**再落回下方 `offset >= max_offset`
# 的「存回临时变量」分支 —— 该临时变量不在对象布局里,后续
# `self.attr` 读路径按 GEP 取值,写入会被**静默丢弃**。此处 fail-fast。
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE0
raise _FFE0(
f"属性写入 '{ClassName}.{AttrName}' 的 self 基址指向**未具化**"
f"结构体:'{getattr(_SelfPointee, 'name', '?')}'(opaque)。"
f"已尝试:类键 {ClassName!r} 的实例 → 同短名孪生体 → "
f"_TryLoadStructFromStub 补 body,均未拿到 elements。"
f"请查该类 body 为何从未 set_body(StructGen._generate_structs)。"
+ (f"\n {Gen._get_node_info()}" if hasattr(Gen, '_get_node_info') else ""))
offset: int = Gen._get_member_offset(AttrName, ClassName, _SelfPointee)
max_offset: int = (len(struct_type.elements)
if isinstance(struct_type, ir.IdentifiedStructType)
and struct_type.elements is not None else 0)
if offset >= max_offset:
VarName: str = f"self.{AttrName}"
if VarName not in Gen.variables:
NewVar: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=AttrName)
Gen.variables[VarName] = NewVar
Gen._store(Value, Gen.variables[VarName])
return
MemberPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
# 成员槽写入失败时补「来源明细」:槽类型的两个来源(class_members
# 声明侧 / struct body 布局侧)是两条独立链,不一致时会静默写进
# 类型完全不同的槽。报文里逐项列出两侧数据,直接判定谁退化了
# (现场:Generator.ExprGenConst 声明 %"...ExprGenConst"* 却落到 i8**)。
try:
self._StoreWithCoerce(Value, MemberPtr)
except Exception as _Me:
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
if not isinstance(_Me, _FFE):
raise
raise _FFE(
f"{_Me}{self._DescribeMemberSlot(Gen, ClassName, AttrName, offset, SelfPtr)}"
) from _Me
elif isinstance(Target.value, ast.Name):
VarName: str = Target.value.id
# 模块全局变量写入(治本修复 2026-08-27):X.attr = ... 且 X 是 import
# 模块(含别名:resolve_alias 命中 _ImportedModules)时,必须优先按
# "模块全局变量"处理——直接 store 到该模块的裸名/带 sha1 前缀全局符号。
# 该检查必须放在 ClassName heuristic(下方 startswith(CN) 类名匹配)之前:
# 模块名与类名同名(如 CompilerFactory)时,heuristic 会把模块误判为类
# 而走 _get_member_offset → fail-fast KeyError"属性在类中找不到"。
# 读端(_lookup_module_global/_get_attr_ptr)已有此判定,写端此前缺失
# → main.vp 的 `CompilerFactory.factory = CompilerFactory(...)` 从未写入
# → Generator.__init__ 读 @factory=NULL → ASan 崩 0x8。
_mod_imported: list[str] | None = getattr(self.Trans, '_ImportedModules', None)
_resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName)
# 模块判定:VarName/ResolvedName 直等命中,或与某个已导入模块全名
# 后缀匹配(如 VarName=CompilerFactory 命中 lib.core.CompilerFactory,
# 与 read 端 _lookup_module_global L212 的 endswith 判定保持一致)。
_is_mod_write: bool = False
if _mod_imported:
_is_mod_write = (VarName in _mod_imported or _resolved_mod in _mod_imported)
if not _is_mod_write:
for _mn in _mod_imported:
if (_mn == VarName or _mn.endswith(f".{VarName}")
or _mn == _resolved_mod or _mn.endswith(f".{_resolved_mod}")):
_is_mod_write = True
break
if _is_mod_write:
# 优先带前缀:{module_sha1}.{AttrName}(TPC 编译定义端为裸名,VPC 自举
# 后端为 sha1 前缀名,两代都要覆盖),再回退裸名 {AttrName}。
_mod_sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {})
_cands: list[str] = [VarName, _resolved_mod]
for _mn in _mod_imported:
if _mn.endswith(f".{VarName}") or _mn == VarName:
if _mn not in _cands:
_cands.append(_mn)
for _cand in _cands:
_sha1: str | None = _mod_sha1_map.get(_cand)
if _sha1:
_prefixed: str = f"{_sha1}.{AttrName}"
if _prefixed in Gen.module.globals:
_pgv: ir.GlobalVariable = Gen.module.globals[_prefixed]
if isinstance(_pgv, ir.Function):
return
self._StoreWithCoerce(Value, _pgv)
return
if AttrName in Gen.module.globals:
_gv: ir.GlobalVariable = Gen.module.globals[AttrName]
if isinstance(_gv, ir.Function):
return
self._StoreWithCoerce(Value, _gv)
return
# FakeDuck: 检查是否有影子结构体 a__meta__ (str 变量的 per-instance 字段存储)
# 直接通过 a.__field__ = value 访问影子结构体字段 (语法糖)
_fd_meta_name: str = f"{VarName}__meta__"
if _fd_meta_name in Gen.variables and '_str' in Gen.structs:
_fd_offset = Gen._get_member_offset(AttrName, '_str')
if _fd_offset is not None:
_fd_meta_ptr: ir.Value = Gen.variables[_fd_meta_name]
_fd_field_ptr: ir.Value = Gen.builder.gep(_fd_meta_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _fd_offset)], name=f"{VarName}_{AttrName}_ptr")
self._StoreWithCoerce(Value, _fd_field_ptr)
return
ClassName: str | None = Gen.var_struct_class.get(VarName)
if not ClassName and Gen.global_struct_class:
ClassName = Gen.global_struct_class.get(VarName)
if ClassName:
Gen.var_struct_class[VarName] = ClassName
if not ClassName:
for CN in Gen.structs:
if VarName == CN.lower() or VarName.startswith(CN):
ClassName = CN
break
if not ClassName and VarName in Gen.module.globals:
GVar: ir.GlobalVariable = Gen.module.globals[VarName]
if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(GVar.type.pointee)
if found:
ClassName = found[0]
Gen.var_struct_class[VarName] = ClassName
if Gen.global_struct_class:
Gen.global_struct_class[VarName] = ClassName
if not ClassName and VarName in Gen.variables:
VarPtr: ir.Value = Gen.variables[VarName]
if isinstance(VarPtr.type, ir.PointerType):
pointee: ir.Type = VarPtr.type.pointee
if isinstance(pointee, ir.PointerType):
pointee = pointee.pointee
found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(pointee)
if found:
CN: str = found[0]
ST: ir.Type = found[1]
ClassName = CN
elif isinstance(pointee, ir.IdentifiedStructType):
# sha1 冲突 fallback:find_struct_by_pointee 因 sha1 前缀不匹配失败时,
# 用短名(去掉 sha1 前缀)匹配,优先选字段最多的完整版本
pointee_short: str = Gen._extract_short_name(pointee.name)
best_elem_count: int = 0
for CN, ST in Gen.structs.items():
if isinstance(ST, ir.IdentifiedStructType):
st_short: str = Gen._extract_short_name(ST.name)
if st_short == pointee_short:
elem_count: int = len(ST.elements) if ST.elements else 0
if elem_count > best_elem_count:
ClassName = CN
best_elem_count = elem_count
IsUnion: bool = False
if ClassName:
TypeInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(ClassName)
if TypeInfo and TypeInfo.IsUnion:
IsUnion = True
if ClassName and ClassName in Gen.structs:
PropKey: str = f'{ClassName}.{AttrName}'
PropInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(PropKey)
if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_SETTER in PropInfo.MetaList:
ObjVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value)
SetterFunc: ir.Function | None = Gen._get_function(PropKey + '$set')
if SetterFunc and ObjVal:
if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType):
ObjVal = Gen._load(ObjVal, name=f"Load_{ClassName}")
Gen.builder.call(SetterFunc, [ObjVal, Value], name=f"prop_set_{PropKey}")
return
ObjVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value)
if ObjVal:
if BaseHandle._is_char_pointer(ObjVal):
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType):
ObjVal = Gen._load(ObjVal, name=f"Load_{ClassName}")
# 跨模块 opaque 类型:ObjVal.type.pointee 与 Gen.structs[ClassName] 可能是
# 不同的 IdentifiedStructType 对象(虽然 name 相同),== 返回 False。
# 使用 name 匹配作为 fallback,与链式赋值路径(第 926-927 行)一致。
# sha1 冲突时还需短名匹配(如 6bc91356c652f8ca.Function vs 8ad0c0a2934ca5b4.Function)
_ObjPointee = ObjVal.type.pointee if isinstance(ObjVal.type, ir.PointerType) else None
_TargetStruct = Gen.structs.get(ClassName)
_MatchesStruct = (_ObjPointee is not None and _TargetStruct is not None and
(_ObjPointee == _TargetStruct or
(isinstance(_ObjPointee, ir.IdentifiedStructType) and isinstance(_TargetStruct, ir.IdentifiedStructType) and
(_ObjPointee.name == _TargetStruct.name or
Gen._extract_short_name(_ObjPointee.name) == Gen._extract_short_name(_TargetStruct.name)))))
# opaque 泛型结构体 → 特化版本 bitcast
# 当 ObjVal 的 pointee 是 opaque 基础泛型结构体(如 GSList,来自跨模块 stub),
# 而 ClassName 是特化版本(如 GSList[BasicBlock])时,两者 short name 不同
# (GSList vs GSList[BasicBlock]),_MatchesStruct 失败。
# 验证特化 short name 以 基础名+'[' 开头,bitcast 到特化版本使后续 GEP 能正确访问字段。
if (not _MatchesStruct
and isinstance(ObjVal.type, ir.PointerType)
and isinstance(_ObjPointee, ir.IdentifiedStructType)
and isinstance(_TargetStruct, ir.IdentifiedStructType)
and '[' in ClassName):
_BaseShort: str = Gen._extract_short_name(_ObjPointee.name)
_SpecShort: str = Gen._extract_short_name(_TargetStruct.name)
if _BaseShort and _SpecShort.startswith(_BaseShort + '['):
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(_TargetStruct), name=f"cast_spec_{ClassName}")
_MatchesStruct = True
if isinstance(ObjVal.type, ir.PointerType) and _MatchesStruct:
if IsUnion:
NestedStructName: str = f"{ClassName}_{AttrName}"
if NestedStructName in Gen.structs:
NestedStructType: ir.Type = Gen.structs[NestedStructName]
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(NestedStructType), name=f"cast_{NestedStructName}")
offset: int | None = Gen._get_member_offset(AttrName, NestedStructName)
if offset is not None and offset < len(NestedStructType.elements):
MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
return
bitfield_offsets: dict[str, int] = Gen.class_member_bitoffsets.get(ClassName, {})
if AttrName in bitfield_offsets:
self._store_bitfield_member(ObjVal, ClassName, AttrName, Value)
return
bitfields: dict[str, int] = Gen.class_member_bitfields.get(ClassName, {})
if AttrName in bitfields and bitfields[AttrName] > 0:
if ClassName not in Gen.class_member_bitoffsets:
Gen.class_member_bitoffsets[ClassName] = {}
if AttrName not in Gen.class_member_bitoffsets[ClassName]:
bo: int = 0
for bf_name, bf_width in bitfields.items():
if bf_name == AttrName:
break
bo += bf_width
Gen.class_member_bitoffsets[ClassName][AttrName] = bo
self._store_bitfield_member(ObjVal, ClassName, AttrName, Value)
return
is_vtable_method: bool = False
method_idx: int = 0
if ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes:
if ClassName in Gen.class_methods:
for mi, mn in enumerate(Gen.class_methods[ClassName]):
method_short: str = mn.split('.')[-1] if '.' in mn else mn
if method_short == AttrName or mn == AttrName:
is_vtable_method = True
method_idx = mi
break
if is_vtable_method:
VtableSlotPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"vtable_slot_{ClassName}")
VtablePtr: ir.Value = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}")
methods: list[str] = Gen.class_methods[ClassName]
VtableArrayType: ir.ArrayType = ir.ArrayType(ir.PointerType(ir.IntType(8)), len(methods))
ClassVtable: ir.GlobalVariable | None = Gen.Vtables.get(ClassName)
if ClassVtable:
ClassVtableAddr: ir.Value = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"class_vtable_addr_{ClassName}")
IsSameVtable: ir.Value = Gen.builder.icmp_unsigned('==', VtablePtr, ClassVtableAddr, name=f"is_same_vtable_{ClassName}")
CopyBlock: ir.Block = Gen.func.append_basic_block(name=f"vtable_copy_{AttrName}")
SkipBlock: ir.Block = Gen.func.append_basic_block(name=f"vtable_skip_{AttrName}")
Gen.builder.cbranch(IsSameVtable, CopyBlock, SkipBlock)
Gen.builder.position_at_end(CopyBlock)
Gen._vtable_copy_counter += 1
CopyName: str = f"{Gen._mangle_name(ClassName)}_vtable_copy_{Gen._vtable_copy_counter}"
VtableCopyGlobal: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VtableArrayType, name=CopyName)
VtableCopyGlobal.linkage = 'internal'
VtableCopyGlobal.initializer = ir.Constant(VtableArrayType, [ir.Constant(ir.PointerType(ir.IntType(8)), None)] * len(methods))
VtableCopyTyped: ir.Value = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_typed")
SrcTyped: ir.Value = Gen.builder.bitcast(ClassVtable, ir.PointerType(ir.IntType(8)), name=f"src_vtable_typed")
VtableSize: int = len(methods) * Gen.ptr_size
MemcpyFunc: ir.Function | None = Gen._find_function('llvm.memcpy.p0i8.p0i8.i64')
if not MemcpyFunc:
MemcpyType: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(1)])
MemcpyFunc = Gen._CreateOrReuseFuncDecl('llvm.memcpy.p0i8.p0i8.i64', MemcpyType)
Gen.functions['llvm.memcpy.p0i8.p0i8.i64'] = MemcpyFunc
Gen.builder.call(MemcpyFunc, [VtableCopyTyped, SrcTyped, ir.Constant(ir.IntType(64), VtableSize), ir.Constant(ir.IntType(1), 0)])
VtableCopyGlobalAddr: ir.Value = Gen.builder.bitcast(VtableCopyGlobal, ir.PointerType(ir.IntType(8)), name=f"vtable_copy_addr")
Gen._store(VtableCopyGlobalAddr, VtableSlotPtr)
Gen.builder.branch(SkipBlock)
Gen.builder.position_at_end(SkipBlock)
VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}_after_copy")
VtableTyped: ir.Value = Gen.builder.bitcast(VtablePtr, ir.PointerType(VtableArrayType), name=f"vtable_typed_{ClassName}")
MethodPtrAddr: ir.Value = Gen.builder.gep(VtableTyped, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), method_idx)], name=f"vmethod_slot_{AttrName}")
FuncPtrI8: ir.Value = Gen.builder.bitcast(Value, ir.PointerType(ir.IntType(8)), name=f"func_ptr_i8_{AttrName}")
Gen._store(FuncPtrI8, MethodPtrAddr)
return
offset: int | None = Gen._get_member_offset(AttrName, ClassName)
StructType: ir.Type | None = Gen.structs.get(ClassName)
if isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0):
self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen)
StructType = Gen.structs.get(ClassName)
if isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0):
return
# 重新计算 offset:_TryLoadStructFromStub 刚加载了 struct body,
# 之前 offset 可能因 struct opaque / class_members 不完整而返回 None
offset = Gen._get_member_offset(AttrName, ClassName)
# sha1 冲突修复:Gen.structs[ClassName] 可能是字段不全的版本(如 2 字段 Function
# 来自 MetaType.py scope,而正确版本是 8 字段 Function 来自 __function.py)。
# _get_member_offset 通过 class_sha1_map 已返回正确的 offset(如 5),
# 但 StructType 可能只有 2 个 elements,导致 offset < len(elements) 失败。
# 在所有 structs 中查找同短名且字段最多的版本。
if offset is not None and isinstance(StructType, ir.IdentifiedStructType) and StructType.elements is not None and offset >= len(StructType.elements):
_short: str = Gen._extract_short_name(StructType.name)
_best: int = len(StructType.elements)
for _cn, _st in Gen.structs.items():
if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None:
if Gen._extract_short_name(_st.name) == _short and len(_st.elements) > _best:
StructType = _st
_best = len(_st.elements)
# bitcast:当 ObjVal 的 pointee 是 opaque 或与 StructType 不是同一个类型对象
# (sha1 前缀不同但短名相同)时,需要 bitcast 到 StructType 才能正确 GEP
if StructType is not None and StructType.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and (ObjVal.type.pointee.elements is None or ObjVal.type.pointee != StructType):
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(StructType), name=f"cast_{ClassName}")
if offset is not None and StructType is not None and StructType.elements is not None and offset < len(StructType.elements):
MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
else:
return
byte_order: str = Gen.class_member_byteorders.get(ClassName, {}).get(AttrName, "")
if byte_order == 'big':
if StructType is None or StructType.elements is None or len(StructType.elements) == 0:
return
member_type: ir.Type = StructType.elements[offset]
if isinstance(member_type, ir.IntType):
Value = Gen._apply_bswap_if_big(Value, byte_order, f"bswap_store_{AttrName}")
# 成员槽写入失败时补「来源明细」(与 `self.x = ...` 路径 :897-905
# 同一套 `_DescribeMemberSlot`):槽类型的两条独立来源
# (class_members 声明侧 / struct body 布局侧)不一致时,值会被
# 写进类型完全不同的槽(现场:`self.Gen.TypeGenCType.
# LastBuiltinsTypedefResult = "CPtr"` 落到 i32 槽 → 指针隐式
# 转整数 fail-fast);报文里逐项列出两侧数据即可直接判定谁退化
# (此处目标形态是 `对象.字段 = 值`,Base 侧同理取 ObjVal 所指布局)。
try:
self._StoreWithCoerce(Value, MemberPtr)
except Exception as _Me:
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
if not isinstance(_Me, _FFE):
raise
raise _FFE(
f"{_Me}{self._DescribeMemberSlot(Gen, ClassName, AttrName, offset, ObjVal)}"
) from _Me
if not ClassName:
imported_modules: list[str] | None = getattr(self.Trans, '_ImportedModules', None)
resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName)
if imported_modules and (VarName in imported_modules or resolved_mod in imported_modules):
PossibleKeys: list[str] = [f"{VarName}.{AttrName}", AttrName]
for mod_name in imported_modules:
if mod_name.endswith(f".{VarName}") or mod_name == VarName:
key: str = f"{mod_name}.{AttrName}"
if key not in PossibleKeys:
PossibleKeys.append(key)
for key in PossibleKeys:
if key in Gen.module.globals:
GVar: ir.GlobalVariable = Gen.module.globals[key]
if isinstance(GVar, ir.Function):
return
if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
return
self._StoreWithCoerce(Value, GVar)
return
ModuleSha1Map: dict[str, str] = Gen.ModuleSha1Map
sha1_candidates: list[str] = [VarName, resolved_mod]
for mod_name in imported_modules:
if mod_name.endswith(f".{VarName}") or mod_name == VarName:
if mod_name not in sha1_candidates:
sha1_candidates.append(mod_name)
for cand in sha1_candidates:
sha1: str | None = ModuleSha1Map.get(cand)
if sha1:
prefixed: str = f"{sha1}.{AttrName}"
if prefixed in Gen.module.globals:
GVar2: ir.GlobalVariable = Gen.module.globals[prefixed]
if isinstance(GVar2, ir.Function):
return
if isinstance(GVar2.type, ir.PointerType) and isinstance(GVar2.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
return
self._StoreWithCoerce(Value, GVar2)
return
elif isinstance(Target.value, ast.Attribute):
ObjVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value)
if ObjVal and isinstance(ObjVal.type, ir.PointerType):
pointee: ir.Type = ObjVal.type.pointee
if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is None:
for CN, ST in Gen.structs.items():
if isinstance(ST, ir.IdentifiedStructType) and ST.name == pointee.name and ST.elements is not None:
pointee = ST
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{CN}")
break
found_match: bool = False
for CN, ST in Gen.structs.items():
if pointee == ST or (isinstance(pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType) and pointee.name == ST.name):
found_match = True
if ST.elements is None:
self.Trans.ImportHandler._TryLoadStructFromStub(CN, Gen)
ST = Gen.structs.get(CN, ST)
if ST.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and ObjVal.type.pointee.elements is None:
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{CN}")
bitfield_offsets: dict[str, int] = Gen.class_member_bitoffsets.get(CN, {})
if AttrName in bitfield_offsets:
self._store_bitfield_member(ObjVal, CN, AttrName, Value)
return
offset: int = Gen._get_member_offset(AttrName, CN)
if offset is not None and ST.elements is not None and offset < len(ST.elements):
MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
break
if not found_match:
if isinstance(pointee, ir.IdentifiedStructType):
matched_cn: str | None = None
for CN in Gen.class_members:
members: list[tuple[str, ir.Type]] = Gen.class_members[CN]
for mname, mtype in members:
if mname == AttrName:
matched_cn = CN
break
if matched_cn:
break
if matched_cn and matched_cn in Gen.structs:
ST: ir.Type = Gen.structs[matched_cn]
if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0):
self.Trans.ImportHandler._TryLoadStructFromStub(matched_cn, Gen)
ST = Gen.structs.get(matched_cn, ST)
if ST.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and ObjVal.type.pointee.elements is None:
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{matched_cn}")
offset: int = Gen._get_member_offset(AttrName, matched_cn)
if offset is not None and ST.elements is not None and offset < len(ST.elements):
MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
return
elif isinstance(Target.value, ast.Call):
# Check if this is Deref(...) — we need the pointer, not the Loaded value
ObjVal: ir.Value | None = None
is_cderef: bool = (isinstance(Target.value.func, ast.Name) and
Target.value.func.id == 'Deref' and
Target.value.args)
if is_cderef:
# Get the pointer from Deref argument, don't Load the value
deref_arg: ast.expr = Target.value.args[0]
inner_val: ir.Value | None = self.HandleExprLlvm(deref_arg)
if inner_val and isinstance(inner_val.type, ir.PointerType):
if isinstance(inner_val.type.pointee, ir.PointerType):
ObjVal = Gen._load(inner_val, name="deref_load_ptr")
else:
ObjVal = inner_val
if ObjVal is None:
ObjVal = self.Trans.ExprHandler.HandleExprLlvm(Target.value)
if ObjVal and isinstance(ObjVal.type, ir.PointerType):
pointee: ir.Type = ObjVal.type.pointee
if isinstance(pointee, ir.PointerType):
ObjVal = Gen._load(ObjVal, name="load_ptr_for_attr")
pointee = ObjVal.type.pointee
for CN, ST in Gen.structs.items():
if pointee == ST or (isinstance(pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType) and pointee.name == ST.name):
if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0):
self.Trans.ImportHandler._TryLoadStructFromStub(CN, Gen)
ST = Gen.structs.get(CN, ST)
if ST.elements is not None and isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and ObjVal.type.pointee.elements is None:
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{CN}")
bitfield_offsets: dict[str, int] = Gen.class_member_bitoffsets.get(CN, {})
if AttrName in bitfield_offsets:
self._store_bitfield_member(ObjVal, CN, AttrName, Value)
return
offset: int = Gen._get_member_offset(AttrName, CN)
if offset is not None and ST.elements is not None and offset < len(ST.elements):
MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
break
elif isinstance(Target.value, ast.Subscript):
SubPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Target.value)
if SubPtr and isinstance(SubPtr.type, ir.PointerType):
pointee: ir.Type = SubPtr.type.pointee
if isinstance(Target.value.value, ast.Attribute):
AttrNameSub: str = Target.value.value.attr
if AttrNameSub == 'items' and 'Item' in Gen.structs:
ObjValCast: ir.Value = Gen.builder.bitcast(SubPtr, ir.PointerType(Gen.structs['Item']), name=f"cast_Item")
offset: int = Gen._get_member_offset(AttrName, 'Item')
MemberPtr: ir.Value = Gen.builder.gep(ObjValCast, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
return
if BaseHandle._is_char_pointer(SubPtr):
for CN, ST in Gen.structs.items():
ObjValCast: ir.Value = Gen.builder.bitcast(SubPtr, ir.PointerType(ST), name=f"cast_{CN}")
offset: int = Gen._get_member_offset(AttrName, CN)
MemberPtr: ir.Value = Gen.builder.gep(ObjValCast, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
break
elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(pointee)
if found:
CN: str = found[0]
ST: ir.Type = found[1]
if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0):
self.Trans.ImportHandler._TryLoadStructFromStub(CN, Gen)
ST = Gen.structs.get(CN, ST)
if ST.elements is not None and isinstance(SubPtr.type, ir.PointerType) and isinstance(SubPtr.type.pointee, ir.IdentifiedStructType) and SubPtr.type.pointee.elements is None:
SubPtr = Gen.builder.bitcast(SubPtr, ir.PointerType(ST), name=f"cast_{CN}")
offset: int = Gen._get_member_offset(AttrName, CN)
if offset is not None and ST.elements is not None and offset < len(ST.elements):
MemberPtr: ir.Value = Gen.builder.gep(SubPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
elif isinstance(pointee, ir.PointerType):
LoadedPtr: ir.Value = Gen._load(SubPtr, name=f"Load_{AttrName}_ptr")
if isinstance(LoadedPtr.type, ir.PointerType) and isinstance(LoadedPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(LoadedPtr.type.pointee)
if found:
CN: str = found[0]
ST: ir.Type = found[1]
offset: int = Gen._get_member_offset(AttrName, CN)
MemberPtr: ir.Value = Gen.builder.gep(LoadedPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
def _StoreArrElemWithCoerceBswap(self, Value: ir.Value, Ptr: ir.Value, arr_byte_order: str,
Node: ast.AST | None = None) -> None:
"""对数组元素存储应用 bswap 后再 coerce 存储"""
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
Value = Gen._apply_bswap_if_big(Value, arr_byte_order, "bswap_arr_store")
self._StoreWithCoerce(Value, Ptr, Node)
def _HandleSubscriptStoreLlvm(self, Target: ast.Subscript, Value: ir.Value,
Node: ast.AST | None = None) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
# pointee-volatile 基址名后缀(与 HandlesExprAttr._HandleSubscriptLlvm 同款);
# 空后缀 ⇒ 下标 GEP 名仍是 "subscript",既有代码 IR 逐字节不变。
_PVSuffix: str = Gen._SubscriptSuffix(Target.value)
# 查询数组元素字节序
arr_byte_order: str = ''
if isinstance(Target.value, ast.Name) and Target.value.id in Gen.local_var_byteorders:
arr_byte_order = Gen.local_var_byteorders[Target.value.id]
if isinstance(Target.value, ast.Name):
SubVarName: str = Target.value.id
if Gen._IsVarConst(SubVarName):
# 与 Name 赋值/增量赋值同一条 fail-fast(原先此处 LogError 后继续,
# 属静默继续;现统一 error + exit,判据与报文两侧一致)。
_vlog().error(Gen._ConstAssignMessage("的元素赋值", SubVarName))
sys.exit(1)
ClassName: str | None = self.Trans.ExprHandler._get_var_class(Target.value, Gen)
if ClassName and Gen._has_function(f'{ClassName}.__setitem__'):
obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value)
idx_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.slice)
if obj_val and idx_val:
# 处理 obj_val 为 i8(整数)的情况:指针下标类型降级后可能被 load 为 i8
if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8:
obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}")
if BaseHandle._is_char_pointer(obj_val):
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
setitem_args: list[ir.Value] = [obj_val, idx_val, Value]
setitem_func_name: str = f'{ClassName}.__setitem__'
# 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。
setitem_func: ir.Function = Gen._get_function(setitem_func_name)
setitem_args = Gen._adjust_args(setitem_args, setitem_func)
Gen.builder.call(setitem_func, setitem_args, name=f"call_{setitem_func_name}")
return
ValueVal: ir.Value | None = self.HandleExprLlvm(Target.value)
IndexVal: ir.Value | None = self.HandleExprLlvm(Target.slice)
if not ValueVal or not IndexVal:
return
# 当变量本身是二级指针(如 elem_ptr: CPtr[CPtr[T]] = AST**),
# 且存储的值也是指针时,elem_ptr[i] = item 应直接存储指针(8 字节),
# 而非解引用后复制结构体(如 48 字节的 AST),避免对象切片
if isinstance(Target.value, ast.Name):
VarName: str = Target.value.id
if VarName in Gen.variables and Gen.variables[VarName] is not None:
VarAlloca: ir.Value = Gen.variables[VarName]
if isinstance(VarAlloca.type, ir.PointerType) and isinstance(VarAlloca.type.pointee, ir.PointerType):
if (isinstance(ValueVal.type, ir.PointerType) and isinstance(Value.type, ir.PointerType)
and isinstance(Value.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType))):
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_store")
# ValueVal 是 AST*(加载自 elem_ptr 的基指针),
# 但 GEP 默认将其视为 AST 结构体数组。需要先 bitcast 为
# AST**(指向 AST* 的指针),让 GEP 正确索引 AST* 指针数组,
# 而非 AST 结构体数组。然后用 builder.store 直接存储指针,
# 避免 _store 的类型强制转换将指针解引用为结构体。
PtrToStruct: ir.Type = Value.type.pointee
BaseAsPP: ir.Value = Gen.builder.bitcast(ValueVal,
ir.PointerType(ir.PointerType(PtrToStruct)), name="cast_base_pp")
ElemPtr: ir.Value = Gen.builder.gep(BaseAsPP, [IndexVal], name=f"subscript{_PVSuffix}")
Gen.builder.store(Value, ElemPtr)
return
if BaseHandle._is_char_pointer(IndexVal):
Loaded_byte: ir.Value = Gen._load(IndexVal, name="Load_char_idx_store")
IndexVal = Gen.builder.zext(Loaded_byte, ir.IntType(32), name="char_to_int_idx_store")
if isinstance(ValueVal.type, ir.IntType):
var_ptr: ir.Value | None = self._get_int_ptr(Target.value)
if var_ptr:
i8_ptr: ir.Value = Gen.builder.bitcast(var_ptr, ir.PointerType(ir.IntType(8)), name="int_to_i8ptr_store")
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_int_index_store")
ptr: ir.Value = Gen.builder.gep(i8_ptr, [IndexVal], name="int_subscript_ptr_store")
self._StoreWithCoerce(Value, ptr)
return
if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType):
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_store")
ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order, Node)
return
if isinstance(ValueVal.type, ir.PointerType):
pointee: ir.Type = ValueVal.type.pointee
if isinstance(pointee, ir.ArrayType):
zero: ir.Constant = ir.Constant(ir.IntType(32), 0)
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32:
if IndexVal.type.width < 32:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}")
elif (isinstance(pointee, ir.IntType) and pointee.width == 8
and isinstance(Target.value, ast.Name)
and (Gen.var_ptr_element.get(Target.value.id, False)
or Gen.global_var_ptr_element.get(Target.value.id, False))):
# 指针到 str/bytes 的变量(指针下标注解如 CPtr[str])
# elem_ptr 是 i8* 但实际指向 i8* (字符串指针), elem_ptr[0] = item 应存储 8 字节
CastedPtrPtr: ir.Value = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_store")
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_store")
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}")
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
return
else:
ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
# fail-fast:rawptr[T](基座是指针存储)的元素为对象 T、而 RHS 是指向 T
# 的指针时,下游 _StoreWithCoerce/_coerce_value 会静默 load 整对象再按
# sizeof(T) 存储(写 sizeof(T) 而非 8 字节指针)。数组槽(CArray[T,N])
# 本就按 sizeof(T) 布局,整对象赋值合法 ⇒ 由 IsArrayBase 排除。
self._RejectWholeObjectCopyIntoPtrSlot(
Target, Value, ElemPtr, isinstance(pointee, ir.ArrayType))
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
elif isinstance(ValueVal.type, ir.ArrayType):
zero: ir.Constant = ir.Constant(ir.IntType(32), 0)
if isinstance(Target.value, ast.Name):
VarName: str = Target.value.id
if VarName in Gen.variables and Gen.variables[VarName] is not None:
VarPtr: ir.Value = Gen.variables[VarName]
if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.ArrayType):
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32:
if IndexVal.type.width < 32:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr: ir.Value = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
return
if isinstance(Target.value, ast.Subscript):
InnerPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Target.value)
if InnerPtr:
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32:
if IndexVal.type.width < 32:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr: ir.Value = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
return
if isinstance(Target.value, ast.Attribute):
AttrPtr: ir.Value | None = self.Trans.ExprAttrHandle._get_attr_ptr(Target.value)
if AttrPtr and isinstance(AttrPtr.type, ir.PointerType) and isinstance(AttrPtr.type.pointee, ir.ArrayType):
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32:
if IndexVal.type.width < 32:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index")
else:
IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index")
ElemPtr: ir.Value = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
return
arr_alloc: ir.AllocaInstr = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
Gen._store(ValueVal, arr_alloc)
ElemPtr: ir.Value = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}")
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
ModifiedArr: ir.Value = Gen._load(arr_alloc, name="modified_arr")
if isinstance(Target.value, ast.Subscript):
OuterPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Target.value)
if OuterPtr:
Gen._store(ModifiedArr, OuterPtr)
elif isinstance(Target.value, ast.Attribute):
OuterPtr: ir.Value | None = self.Trans.ExprAttrHandle._get_attr_ptr(Target.value)
if OuterPtr:
Gen._store(ModifiedArr, OuterPtr)
@staticmethod
def _StructTypeKey(ty: ir.Type) -> str:
"""结构体类型的稳定标识(identified 用 name,literal 用类型文本)"""
if isinstance(ty, ir.IdentifiedStructType):
return ty.name
return str(ty)
# ============================================================
# _RejectWholeObjectCopyIntoPtrSlot - 下标赋值的"整对象拷贝"fail-fast
#
# 目标形态(唯一判据):赋值左值是 rawptr[T] 下标(元素 = 对象/结构体 T,
# 基座为指针存储),而 RHS 的值类型是指向 T 的指针。此时 _StoreWithCoerce /
# _coerce_value 的"指针 → 非指针值: load 解引用"分支会生成
# %v = load %T, %T* %rhs
# store %T %v, %T* %elem (写 sizeof(T) 字节)
# 而该槽只该写 8 字节指针(槽是按 8 字节排布的缓冲/空闲链表)⇒ 每个元素
# 越界 sizeof(T)-8 字节,写穿相邻块(MemBuddy 空闲链表被写穿的静默降级根因)。
#
# 不拦(判据刻意收窄,避免误伤):
# · 数组槽 CArray[T,N](IsArrayBase=True):元素槽本就按 sizeof(T) 布局,
# `v[i] = T(...)`(构造函数返回临时对象地址)是合法的整对象复制;
# · 元素是标量(rawptr[int]):ElemPtr 元素不是结构体;
# · 元素本身是指针(rawptr[rawptr[T]] / X|CPtr):元素类型是 Ptr;
# · RHS 所指类型与元素类型不一致(非本形态)。
# 与 VPC AssignGenMixin._reject_whole_object_copy_into_ptr_slot 同口径。
# ============================================================
def _RejectWholeObjectCopyIntoPtrSlot(self, Target: ast.expr, Value: ir.Value,
ElemPtr: ir.Value, IsArrayBase: bool) -> None:
"""rawptr[T] 下标赋值 + RHS 为指向 T 的指针 ⇒ fail-fast(整对象拷贝)"""
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if IsArrayBase:
return
if ElemPtr is None or not isinstance(ElemPtr.type, ir.PointerType):
return
ElemTy: ir.Type = ElemPtr.type.pointee
if not isinstance(ElemTy, (ir.IdentifiedStructType, ir.LiteralStructType)):
return
if Value is None or not isinstance(Value.type, ir.PointerType):
return
RhsInner: ir.Type = Value.type.pointee
if not isinstance(RhsInner, (ir.IdentifiedStructType, ir.LiteralStructType)):
return
ElemKey: str = AssignHandle._StructTypeKey(ElemTy)
RhsKey: str = AssignHandle._StructTypeKey(RhsInner)
if ElemKey is None or RhsKey is None:
return
if ElemKey != RhsKey:
return
ElemSize: int = Gen._get_struct_size(ElemTy)
PtrSize: int = 8
Over: int = ElemSize - PtrSize
if Over < 0:
Over = 0
SrcInfo: str = Gen._get_node_info()
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"下标赋值的左值元素是对象 '{ElemKey}'({ElemSize} 字节),"
f"右值却是指向 '{RhsKey}' 的指针:编译器会静默生成整对象拷贝"
f"(load {ElemKey} + store {ElemKey},写 {ElemSize} 字节),"
f"而不是按指针写 {PtrSize} 字节。若该地址实际按 8 字节槽存放指针"
f"(如 rawptr[T] 缓冲/空闲链表),每写一个元素就越界 {Over} 字节,"
f"直接写穿相邻块。修法:把指针槽的基座类型写成指针元素形态"
f"(`CPtr[rawptr[T]]` 或 `rawptr[rawptr[T]]`)后再赋值;"
f"或把基座显式转为 CPtr(无类型指针)后按 8 字节指针语义写入。{SrcInfo}")
def _DescribeMemberSlot(self, Gen: "Translator.LlvmGen", ClassName: str | None,
FieldName: str, Offset: int, SelfPtr: ir.Value,
StructHint: Any = None) -> str:
"""`self.字段 = 值` 成员槽写入失败的**来源明细**(供 fail-fast 报文引用)。
槽类型有两个**互相独立**的来源,必须自洽:
- 声明侧:`Gen.class_members[类][下标]`(CollectAnnAssignMember / .vpi 加载);
- 布局侧:`SelfPtr` 所指 struct 的 `elements[offset]`(StructGen / stub 解析)。
两者不一致时,GEP 会把值写进**类型完全不同**的槽(不是"转换",是越界/错位),
现场:Generator.ExprGenConst 声明 `%"...ExprGenConst"*` 却落到 `i8**`。
此处把两侧数据逐项列出,直接判定是声明侧退化还是布局侧退化(含 structs
键空间大小与"是否同一对象",用于识别裸名 / sha1 全名双条目)。
`StructHint`(可选):调用方实际 GEP 用的那个 struct 实例。偏移基址必须与**那份
布局**同源(见 FuncGen._member_base 的 hint 说明)——传入时本报文报出的 base /
下标才与失败现场一致;不传时行为与改前逐字一致(回落到类键实例)。
"""
Lines: list[str] = []
try:
Members: list = Gen.class_members.get(ClassName) or []
Base: int = Gen._member_base(ClassName, StructHint)
Idx: int = Offset - Base
Declared: Any = Members[Idx][1] if 0 <= Idx < len(Members) else None
Lines.append(
f" class_members[{ClassName}] 项数={len(Members)} base={Base} "
f"下标={Idx} 声明槽类型={Declared}")
# 名序(index:name):偏移=下标法/配对法算错时,第一现场就是"名序与 body
# 布局不同源"(如把某字段排到表尾/表头)。逐项列出即可直接与 body 槽对照,
# 不必再猜是哪一侧漂移(现场:TypeGenMixin 41 项里 'Gen' 本应在 0)。
Lines.append(" class_members 名序: ["
+ ", ".join(f"{_mi}:{_mn}" for _mi, (_mn, _) in enumerate(Members))
+ "]")
except Exception as _e1:
Lines.append(f" class_members 读取失败: {type(_e1).__name__}: {_e1}")
Layout: Any = None
try:
Layout = getattr(SelfPtr.type, 'pointee', None)
Elems: Any = getattr(Layout, 'elements', None)
try:
LayoutName: Any = getattr(Layout, 'name', None)
except Exception:
LayoutName = '<未命名>'
Elem: Any = None
if Elems is not None and 0 <= Offset < len(Elems):
Elem = Elems[Offset]
Lines.append(
f" 布局 struct name={LayoutName!r} 元素数="
f"{len(Elems) if Elems is not None else None} elements[{Offset}]={Elem}")
except Exception as _e2:
Lines.append(f" 布局 struct 读取失败: {type(_e2).__name__}: {_e2}")
try:
_Structs: dict = getattr(Gen, 'structs', {}) or {}
_ByName: Any = _Structs.get(ClassName)
Lines.append(
f" structs 键数={len(_Structs)} structs[{ClassName}] 与布局同一对象="
f"{_ByName is Layout}")
except Exception:
pass
return f"\n [成员槽来源] self.{FieldName} = ...\n" + "\n".join(Lines)
def _StoreWithCoerce(self, Value: ir.Value, Ptr: ir.Value, Node: ast.AST | None = None) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if isinstance(Ptr.type, ir.PointerType):
TargetType: ir.Type = Ptr.type.pointee
if Value.type != TargetType:
if Gen._IsNoneValue(Value, Node):
# None(空值常量)允许隐式转换为任意目标类型:指针→空指针,其余→零值。
Value = (ir.Constant(TargetType, None) if isinstance(TargetType, ir.PointerType)
else Gen._ZeroConst(TargetType))
elif isinstance(TargetType, ir.ArrayType) and isinstance(Value.type, ir.PointerType):
ElemType: ir.Type = TargetType.element
ElemSize: int = Gen._get_struct_size(ElemType)
for i in range(TargetType.count):
DstElem: ir.Value = Gen.builder.gep(Ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), i)], name=f"arr_dst_{i}")
SrcPtr: ir.Value
if ElemSize == 1:
SrcPtr = Gen.builder.gep(Value, [ir.Constant(ir.IntType(32), i)], name=f"arr_src_{i}")
else:
PtrInt: ir.Value = Gen.builder.ptrtoint(Value, ir.IntType(64))
Offset: ir.Constant = ir.Constant(ir.IntType(64), i * ElemSize)
NewPtrInt: ir.Value = Gen.builder.add(PtrInt, Offset)
SrcPtr = Gen.builder.inttoptr(NewPtrInt, ir.PointerType(ElemType), name=f"arr_src_{i}")
SrcVal: ir.Value = Gen._load(SrcPtr, name=f"arr_elem_{i}")
if SrcVal.type != ElemType:
try:
SrcVal = Gen.builder.bitcast(SrcVal, ElemType, name=f"arr_cast_{i}")
except Exception: # 回退:bitcast 失败时跳过该元素
continue
Gen._store(SrcVal, DstElem)
return
elif isinstance(TargetType, ir.PointerType) and isinstance(Value.type, ir.PointerType):
# 新增指针隐式转换规则①/②:通用指针通配放行;非通用 T*→U* 仅在
# 被指类型无损时放行,非基本/有损/层数不同 fail-fast。
Allowed: bool
Reason: str
Allowed, Reason = Gen._ClassifyPtrImplicitConvert(Value.type, TargetType, Node)
if not Allowed:
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转换已禁止({Reason}):不能把指针类型 {Value.type} 隐式转为 {TargetType};"
f"如需转换请显式 cast(如 CPtr(...))"
f"{Gen._DescribeValueNode(Node)}"
)
Value = Gen.builder.bitcast(Value, TargetType, name="ptr_bitcast")
else:
Coerced: ir.Value | None = Gen._coerce_value(Value, TargetType, Node)
if Coerced is not None:
Value = Coerced
Gen._store(Value, Ptr, Node=Node)
def _get_int_ptr(self, target: ast.expr) -> ir.Value | None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if isinstance(target, ast.Name):
VarName: str = target.id
if VarName in Gen.variables and Gen.variables[VarName] is not None:
VarPtr: ir.Value = Gen.variables[VarName]
if isinstance(VarPtr.type, ir.PointerType):
pointee: ir.Type = VarPtr.type.pointee
if isinstance(pointee, ir.ArrayType):
return Gen.builder.gep(VarPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"arr_start_{VarName}")
return None
def _BuildStringPtrInitializer(self, Gen: "Translator.LlvmGen", VarName: str, str_value: str) -> ir.Constant:
"""模块级 `X: str = "字面量"` 全局的初始化器:指向常量字符数组**首元素**
字符串常量以 `@X_str`(internal global [N x i8])承载,重复调用复用同一
全局;返回 `getelementptr([N x i8], [N x i8]* @X_str, i32 0, i32 0)`。
注意:**不能**用 `ir.Constant(ptr_type, str_gv)` —— 该写法把整个全局定义
内联成初始化器(渲染出 `@X = global i8* @X_str = internal global [...]`
这种非法 IR),必须用 `ir.Constant.gep` 取首元素地址(与 import 侧
HandlesImports._EmitExternalGlobalDeclLlvm 的字符串分支同口径)。
"""
str_bytes: bytes = (str_value + '\x00').encode('utf-8')
arr_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(str_bytes))
str_gv_name: str = VarName + '_str'
str_gv: ir.GlobalVariable
if str_gv_name not in Gen.module.globals:
str_gv = ir.GlobalVariable(Gen.module, arr_type, name=str_gv_name)
str_gv.initializer = ir.Constant(arr_type, bytearray(str_bytes))
str_gv.linkage = 'internal'
else:
str_gv = Gen.module.globals[str_gv_name]
zero: ir.Constant = ir.Constant(ir.IntType(32), 0)
return ir.Constant.gep(str_gv, [zero, zero])
def _EmitGlobalAnnAssignLlvm(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> None:
# 内建定长数组构造子 array 的注解校验(误用即 fail-fast,含文件:行:列):
# 模块级全局注解不经过 ResolveAnnotationType,故在此显式校验。
self._CheckFixedArrayKeyword(Node.annotation)
if isinstance(Node.target, ast.Name):
VarName: str = Node.target.id
Info: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(VarName)
if Info:
if Info.IsTypedef:
return
# 模块级全局 attr.* 属性(注解联合形态 `T | attr.section("...")`):
# 先解析(空/缺段名、非正整数字面量 aligned → fail-fast 终止),再在各
# 全局创建点用 _ApplyGlobalAttrs 施加。函数侧 section 只来自装饰器
# (见 HandlesFunctions.py:1361-1362 → :1767)。
GlobalAttrs: dict[str, Any] = self._ExtractGlobalAttrs(Node, Gen)
parse_result = ParseListAnnotation(Node.annotation)
if parse_result:
elem_type_node: ast.expr = parse_result.elem_type_node
ElemType, ElemTypeInfo = self.ResolveListElementType(
elem_type_node, Gen,
check_func_ptr=True, void_fallback_width=32, fallback_to_cint=True,
)
if parse_result.is_pointer:
# 单参数 CArray[T]:与 CArray[T, None] 统一为"长度自动推导数组"。
# 有可推导初值(列表字面量 / 字符串字面量)时不再生成裸指针,而是落入
# 下方数组路径:由 ParseArrayCount(count_node=None) 从字面量推导长度、
# 字节内联到变量自身(与类成员 DeclarationGenerator.py:775-784、
# 局部 HandlesAnnAssign.py:446-489 同一口径;用户语义:单参数即
# "长度自动推导",不再是隐式指针)。
# 无可推导初值(纯声明 / 真正的指针用法)时保持既有指针语义逐字不变。
HasDerivableInit: bool = bool(
Node.value and (
isinstance(Node.value, ast.List)
or (isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str))
)
)
if not HasDerivableInit:
VarType: ir.Type = ir.PointerType(ElemType)
if VarName not in Gen.variables and VarName not in Gen.module.globals:
GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName)
GlobalVar.initializer = ir.Constant(VarType, None)
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
Gen.variables[VarName] = GlobalVar
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
Gen.global_struct_class[VarName] = elem_type_node.id
return
# 有初值 → 落入下方数组路径(count_node=None → 从字面量推导长度)
count_node: ast.expr | None = parse_result.count_node
ArrayCount: int = self.ParseArrayCount(count_node, Gen, Node.value, mode='global')
VarType = ir.ArrayType(ElemType, ArrayCount)
if VarName not in Gen.variables and VarName not in Gen.module.globals:
GlobalVar = ir.GlobalVariable(Gen.module, VarType, name=VarName)
if isinstance(ElemType, ir.IdentifiedStructType):
GlobalVar.linkage = 'common'
elif Node.value and isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str):
str_val: str = Node.value.value + '\x00'
str_bytes: bytearray = bytearray(str_val, 'utf-8')
if len(str_bytes) < ArrayCount:
str_bytes.extend(b'\x00' * (ArrayCount - len(str_bytes)))
try:
GlobalVar.initializer = ir.Constant(VarType, str_bytes[:ArrayCount])
except Exception: # 回退:初始化常量失败时用零值
GlobalVar.initializer = Gen._zero_value_for_type(VarType)
else:
InitConstants: list[ir.Constant] | None = self.Trans._BuildArrayInitConstants(Node.value, ElemType, elem_type_node, ArrayCount, Gen, byte_order=ElemTypeInfo.ByteOrder if ElemTypeInfo else '')
if InitConstants:
GlobalVar.initializer = ir.Constant(VarType, InitConstants)
elif ArrayCount > 256:
GlobalVar.linkage = 'common'
else:
if self._contains_identified_struct(VarType):
GlobalVar.linkage = 'common'
else:
GlobalVar.initializer = ir.Constant(VarType, [ir.Constant(ElemType, ir.Undefined)] * ArrayCount)
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
Gen.variables[VarName] = GlobalVar
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
Gen.global_struct_class[VarName] = elem_type_node.id
return
if isinstance(Node.annotation, ast.Name) and Node.annotation.id in Gen.structs:
StructName: str = Node.annotation.id
VarType: ir.Type = Gen.structs[StructName]
if VarName not in Gen.variables and VarName not in Gen.module.globals:
GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName)
if Node.value and isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name) and Node.value.func.id == StructName:
const: ir.Constant | None = self.Trans.ClassHandler._BuildStructConstant(Node.value, StructName, Gen)
if const:
GlobalVar.initializer = const
else:
GlobalVar.initializer = ir.Constant(VarType, None)
else:
GlobalVar.initializer = ir.Constant(VarType, None)
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
Gen.variables[VarName] = GlobalVar
Gen.global_struct_class[VarName] = StructName
return
TypeInfo: CTypeInfo | None = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable)
IsCDefine: bool = False
if TypeInfo and TypeInfo.BaseType:
if isinstance(TypeInfo.BaseType, TypeSystem._CTypedef):
return
if isinstance(TypeInfo.BaseType, TypeSystem.CDefine):
IsCDefine = True
if not IsCDefine and self._IsTypedefAnnotation(Node.annotation):
IsCDefine = True
TTypeInfo: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.annotation)
if TTypeInfo:
TTypeInfo.IsTypedef = True
TTypeInfo.Name = VarName
self.Trans.SymbolTable.insert(VarName, TTypeInfo)
if IsCDefine:
if Node.value:
define_constants: dict[str, int | float] = vars(Gen).setdefault('_define_constants', {})
try:
const_value: int | float | None = self._eval_const_expr(Node.value, Gen)
if const_value is not None:
define_constants[VarName] = const_value
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
return
if TypeInfo is None:
TypeInfo = CTypeInfo()
TypeInfo.BaseType = TypeSystem.CInt()
VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo)
if isinstance(VarType, ir.VoidType):
VarType = ir.IntType(32)
# State 标记的模块级全局(`extern 名字: 类型` 的展开产物,见
# SymbolUtils._ExpandExportExternVar):**只声明不定义**(external linkage),
# 符号定义在外部(C 库 / 汇编 / 其它翻译单元),本模块不得再生成定义
# (否则 ld.lld duplicate symbol)。与 import 侧
# _EmitExternalGlobalDeclLlvm 的同名分支(HandlesImports.py:1211-1217)同口径。
# 位置必须早于下方「VarName 已在 module.globals → 升级 common 定义」分支:
# 否则 extern 会被反向升级成定义,抵消 external 声明语义。
if TypeInfo is not None and TypeInfo.IsState:
if VarName not in Gen.module.globals:
_ext_gv: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName)
_ext_gv.linkage = 'external'
Gen._export_funcs.add(VarName)
Gen.variables[VarName] = Gen.module.globals[VarName]
return
# const / volatile 模块级全局登记(统一登记入口,持久层 global_*)。
# 局限(明确记录,见 Debugger/type-descriptors.md 已知差异):本分支只覆盖
# 标量/指针型全局;数组(CArray[...])与结构体型全局在更早的分支
# (上方 1568-1627)已 return,不在此登记 —— 全仓普查无这类带 const/
# volatile 注解的全局,故无可观测差异。
Gen.RegisterVarQualifiers(VarName, TypeInfo, Node.annotation, IsGlobal=True)
# 记录模块级注解类型:供函数内 global 变量的 += (HandlesAugAssign)参考,
# 避免用 RHS 字面量类型(i32)误建 i32 全局导致 i64/i32 类型不匹配。
if not hasattr(Gen, '_module_ann_types'):
Gen._module_ann_types = {}
Gen._module_ann_types[VarName] = VarType
if VarName in Gen.variables or VarName in Gen.module.globals:
# 根因修复:import 端 _EmitExternalGlobalDeclLlvm 会把被 import 模块
# 的模块级全局(如下划线私有 _g_gen)以裸名 external 声明注入本模块
# module.globals,导致本模块同名全局定义被此分支跳过 → 全工程无定义
# (ld.lld undefined symbol: _g_gen)。external 声明遇到本模块定义时
# 应升级为 common 定义(多模块同名合并为零值,对齐 None 语义),
# 而非静默跳过。
existing_gv = Gen.module.globals.get(VarName) if VarName in Gen.module.globals else None
# 注:llvmlite.ir.GlobalVariable 无 is_declaration 属性(binding 层才有),
# 声明/定义靠 initializer 区分:external 声明 initializer 恒为 None。
if isinstance(existing_gv, ir.GlobalVariable) and getattr(existing_gv, 'initializer', None) is None:
try:
if (isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str)
and isinstance(getattr(existing_gv, 'value_type', None), ir.PointerType)):
# 字符串字面量全局:补"指向常量数组首元素"的初始化器。
# **不能**设 common —— LLVM 要求 common 全局的初始化器必须是
# 零值(verifier: "'common' global must have a zero
# initializer!"),配非空初值直接非法;保持默认 external
# 强定义即可跨模块共享(Config.BackendTarget /
# _inc_bin_suffix 都在运行时被赋值/读取,符号必须唯一且可见)。
existing_gv.initializer = self._BuildStringPtrInitializer(Gen, VarName, Node.value.value)
else:
existing_gv.linkage = 'common'
if Node.value is None or (isinstance(Node.value, ast.Constant) and Node.value.value is None):
existing_gv.initializer = ir.Constant(existing_gv.type.pointee, None)
Gen.variables[VarName] = existing_gv
except Exception:
pass
return
# 时序修复:第一遍创建全局时类未处理(SymbolTable 无枚举成员),
# initializer 回退为 0。第二遍类已处理,尝试用解析出的枚举值更新 initializer。
if VarName in Gen.module.globals and Node.value:
existing: Any = Gen.module.globals[VarName]
if isinstance(existing, ir.GlobalVariable) and isinstance(VarType, ir.IntType):
new_init: ir.Constant | None = self._eval_global_const(Node.value, VarType, Gen)
if new_init is not None:
existing.initializer = new_init
return
if isinstance(VarType, (ir.IdentifiedStructType, ir.LiteralStructType)):
GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName)
GlobalVar.initializer = ir.Constant(VarType, None)
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
Gen.variables[VarName] = GlobalVar
if TypeInfo and TypeInfo.IsStruct and TypeInfo.Name:
Gen.global_struct_class[VarName] = TypeInfo.Name
return
GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName)
# 全局变量指针元素标志:指向字符串指针(i8**)的变量,
# 使 var[index] 使用 8 字节指针步长(而非 1 字节字符步长)
if TypeInfo is not None and TypeInfo.IsStr and TypeInfo.PtrCount >= 2:
Gen.global_var_ptr_element[VarName] = True
if Node.value:
if isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str) and isinstance(VarType, ir.PointerType):
# 字符串字面量全局:初始化器 = 指向 @{VarName}_str 常量数组首元素
# 的 GEP。此前这里在创建 GlobalVar(上方数行)之后又查
# `VarName not in Gen.module.globals` —— 该条件恒为假(GlobalVar
# 刚被登记),于是永远走 else 分支、**从不写初始化器**:全工程模块级
# `str = "字面量"` 全局退化成无初值的声明,第二遍再被升级成
# `common global i8* null`,字符串常量沦为无人引用的 @NAME_str。
# 只读常量(如 BuildPipeline.MLIR_OPT_CMD)在 mlir 后端
# strlen(NULL) 直接崩(ASan 0x0)。
if getattr(GlobalVar, 'initializer', None) is None:
GlobalVar.initializer = self._BuildStringPtrInitializer(Gen, VarName, Node.value.value)
Gen.variables[VarName] = GlobalVar
elif isinstance(VarType, ir.PointerType) and isinstance(VarType.pointee, ir.FunctionType) and isinstance(Node.value, ast.Name):
# 函数指针赋值:f3: Callable[[int], int] = f
RefName: str = Node.value.id
SymInfo: Any = self.Trans.SymbolTable.lookup(RefName)
if SymInfo and (SymInfo.IsFunction or SymInfo.IsFuncPtr):
MangledName: str = Gen._mangle_func_name(RefName)
if MangledName in Gen.module.globals:
func: Any = Gen.module.globals[MangledName]
if isinstance(func, ir.Function):
GlobalVar.initializer = func
else:
if isinstance(VarType, ir.ArrayType) and isinstance(Node.value, (ast.List, ast.Set)):
InitConstants: list[ir.Constant] | None = self.Trans._BuildMultiDimArrayInitConstants(Node.value, VarType, Gen)
if InitConstants:
GlobalVar.initializer = ir.Constant(VarType, InitConstants)
else:
if self._contains_identified_struct(VarType):
GlobalVar.linkage = 'common'
else:
zv: ir.Constant | None = Gen._zero_value_for_type(VarType)
if zv:
GlobalVar.initializer = zv
else:
GlobalVar.linkage = 'common'
else:
init_val: ir.Constant | None = self._eval_global_const(Node.value, VarType, Gen)
if init_val:
GlobalVar.initializer = init_val
elif isinstance(VarType, ir.BaseStructType) or (isinstance(Node.value, ast.Constant) and Node.value.value is None):
# None 初值全局(如各 Handles 模块的 _g_gen: CVoid|CPtr = None):
# 多模块同名定义用 common linkage 让链接器合并(避免
# ld.lld duplicate symbol)。common 无 initializer,合并为零值,
# 符合 None(=null) 语义。
GlobalVar.linkage = 'common'
else:
GlobalVar.initializer = Gen._zero_value_for_type(VarType)
else:
if isinstance(VarType, ir.BaseStructType) or self._contains_identified_struct(VarType):
GlobalVar.linkage = 'common'
else:
GlobalVar.initializer = Gen._zero_value_for_type(VarType)
# 属性最后施加:weak 需覆盖分支内可能设过的 'common'(对齐 ViperC
# TranslatorGenMixin.vp:926-929 先清 Linkage 再设 weak 的语义)。
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
Gen.variables[VarName] = GlobalVar
# ======================================================================
# 模块级全局的 attr.* 属性(注解联合形态 `T | attr.section("...")`)
#
# 与 ViperC(Viper 自举侧)实现对应:
# TranslatorGenMixin._scan_global_tattr / _extract_global_attr_section /
# _extract_global_attr_aligned / _extract_global_attr_weak(.vp:590-651)
# + ClassGenBase._tattr_name(.vp:341-358)+ _class_has_tattr 的
# `Attribute(attr.xxx)` 解包(.vp:359-394)。
# ======================================================================
def _TAttrName(self, node: ast.AST | None) -> str | None:
"""从 attr.<name> / attr.<name>(args) 节点提取最外层属性名。
对应 ViperC ClassGenBase._tattr_name:Call → 递归取 func;
Attribute → 取 attr。
"""
if isinstance(node, ast.Call):
return self._TAttrName(node.func)
if isinstance(node, ast.Attribute):
return node.attr
return None
def _ScanGlobalTAttr(self, node: ast.AST | None, want_name: str) -> ast.AST | None:
"""在全局变量注解中查找 attr.<want_name> 节点,命中返回该节点。
命中节点形态:Call(attr.aligned(16))或 Attribute(attr.weak)。
同时兼容包装写法 `Attribute(attr.section("..."))`(解包参考 ViperC
ClassGenBase._class_has_tattr,.vp:375-389)。
"""
if node is None:
return None
# 包装写法:Attribute(attr.xxx / attr.xxx(...)) → 拆 args 再扫
# (与 HandlesFunctions.py 装饰器侧同一判据:func.id == 'Attribute')
if (isinstance(node, ast.Call) and isinstance(node.func, ast.Name)
and node.func.id == 'Attribute'):
for arg in node.args:
hit_arg: ast.AST | None = self._ScanGlobalTAttr(arg, want_name)
if hit_arg is not None:
return hit_arg
return None
if self._TAttrName(node) == want_name:
return node
return None
@staticmethod
def _TAttrArgStr(node: ast.AST | None) -> str | None:
"""取 attr.<name>("...") 的字符串字面量参数(缺参/非字面量返回 None)。"""
if isinstance(node, ast.Call) and node.args:
arg0: ast.AST = node.args[0]
if isinstance(arg0, ast.Constant) and isinstance(arg0.value, str):
return arg0.value
return None
@staticmethod
def _TAttrAlignInt(node: ast.AST | None) -> int | None:
"""取 attr.aligned(N) 的整数字面量 N(缺参/非字面量返回 None)。
对应 ViperC ClassGenBase._tattr_aligned_int(.vp:395-415)。
"""
if isinstance(node, ast.Call) and node.args:
arg0: ast.AST = node.args[0]
if (isinstance(arg0, ast.Constant) and isinstance(arg0.value, int)
and not isinstance(arg0.value, bool)):
return arg0.value
return None
def _RaiseGlobalAttrError(self, Msg: str, Node: ast.AST, Gen: "Translator.LlvmGen", VarName: str) -> None:
"""fail-fast:全局 attr.* 属性非法(缺/空段名、非正整数字面量 aligned)。
定位串(文件:行:列)与报错出口对齐 HandlesBase._RaiseUnresolvedExpr
(HandlesBase.py:552-615)——错误信息含源码位置后 sys.exit(1)。
"""
raw_lineno: int = getattr(Node, 'lineno', 0) or 0
lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0
col: int = getattr(Node, 'col_offset', -1)
src_file: str = getattr(Gen, '_current_source_file', '') or ''
# 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的行数
_line_offset: int = int(getattr(getattr(self, 'Trans', None), '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
loc: str = src_file
if lineno > 0:
loc += f":{lineno}"
if col >= 0:
loc += f":{col}"
_vlog().compile_error(
f"模块级变量 '{VarName}' 的 {Msg}(位置 {loc})", src_file, lineno, '')
sys.exit(1)
@staticmethod
def _ParseAttrText(Raw: object) -> tuple[str, str | None]:
"""解析 Attributes 字段中的单条文本(如 'section(".x")' / 'aligned(16)')。"""
text: str = str(Raw).strip()
name, sep, rest = text.partition('(')
name = name.strip()
if not sep:
return name, None
rest = rest.rstrip(')').strip()
if len(rest) >= 2 and rest[0] == rest[-1] and rest[0] in ('"', "'"):
rest = rest[1:-1]
return name, rest
def _ExtractGlobalAttrs(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> dict[str, Any]:
"""从模块级全局注解提取 attr.section/aligned/weak。
返回 {'section': str|None, 'align': int, 'weak': bool}。
非法值(空/缺段名、非正整数 aligned)→ fail-fast 终止(对齐 ViperC
TranslatorGenMixin._extract_global_attr_section/aligned 的致命错误语义)。
"""
Attrs: dict[str, Any] = {'section': None, 'align': 0, 'weak': False}
Ann: ast.AST = Node.annotation
VarName: str = Node.target.id if isinstance(Node.target, ast.Name) else '<anonymous>'
RawAttrs: list[object] = list(getattr(Node, 'Attributes', None) or ()) + list(
getattr(Ann, 'Attributes', None) or ())
for RawAttr in RawAttrs:
attr_name, attr_arg = self._ParseAttrText(RawAttr)
if attr_name == 'section':
if not attr_arg:
self._RaiseGlobalAttrError(
"attr.section(...) 段名缺失或为空(空段名会生成非法的 section \"\")",
Ann, Gen, VarName)
Attrs['section'] = attr_arg
elif attr_name == 'aligned':
align_val: int | None = None
try:
align_val = int(attr_arg) if attr_arg else None
except ValueError:
align_val = None
if align_val is None or align_val <= 0:
self._RaiseGlobalAttrError(
"attr.aligned(N) 需要正整数字面量 N", Ann, Gen, VarName)
Attrs['align'] = align_val
elif attr_name == 'weak':
Attrs['weak'] = True
sec_node: ast.AST | None = self._ScanGlobalTAttr(Ann, 'section')
if sec_node is not None:
sec_val: str | None = self._TAttrArgStr(sec_node)
if not sec_val:
self._RaiseGlobalAttrError(
"attr.section(...) 段名缺失或为空(空段名会生成非法的 section \"\")",
sec_node, Gen, VarName)
Attrs['section'] = sec_val
align_node: ast.AST | None = self._ScanGlobalTAttr(Ann, 'aligned')
if align_node is not None:
align_val: int | None = self._TAttrAlignInt(align_node)
if align_val is None or align_val <= 0:
self._RaiseGlobalAttrError(
"attr.aligned(N) 需要正整数字面量 N", align_node, Gen, VarName)
Attrs['align'] = align_val
if self._ScanGlobalTAttr(Ann, 'weak') is not None:
Attrs['weak'] = True
return Attrs
@staticmethod
def _ApplyGlobalAttrs(GlobalVar: ir.GlobalVariable, Attrs: dict[str, Any]) -> None:
"""把提取出的 section/align/weak 施加到已创建的 LLVM 全局变量上。
llvmlite 口径(llvmlite/ir/values.py):
- section 写**裸**段名(序列化时自动加引号,:843-844);
- align 需 int(:846-847);GlobalVariable.align 默认 None(:807);
- weak 用 linkage='weak'(GlobalVariable 无 weak 属性,对照函数侧
HandlesFunctions.py:1772)。
"""
if not Attrs:
return
sec: str | None = Attrs.get('section')
if sec:
GlobalVar.section = sec
align_val: int = int(Attrs.get('align') or 0)
if align_val > 0:
GlobalVar.align = align_val
if Attrs.get('weak'):
GlobalVar.linkage = 'weak'
def _eval_global_const(self, value_node: ast.expr, var_type: ir.Type, Gen: "Translator.LlvmGen") -> ir.Constant | None:
if isinstance(value_node, ast.Constant):
if isinstance(value_node.value, bool):
if isinstance(var_type, ir.IntType):
return ir.Constant(var_type, int(value_node.value))
elif isinstance(value_node.value, int):
if isinstance(var_type, ir.IntType):
return ir.Constant(var_type, value_node.value)
elif isinstance(var_type, (ir.FloatType, ir.DoubleType)):
return ir.Constant(var_type, float(value_node.value))
elif isinstance(value_node.value, float):
if isinstance(var_type, (ir.FloatType, ir.DoubleType)):
return ir.Constant(var_type, value_node.value)
elif isinstance(var_type, ir.IntType):
return ir.Constant(var_type, int(value_node.value))
elif isinstance(value_node.value, str):
return None
# Handle type constructor calls like CDouble(1e-9), CFloat(3.14), CInt(42)
if isinstance(value_node, ast.Call):
inner_val: ir.Constant | None = self._eval_type_ctor_const(value_node, var_type, Gen)
if inner_val is not None:
return inner_val
if isinstance(value_node, ast.UnaryOp) and isinstance(value_node.op, ast.USub):
inner: ir.Constant | None = self._eval_global_const(value_node.operand, var_type, Gen)
if inner and isinstance(inner.constant, int):
# 关键:llvmlite 整型常量的 `.constant` 是**无符号位型读回值**,直接
# `-inner.constant` 会把负数算成 `2^N - |v|` 那种“正数”:
# `b: int64 = -196608` 曾落成 4294770688(0x00000000FFFD0000)
# ⇒ 随后 `b >> 1` 变大正数(IntWidthTest Test 5/6 的 FAIL)。
# 这里先把读回值按**它自己的位宽**还原成带符号真值,再取负,最后按目标
# 类型发射(llvmlite 按目标宽度取模,i32/i64 目标都正确)。
_iw: int = inner.type.width if isinstance(inner.type, ir.IntType) else 64
_umask: int = (1 << _iw) - 1
_uval: int = int(inner.constant) & _umask
_sval: int = _uval - (1 << _iw) if _uval >= (1 << (_iw - 1)) else _uval
return ir.Constant(var_type, -_sval)
# Handle enum member references like ASTVType.Module (ast.Attribute)
if isinstance(value_node, ast.Attribute):
attr_name: str = value_node.attr
# 优先用带前缀的 key 查找(避免短名被同名函数/变量覆盖)
if isinstance(value_node.value, ast.Name):
FullKey2: str = f"{value_node.value.id}.{attr_name}"
FullInfo: Any = self.Trans.SymbolTable.lookup(FullKey2)
if FullInfo and FullInfo.IsEnumMember and isinstance(FullInfo.value, int):
if isinstance(var_type, ir.IntType):
return ir.Constant(var_type, FullInfo.value)
SymInfo: Any = self.Trans.SymbolTable.lookup(attr_name)
if SymInfo:
if SymInfo.IsEnumMember and isinstance(SymInfo.value, int):
if isinstance(var_type, ir.IntType):
return ir.Constant(var_type, SymInfo.value)
return None
def _eval_type_ctor_const(self, call_node: ast.Call, var_type: ir.Type, Gen: "Translator.LlvmGen") -> ir.Constant | None:
"""Try to evaluate CDouble(val), CFloat(val), CInt(val) etc. as a compile-time constant."""
if not isinstance(call_node.func, ast.Name):
return None
ctor_cls: type | None = getattr(TypeSystem, call_node.func.id, None)
if not isinstance(ctor_cls, type) or not issubclass(ctor_cls, TypeSystem.CType):
return None
if not call_node.args or len(call_node.args) != 1:
return None
arg: ast.expr = call_node.args[0]
# Recursively evaluate the argument as a constant
arg_val: ir.Constant | None = self._eval_global_const(arg, var_type, Gen)
if arg_val is not None:
return arg_val
return None
def _store_bitfield_member(self, struct_ptr: ir.Value, ClassName: str, field_name: str, Value: ir.Value) -> bool:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
bitfield_offsets: dict[str, int] = Gen.class_member_bitoffsets.get(ClassName, {})
bitfield_widths: dict[str, int] = Gen.class_member_bitfields.get(ClassName, {})
if field_name not in bitfield_offsets:
return False
bit_offset: int = bitfield_offsets[field_name]
bit_width: int = bitfield_widths.get(field_name, 0)
if bit_width == 0:
return False
struct_type: ir.Type | None = Gen.structs.get(ClassName)
if struct_type is None or struct_type.elements is None or len(struct_type.elements) == 0:
return False
storage_type: ir.Type = struct_type.elements[0]
member_ptr: ir.Value = Gen.builder.gep(struct_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{field_name}_storage_ptr")
old_val: ir.Value = Gen._load(member_ptr, name=f"{field_name}_old_storage")
mask: int = (1 << bit_width) - 1
shifted_mask: int = mask << bit_offset
inverted_mask: ir.Constant = ir.Constant(storage_type, ~shifted_mask & ((1 << storage_type.width) - 1))
cleared: ir.Value = Gen.builder.and_(old_val, inverted_mask, name=f"{field_name}_cleared")
if isinstance(Value.type, ir.IntType) and Value.type.width < storage_type.width:
if getattr(Value.type, 'is_signed', False):
Value = Gen.builder.sext(Value, storage_type, name=f"{field_name}_sext_store")
else:
Value = Gen.builder.zext(Value, storage_type, name=f"{field_name}_zext_store")
elif Value.type.width != storage_type.width:
Value = Gen.builder.trunc(Value, storage_type, name=f"{field_name}_trunc_store") if Value.type.width > storage_type.width else Gen.builder.zext(Value, storage_type, name=f"{field_name}_zext_store")
shifted_value: ir.Value = Gen.builder.shl(Value, ir.Constant(storage_type, bit_offset), name=f"{field_name}_shift_store")
masked_value: ir.Value = Gen.builder.and_(shifted_value, ir.Constant(storage_type, shifted_mask), name=f"{field_name}_mask_store")
new_val: ir.Value = Gen.builder.or_(cleared, masked_value, name=f"{field_name}_new_storage")
Gen._store(new_val, member_ptr)
return True
def _CheckEnumMemberAssignment(self, node: ast.expr) -> str | None:
def _get_attr_path(n: ast.expr) -> str | None:
if isinstance(n, ast.Name):
return n.id
elif isinstance(n, ast.Attribute):
return f"{_get_attr_path(n.value)}.{n.attr}"
return None
attr_path: str | None = _get_attr_path(node)
if not attr_path:
return None
parts: list[str] = attr_path.split('.')
if len(parts) >= 2:
enum_class_name: str = parts[-2]
member_name: str = parts[-1]
qualified_name: str = f"{enum_class_name}.{member_name}"
info: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(qualified_name)
if info:
if info.IsEnumMember and info.EnumName:
return info.EnumName
under_name: str = f"{enum_class_name}_{member_name}"
if self.Trans.SymbolTable.has(under_name):
info: "SymbolTable.SymbolInfo" = self.Trans.SymbolTable[under_name]
if info.IsEnumMember and info.EnumName:
return info.EnumName
for key in self.Trans.SymbolTable:
if key == member_name:
info: "SymbolTable.SymbolInfo" = self.Trans.SymbolTable[key]
if info.IsEnumMember and info.EnumName and info.EnumName == enum_class_name:
return enum_class_name
return None
def _IsTypedefAnnotation(self, annotation: ast.expr) -> bool:
Markers: tuple[str, ...] = getattr(annotation, 'Markers', None) or ()
if 'typedef' in Markers or 'define' in Markers:
return True
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
Line: int = getattr(annotation, 'lineno', 0) or 0
raise TypeError(
f"[TPC] 不支持类型联合 'T1 | T2'(第 {Line} 行);"
f"类型注解只能是单一类型:指针请写 CPtr[T],修饰符请用关键字")
if isinstance(annotation, ast.Subscript):
return self._IsTypedefAnnotation(annotation.value)
return False
def _EmitGlobalAssignLlvm(self, Node: ast.Assign, Gen: "Translator.LlvmGen") -> None:
if len(Node.targets) == 1 and isinstance(Node.targets[0], ast.Name):
VarName: str = Node.targets[0].id
if VarName in Gen.variables or VarName in Gen.module.globals:
return
if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name):
FuncName: str = Node.value.func.id
if FuncName in Gen.structs:
IsUnion: bool = False
TypeInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(FuncName)
if TypeInfo:
if TypeInfo.IsUnion:
IsUnion = True
if IsUnion:
StructType: ir.Type = Gen.structs[FuncName]
StructPtrType: ir.PointerType = ir.PointerType(StructType)
GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, StructPtrType, name=VarName)
GlobalVar.initializer = ir.Constant(StructPtrType, None)
Gen.variables[VarName] = GlobalVar
Gen.global_struct_class[VarName] = FuncName
return
StructName: str = FuncName
VarType: ir.Type = Gen.structs[StructName]
GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName)
const: ir.Constant | None = self.Trans.ClassHandler._BuildStructConstant(Node.value, StructName, Gen)
if const:
GlobalVar.initializer = const
else:
GlobalVar.linkage = 'common'
Gen.variables[VarName] = GlobalVar
Gen.global_struct_class[VarName] = StructName
return
GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, ir.IntType(32), name=VarName)
init_val: ir.Constant | None = self._eval_global_const(Node.value, ir.IntType(32), Gen)
GlobalVar.initializer = init_val if init_val else ir.Constant(ir.IntType(32), 0)
Gen.variables[VarName] = GlobalVar
def _eval_const_expr(self, node: ast.expr, Gen: "Translator.LlvmGen") -> int | float | None:
"""计算常量表达式的值(委托到 ConstEvaluator)"""
ctx: EvalContext = EvalContext(Gen=Gen, symtab=self.Trans.SymbolTable, translator=self.Trans)
return ConstEvaluator.eval_full(node, ctx)
def _eval_global_count(self, node: ast.expr, Gen: "Translator.LlvmGen") -> int | None:
val: int | float | None = self._eval_const_expr(node, Gen)
if val is not None:
if isinstance(val, float):
return int(val)
if isinstance(val, int):
return val
return None