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