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.VLogger import get_logger as _vlog from lib.core.ConstEvaluator import ConstEvaluator, EvalContext from lib.constants.config import mode as _config_mode from lib.includes import t from lib.includes.t import CTypeRegistry from lib.core.SymbolUtils import IsTModule, ParseListAnnotation, AnnotationContainsName 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)就变成被禁止的 **向下转型** —— 现场 includes/ast/parser.vp:1919,TPV 报 "指针隐式转指针已禁止:源类型 %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) and IsTModule(Node.value.value.id, self.Trans.SymbolTable): 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) # 大端局部变量:赋值时 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]*` ≡ # `list[str] | t.CPtr`(只读探针确认二者 AST 逐字同形)被映射成 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] 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 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] Gen._store(Value, GVar, Node=Node.value) Gen.variables[VarName] = GVar elif 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()}" ) DerefValue: ir.Value | None = self.HandleExprLlvm(Node.value) if DerefValue is None: self._RaiseUnresolvedExpr("解引用写入", Node.value, Gen) return 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:`@c.CReturn` 已移除,扫到即报错。 if decorator.func.attr == 'CReturn': raise SyntaxError( f"函数 '{FuncName}':`@c.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 = t.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 '' 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 编译定义端为裸名,TPV 自举 # 后端为 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 = "t.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 c.Deref(...) — we need the pointer, not the Loaded value ObjVal: ir.Value | None = None is_cderef: bool = (isinstance(Target.value.func, ast.Attribute) and isinstance(Target.value.func.value, ast.Name) and Target.value.func.value.id == 'c' and Target.value.func.attr == 'Deref' and Target.value.args) if is_cderef: # Get the pointer from c.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(整数)的情况:联合类型 | t.CPtr 降级后可能被 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__' setitem_args = Gen._apply_auto_addr(setitem_func_name, setitem_args) 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: T | t.CPtr = 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 的变量 (BinOp 注解如 str | t.CPtr) # 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 字节指针)。数组槽(t.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 空闲链表被写穿的静默降级根因)。 # # 不拦(判据刻意收窄,避免误伤): # · 数组槽 t.CArray[T,N](IsArrayBase=True):元素槽本就按 sizeof(T) 布局, # `v[i] = T(...)`(构造函数返回临时对象地址)是合法的整对象复制; # · 元素是标量(rawptr[int]):ElemPtr 元素不是结构体; # · 元素本身是指针(rawptr[rawptr[T]] / X|t.CPtr):元素类型是 Ptr; # · RHS 所指类型与元素类型不一致(非本形态)。 # 与 TPV 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"(`rawptr[T] | t.CPtr` 或 `rawptr[rawptr[T]]`)后再赋值;" f"或把基座显式转为 t.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(如 t.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 _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 # 模块级全局 t.attr.* 属性(注解联合形态 `T | t.attr.section("...")`): # 先解析(空/缺段名、非正整数字面量 aligned → fail-fast 终止),再在各 # 全局创建点用 _ApplyGlobalAttrs 施加。此前 TPC 全局路径完全没有 # section/align/weak 代码(函数侧 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: # 单参数 t.CArray[T]:与 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, t._CTypedef): return if isinstance(TypeInfo.BaseType, t.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 = t.CInt() VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo) if isinstance(VarType, ir.VoidType): VarType = ir.IntType(32) # t.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 已知差异):本分支只覆盖 # 标量/指针型全局;数组(t.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: 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) # 全局变量指针元素标志:bytes|t.CPtr 或 str|t.CPtr 注解的变量, # 使 var[index] 使用 8 字节指针步长(而非 1 字节字符步长) if isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr): if AnnotationContainsName(Node.annotation, 'str') or AnnotationContainsName(Node.annotation, 'bytes'): 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): str_val: str = Node.value.value + '\x00' str_bytes: bytes = str_val.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] ptr_type: ir.PointerType = ir.PointerType(ir.IntType(8)) ptr_gv: ir.GlobalVariable if VarName not in Gen.module.globals: ptr_gv = ir.GlobalVariable(Gen.module, ptr_type, name=VarName) ptr_gv.initializer = ir.Constant(ptr_type, str_gv) ptr_gv.linkage = 'internal' else: ptr_gv = Gen.module.globals[VarName] Gen.variables[VarName] = ptr_gv elif isinstance(VarType, ir.PointerType) and isinstance(VarType.pointee, ir.FunctionType) and isinstance(Node.value, ast.Name): # 函数指针赋值:f3: t.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: t.CVoid|t.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'(对齐 TransPyV # TranslatorGenMixin.vp:926-929 先清 Linkage 再设 weak 的语义)。 self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs) Gen.variables[VarName] = GlobalVar # ====================================================================== # 模块级全局的 t.attr.* 属性(注解联合形态 `T | t.attr.section("...")`) # # 镜像 TransPyV(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 的 # `c.Attribute(t.attr.xxx)` 解包(.vp:359-394)。 # TPC(Python 侧)此前完全没有全局 section/align/weak 代码。 # ====================================================================== def _TAttrName(self, node: ast.AST | None) -> str | None: """从 t.attr. / t.attr.(args) 节点提取最外层属性名。 对应 TransPyV 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: """在全局变量注解中查找 t.attr. 节点,命中返回该节点。 注解形态是 BinOp(BitOr) 左深链(`A | B | C` = BinOp(BinOp(A,B),C)), 递归左右子节点。命中节点形态:Call(t.attr.aligned(16))或 Attribute(t.attr.weak)。同时兼容包装写法 `T | c.Attribute(t.attr.section("..."))`(解包参考 TransPyV ClassGenBase._class_has_tattr,.vp:375-389)。 """ if node is None: return None if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): hit_left: ast.AST | None = self._ScanGlobalTAttr(node.left, want_name) if hit_left is not None: return hit_left return self._ScanGlobalTAttr(node.right, want_name) # 包装写法:c.Attribute(t.attr.xxx / t.attr.xxx(...)) → 拆 args 再扫 # (与 HandlesFunctions.py:1354 装饰器侧同一判据:func.attr=='Attribute' # 且 func.value 为 Name('c')) if (isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute) and node.func.attr == 'Attribute' and isinstance(node.func.value, ast.Name) and node.func.value.id == 'c'): 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: """取 t.attr.("...") 的字符串字面量参数(缺参/非字面量返回 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: """取 t.attr.aligned(N) 的整数字面量 N(缺参/非字面量返回 None)。 对应 TransPyV 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:全局 t.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) def _ExtractGlobalAttrs(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> dict[str, Any]: """从模块级全局注解提取 t.attr.section/aligned/weak。 返回 {'section': str|None, 'align': int, 'weak': bool}。 非法值(空/缺段名、非正整数 aligned)→ fail-fast 终止(对齐 TransPyV 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 '' 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( "t.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( "t.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 t.CDouble(1e-9), t.CFloat(3.14), t.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: return ir.Constant(var_type, -inner.constant) # 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 t.CDouble(val), t.CFloat(val), t.CInt(val) etc. as a compile-time constant.""" if not isinstance(call_node.func, ast.Attribute): return None if not isinstance(call_node.func.value, ast.Name): return None if call_node.func.value.id != 't': 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: if isinstance(annotation, ast.Attribute): if annotation.attr in ('CTypedef', 'CDefine'): return True if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): return self._IsTypedefAnnotation(annotation.left) or self._IsTypedefAnnotation(annotation.right) 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