Files
TransPyC/lib/core/Handles/HandlesAnnAssign.py
T
TermiNexus c86ecb0c89 refactor(LLVMCG/Handles): 直建 ir.Function 点全部收敛到幂等入口,3 处不再吞 fail-fast
类别:a 编译器缺陷(同一根因的其它变体收口:函数声明创建的唯一入口 + fail-fast 不被吞)

背景(前几轮已修两处):llvmlite 的 `Function.__init__` 不是事务性的(名字在 `_set_name`
就进 module.scope,之后建 Argument/ReturnValue 才断言类型,最后才 add_global),中途失败会
留下悬空名字;本仓已加两道防线:全局守卫 `InstallFuncCreateGuard()`(回滚名字 + 带调用点的
FailFastError)与唯一幂等入口 `_CreateOrReuseFuncDecl`(创建前自检、同名同类型复用、异类型
带两侧签名 fail-fast)。但仍有 20+ 处**绕过入口直建**(同名时抛裸 DuplicatedNameError、且
名字被占/半注册时只能看到没有上下文的名字),以及若干处 `except Exception` 会把这颗
FailFastError 吞掉(声明静默缺失、真因丢失)。

改动:
1. 创建点收敛(21 处,行为等价或更严)——`X = ir.Function(<gen>.module, <fty>, name=<N>)`
   → `X = <gen>._CreateOrReuseFuncDecl(<N>, <fty>)`:
   · Handles:HandlesAnnAssign[llvm.memcpy]、HandlesAssign[llvm.memcpy.p0i8.p0i8.i64 ×2]、
     HandlesBase[GetOrCreateFuncDecl / GetOrCreateStubFuncDecl]、
     HandlesExpr[malloc]、HandlesExprBuiltin[strlen / llvm.memcpy]、
     HandlesExprCall[llvm.* 与普通 / win32 API / strlen / llvm.memcpy]、
     HandlesAsyncFunctions[malloc / create_* / resume_* / CRT 声明 / free]、
     HandlesExprLambda[lambda 包装]、HandlesFunctions[_replace_function_decl 的 old_func=None 分支]、
     HandlesImports[output stub 按需加载]
   · LLVMCG:BaseGen[_DeclareExternalFunc 与 6 处 --hotspot-sites 内部符号]、
     ExprGen[llvm.pow.f64]、VaArg[llvm.va_start / llvm.va_end]
   原有 globals/functions 预检查与随后的 `Gen.functions[...] = X` 赋值一律保留。
   注:`_DeclareExternalFunc` 收敛后外部声明会同时登记进 `self.functions`(幂等入口固有行为,
   与既有 `get_or_declare_c_func` 一致;该路径仅 --hotspot-sites 调试特性使用)。
2. fail-fast 不再被吞(3 处):在包裹创建的 `except Exception` 首部加
   `isinstance(_e, FailFastError) → raise`:
   · HandlesExpr[malloc 的 null 回退分支]、HandlesExprBuiltin[strlen]、HandlesExprCall[strlen]
   (其余异常维持原行为,本轮不扩大 fatal 面。)
   另有 HandlesFunctions / HandlesImports 的同类 handler 前几轮已具备 FFE 重抛,本次未重复改。

验证:13 个改动文件 `py_compile` 全部 EXIT=0;冒烟 4 例通过——同名同类型复用同一对象、
异签名 → FailFastError(报文含两侧签名 i64/i32)、名字被非函数全局占用 → FailFastError、
`llvm.va_start` 幂等。另:`ir.Function(` 在 lib 内仅剩 `BaseGen._CreateOrReuseFuncDecl` 内部
这一处(豁免点);`DecoratorPass.py` 的 `module` 为外部传入(非 Gen 的模块)故其两处保持原样。
2026-09-30 09:53:41 +08:00

1127 lines
76 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
import vast as ast
import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
from lib.includes import t
from lib.core.SymbolUtils import (
IsListAnnotation, ParseListAnnotation, FindStructNameInAnnotationWithContext,
AnnotationContainsName, IsCPtrNode, RequireDefineConstStorable,
)
def _IsGenericTpName(name: str) -> bool:
"""判断名称是否为泛型类型参数(单字母大写,如 T/U/V)
用于 _is_struct_ptr_union 判定:elem_ptr: T | t.CPtr = t.CPtr(...)
(_list.vp 泛型方法体,T 未解析)时按 8 字节指针槽处理,避免
var_struct_class 误注册 llvmlite.Value 后 elem_ptr[0] cast 到 Value*
与函数声明 i8* 不匹配(8fbba llc 报错根因)。标量 T(如 int)退化为
Ptr(i32) 不命中 _HandleSubscriptLlvm 的 i8 分支,不受此影响。
"""
if not name:
return False
return len(name) == 1 and name.isalpha() and name.isupper()
class AnnAssignHandle(BaseHandle):
def _StoreOrStrCopy(self, VarName: str, VarPtr: ir.Value, Value: ir.Value) -> None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
DestPtr: ir.Value = Gen._load(VarPtr, name=VarName)
if isinstance(DestPtr.type, ir.PointerType) and isinstance(DestPtr.type.pointee, ir.IntType) and DestPtr.type.pointee.width == 8:
if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, ir.IntType) and Value.type.pointee.width == 8:
if 'llvm.memcpy' not in Gen.functions:
memcpy_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(32), ir.IntType(1)])
Gen.functions['llvm.memcpy'] = Gen._CreateOrReuseFuncDecl('llvm.memcpy', memcpy_type)
if isinstance(DestPtr.type.pointee, ir.IntType) and not isinstance(DestPtr.type.pointee, ir.IntType(8)):
DestPtr = Gen.builder.bitcast(DestPtr, ir.PointerType(ir.IntType(8)), name=f"strcpy_dst_cast_{VarName}")
if isinstance(Value.type.pointee, ir.IntType) and not isinstance(Value.type.pointee, ir.IntType(8)):
Value = Gen.builder.bitcast(Value, ir.PointerType(ir.IntType(8)), name=f"strcpy_src_cast_{VarName}")
size: ir.Constant = ir.Constant(ir.IntType(64), 8)
align: ir.Constant = ir.Constant(ir.IntType(32), 1)
isvolatile: ir.Constant = ir.Constant(ir.IntType(1), 0)
Gen.builder.call(Gen.functions['llvm.memcpy'], [DestPtr, Value, size, align, isvolatile], name=f"strcpy_call_{VarName}")
return
try:
CastedVal: ir.Value = Gen.builder.bitcast(Value, VarPtr.type.pointee, name=f"cast_{VarName}")
Gen._store(CastedVal, VarPtr)
except Exception: # 回退:bitcast 失败时 alloca 新变量
NewVar: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName)
Gen._store(Value, NewVar)
Gen.variables[VarName] = NewVar
@staticmethod
def _RhsEndsWithVoidCall(Gen: LlvmCodeGenerator, BlockBefore: ir.Block | None, InsnStart: int) -> bool:
"""判定刚求值的带注解赋值 RHS 是否"按类型即无值"(以返回 void 的调用收尾)。
判据完全取自**实际发射的指令**(不依赖 RHS 的 AST 形态白名单):仅当求值
发生在同一基本块、且新增指令的最后一条是返回 void 的 call 时,才认为该
表达式语义上本就无值(如返回 void 的内建/库调用),此时"无值可存"合法。
其余情况——无任何指令产出(类型下标被当乘法 ⇒ 整条 RHS 未生成任何东西)
或只产出 load/gep 等非收尾指令后求值失败(属性链解析失败,实测同块新增
1 条 load)——都属编译期求值失败,必须 fail-fast。
"""
BlockAfter: ir.Block | None = Gen.builder.block if Gen.builder is not None else None
if InsnStart < 0 or BlockAfter is None or BlockAfter is not BlockBefore:
return False
NewInstrs: list[ir.Instruction] = BlockAfter.instructions[InsnStart:]
if not NewInstrs:
return False
Last: ir.Instruction = NewInstrs[-1]
return isinstance(Last, ir.CallInstr) and isinstance(Last.type, ir.VoidType)
def _HandleAttributeStoreLlvm(self, Target: ast.Attribute, Value: ir.Value) -> None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
AttrName: str = Target.attr
if isinstance(Target.value, ast.Name) and Target.value.id == 'self':
ClassName: str | None = self._CurrentCpythonObjectClass
if not ClassName:
self_var: ir.Value | None = Gen.variables.get('self')
if self_var:
var_type: ir.Type = self_var.type
if isinstance(var_type, ir.PointerType) and isinstance(var_type.pointee, ir.PointerType):
var_type = var_type.pointee
if isinstance(var_type, ir.PointerType):
pointee: ir.Type = var_type.pointee
for cn, st in Gen.structs.items():
if st == pointee:
ClassName = cn
break
if ClassName and ClassName in Gen.structs:
SelfVar: ir.Value | None = Gen._get_var_ptr('self')
if SelfVar:
SelfPtr: ir.Value = Gen._load(SelfVar, name="self")
offset: int = Gen._get_member_offset(AttrName, ClassName)
MemberPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
elif isinstance(Target.value, ast.Name):
VarName: str = Target.value.id
# 模块全局变量写入(治本修复 2026-08-27,与 HandlesAssign 同款):
# X.attr = ... 且 X 是 import 模块(含别名/后缀匹配 _ImportedModules)
# 时按"模块全局变量"直接 store,优先 {sha1}.{Attr} 前缀名,回退裸名。
# 必须放在 ClassName heuristic(startswith(CN))之前,否则模块名与
# 类名同名(如 CompilerFactory)会被误判为类字段 → fail-fast。
_mod_imported: list[str] | None = getattr(self.Trans, '_ImportedModules', None)
_resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName)
_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:
_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:
for CN in Gen.structs:
if VarName == CN.lower() or VarName.startswith(CN):
ClassName = CN
break
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
for CN, ST in Gen.structs.items():
if pointee == ST:
ClassName = CN
break
IsUnion: bool = False
if ClassName:
ClassTypeInfo: Any = self.Trans.SymbolTable.lookup(ClassName)
if ClassTypeInfo:
if ClassTypeInfo.IsUnion:
IsUnion = True
if ClassName and ClassName in Gen.structs:
ObjVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value)
if ObjVal:
if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IntType) and ObjVal.type.pointee.width == 8:
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 泛型结构体 → 特化版本 bitcast
# 当 ObjVal 的 pointee 是 opaque 基础泛型结构体(如 GSList,来自跨模块 stub),
# 而 ClassName 是特化版本(如 GSList[BasicBlock])时,两者是不同的
# IdentifiedStructType,直接比较会失败。这里 bitcast 到特化版本,
# 使后续 GEP 能正确访问字段。
if (isinstance(ObjVal.type, ir.PointerType)
and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType)
and ObjVal.type.pointee is not Gen.structs[ClassName]
and '[' in ClassName):
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_spec_{ClassName}")
if isinstance(ObjVal.type, ir.PointerType) and ObjVal.type.pointee == Gen.structs[ClassName]:
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 = 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
offset: int = Gen._get_member_offset(AttrName, ClassName)
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)
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)):
for CN, ST in Gen.structs.items():
if pointee == ST:
offset: int = Gen._get_member_offset(AttrName, CN)
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
def _StoreWithCoerce(self, Value: ir.Value, Ptr: ir.Value, Node: ast.AST | None = None) -> None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
if isinstance(Ptr.type, ir.PointerType):
TargetType: ir.Type = Ptr.type.pointee
if Value.type != TargetType:
# None(空值常量)允许隐式转换为任意字段类型:指针→空指针,其余→零值。
if Gen._IsNoneValue(Value, Node):
Value = (ir.Constant(TargetType, None) if isinstance(TargetType, ir.PointerType)
else Gen._ZeroConst(TargetType))
elif isinstance(TargetType, ir.IntType) and isinstance(Value.type, ir.IntType):
if TargetType.width < Value.type.width:
Value = Gen.builder.trunc(Value, TargetType, name="trunc")
else:
Value = Gen.builder.zext(Value, TargetType, name="zext")
elif isinstance(TargetType, ir.PointerType) and isinstance(Value.type, ir.PointerType):
Allowed: bool
Reason: str
Allowed, Reason = Gen._ClassifyPtrImplicitConvert(Value.type, TargetType)
if not Allowed:
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转换已禁止({Reason}):不能把指针类型 {Value.type} 隐式转为 {TargetType};"
f"如需转换请显式 cast(如 t.CPtr(...))"
f"{Gen._DescribeValueNode(Node)}"
)
Value = Gen.builder.bitcast(Value, TargetType, name="ptr_bitcast")
elif isinstance(TargetType, ir.PointerType) and isinstance(Value.type, ir.IntType):
Value = Gen.builder.inttoptr(Value, TargetType, name="int_to_ptr")
elif isinstance(TargetType, ir.IntType) and isinstance(Value.type, ir.PointerType):
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)
# (None 已在上方短路为零值,不会落到这里)。
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:不能把指针类型 {Value.type} 赋给整数类型 {TargetType};"
f"如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
f"{Gen._DescribeValueNode(Node)}"
)
Gen._store(Value, Ptr)
def _HandleGenericListLiteralAssign(self, VarName: str, Node: ast.AnnAssign, Gen: LlvmCodeGenerator, list_sub: ast.Subscript) -> None:
"""处理 list[T] = [...] → 构造 list[T] 对象 + append 序列。
将 list 字面量翻译为:
1. 特化 list[T] 类
2. alloca + __before_init__ + __new__(pool) + __before_init__ + __init__(pool)
3. 对每个元素调用 append(elem)
4. 将堆指针存入变量
list_sub 是从注解中提取的 list[...] Subscript 节点(支持 list[T] 和 list[T] | t.CPtr)
"""
# 1. 解析元素类型
slice_node: ast.expr = list_sub.slice
result: tuple[str, str] | None = self.Trans.ExprCallHandle._resolve_generic_slice_type(slice_node)
if result is None:
return
type_arg: str = result[0]
type_name: str = result[1]
# 2. 特化 list[T] 类
spec_name: str | None = self.Trans.ClassHandler._specialize_generic_class(
'list', [type_arg], Gen, type_names=[type_name])
if spec_name is None:
return
# 3. 获取结构体类型
StructType: ir.Type | None = Gen.structs.get(spec_name)
if StructType is None:
return
if isinstance(StructType, ir.PointerType):
StructType = StructType.pointee
# 4. 查找 pool 变量(pool / _mbuddy / mbuddy / mb)
pool_val: ir.Value | None = None
for pool_name in ('pool', '_mbuddy', 'mbuddy', 'mb'):
if pool_name in Gen.variables:
pool_var: ir.Value = Gen.variables[pool_name]
if isinstance(pool_var.type, ir.PointerType):
pointee: ir.Type = pool_var.type.pointee
if isinstance(pointee, ir.PointerType):
# alloca of pointer → load
pool_val = Gen._load(pool_var, name=f"pool_load_{VarName}")
else:
pool_val = pool_var
else:
pool_val = pool_var
break
if pool_val is None:
return
# 5. alloca 临时结构体
alloca_ptr: ir.AllocaInstr = Gen._allocaEntry(StructType, name=f"{VarName}_alloca")
# 6. 调用 __before_init__(alloca_ptr)
before_init_name: str = f'{spec_name}.__before_init__'
before_init_func: ir.Function | None = None
if Gen._has_function(before_init_name):
before_init_func = Gen._get_function(before_init_name)
Gen.builder.call(before_init_func, [alloca_ptr], name=f"before_init_{VarName}")
# 7. 调用 __new__(alloca_ptr, pool_val) → heap_ptr
new_method_name: str = f'{spec_name}.__new__'
heap_ptr: ir.Value = alloca_ptr
has_new: bool = Gen._find_function(new_method_name) is not None
if has_new:
new_func: ir.Function | None = Gen._find_function(new_method_name)
if new_func:
new_args: list[ir.Value] = [alloca_ptr, pool_val]
self.Trans.ExprCallHandle._fill_default_args(new_args, new_func, new_method_name)
new_args = Gen._apply_auto_addr(new_method_name, new_args)
adjusted_new: list[ir.Value] = Gen._adjust_args(new_args, new_func)
heap_ptr = Gen.builder.call(new_func, adjusted_new, name=f"new_{VarName}")
# 转换返回指针类型
target_ptr_type: ir.PointerType = ir.PointerType(StructType)
if isinstance(heap_ptr.type, ir.PointerType) and heap_ptr.type != target_ptr_type:
heap_ptr = Gen.builder.bitcast(heap_ptr, target_ptr_type, name=f"new_{VarName}_cast")
elif isinstance(heap_ptr.type, ir.IntType) and target_ptr_type:
if heap_ptr.type.width < 64:
i64_val: ir.Value = Gen.builder.zext(heap_ptr, ir.IntType(64), name=f"new_{VarName}_i64")
heap_ptr = Gen.builder.inttoptr(i64_val, target_ptr_type, name=f"new_{VarName}_ptr")
else:
heap_ptr = Gen.builder.inttoptr(heap_ptr, target_ptr_type, name=f"new_{VarName}_ptr")
# 对堆指针重新调用 __before_init__
if before_init_func:
Gen.builder.call(before_init_func, [heap_ptr], name=f"before_init_{VarName}_heap")
# 8. 调用 __init__(heap_ptr, pool_val, elem_size=0)
init_name: str = f'{spec_name}.__init__'
if Gen._has_function(init_name):
init_func: ir.Function | None = Gen._get_function(init_name)
if init_func:
elem_size_val: ir.Constant = ir.Constant(ir.IntType(64), 0)
init_args: list[ir.Value] = [heap_ptr, pool_val, elem_size_val]
self.Trans.ExprCallHandle._fill_default_args(init_args, init_func, init_name)
init_args = Gen._apply_auto_addr(init_name, init_args)
adjusted_init: list[ir.Value] = Gen._adjust_args(init_args, init_func)
Gen.builder.call(init_func, adjusted_init, name=f"init_{VarName}")
# 9. 对每个元素调用 append(heap_ptr, elem_val)
append_name: str = f'{spec_name}.append'
append_func: ir.Function | None = Gen._find_function(append_name)
if append_func and Node.value and isinstance(Node.value, ast.List):
elts: list[ast.expr] = Node.value.elts
for i, elem_node in enumerate(elts):
elem_val: ir.Value | None = self.HandleExprLlvm(elem_node)
if elem_val:
append_args: list[ir.Value] = [heap_ptr, elem_val]
self.Trans.ExprCallHandle._fill_default_args(append_args, append_func, append_name)
append_args = Gen._apply_auto_addr(append_name, append_args)
adjusted_append: list[ir.Value] = Gen._adjust_args(append_args, append_func)
Gen.builder.call(append_func, adjusted_append, name=f"append_{VarName}_{i}")
# 10. 创建变量并存储堆指针
# 判断变量类型:list[T] | t.CPtr → i8*;list[T] → Ptr(StructType)
is_cptr_union: bool = isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr)
if is_cptr_union:
# list[T] | t.CPtr → 变量类型为 i8*
i8_ptr_type: ir.PointerType = ir.PointerType(ir.IntType(8))
var: ir.AllocaInstr = Gen._allocaEntry(i8_ptr_type, name=VarName)
# bitcast heap_ptr (Ptr(StructType)) → i8*
if isinstance(heap_ptr.type, ir.PointerType) and heap_ptr.type != i8_ptr_type:
stored_val: ir.Value = Gen.builder.bitcast(heap_ptr, i8_ptr_type, name=f"store_cast_{VarName}")
else:
stored_val = heap_ptr
Gen._store(stored_val, var)
else:
# list[T] → 变量类型为 Ptr(StructType)
var = Gen._allocaEntry(ir.PointerType(StructType), name=VarName)
Gen._store(heap_ptr, var)
Gen.variables[VarName] = var
Gen.var_struct_class[VarName] = spec_name
Gen.global_struct_class[VarName] = spec_name
Gen._record_var_signedness(VarName, False)
def _HandleAnnAssignLlvm(self, Node: ast.AnnAssign) -> None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
self.Trans._current_assign_node = Node
try:
self._HandleAnnAssignLlvmInner(Node)
finally:
self.Trans._current_assign_node = None
def _HandleAnnAssignLlvmInner(self, Node: ast.AnnAssign) -> None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
# 内建定长数组构造子 array 的注解校验(误用即 fail-fast,含文件:行:列):
# 局部变量/self 字段注解不经过 ResolveAnnotationType,故在此显式校验。
self._CheckFixedArrayKeyword(Node.annotation)
# M1 协程:记录 async 函数调用赋值的变量名(coro.coro_run 内联翻译需要)
if isinstance(Node.target, ast.Name) and Node.value is not None:
if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name):
callee: str = Node.value.func.id
if hasattr(self.Trans, 'AsyncHandler') and self.Trans.AsyncHandler._IsAsyncFunc(callee):
if not hasattr(Gen, '_coro_var_async'):
Gen._coro_var_async = {}
Gen._coro_var_async[Node.target.id] = callee
if isinstance(Node.target, ast.Name):
VarName: str = Node.target.id
TypeInfo: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.annotation)
if TypeInfo is None:
TypeInfo = CTypeInfo()
TypeInfo.BaseType = t.CInt()
IsPtr: bool = TypeInfo.IsPtr
if TypeInfo.IsDefine:
if Node.value and isinstance(Node.value, ast.Constant):
# fail-fast:常量值必须能落进声明类型(define 带类型形态)/ 64 位存储
# (自动推导形态)。唯一判据见 SymbolUtils.RequireDefineIntStorable。
RequireDefineConstStorable(Gen, self.Trans, Node, TypeInfo.BaseType,
Node.annotation, Node.value.value, VarName)
if not hasattr(Gen, '_define_constants'):
Gen._define_constants = {}
Gen._define_constants[VarName] = Node.value.value
info: CTypeInfo = CTypeInfo()
info.IsDefine = True
info.DefineValue = Node.value.value
self.Trans.SymbolTable.insert(VarName, info)
Gen._record_var_signedness(VarName, TypeInfo.IsUInt)
elif Node.value:
# 非 Constant 表达式:尝试编译期求值(如 t.CSizeT().Size // 8)
from lib.core.ConstEvaluator import ConstEvaluator, EvalContext
ctx = EvalContext(Gen=Gen, symtab=self.Trans.SymbolTable, translator=self.Trans)
eval_val = ConstEvaluator.eval_full(Node.value, ctx)
if eval_val is not None and isinstance(eval_val, (int, float)):
RequireDefineConstStorable(Gen, self.Trans, Node, TypeInfo.BaseType,
Node.annotation, eval_val, VarName)
if not hasattr(Gen, '_define_constants'):
Gen._define_constants = {}
Gen._define_constants[VarName] = eval_val
info: CTypeInfo = CTypeInfo()
info.IsDefine = True
info.DefineValue = eval_val
self.Trans.SymbolTable.insert(VarName, info)
Gen._record_var_signedness(VarName, TypeInfo.IsUInt)
return
if TypeInfo.IsStr or (isinstance(Node.annotation, ast.Name) and Node.annotation.id == 'str'):
# 仅指针层数 <= 1 才算裸 str/bytes(i8*),可以压平:`str | t.CPtr` /
# `str*` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 指针数组),
# 压平会让局部变量比模块级同名全局槽少一层指针
# (全局走 HandlesAssign._EmitGlobalAnnAssignLlvm 的 FromNode,得到 i8**),
# 于是 includes/sys.vp `_argv = argv_arr` 被指针隐式转换 fail-fast 拦下。
# 与 HandlesFunctions.py:599 / HandlesImports.py:1440 的
# `IsStr and PtrCount <= 1` 同一条规则源。
if TypeInfo.PtrCount <= 1:
TypeInfo = CTypeInfo()
TypeInfo.BaseType = t.CChar()
TypeInfo.PtrCount = 1
IsPtr = True
# FakeDuck: 为 str 变量创建影子结构体 a__meta__ (栈上, per-instance)
# a 保持为 char* 用于 printf; a__meta__ 存储 __data__ 和 __mbuddy__ 字段
# 仅对 str 类型创建,bytes 和 t.CChar|t.CPtr 不创建
if '_str' in Gen.structs and isinstance(Node.annotation, ast.Name) and Node.annotation.id == 'str':
_meta_name: str = f"{VarName}__meta__"
_meta_var: ir.AllocaInstr = Gen._allocaEntry(Gen.structs['_str'], name=_meta_name)
Gen.variables[_meta_name] = _meta_var
# 标记指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr)
# elem_ptr[0] 应加载/存储 8 字节 (指针), 而非 1 字节 (字符)
# 仅当注解包含 str 或 bytes 时设置(t.CChar | t.CPtr 不应设置,它是纯字符指针)
if isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr):
if AnnotationContainsName(Node.annotation, 'str') or AnnotationContainsName(Node.annotation, 'bytes'):
Gen.var_ptr_element[VarName] = True
# 同时记录到全局字典,使全局变量在跨函数访问时仍能用 8 字节指针步长
Gen.global_var_ptr_element[VarName] = True
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,
handle_str=True, void_fallback_width=32,
)
if parse_result.is_pointer:
# 单参数 list[type] → 指针
# 但如果有列表字面量初始化值 [a, b, c],则按数组 list[type, N] 处理
if not (Node.value and isinstance(Node.value, ast.List)):
VarType: ir.Type = ir.PointerType(ElemType)
var: ir.AllocaInstr = Gen._allocaEntry(VarType, name=VarName)
Gen.variables[VarName] = var
Gen._record_var_signedness(VarName, False)
if self.Trans.VarScopes:
self.Trans.VarScopes[-1][VarName] = ElemTypeInfo
return
# 有列表字面量值,继续走数组路径(count_node=None 时从列表长度推断)
count_node: ast.expr | None = parse_result.count_node
ArrayCount: int = self.ParseArrayCount(count_node, Gen, Node.value, mode='local')
VarType = ir.ArrayType(ElemType, ArrayCount)
var = Gen._allocaEntry(VarType, name=VarName)
Gen.variables[VarName] = var
Gen._record_var_signedness(VarName, False)
if Node.value and isinstance(Node.value, ast.List):
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:
try:
InitVal: ir.Constant = ir.Constant(VarType, InitConstants)
Gen.builder.store(InitVal, var)
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
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 isinstance(VarType, ir.ArrayType) and len(str_bytes) < VarType.count:
str_bytes.extend(b'\x00' * (VarType.count - len(str_bytes)))
try:
InitVal: ir.Constant = ir.Constant(VarType, str_bytes[:VarType.count] if isinstance(VarType, ir.ArrayType) else str_bytes)
Gen.builder.store(InitVal, var)
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
if self.Trans.VarScopes:
self.Trans.VarScopes[-1][VarName] = ElemTypeInfo
# 记录数组元素字节序
if ElemTypeInfo and ElemTypeInfo.ByteOrder == 'big' and isinstance(ElemType, ir.IntType) and ElemType.width in (16, 32, 64):
Gen.local_var_byteorders[VarName] = 'big'
return
# list[T] 泛型类注解 + list 字面量值 → 构造 list[T] 对象 + append 序列
# (list[str] = ["a", "b"] 不应走 C 数组路径,而应构造 list[str] 对象)
# 支持两种注解形式:list[T] 和 list[T] | t.CPtr
list_sub: ast.Subscript | None = None
if (isinstance(Node.annotation, ast.Subscript)
and isinstance(Node.annotation.value, ast.Name)
and Node.annotation.value.id == 'list'):
list_sub = Node.annotation
elif (isinstance(Node.annotation, ast.BinOp)
and isinstance(Node.annotation.op, ast.BitOr)):
for side in (Node.annotation.left, Node.annotation.right):
if (isinstance(side, ast.Subscript)
and isinstance(side.value, ast.Name)
and side.value.id == 'list'):
list_sub = side
break
if (list_sub is not None
and not isinstance(list_sub.slice, ast.Tuple)
and Node.value is not None
and isinstance(Node.value, ast.List)
and hasattr(self.Trans.ClassHandler, '_generic_class_templates')
and 'list' in self.Trans.ClassHandler._generic_class_templates):
self._HandleGenericListLiteralAssign(VarName, Node, Gen, list_sub)
return
# Only fall back to the left operand when the union neither resolved a base
# type nor accumulated multi-level pointers. A pointer-marker based union
# (`t.CPtr*` = `t.CPtr | t.CPtr` => CVoid/2) is already a fully resolved
# multi-level pointer; falling back to its left operand (`t.CPtr` => 1 layer)
# used to silently degrade it to i8*. Keep PtrCount > 1 untouched.
if (TypeInfo.IsVoid and TypeInfo.PtrCount <= 1
and isinstance(Node.annotation, ast.BinOp)):
found: str | None = FindStructNameInAnnotationWithContext(
Node.annotation, self.Trans, Gen)
if found:
TypeInfo = CTypeInfo()
TypeInfo.BaseType = t.CStruct(name=found)
TypeInfo.PtrCount = 1
IsPtr = True
elif isinstance(Node.annotation.left, ast.Attribute):
AttrTypeInfo: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.annotation.left)
if AttrTypeInfo and AttrTypeInfo.BaseType:
TypeInfo = AttrTypeInfo
TypeInfo.PtrCount = max(TypeInfo.PtrCount, 1)
IsPtr = True
# 对于局部变量声明 s: TestStruct(不带 | t.CPtr),应为栈上结构体而非指针
# GetCTypeInfo 默认设置 PtrCount=1,但纯结构体名注解应为值类型
if (isinstance(Node.annotation, ast.Name)
and Node.annotation.id in Gen.structs
and TypeInfo.IsStruct
and Node.annotation.id not in Gen.class_vtable):
TypeInfo.PtrCount = 0
IsPtr = False
# const / volatile 局部变量登记(统一登记入口)。放在此处:TypeInfo 的
# PtrCount/IsStruct 已定稿(上方 526-547),且早于本函数所有后续分支与
# 提前 return(数组/list/_reg_values/已存在 alloca 等),保证「局部变量」
# 这一登记点只有一处、无遗漏。
Gen.RegisterVarQualifiers(VarName, TypeInfo, Node.annotation, IsGlobal=False)
VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo)
if not IsPtr and TypeInfo.IsStruct and TypeInfo.Name:
stripped: str = TypeInfo.Name
if stripped in Gen.class_vtable and stripped in Gen.structs:
VarType = ir.PointerType(Gen.structs[stripped])
IsPtr = True
if isinstance(VarType, ir.IdentifiedStructType):
struct_name: str = getattr(VarType, 'name', '')
if struct_name and Gen.class_vtable:
for cls_name in Gen.class_vtable:
if struct_name.endswith(f'.{cls_name}') or struct_name == cls_name:
VarType = ir.PointerType(VarType)
IsPtr = True
break
if isinstance(VarType, ir.VoidType):
VarType = ir.IntType(32)
IsUnsigned: bool = TypeInfo.IsUInt
# 修复 3: BinOp 注解(如 ast.Arguments | t.CPtr)的 var_struct_class 设置
# TypeInfo.Name 由 _HandleSubscript 设置(特化名如 list[sha1.ASTptr])或
# GetCTypeInfo 设置(普通结构体名),经 _MergeCTypeInfoInto 传播后保留。
# 此处确保 BinOp 注解的变量也能被 _get_var_class 识别,避免方法调用分派
# fallthrough 到 char pointer fallback 错误匹配其他类(如 AST.__len__)。
# 使用 not in 条件,不覆盖后续 L337-339/L396-398 对 ast.Name 注解的精确设置。
if TypeInfo.IsStruct and TypeInfo.Name and TypeInfo.Name in Gen.structs:
if VarName not in Gen.var_struct_class:
Gen.var_struct_class[VarName] = TypeInfo.Name
Gen.global_struct_class[VarName] = TypeInfo.Name
# 联合指针注解(X | t.CPtr)短名回退:resolver 对模块限定 struct
# 段(如 llvmlite.Value)无法精确 in Gen.structs(键可能是 'Value'
# 或带 sha1 前缀),Name 常退化为指针别名 'rawptr'(被注册成 opaque
# struct)→ 变量 var_struct_class 登记为 rawptr → 字段访问
# "属性 'Next' 在类 'rawptr' 中找不到"(FuncGenMixin.vp L307)。
# FindStructNameInAnnotation 以 attr/短名命中,找回真实类名。
if (isinstance(Node.annotation, ast.BinOp)
and isinstance(Node.annotation.op, ast.BitOr)):
_union_struct_name2: str | None = FindStructNameInAnnotationWithContext(
Node.annotation, self.Trans, Gen)
# resolver 对模块限定 struct 联合(llvmlite.Value | t.CPtr)常给出
# 指针占位名(如 '{sha1}.rawptr')且该占位恰好注册在 Gen.structs,
# 使上方 TypeInfo.Name in Gen.structs 通过并登记错误类名 → 字段
# 访问 "属性 'Next' 在类 'rawptr' 中找不到"。凡短名命中真实类
# (attr 精确 in structs),一律覆盖为真实类名(Value)。
if _union_struct_name2 and _union_struct_name2 in Gen.structs:
if Gen.var_struct_class.get(VarName) != _union_struct_name2:
Gen.var_struct_class[VarName] = _union_struct_name2
if getattr(Gen, 'global_struct_class', None) is not None:
Gen.global_struct_class[VarName] = _union_struct_name2
# Subscript rawptr[X] / t.CPtr[X] 注解的本地结构指针变量(如
# includes/ast/parser.vp _fix_store_ctx 里 `tp: rawptr[Tuple] =
# (rawptr[Tuple])(node)`,随后 `tp_elts = tp.elts`):TypeMerge 的
# builtins 分支把 rawptr[X] 折叠为 i8*(PtrCount=1/CVoid),既不登记
# var_struct_class 也不保留内层 X —— 单级 Name 字段取值(tp.elts)
# 在属性访问层 ClassName 解析为 None → 求值 None → 赋值被 fail-fast
# 拦截(parser.vp L427 bootstrap 根因)。与上方 BinOp 联合的短名回退
# 对称:把内层 X 解析为真实 struct 键(兼容短名 / 带 class_sha1 前缀
# 的键)并登记;仅在缺失或指针占位(rawptr…)时覆盖,不碰已精确登记值。
_rst_sub: ast.AST | None = None
if isinstance(Node.annotation, ast.Subscript):
_rst_v: ast.expr = Node.annotation.value
_rst_nm: str = ''
if isinstance(_rst_v, ast.Name):
_rst_nm = _rst_v.id
elif isinstance(_rst_v, ast.Attribute) and isinstance(_rst_v.value, ast.Name):
_rst_nm = f'{_rst_v.value.id}.{_rst_v.attr}'
if _rst_nm in ('rawptr', 't.CPtr'):
_rst_sub = Node.annotation
if _rst_sub is not None:
_rst_sl: ast.AST = _rst_sub.slice
if hasattr(ast, 'Index') and isinstance(_rst_sl, ast.Index):
_rst_sl = _rst_sl.value
_rst_tn: str = ''
if isinstance(_rst_sl, ast.Name):
_rst_tn = _rst_sl.id
elif isinstance(_rst_sl, ast.Attribute) and isinstance(_rst_sl.value, ast.Name):
_rst_tn = _rst_sl.attr
_rst_tk: str | None = None
if _rst_tn:
if _rst_tn in Gen.structs:
_rst_tk = _rst_tn
else:
_rst_sha: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(_rst_tn)
if _rst_sha and f'{_rst_sha}.{_rst_tn}' in Gen.structs:
_rst_tk = f'{_rst_sha}.{_rst_tn}'
else:
for _rst_k3 in Gen.structs:
_rst_short3: str = _rst_k3.split('.')[-1] if '.' in _rst_k3 else _rst_k3
if _rst_short3 == _rst_tn:
_rst_tk = _rst_k3
break
if _rst_tk:
_rst_cur: str | None = Gen.var_struct_class.get(VarName)
_rst_need: bool = (not _rst_cur or _rst_cur == 'rawptr' or '.rawptr' in _rst_cur
or _rst_cur.startswith('rawptr['))
if _rst_need and _rst_cur != _rst_tk:
Gen.var_struct_class[VarName] = _rst_tk
if getattr(Gen, 'global_struct_class', None) is not None:
Gen.global_struct_class[VarName] = _rst_tk
# 检测指针到指针缓冲区变量:注解为 StructName | t.CPtr,且赋值来自 t.CPtr(...) 调用。
# 这种变量(如 list[T].__getitem__ 中的 elem_ptr: T | t.CPtr = t.CPtr(...))
# 实际指向存储结构体指针的缓冲区(T=AST|t.CPtr 时特化为 AST*,缓冲区存 AST* 数组)。
# [index] 应 bitcast 为 AST** 后 GEP 指针数组再 load,而非 GEP 结构体数组。
# 泛型特化将 T|t.CPtr 降级为 StructName|t.CPtr(丢失一层指针),故通过赋值来源区分:
# elem_ptr 的值是 t.CPtr(buf + idx*elem_size)(原始指针运算,缓冲区内容未知),
# pts 的值是 (Point|t.CPtr)(...)(类型转换,明确指向 Point 结构体)。
_is_cptr_assign: bool = (
isinstance(Node.value, ast.Call)
and isinstance(Node.value.func, ast.Attribute)
and isinstance(Node.value.func.value, ast.Name)
and Node.value.func.value.id == 't'
and Node.value.func.attr == 'CPtr'
)
_is_struct_ptr_union: bool = (
isinstance(Node.annotation, ast.BinOp)
and isinstance(Node.annotation.op, ast.BitOr)
and ((IsCPtrNode(Node.annotation.left)
and isinstance(Node.annotation.right, ast.Name)
and (Node.annotation.right.id in Gen.structs
or _IsGenericTpName(Node.annotation.right.id)))
or (IsCPtrNode(Node.annotation.right)
and isinstance(Node.annotation.left, ast.Name)
and (Node.annotation.left.id in Gen.structs
or _IsGenericTpName(Node.annotation.left.id))))
)
if _is_cptr_assign and _is_struct_ptr_union:
Gen.var_ptr_element[VarName] = True
Gen.global_var_ptr_element[VarName] = True
if 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]
if Node.value:
InitValue: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=VarType)
if InitValue:
self._StoreOrStrCopy(VarName, var, InitValue)
elif isinstance(Node.value, ast.Attribute):
# 防静默:带值带注解属性链求值失败禁止跳过(见 __function.vp
# 两级链 blk = func.Blocks.Head 编译产物 store 丢失根因)。
self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen, Node.annotation, VarName)
Gen._record_var_signedness(VarName, IsUnsigned)
return
del Gen._reg_values[VarName]
InitValue: ir.Value | None = None
RhsBlock: ir.Block | None = Gen.builder.block if Gen.builder is not None else None
RhsInsnStart: int = len(RhsBlock.instructions) if RhsBlock is not None else -1
if Node.value:
InitValue = self.HandleExprLlvm(Node.value, VarType=VarType)
# 闭包变量追踪:当赋值右侧是返回闭包的函数调用时,记录变量为闭包类型
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 InitValue is None and Node.value is not None:
# 防静默(通用判据,不再只认 ast.Attribute):带值带注解赋值 RHS
# 求值为 None 且非"按类型即无值"(未以返回 void 的调用收尾)时,
# 变量 alloca 已创建却无 store → 后续 load 读未初始化栈变量
# (运行期随机崩溃;如类型下标被当乘法的 list[X*]*(...) 整条 RHS
# 不产任何指令)。判据取自实际发射的指令,见 _RhsEndsWithVoidCall。
if not self._RhsEndsWithVoidCall(Gen, RhsBlock, RhsInsnStart):
self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen, Node.annotation, VarName)
if VarName in Gen._reg_values:
del Gen._reg_values[VarName]
if VarName in Gen.variables and Gen.variables[VarName] is not None:
if InitValue and isinstance(InitValue.type, ir.PointerType):
pointee: ir.Type = InitValue.type.pointee
if isinstance(pointee, ir.IdentifiedStructType):
struct_name: str = getattr(pointee, 'name', '')
is_vtable_class: bool = False
for cls_name in Gen.class_vtable:
if struct_name.endswith(f'.{cls_name}') or struct_name == cls_name:
is_vtable_class = True
break
if is_vtable_class:
var: ir.AllocaInstr = Gen._allocaEntry(InitValue.type, name=VarName)
Gen._store(InitValue, var)
Gen.variables[VarName] = var
if isinstance(Node.annotation, ast.Name) and Node.annotation.id in Gen.structs:
Gen.var_struct_class[VarName] = Node.annotation.id
Gen.global_struct_class[VarName] = Node.annotation.id
Gen._record_var_signedness(VarName, IsUnsigned)
return
if InitValue:
try:
VarPtr: ir.Value = Gen.variables[VarName]
if isinstance(VarPtr.type, ir.PointerType):
TargetType: ir.Type = VarPtr.type.pointee
if InitValue.type != TargetType:
if isinstance(InitValue.type, ir.IntType) and isinstance(TargetType, ir.IntType):
if TargetType.width > InitValue.type.width:
InitValue = Gen.builder.zext(InitValue, TargetType, name=f"zext_{VarName}")
elif TargetType.width < InitValue.type.width:
InitValue = Gen.builder.trunc(InitValue, TargetType, name=f"trunc_{VarName}")
elif isinstance(InitValue.type, ir.PointerType) and isinstance(TargetType, ir.PointerType):
InitValue = Gen.builder.bitcast(InitValue, TargetType, name=f"cast_{VarName}")
Gen._store(InitValue, VarPtr)
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
Gen._record_var_signedness(VarName, IsUnsigned)
return
if VarName in Gen.global_vars:
if VarName in Gen.module.globals:
GVar: ir.GlobalVariable = Gen.module.globals[VarName]
if InitValue:
Gen._store(InitValue, GVar)
Gen.variables[VarName] = GVar
Gen._record_var_signedness(VarName, IsUnsigned)
return
var: ir.AllocaInstr = Gen._allocaEntry(VarType, name=VarName)
if InitValue and isinstance(InitValue.type, ir.PointerType):
pointee: ir.Type = InitValue.type.pointee
# 当 InitValue 是 i8* (t.CPtr) 且注解是 Gen.structs 中的结构体名时,
# bitcast 为对应的结构体指针,以便正确存储和设置 var_struct_class
# 例如: sub: dict = sub_p (sub_p 是 t.CPtr)
if (isinstance(pointee, ir.IntType) and pointee.width == 8
and isinstance(Node.annotation, ast.Name)
and Node.annotation.id in Gen.structs):
TargetStructType: ir.Type = Gen.structs[Node.annotation.id]
if isinstance(TargetStructType, ir.PointerType):
TargetStructType = TargetStructType.pointee
InitValue = Gen.builder.bitcast(InitValue, ir.PointerType(TargetStructType), name=f"cast_{VarName}_to_{Node.annotation.id}")
pointee = InitValue.type.pointee
if isinstance(pointee, ir.IdentifiedStructType):
struct_name: str = getattr(pointee, 'name', '')
is_vtable_class: bool = False
for cls_name in Gen.class_vtable:
if struct_name.endswith(f'.{cls_name}') or struct_name == cls_name:
is_vtable_class = True
break
if is_vtable_class and not isinstance(VarType, ir.PointerType):
var = Gen._allocaEntry(InitValue.type, name=VarName)
VarType = InitValue.type
IsPtr = True
Gen._store(InitValue, var)
Gen.variables[VarName] = var
if isinstance(Node.annotation, ast.Name) and Node.annotation.id in Gen.structs:
Gen.var_struct_class[VarName] = Node.annotation.id
Gen.global_struct_class[VarName] = Node.annotation.id
Gen._record_var_signedness(VarName, IsUnsigned)
return
# 方向判定用:取注解一侧的泛型基名(`Base[...]` 或 `Base[...] | t.CPtr`
# 形态中的 Base)。此判据**不依赖泛型模板注册表**(list/dict 等内建
# 容器可能未登记),只做结构性识别;仅 Name 形态可识别,属性链
# (如 t.CPtr[...])不参与。
_ann_gen_base: str | None = None
_ann_sub_side: ast.expr | None = None
if isinstance(Node.annotation, ast.Subscript):
_ann_sub_side = Node.annotation
elif (isinstance(Node.annotation, ast.BinOp)
and isinstance(Node.annotation.op, ast.BitOr)):
for _ann_side in (Node.annotation.left, Node.annotation.right):
if isinstance(_ann_side, ast.Subscript):
_ann_sub_side = _ann_side
break
if _ann_sub_side is not None:
_ann_base_v: ast.expr = _ann_sub_side.value
# 兼容嵌套下标链(Base[...][...]):逐层剥到最内层 Name
while isinstance(_ann_base_v, ast.Subscript):
_ann_base_v = _ann_base_v.value
if isinstance(_ann_base_v, ast.Name):
_ann_gen_base = _ann_base_v.id
# 「反向重建」方向判定(其二,见下方 _rb_is_upcast)所需的**注解侧
# 被指类**:注解被 TypeMerge 折叠成 i8*(rawptr[X] / t.CPtr[X],见
# 上方 635-684)时 VarType.pointee 不是结构体,此时用该处恢复进
# var_struct_class 的 X —— 那正是注解写明的被指类。
_ann_pointee: ir.Type | None = None
if isinstance(VarType, ir.PointerType):
_ap: ir.Type = VarType.pointee
if isinstance(_ap, ir.IdentifiedStructType):
_ann_pointee = _ap
else:
_ask: str | None = Gen.var_struct_class.get(VarName)
_ann_st: ir.Type | None = Gen.structs.get(_ask) if _ask else None
if isinstance(_ann_st, ir.PointerType):
_ann_st = _ann_st.pointee
_ann_pointee = _ann_st
for CN, ST in Gen.structs.items():
# 泛型占位类型(T/U/V,如 GSList[T].Head 的元素类型)opaque 无 body,
# 不参与变量类型重建/var_struct_class 设置:跟随 T* 会污染注解声明的
# 真实类型(如 BasicBlock*),导致后续字段访问/循环体翻译静默失败。
# (属性访问层已特化返回真实元素类型,此处为防御性兜底。)
_cn_short: str = CN.split('.')[-1] if '.' in CN else CN
if _IsGenericTpName(_cn_short):
continue
# rawptr 占位 struct('rawptr' / '{sha1}.rawptr' / 'rawptr[...]')是
# 模块限定联合 X | t.CPtr 被 resolver 退化的产物(无真实字段),
# 绝不能覆盖注解已解析出的真实类名(如 Value)→ env_size_val.Next
# 报 "属性在类 'rawptr' 中找不到"(FuncGenMixin.vp L307 根因)。
if CN == 'rawptr' or CN.endswith('.rawptr') or CN.startswith('rawptr['):
continue
if pointee is ST or pointee == ST:
# 若注解是泛型特化(如 ndarray[t.CDouble] | t.CPtr),
# 构造正确的特化名(如 ndarray[double]),而非用 opaque 基础名
resolved_cn: str = CN
if isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr) and '[' not in CN:
for side in (Node.annotation.left, Node.annotation.right):
if isinstance(side, ast.Subscript):
bn: ast.expr = side.value
bnm: str | None = bn.id if isinstance(bn, ast.Name) else (bn.attr if isinstance(bn, ast.Attribute) else None)
# 归一化比较:CN 可能带 16 位 sha1 模块前缀
# (如 'e94ef9e139461915.list'),注解侧裸名为
# 'list',直接与 CN 比较恒不等 ⇒ 特化名恢复失败
# ⇒ 写入裸泛型基名 ⇒ .get() 按错误元素类型
# 分派崩溃。用 _cn_short(已去前缀)比较。
if bnm and bnm == _cn_short:
sn2: ast.AST = side.slice
if hasattr(ast, 'Index') and isinstance(sn2, ast.Index):
sn2 = sn2.value
r2: tuple[str, str] | None = self.Trans.ExprCallHandle._resolve_generic_slice_type(sn2)
if r2:
resolved_cn = f'{CN}[{r2[0]}]'
break
if resolved_cn != Gen.var_struct_class.get(VarName):
Gen.var_struct_class[VarName] = resolved_cn
Gen.global_struct_class[VarName] = resolved_cn
if isinstance(VarType, ir.PointerType) and VarType.pointee is not ST and VarType.pointee != ST:
# 方向判定:本重建的本意是「注解退化成裸基类 / i8*,
# RHS 构造出精确特化 ⇒ 用 RHS 反向升级」(见 L765 注释)。
# 但 RHS 的 pointee 若为**裸泛型基类**(丢类型实参,如
# {sha1}.list)而注解一侧本身就是带类型实参的泛型
# (`Base[...]` 或 `Base[...] | t.CPtr`),以 RHS 重建
# alloca 会把注解已解析出的特化类型覆盖成裸基类 → 之后
# .get() 按错误元素类型分派(list[int].get 返回 i32)→
# inttoptr 截断指针 → 读垃圾 AST 节点崩溃
# (MatchGenMixin._handle_renum_match 现场根因)。
# 判据(_cn_short 无 '[' 且与注解泛型基名同名)只拦这一
# 确定的方向错误,属**结构性识别**而非兜底:注解退化为
# i8*/裸基类、RHS 精确特化的合法升级路径不受影响。
_rb_is_bare_generic: bool = (
_ann_gen_base is not None
and '[' not in _cn_short
and _cn_short == _ann_gen_base
)
# 方向判定(其二):注解写明的被指类是 RHS 被指类的
# **基类**时,注解就是合法的「基类指针」声明,RHS 只是
# 继承上转 —— 以 RHS 重建 alloca 会把声明类型**窄化**成
# 派生类,使同一变量随后赋基类/兄弟类的值被指针隐式
# 转换 fail-fast 拦下(includes/ast/parser.vp
# `node: rawptr[AST] = Name(...)` 之后
# `node = _new_const_str(...)` 现场根因)。
# 判据只拦「上转被窄化」这一个确定的方向错误:注解退化
# 成 i8*/裸占位(_ann_pointee 非其基类)时不受影响。
_rb_is_upcast: bool = (
_ann_pointee is not None
and _ann_pointee is not ST
and _ann_pointee != ST
and Gen._IsInheritanceUpcastPointee(pointee, _ann_pointee)
)
if not _rb_is_bare_generic and not _rb_is_upcast:
var = Gen._allocaEntry(ir.PointerType(pointee), name=VarName)
VarType = ir.PointerType(pointee)
break
if InitValue:
try:
if InitValue.type != VarType:
if Gen._IsNoneValue(InitValue, Node.value):
# None(空值常量)允许隐式转换为任意变量类型:
# 指针→空指针,其余→零值(整数 0 / 浮点 0.0 / 聚合 zeroinitializer)。
InitValue = (ir.Constant(VarType, None) if isinstance(VarType, ir.PointerType)
else Gen._ZeroConst(VarType))
elif isinstance(InitValue.type, ir.IntType) and isinstance(VarType, ir.IntType):
if VarType.width > InitValue.type.width:
InitValue = Gen.builder.zext(InitValue, VarType, name=f"zext_{VarName}")
elif VarType.width < InitValue.type.width:
InitValue = Gen.builder.trunc(InitValue, VarType, name=f"trunc_{VarName}")
elif isinstance(InitValue.type, ir.PointerType) and isinstance(VarType, ir.PointerType):
Allowed: bool
Reason: str
Allowed, Reason = Gen._ClassifyPtrImplicitConvert(InitValue.type, VarType, Node.value)
if not Allowed:
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转换已禁止({Reason}):不能用指针类型 {InitValue.type} 初始化 "
f"{VarName}: {VarType};如需转换请显式 cast(如 t.CPtr(...))"
f"{Gen._DescribeValueNode(Node.value)}"
)
InitValue = Gen.builder.bitcast(InitValue, VarType, name=f"cast_{VarName}")
elif isinstance(VarType, ir.PointerType) and isinstance(InitValue.type, ir.IntType):
InitValue = Gen.builder.inttoptr(InitValue, VarType, name=f"int2ptr_{VarName}")
elif isinstance(VarType, ir.IntType) and isinstance(InitValue.type, ir.PointerType):
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)
# (None 已在上方短路为零值,不会落到这里)。
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:不能用指针类型 {InitValue.type} 初始化整数变量 "
f"{VarName}: {VarType};如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
f"{Gen._DescribeValueNode(Node.value)}"
)
# 大端局部变量:存储时 bswap
InitValue = Gen._apply_bswap_if_big(InitValue, TypeInfo.ByteOrder, f"bswap_store_{VarName}")
Gen._store(InitValue, var, Node=Node.value)
except (TypeError, ValueError) as _e:
# 仅回退 llvmlite 自身的类型/值错误;FailFastError 等一律上抛(不吞)
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
Gen.variables[VarName] = var
Gen._record_var_signedness(VarName, IsUnsigned)
# FakeDuck: 初始化影子结构体 a__meta__ 的字段
_meta_name_check: str = f"{VarName}__meta__"
if _meta_name_check in Gen.variables and '_str' in Gen.structs:
_meta_var_init: ir.Value = Gen.variables[_meta_name_check]
_str_st: ir.Type = Gen.structs['_str']
# __data__ 字段 = 字符串指针
_data_off = Gen._get_member_offset('__data__', '_str')
if _data_off is not None and InitValue:
_data_ptr = Gen.builder.gep(_meta_var_init, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _data_off)], name=f"{VarName}_meta_data")
Gen._store(InitValue, _data_ptr)
# __mbuddy__ 字段 = NULL
_mpool_off = Gen._get_member_offset('__mbuddy__', '_str')
if _mpool_off is not None:
_mpool_ptr = Gen.builder.gep(_meta_var_init, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _mpool_off)], name=f"{VarName}_meta_mbuddy")
Gen._store(ir.Constant(ir.PointerType(ir.IntType(8)), None), _mpool_ptr)
# FakeDuck: 从 with 上下文自动注入 requires 字段(如 __mbuddy__)
self.Trans.ExprCallHandle._InjectRequiresFields(_meta_var_init, '_str', Gen)
# 记录局部变量字节序
if TypeInfo.ByteOrder == 'big':
if isinstance(VarType, ir.IntType) and VarType.width in (16, 32, 64):
Gen.local_var_byteorders[VarName] = 'big'
elif isinstance(VarType, ir.ArrayType) and isinstance(VarType.element, ir.IntType) and VarType.element.width in (16, 32, 64):
Gen.local_var_byteorders[VarName] = 'big'
if isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr):
found_struct: str | None = FindStructNameInAnnotationWithContext(
Node.annotation, self.Trans, Gen)
# 若 FindStructNameInAnnotation 仅返回裸模板名(如 'ndarray'),但注解中
# 含泛型特化(如 numpy.ndarray[t.CDouble]),用 LLVM 类型名构造特化名
# (如 'ndarray[double]')。main.py 的 Gen.structs 可能只有 opaque 'ndarray',
# 但 numpy 模块已特化 ndarray[double],方法调用通过 sha1 前缀查找。
if found_struct and '[' not in found_struct:
for side in (Node.annotation.left, Node.annotation.right):
if isinstance(side, ast.Subscript):
base_node: ast.expr = side.value
base_nm: str | None = None
if isinstance(base_node, ast.Name):
base_nm = base_node.id
elif isinstance(base_node, ast.Attribute):
base_nm = base_node.attr
if base_nm and base_nm == found_struct:
slice_node: ast.AST = side.slice
if hasattr(ast, 'Index') and isinstance(slice_node, ast.Index):
slice_node = slice_node.value
result: tuple[str, str] | None = self.Trans.ExprCallHandle._resolve_generic_slice_type(slice_node)
if result:
found_struct = f'{base_nm}[{result[0]}]'
break
if found_struct:
Gen.var_struct_class[VarName] = found_struct
Gen.global_struct_class[VarName] = found_struct
if InitValue and isinstance(InitValue.type, ir.PointerType):
pointee: ir.Type = InitValue.type.pointee
if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
for CN, ST in Gen.structs.items():
if pointee == ST:
# rawptr 占位 struct 不参与覆盖(见 L706-711 说明)
if CN == 'rawptr' or CN.endswith('.rawptr') or CN.startswith('rawptr['):
break
Gen._var_to_heap_ptr[VarName] = InitValue
# 不覆盖已设置的特化名(含 '['):opaque 基础类型(如 ndarray)
# 会匹配到裸模板名,但注解已提供更精确的特化名(如 ndarray[double])
existing_cn: str | None = Gen.var_struct_class.get(VarName)
if not (existing_cn and '[' in existing_cn) and CN != existing_cn:
Gen.var_struct_class[VarName] = CN
Gen.global_struct_class[VarName] = CN
break
if self.Trans.VarScopes:
if TypeInfo:
self.Trans.VarScopes[-1][VarName] = TypeInfo
else:
DefaultInfo: CTypeInfo = CTypeInfo()
DefaultInfo.BaseType = t.CInt
self.Trans.VarScopes[-1][VarName] = DefaultInfo
elif isinstance(Node.target, ast.Attribute):
if isinstance(Node.target.value, ast.Name) and Node.target.value.id == 'self':
if Node.value:
AttrVarType: Any = None
try:
if IsListAnnotation(Node.annotation):
slice_node: ast.AST = Node.annotation.slice
if isinstance(slice_node, ast.Name):
AttrVarType = f't.CArray[{slice_node.id}]'
elif isinstance(slice_node, ast.Tuple):
elts: str = ', '.join(getattr(e, 'id', str(e)) for e in slice_node.elts)
AttrVarType = f't.CArray[{elts}]'
elif isinstance(slice_node, ast.Attribute):
# 还原 t.xxx 形式(如 t.CInt、t.CArray)
if isinstance(slice_node.value, ast.Name):
AttrVarType = f't.CArray[{slice_node.value.id}.{slice_node.attr}]'
else:
AttrVarType = f't.CArray[{ast.unparse(slice_node)}]'
if AttrVarType is None:
AttrTypeInfo: CTypeInfo | None = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable)
if AttrTypeInfo:
AttrVarType = Gen._ctype_to_llvm(AttrTypeInfo)
except Exception as _e:
if _config_mode == "strict":
raise
_vlog().warning(f"解析类型失败: {_e}", "Exception")
_RhsBlock: ir.Block | None = Gen.builder.block if Gen.builder is not None else None
_RhsInsnStart: int = len(_RhsBlock.instructions) if _RhsBlock is not None else -1
Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=AttrVarType)
if Value:
self._HandleAttributeStoreLlvm(Node.target, Value)
elif not self._RhsEndsWithVoidCall(Gen, _RhsBlock, _RhsInsnStart):
# 防静默(通用判据,不再只认 ast.Attribute):self 字段带注解
# 赋值右侧求值失败(None)必须报错,否则字段写被静默跳过。
self._RaiseUnresolvedExpr("self 字段带注解赋值", Node.value, Gen, Node.annotation, Node.target.attr)
if Value:
AttrName: str = Node.target.attr
len_name: str = f'{AttrName}__len'
ClassName: str | None = self._CurrentCpythonObjectClass
if not ClassName:
self_var: ir.Value | None = Gen.variables.get('self')
if self_var:
var_type: ir.Type = self_var.type
if isinstance(var_type, ir.PointerType) and isinstance(var_type.pointee, ir.PointerType):
var_type = var_type.pointee
if isinstance(var_type, ir.PointerType):
pointee: ir.Type = var_type.pointee
for cn, st in Gen.structs.items():
if st == pointee:
ClassName = cn
break
if ClassName and ClassName in Gen.class_members:
for m_name, m_type in Gen.class_members[ClassName]:
if m_name == len_name:
list_len: ir.Constant | None = None
if isinstance(Node.value, ast.List):
list_len = ir.Constant(ir.IntType(64), len(Node.value.elts))
if list_len is not None:
SelfVar: ir.Value | None = Gen._get_var_ptr('self')
if SelfVar:
SelfPtr: ir.Value = Gen._load(SelfVar, name="self")
offset: int = Gen._get_member_offset(len_name, ClassName)
LenPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=len_name)
Gen._store(list_len, LenPtr)
break