fix(sizeof,parse): 尺寸链去静默兜底,解析报错可定位,补 C 解析器缺口

StructGen: _get_struct_size/_get_align 去 opaque→8/ptr_size、未知→4 兜底并补 VectorType;LiteralStructType.fields→.elements(llvmlite 无 fields,必然 AttributeError)

HandlesClassDef: union 载荷尺寸经不存在的 ir.DataLayout 静默退化成 8 字节;HandlesExprCall/ExprBuiltin: sizeof 各静默 0 分支改 fail-fast,数组元素 sizeof 统一走 _get_struct_size

HandlesFor: nonlocal struct/array 堆分配不再退化成 malloc(8)

vast/vast_c: 补一元 * / & 解析,装饰器后 extern/export 分派修复,C 侧报错带 token 与行号;StubGen.Converter: 宏展开丢尾随空行修 .pyi 缩进非法

其余为 rawptr 迁移 / fail-fast 口径等历史改动一并入库
This commit is contained in:
TermiNexus committed 2026-09-26 19:27:22 +08:00
1 parent 53a345a59a
commit c83fc752c6
53 files changed
+6238 -887

No files matched your search

+84 -13
View File
@@ -9,7 +9,10 @@ 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
from lib.core.SymbolUtils import (
IsListAnnotation, ParseListAnnotation, FindStructNameInAnnotationWithContext,
AnnotationContainsName, IsCPtrNode, RequireDefineConstStorable,
)
def _IsGenericTpName(name: str) -> bool:
@@ -52,6 +55,26 @@ class AnnAssignHandle(BaseHandle):
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
@@ -210,7 +233,16 @@ class AnnAssignHandle(BaseHandle):
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):
Value = Gen.builder.ptrtoint(Value, TargetType, name="ptr_to_int")
if isinstance(Value, ir.Constant) and Value.constant is None:
# null 指针常量赋给整数字段 → 0(C 空指针常量语义)
Value = ir.Constant(TargetType, 0)
else:
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:不能把指针类型 {Value.type} 赋给整数类型 {TargetType};"
f"如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
Gen._store(Value, Ptr)
def _HandleGenericListLiteralAssign(self, VarName: str, Node: ast.AnnAssign, Gen: LlvmCodeGenerator, list_sub: ast.Subscript) -> None:
@@ -356,6 +388,9 @@ class AnnAssignHandle(BaseHandle):
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):
@@ -373,6 +408,10 @@ class AnnAssignHandle(BaseHandle):
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
@@ -387,6 +426,8 @@ class AnnAssignHandle(BaseHandle):
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
@@ -491,7 +532,13 @@ class AnnAssignHandle(BaseHandle):
and 'list' in self.Trans.ClassHandler._generic_class_templates):
self._HandleGenericListLiteralAssign(VarName, Node, Gen, list_sub)
return
if TypeInfo.IsVoid and isinstance(Node.annotation, ast.BinOp):
# 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:
@@ -513,6 +560,11 @@ class AnnAssignHandle(BaseHandle):
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
@@ -653,11 +705,13 @@ class AnnAssignHandle(BaseHandle):
elif isinstance(Node.value, ast.Attribute):
# 防静默:带值带注解属性链求值失败禁止跳过(见 __function.vp
# 两级链 blk = func.Blocks.Head 编译产物 store 丢失根因)。
self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen)
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)
# 闭包变量追踪:当赋值右侧是返回闭包的函数调用时,记录变量为闭包类型
@@ -665,10 +719,14 @@ class AnnAssignHandle(BaseHandle):
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 and isinstance(Node.value, ast.Attribute):
# 防静默:带值带注解的属性链求值失败(返回 None)必须报编译错误,
# 否则变量 alloca 已创建却无 store → 后续 load 未初始化栈变量。
self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen)
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:
@@ -844,11 +902,21 @@ class AnnAssignHandle(BaseHandle):
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):
InitValue = Gen.builder.ptrtoint(InitValue, VarType, name=f"ptr2int_{VarName}")
if isinstance(InitValue, ir.Constant) and InitValue.constant is None:
# null 指针常量初始化整数变量 → 0(C 空指针常量语义)
InitValue = ir.Constant(VarType, 0)
else:
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:不能用指针类型 {InitValue.type} 初始化整数变量 "
f"{VarName}: {VarType};如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
# 大端局部变量:存储时 bswap
InitValue = Gen._apply_bswap_if_big(InitValue, TypeInfo.ByteOrder, f"bswap_store_{VarName}")
Gen._store(InitValue, var)
except Exception as _e:
except (TypeError, ValueError) as _e:
# 仅回退 llvmlite 自身的类型/值错误;FailFastError 等一律上抛(不吞)
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
Gen.variables[VarName] = var
@@ -955,12 +1023,15 @@ class AnnAssignHandle(BaseHandle):
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 isinstance(Node.value, ast.Attribute):
# 防静默:self 字段带注解赋值右侧属性链求值失败必须报错
self._RaiseUnresolvedExpr("self 字段带注解赋值", Node.value, Gen)
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'
+298 -28
View File
@@ -31,8 +31,9 @@ class AssignHandle(BaseHandle):
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) -> ir.Value | None:
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType)
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
@@ -41,9 +42,8 @@ class AssignHandle(BaseHandle):
Target: ast.expr = Node.targets[0]
if isinstance(Target, ast.Name):
VarName: str = Target.id
if VarName in Gen.var_const_flags:
src_info: str = Gen._get_node_info()
_vlog().error(f"不能对 const 变量 '{VarName}' 赋值{src_info}")
if Gen._IsVarConst(VarName):
_vlog().error(Gen._ConstAssignMessage("赋值", VarName))
sys.exit(1)
if isinstance(Node.value, ast.Attribute) and isinstance(Node.value.value, ast.Name) and IsTModule(Node.value.value.id, self.Trans.SymbolTable):
AttrName: str = Node.value.attr
@@ -698,8 +698,9 @@ class AssignHandle(BaseHandle):
val: ir.Value | None = self.HandleExprLlvm(kw.value)
if val:
member_values[kw.arg] = val
has_vtable: bool = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes
base: int = 1 if has_vtable else 0
# 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
@@ -719,6 +720,15 @@ class AssignHandle(BaseHandle):
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:
@@ -1256,15 +1266,20 @@ class AssignHandle(BaseHandle):
def _HandleSubscriptStoreLlvm(self, Target: ast.Subscript, Value: ir.Value) -> 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 SubVarName in Gen.var_const_flags:
src_info: str = Gen._get_node_info()
self.Trans.LogError(f"不能对 const 变量 '{SubVarName}' 的元素赋值{src_info}")
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)
@@ -1306,7 +1321,7 @@ class AssignHandle(BaseHandle):
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="subscript")
ElemPtr: ir.Value = Gen.builder.gep(BaseAsPP, [IndexVal], name=f"subscript{_PVSuffix}")
Gen.builder.store(Value, ElemPtr)
return
if BaseHandle._is_char_pointer(IndexVal):
@@ -1324,7 +1339,7 @@ class AssignHandle(BaseHandle):
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="subscript")
ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
return
if isinstance(ValueVal.type, ir.PointerType):
@@ -1336,7 +1351,7 @@ class AssignHandle(BaseHandle):
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="subscript")
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)
@@ -1346,11 +1361,17 @@ class AssignHandle(BaseHandle):
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="subscript")
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="subscript")
ElemPtr: ir.Value = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
# fail-fast:rawptr[T](基座是指针存储)的元素为对象 T、而 RHS 是指向 T
# 的指针时,下游 _StoreWithCoerce/_coerce_value 会静默 load 整对象再按
# sizeof(T) 存储(写 sizeof(T) 而非 8 字节指针)。数组槽(t.CArray[T,N])
# 本就按 sizeof(T) 布局,整对象赋值合法 ⇒ 由 IsArrayBase 排除。
self._RejectWholeObjectCopyIntoPtrSlot(
Target, Value, ElemPtr, isinstance(pointee, ir.ArrayType))
self._StoreArrElemWithCoerceBswap(Value, ElemPtr, arr_byte_order)
elif isinstance(ValueVal.type, ir.ArrayType):
zero: ir.Constant = ir.Constant(ir.IntType(32), 0)
@@ -1364,7 +1385,7 @@ class AssignHandle(BaseHandle):
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="subscript")
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):
@@ -1375,7 +1396,7 @@ class AssignHandle(BaseHandle):
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="subscript")
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):
@@ -1386,12 +1407,12 @@ class AssignHandle(BaseHandle):
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="subscript")
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="subscript")
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):
@@ -1403,6 +1424,71 @@ class AssignHandle(BaseHandle):
if OuterPtr:
Gen._store(ModifiedArr, OuterPtr)
@staticmethod
def _StructTypeKey(ty: ir.Type) -> str:
"""结构体类型的稳定标识(identified 用 name,literal 用类型文本)"""
if isinstance(ty, ir.IdentifiedStructType):
return ty.name
return str(ty)
# ============================================================
# _RejectWholeObjectCopyIntoPtrSlot - 下标赋值的"整对象拷贝"fail-fast
#
# 目标形态(唯一判据):赋值左值是 rawptr[T] 下标(元素 = 对象/结构体 T,
# 基座为指针存储),而 RHS 的值类型是指向 T 的指针。此时 _StoreWithCoerce /
# _coerce_value 的"指针 → 非指针值: load 解引用"分支会生成
# %v = load %T, %T* %rhs
# store %T %v, %T* %elem (写 sizeof(T) 字节)
# 而该槽只该写 8 字节指针(槽是按 8 字节排布的缓冲/空闲链表)⇒ 每个元素
# 越界 sizeof(T)-8 字节,写穿相邻块(MemBuddy 空闲链表被写穿的静默降级根因)。
#
# 不拦(判据刻意收窄,避免误伤):
# · 数组槽 t.CArray[T,N](IsArrayBase=True):元素槽本就按 sizeof(T) 布局,
# `v[i] = T(...)`(构造函数返回临时对象地址)是合法的整对象复制;
# · 元素是标量(rawptr[int]):ElemPtr 元素不是结构体;
# · 元素本身是指针(rawptr[rawptr[T]] / X|t.CPtr):元素类型是 Ptr;
# · RHS 所指类型与元素类型不一致(非本形态)。
# 与 TPV AssignGenMixin._reject_whole_object_copy_into_ptr_slot 同口径。
# ============================================================
def _RejectWholeObjectCopyIntoPtrSlot(self, Target: ast.expr, Value: ir.Value,
ElemPtr: ir.Value, IsArrayBase: bool) -> None:
"""rawptr[T] 下标赋值 + RHS 为指向 T 的指针 ⇒ fail-fast(整对象拷贝)"""
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if IsArrayBase:
return
if ElemPtr is None or not isinstance(ElemPtr.type, ir.PointerType):
return
ElemTy: ir.Type = ElemPtr.type.pointee
if not isinstance(ElemTy, (ir.IdentifiedStructType, ir.LiteralStructType)):
return
if Value is None or not isinstance(Value.type, ir.PointerType):
return
RhsInner: ir.Type = Value.type.pointee
if not isinstance(RhsInner, (ir.IdentifiedStructType, ir.LiteralStructType)):
return
ElemKey: str = AssignHandle._StructTypeKey(ElemTy)
RhsKey: str = AssignHandle._StructTypeKey(RhsInner)
if ElemKey is None or RhsKey is None:
return
if ElemKey != RhsKey:
return
ElemSize: int = Gen._get_struct_size(ElemTy)
PtrSize: int = 8
Over: int = ElemSize - PtrSize
if Over < 0:
Over = 0
SrcInfo: str = Gen._get_node_info()
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"下标赋值的左值元素是对象 '{ElemKey}'({ElemSize} 字节),"
f"右值却是指向 '{RhsKey}' 的指针:编译器会静默生成整对象拷贝"
f"(load {ElemKey} + store {ElemKey},写 {ElemSize} 字节),"
f"而不是按指针写 {PtrSize} 字节。若该地址实际按 8 字节槽存放指针"
f"(如 rawptr[T] 缓冲/空闲链表),每写一个元素就越界 {Over} 字节,"
f"直接写穿相邻块。修法:把指针槽的基座类型写成指针元素形态"
f"(`rawptr[T] | t.CPtr` 或 `rawptr[rawptr[T]]`)后再赋值;"
f"或把基座显式转为 t.CPtr(无类型指针)后按 8 字节指针语义写入。{SrcInfo}")
def _StoreWithCoerce(self, Value: ir.Value, Ptr: ir.Value) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if isinstance(Ptr.type, ir.PointerType):
@@ -1456,12 +1542,21 @@ class AssignHandle(BaseHandle):
return None
def _EmitGlobalAnnAssignLlvm(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> None:
# 内建定长数组构造子 array 的注解校验(误用即 fail-fast,含文件:行:列):
# 模块级全局注解不经过 ResolveAnnotationType,故在此显式校验。
self._CheckFixedArrayKeyword(Node.annotation)
if isinstance(Node.target, ast.Name):
VarName: str = Node.target.id
Info: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(VarName)
if Info:
if Info.IsTypedef:
return
# 模块级全局 t.attr.* 属性(注解联合形态 `T | t.attr.section("...")`):
# 先解析(空/缺段名、非正整数字面量 aligned → fail-fast 终止),再在各
# 全局创建点用 _ApplyGlobalAttrs 施加。此前 TPC 全局路径完全没有
# section/align/weak 代码(函数侧 section 只来自装饰器,见
# HandlesFunctions.py:1361-1362 → :1767)。
GlobalAttrs: dict[str, Any] = self._ExtractGlobalAttrs(Node, Gen)
parse_result = ParseListAnnotation(Node.annotation)
if parse_result:
elem_type_node: ast.expr = parse_result.elem_type_node
@@ -1470,15 +1565,30 @@ class AssignHandle(BaseHandle):
check_func_ptr=True, void_fallback_width=32, fallback_to_cint=True,
)
if parse_result.is_pointer:
# 单参数 list[type] → 指针
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)
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
# 单参数 t.CArray[T]:与 t.CArray[T, None] 统一为"长度自动推导数组"。
# 有可推导初值(列表字面量 / 字符串字面量)时不再生成裸指针,而是落入
# 下方数组路径:由 ParseArrayCount(count_node=None) 从字面量推导长度、
# 字节内联到变量自身(与类成员 DeclarationGenerator.py:775-784、
# 局部 HandlesAnnAssign.py:446-489 同一口径;用户语义:单参数即
# "长度自动推导",不再是隐式指针)。
# 无可推导初值(纯声明 / 真正的指针用法)时保持既有指针语义逐字不变。
HasDerivableInit: bool = bool(
Node.value and (
isinstance(Node.value, ast.List)
or (isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str))
)
)
if not HasDerivableInit:
VarType: ir.Type = ir.PointerType(ElemType)
if VarName not in Gen.variables and VarName not in Gen.module.globals:
GlobalVar: ir.GlobalVariable = ir.GlobalVariable(Gen.module, VarType, name=VarName)
GlobalVar.initializer = ir.Constant(VarType, None)
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
Gen.variables[VarName] = GlobalVar
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
Gen.global_struct_class[VarName] = elem_type_node.id
return
# 有初值 → 落入下方数组路径(count_node=None → 从字面量推导长度)
count_node: ast.expr | None = parse_result.count_node
ArrayCount: int = self.ParseArrayCount(count_node, Gen, Node.value, mode='global')
VarType = ir.ArrayType(ElemType, ArrayCount)
@@ -1506,6 +1616,7 @@ class AssignHandle(BaseHandle):
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
@@ -1523,6 +1634,7 @@ class AssignHandle(BaseHandle):
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
@@ -1557,6 +1669,12 @@ class AssignHandle(BaseHandle):
VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo)
if isinstance(VarType, ir.VoidType):
VarType = ir.IntType(32)
# const / volatile 模块级全局登记(统一登记入口,持久层 global_*)。
# 局限(明确记录,见 Debugger/type-descriptors.md 已知差异):本分支只覆盖
# 标量/指针型全局;数组(t.CArray[...])与结构体型全局在更早的分支
# (上方 1568-1627)已 return,不在此登记 —— 全仓普查无这类带 const/
# volatile 注解的全局,故无可观测差异。
Gen.RegisterVarQualifiers(VarName, TypeInfo, Node.annotation, IsGlobal=True)
# 记录模块级注解类型:供函数内 global 变量的 += (HandlesAugAssign)参考,
# 避免用 RHS 字面量类型(i32)误建 i32 全局导致 i64/i32 类型不匹配。
if not hasattr(Gen, '_module_ann_types'):
@@ -1593,6 +1711,7 @@ class AssignHandle(BaseHandle):
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
@@ -1666,8 +1785,159 @@ class AssignHandle(BaseHandle):
GlobalVar.linkage = 'common'
else:
GlobalVar.initializer = Gen._zero_value_for_type(VarType)
# 属性最后施加:weak 需覆盖分支内可能设过的 'common'(对齐 TransPyV
# TranslatorGenMixin.vp:926-929 先清 Linkage 再设 weak 的语义)。
self._ApplyGlobalAttrs(GlobalVar, GlobalAttrs)
Gen.variables[VarName] = GlobalVar
# ======================================================================
# 模块级全局的 t.attr.* 属性(注解联合形态 `T | t.attr.section("...")`)
#
# 镜像 TransPyV(Viper 自举侧)已有实现:
# TranslatorGenMixin._scan_global_tattr / _extract_global_attr_section /
# _extract_global_attr_aligned / _extract_global_attr_weak(.vp:590-651)
# + ClassGenBase._tattr_name(.vp:341-358)+ _class_has_tattr 的
# `c.Attribute(t.attr.xxx)` 解包(.vp:359-394)。
# TPC(Python 侧)此前完全没有全局 section/align/weak 代码。
# ======================================================================
def _TAttrName(self, node: ast.AST | None) -> str | None:
"""从 t.attr.<name> / t.attr.<name>(args) 节点提取最外层属性名。
对应 TransPyV ClassGenBase._tattr_name:Call → 递归取 func;
Attribute → 取 attr。
"""
if isinstance(node, ast.Call):
return self._TAttrName(node.func)
if isinstance(node, ast.Attribute):
return node.attr
return None
def _ScanGlobalTAttr(self, node: ast.AST | None, want_name: str) -> ast.AST | None:
"""在全局变量注解中查找 t.attr.<want_name> 节点,命中返回该节点。
注解形态是 BinOp(BitOr) 左深链(`A | B | C` = BinOp(BinOp(A,B),C)),
递归左右子节点。命中节点形态:Call(t.attr.aligned(16))或
Attribute(t.attr.weak)。同时兼容包装写法
`T | c.Attribute(t.attr.section("..."))`(解包参考 TransPyV
ClassGenBase._class_has_tattr,.vp:375-389)。
"""
if node is None:
return None
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
hit_left: ast.AST | None = self._ScanGlobalTAttr(node.left, want_name)
if hit_left is not None:
return hit_left
return self._ScanGlobalTAttr(node.right, want_name)
# 包装写法:c.Attribute(t.attr.xxx / t.attr.xxx(...)) → 拆 args 再扫
# (与 HandlesFunctions.py:1354 装饰器侧同一判据:func.attr=='Attribute'
# 且 func.value 为 Name('c'))
if (isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute)
and node.func.attr == 'Attribute'
and isinstance(node.func.value, ast.Name) and node.func.value.id == 'c'):
for arg in node.args:
hit_arg: ast.AST | None = self._ScanGlobalTAttr(arg, want_name)
if hit_arg is not None:
return hit_arg
return None
if self._TAttrName(node) == want_name:
return node
return None
@staticmethod
def _TAttrArgStr(node: ast.AST | None) -> str | None:
"""取 t.attr.<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:
"""取 t.attr.aligned(N) 的整数字面量 N(缺参/非字面量返回 None)。
对应 TransPyV ClassGenBase._tattr_aligned_int(.vp:395-415)。
"""
if isinstance(node, ast.Call) and node.args:
arg0: ast.AST = node.args[0]
if (isinstance(arg0, ast.Constant) and isinstance(arg0.value, int)
and not isinstance(arg0.value, bool)):
return arg0.value
return None
def _RaiseGlobalAttrError(self, Msg: str, Node: ast.AST, Gen: "Translator.LlvmGen", VarName: str) -> None:
"""fail-fast:全局 t.attr.* 属性非法(缺/空段名、非正整数字面量 aligned)。
定位串(文件:行:列)与报错出口对齐 HandlesBase._RaiseUnresolvedExpr
(HandlesBase.py:552-615)——错误信息含源码位置后 sys.exit(1)。
"""
raw_lineno: int = getattr(Node, 'lineno', 0) or 0
lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0
col: int = getattr(Node, 'col_offset', -1)
src_file: str = getattr(Gen, '_current_source_file', '') or ''
# 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的行数
_line_offset: int = int(getattr(getattr(self, 'Trans', None), '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
loc: str = src_file
if lineno > 0:
loc += f":{lineno}"
if col >= 0:
loc += f":{col}"
_vlog().compile_error(
f"模块级变量 '{VarName}' 的 {Msg}(位置 {loc})", src_file, lineno, '')
sys.exit(1)
def _ExtractGlobalAttrs(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> dict[str, Any]:
"""从模块级全局注解提取 t.attr.section/aligned/weak。
返回 {'section': str|None, 'align': int, 'weak': bool}。
非法值(空/缺段名、非正整数 aligned)→ fail-fast 终止(对齐 TransPyV
TranslatorGenMixin._extract_global_attr_section/aligned 的致命错误语义)。
"""
Attrs: dict[str, Any] = {'section': None, 'align': 0, 'weak': False}
Ann: ast.AST = Node.annotation
VarName: str = Node.target.id if isinstance(Node.target, ast.Name) else '<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(
"t.attr.section(...) 段名缺失或为空(空段名会生成非法的 section \"\")",
sec_node, Gen, VarName)
Attrs['section'] = sec_val
align_node: ast.AST | None = self._ScanGlobalTAttr(Ann, 'aligned')
if align_node is not None:
align_val: int | None = self._TAttrAlignInt(align_node)
if align_val is None or align_val <= 0:
self._RaiseGlobalAttrError(
"t.attr.aligned(N) 需要正整数字面量 N", align_node, Gen, VarName)
Attrs['align'] = align_val
if self._ScanGlobalTAttr(Ann, 'weak') is not None:
Attrs['weak'] = True
return Attrs
@staticmethod
def _ApplyGlobalAttrs(GlobalVar: ir.GlobalVariable, Attrs: dict[str, Any]) -> None:
"""把提取出的 section/align/weak 施加到已创建的 LLVM 全局变量上。
llvmlite 口径(llvmlite/ir/values.py):
- section 写**裸**段名(序列化时自动加引号,:843-844);
- align 需 int(:846-847);GlobalVariable.align 默认 None(:807);
- weak 用 linkage='weak'(GlobalVariable 无 weak 属性,对照函数侧
HandlesFunctions.py:1772)。
"""
if not Attrs:
return
sec: str | None = Attrs.get('section')
if sec:
GlobalVar.section = sec
align_val: int = int(Attrs.get('align') or 0)
if align_val > 0:
GlobalVar.align = align_val
if Attrs.get('weak'):
GlobalVar.linkage = 'weak'
def _eval_global_const(self, value_node: ast.expr, var_type: ir.Type, Gen: "Translator.LlvmGen") -> ir.Constant | None:
if isinstance(value_node, ast.Constant):
if isinstance(value_node.value, bool):
+60 -10
View File
@@ -303,9 +303,14 @@ class AsyncFunctionHandle(BaseHandle):
if not any(isinstance(n, ast.Await) for n in ast.walk(stmt)):
continue # 无 await:委托 ForHandler,不需额外槽
if not self._IsRangeFor(stmt):
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(stmt, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError(
f"[coro-M3] 协程内 For 仅支持 range 迭代(含 await)"
f"(line {getattr(stmt, 'lineno', 0)})"
f"(line {_lineno})"
)
slots.append(f'__f_idx_{for_counter}')
slots.append(f'__f_len_{for_counter}')
@@ -551,9 +556,14 @@ class AsyncFunctionHandle(BaseHandle):
if isinstance(stmt, ast.Expr):
self.Trans.ExprHandler.HandleExprLlvm(stmt.value)
return
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(stmt, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError(
f"[coro-M2] async 函数体暂不支持语句: {type(stmt).__name__} "
f"(line {getattr(stmt, 'lineno', 0)})"
f"(line {_lineno})"
)
# ------------------------------------------------------------------
@@ -640,9 +650,14 @@ class AsyncFunctionHandle(BaseHandle):
return
async_name: str | None = self._GetAsyncCallName(call) if isinstance(call, ast.Call) else None
if not async_name:
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(await_node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError(
f"[coro-M3] await 目标必须是 async 函数调用或 coro_sleep(ms) "
f"(line {getattr(await_node, 'lineno', 0)})"
f"(line {_lineno})"
)
self.Trans.ExprHandler.HandleExprLlvm(await_node) # → _HandleAwaitLlvm
@@ -1002,8 +1017,13 @@ class AsyncFunctionHandle(BaseHandle):
def _HandleCoroIf(self, Node: ast.If, Gen: LlvmCodeGenerator) -> None:
if any(isinstance(n, ast.Await) for n in ast.walk(Node.test)):
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError(
f"[coro-M2] if 条件中不支持 await (line {getattr(Node, 'lineno', 0)})"
f"[coro-M2] if 条件中不支持 await (line {_lineno})"
)
Cond: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.test)
if not Cond:
@@ -1034,8 +1054,13 @@ class AsyncFunctionHandle(BaseHandle):
def _HandleCoroWhile(self, Node: ast.While, Gen: LlvmCodeGenerator) -> None:
if any(isinstance(n, ast.Await) for n in ast.walk(Node.test)):
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError(
f"[coro-M2] while 条件中不支持 await (line {getattr(Node, 'lineno', 0)})"
f"[coro-M2] while 条件中不支持 await (line {_lineno})"
)
CondBB: ir.Block = Gen.func.append_basic_block(name='coro_wcond')
BodyBB: ir.Block = Gen.func.append_basic_block(name='coro_wbody')
@@ -1076,12 +1101,22 @@ class AsyncFunctionHandle(BaseHandle):
def _HandleCoroTry(self, Node: ast.Try, Gen: LlvmCodeGenerator) -> None:
"""协程内 try/except:捕获 raise 与 await 传播异常(M6:多 except 类型匹配;Exception/裸 except 捕获所有)。"""
if not Node.handlers:
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError(
f"[coro-M6] 协程内 try 需要 except 子句 (line {getattr(Node, 'lineno', 0)})"
f"[coro-M6] 协程内 try 需要 except 子句 (line {_lineno})"
)
if Node.finalbody:
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno = int(getattr(Node, 'lineno', 0) or 0)
_line_offset = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError(
f"[coro-M6] 协程内暂不支持 try/finally (line {getattr(Node, 'lineno', 0)})"
f"[coro-M6] 协程内暂不支持 try/finally (line {_lineno})"
)
except_bb: ir.Block = Gen.func.append_basic_block('coro_try_except')
merge_bb: ir.Block = Gen.func.append_basic_block('coro_try_merge')
@@ -1159,8 +1194,13 @@ class AsyncFunctionHandle(BaseHandle):
"""协程内 try/finally(无 except):正常/异常路径都执行 fbody;
异常时 finally 后重新传播(done=1 + br exit);finally 内暂不支持 await。"""
if any(isinstance(n, ast.Await) for s in Node.finalbody for n in ast.walk(s)):
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError(
f"[coro-M7] finally 内暂不支持 await (line {getattr(Node, 'lineno', 0)})"
f"[coro-M7] finally 内暂不支持 await (line {_lineno})"
)
fin_bb: ir.Block = Gen.func.append_basic_block('coro_fin')
fin_exc_bb: ir.Block = Gen.func.append_basic_block('coro_fin_exc')
@@ -1201,15 +1241,25 @@ class AsyncFunctionHandle(BaseHandle):
def _HandleCoroFor(self, Node: ast.For, Gen: LlvmCodeGenerator) -> None:
if any(isinstance(n, ast.Await) for n in ast.walk(Node)):
if not self._IsRangeFor(Node):
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError(
f"[coro-M3] 协程内 For 仅支持 range 迭代(含 await)"
f"(line {getattr(Node, 'lineno', 0)})"
f"(line {_lineno})"
)
self._HandleCoroForRange(Node, Gen)
return
if any(isinstance(n, ast.Return) for st in Node.body for n in ast.walk(st)):
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError(
f"[coro-M2] For 循环体内暂不支持 return(M3 实现)(line {getattr(Node, 'lineno', 0)})"
f"[coro-M2] For 循环体内暂不支持 return(M3 实现)(line {_lineno})"
)
# 无 await/return:委托现有 ForHandler(迭代索引等 alloca 不跨挂起,安全)
self.Trans.ForHandler._HandleForLlvm(Node)
+8 -9
View File
@@ -65,9 +65,8 @@ class AugAssignHandle(BaseHandle):
src_info: str = Gen._get_node_info()
_vlog().error(f"变量 '{VarName}' 未声明,不能用于增量赋值(需用 global 声明或先声明局部变量){src_info}")
sys.exit(1)
if VarName in Gen.var_const_flags:
src_info: str = Gen._get_node_info()
_vlog().error(f"不能对 const 变量 '{VarName}' 增量赋值{src_info}")
if Gen._IsVarConst(VarName):
_vlog().error(Gen._ConstAssignMessage("增量赋值", VarName))
sys.exit(1)
ClassName: str | None = Gen.var_struct_class.get(VarName)
iop_name: str | None = self._AUGOP_MAP.get(type(Node.op))
@@ -118,7 +117,7 @@ class AugAssignHandle(BaseHandle):
GVar = Gen.module.globals[VarName]
Gen.variables[VarName] = GVar
OldVal: ir.Value = Gen._load(GVar, name=VarName)
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned)
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
Gen._store(NewVal, GVar)
return
if VarName in Gen._reg_values:
@@ -156,7 +155,7 @@ class AugAssignHandle(BaseHandle):
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left")
elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right")
NewVal = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned)
NewVal = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(NewVal.type, ir.IntType) and isinstance(orig_type, ir.IntType):
if NewVal.type.width > orig_type.width:
NewVal = Gen.builder.trunc(NewVal, orig_type, name="trunc_result")
@@ -179,7 +178,7 @@ class AugAssignHandle(BaseHandle):
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_aug")
Gen._store(NewVal, store_ptr)
return
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned)
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(store_ptr.type, ir.PointerType):
TargetType: ir.Type = store_ptr.type.pointee
if NewVal.type != TargetType:
@@ -191,7 +190,7 @@ class AugAssignHandle(BaseHandle):
var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName)
Gen.variables[VarName] = var
OldVal: ir.Value = Gen._load(var, name=VarName)
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned)
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
Gen._store(NewVal, var)
elif isinstance(Node.target, ast.Attribute):
AttrName: str = Node.target.attr
@@ -210,7 +209,7 @@ class AugAssignHandle(BaseHandle):
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_attr")
elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_attr")
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned)
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
self.Trans.AssignHandler._HandleAttributeStoreLlvm(Node.target, NewVal)
elif isinstance(Node.target, ast.Subscript):
SubTarget: ast.Subscript = Node.target
@@ -236,7 +235,7 @@ class AugAssignHandle(BaseHandle):
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_sub")
Gen._store(NewVal, ElemPtr)
return
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned)
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(NewVal.type, ir.IntType) and isinstance(ElemPtr.type.pointee, ir.IntType):
if NewVal.type.width > ElemPtr.type.pointee.width:
NewVal = Gen.builder.trunc(NewVal, ElemPtr.type.pointee, name="trunc_sub_result")
+122 -20
View File
@@ -13,7 +13,8 @@ from lib.constants import config as _config
from lib.includes import t
from lib.includes.t import CTypeRegistry
from lib.core.TypeSpec import TypeSpec, SymbolMeta, FIELD_ROUTES
from lib.core.SymbolUtils import IsPtrSubscriptNode, IsListAnnotation
from lib.core.SymbolUtils import (
IsPtrSubscriptNode, IsListAnnotation, FixedArrayKeywordAnnotationError)
from lib.core.VLogger import get_logger as _vlog
@@ -523,12 +524,13 @@ class BaseHandle:
pointee: _ir.Type = val.type.pointee
return isinstance(pointee, _ir.IntType) and pointee.width == 8
def HandleExprLlvm(self, Node: ast.AST, VarType: _ir.Type | str | None = None) -> _ir.Value | None:
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType)
def HandleExprLlvm(self, Node: ast.AST, VarType: _ir.Type | str | None = None,
ConstWidthHint: int | None = None) -> _ir.Value | None:
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint)
@staticmethod
def _ExprText(Node: ast.AST) -> str:
"""把属性链/名字节点渲染成源码文本(如 func.Blocks.Head),供错误信息使用"""
"""把表达式节点渲染成源码文本(如 func.Blocks.Head、list[X*] * uint64(v)),供错误信息使用"""
if isinstance(Node, ast.Attribute):
parts: list[str] = []
cur: ast.AST = Node
@@ -541,32 +543,97 @@ class BaseHandle:
return '.'.join(reversed(parts))
if isinstance(Node, ast.Name):
return Node.id
try:
rendered: str = ast.unparse(Node)
if rendered:
return rendered
except Exception:
pass
return getattr(Node, 'attr', '') or '<expr>'
def _RaiseUnresolvedExpr(self, What: str, Node: ast.AST, Gen: Any) -> None:
"""fail-fast:属性/成员访问表达式在编译期求值失败(返回 None)时终止。
def _FailFastFixedArrayKeyword(self, Node: ast.AST, Msg: str) -> None:
"""内建定长数组构造子 array 误用的 fail-fast 出口。
属性链(含两级自动解引用链,如 blk = func.Blocks.Head)若解析不出
目标类型,_handle_attr_nested 会返回 None;赋值调用方若静默跳过
store,目标 alloca 永不初始化 → 运行期读未初始化栈变量(随机崩溃)。
此处用 VLogger 输出 文件名:行:列 + 表达式源码后退出(严格模式语义)。
报错含:文件:行:列 + 源码片段 + 期望形态(array[E, N])+ 正确示例
(消息文本由 SymbolUtils.FixedArrayKeywordAnnotationError 给出)。
"""
import sys
Gen: Any = getattr(self.Trans, 'LlvmGen', None)
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 ''
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
code_line: str = ''
if lineno > 0 and Gen is not None and hasattr(Gen, '_get_source_line'):
try:
code_line = (Gen._get_source_line(lineno) or '').strip()
except Exception:
code_line = ''
loc: str = src_file
if lineno > 0:
loc += f":{lineno}"
if col >= 0:
loc += f":{col}"
full: str = f"{Msg}。位置 {loc}" + (f",源码: {code_line}" if code_line else '')
_vlog().compile_error(full, src_file, lineno, code_line)
sys.exit(1)
def _CheckFixedArrayKeyword(self, Annotation: ast.AST) -> None:
"""注解入口统一校验内建定长数组构造子 array(误用即 fail-fast)。
只校验带位置的注解入口(本类的 ResolveAnnotationType / CollectAnnAssignMember
+ HandlesAnnAssign 局部 / HandlesAssign 全局显式调用),**不**在通用表达式
解析路径上调用——以保证 array 在非类型位置仍是普通标识符(软关键字)。
"""
if Annotation is None:
return
Msg: str | None = FixedArrayKeywordAnnotationError(Annotation, self.Trans.SymbolTable)
if Msg:
self._FailFastFixedArrayKeyword(Annotation, Msg)
def _RaiseUnresolvedExpr(self, What: str, Node: ast.AST, Gen: Any, AnnNode: ast.AST | None = None,
VarName: str | None = None) -> None:
"""fail-fast:带值表达式在编译期求值失败(返回 None 且无值可存)时终止。
属性链(含两级自动解引用链,如 blk = func.Blocks.Head)解析不出目标
类型时 _handle_attr_nested 会返回 None;类型下标被当乘法的畸形写法
(如 list[X*]*(...))会让整条 RHS 不产任何指令而求值 None。赋值调用方
若静默跳过 store,目标 alloca 永不初始化 → 运行期读未初始化栈变量(随机
崩溃)。此处用 VLogger 输出 目标变量 + 注解文本 + 表达式源码与形态 +
文件名:行:列 后退出(严格模式语义)。
"""
import sys
ann_text: str = ''
if AnnNode is not None:
try:
ann_text = ast.unparse(AnnNode)
except Exception:
ann_text = ''
# RHS 形态 = AST 节点类名(Name/Subscript/BinOp/Call/Attribute/…),
# 便于报错可直接按形态分类,不必再从表达式文本反推。
rhs_kind: str = type(Node).__name__
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 ''
expr_text: str = self._ExprText(Node)
# 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行,
# 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator)。
_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
code_line: str = ''
if lineno > 0 and src_file:
# 行号基准修正(vasts AST 行号 vs 磁盘原文件行号偏差):
# 行号基准修正(vast AST 行号 vs 磁盘原文件行号偏差):
# Phase2Translator._inject_auto_imports 会在解析前向源码头部注入
# 1~3 行 "import _list/_dict/json",使 AST 节点 lineno 比磁盘原文件
# 真实行号大 N(实测 CallGenCBuiltin.vp 真实 1409 报 1410、
# FuncGenMixin.vp 真实 514 报 515)。而 _get_source_line 基于
# _current_source_lines(原文件),导致报错行号偏大且"代码行"
# 取错行。修正:以表达式文本为锚,从原始行号向上(至多 8 行)
# 回溯取"最近且包含该表达式文本"的源码行作为真实行号。
# FuncGenMixin.vp 真实 514 报 515)。上面已按 _line_offset 精确还原;
# 此处再以表达式文本为锚(从原始行号向上至多 8 行)回溯校正
# 属性链(其 expr_text 逐字出现在磁盘行中)可能残留的偏差。
if expr_text and '.' in expr_text and hasattr(Gen, '_get_source_line'):
_src_lines: Any = getattr(Gen, '_current_source_lines', None)
if isinstance(_src_lines, list) and _src_lines:
@@ -583,8 +650,11 @@ class BaseHandle:
loc += f":{lineno}"
if col >= 0:
loc += f":{col}"
msg: str = (f"{What}表达式 '{expr_text}' 编译期求值为空(属性访问未能解析出"
f"目标类型/成员),赋值将被跳过、变量保持未初始化。位置 {loc}"
msg: str = (f"{What}表达式 '{expr_text}'({rhs_kind} 形态)编译期求值为空("
f"表达式未解析出目标、无值可存;且其求值未以返回 void 的调用收尾),"
f"赋值将被跳过、变量保持未初始化"
f"{(',目标变量 ' + repr(VarName)) if VarName else ''}"
f"{(',注解 ' + ann_text) if ann_text else ''}。位置 {loc}"
f"{(',源码: ' + code_line) if code_line else ''}")
_vlog().compile_error(msg, src_file, lineno, code_line)
sys.exit(1)
@@ -767,6 +837,9 @@ class BaseHandle:
CTypeInfo.FromNode 把容器折叠成 i8*,字段类型与 declare/stub 端
的特化名指针不一致(同一结构体在定义端/声明端布局描述不同)。
"""
# 内建定长数组构造子 array 的注解校验(误用即 fail-fast):
# 本方法覆盖函数参数/返回注解与类字段(CollectAnnAssignMember 亦经此)。
self._CheckFixedArrayKeyword(annotation)
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
merger: Any = getattr(self.Trans, 'TypeMergeHandler', None)
if merger:
@@ -943,6 +1016,15 @@ class BaseHandle:
if isinstance(MemberType, _ir.VoidType):
MemberType = _ir.PointerType(_ir.IntType(8))
return VarName, MemberType, TypeInfo
except RecursionError:
# RecursionError 不是"可忽略异常":它是类型解析/声明发射链**成环**的
# 真缺陷信号。此前被下方 `except Exception` 吞成一行
# `WARN: HandlesBase: 忽略异常 maximum recursion depth exceeded` 并静默
# 退化为 i32 ⇒ 真正的环(如跨模块类声明发射重入
# _EmitExternalClassDeclLlvm ↔ _TryLoadStructFromStub ↔ _TryLoadClassMembersFromPyi)
# 被掩盖,下游只看到类型解析静默退化。此处 fail-fast 上抛(保留原始栈),
# 让环路根因暴露在错误栈顶,不再被 WARN 掩埋。
raise
except Exception as e:
_vlog().warning(f"HandlesBase: 忽略异常 {e}", exc_info=e)
return VarName, _ir.IntType(32), None
@@ -995,15 +1077,35 @@ class BaseHandle:
ret_type = inferred
else:
ret_type = Gen._CType2LLVM('i32', False)
param_type_infos = [pt for _, pt in (sym_info.FuncPtrParams or [])]
param_type_infos = list(sym_info.FuncPtrParams or [])
if isinstance(ret_type, _ir.VoidType):
ret_type = _ir.IntType(32)
llvm_param_types: list[Any] = []
for pt in param_type_infos:
for ParamIdx, (ParamName, pt) in enumerate(param_type_infos):
if isinstance(pt, CTypeInfo) and pt.BaseType:
lp: Any = pt.ToLLVM(Gen)
elif isinstance(pt, str):
lp = Gen._CType2LLVM(pt, '*' in pt)
else:
lp = Gen._CType2LLVM(pt if isinstance(pt, str) else 'i32', '*' in pt if isinstance(pt, str) else False)
# fast-fail:形参类型未解析出 BaseType 时不再硬编码 'i32' 造形参。
# 旧回退会把"类型解析失败"伪装成 llvmlite 的
# Type of #{N} arg mismatch: {expected} != {actual}
# (形参被静默写成 i32 ⇒ 报错方向与真因相反,且不含符号/位置信息,
# 如 "Type of #2 arg mismatch: i32 != %"<sha1>.SymbolTable"*"),
# 这里直接抛出带上下文的编译错误暴露真实缺失。
if isinstance(pt, CTypeInfo):
_PtDiag: str = (
f"CTypeInfo(BaseType=None, Name={pt.Name!r}, PtrCount={pt.PtrCount},"
f" IsTypedef={pt.IsTypedef}, IsFuncPtr={pt.IsFuncPtr}, IsState={pt.IsState},"
f" OriginalType={pt.OriginalType!r})")
else:
_PtDiag = f"{type(pt).__name__}({pt!r})"
raise TypeError(
f"[fast-fail] 函数 '{sym_info.Name}' 第 {ParamIdx + 1} 个形参 '{ParamName}' 的"
f"类型未解析出 BaseType,无法构建 LLVM 形参类型({_PtDiag});"
f"定义位置={getattr(sym_info, 'file', None)!r}:{sym_info.Lineno},"
f" IsVariadic={is_variadic}, mangled_name={mangled_name!r}"
)
if isinstance(lp, _ir.VoidType):
lp = _ir.IntType(8).as_pointer()
llvm_param_types.append(lp)
+9 -2
View File
@@ -62,6 +62,12 @@ class BodyHandle(BaseHandle):
self.Trans.DeleteHandler._HandleDeleteLlvm(Node)
except Exception as e:
lineno: int = getattr(Node, 'lineno', 0)
# 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行,
# 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator);
# 报文行号与源码行必须同用还原后的 lineno,二者一致(否则会显示下一行源码)。
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
source_line: str | None = Gen._get_source_line(lineno)
src_file: str = Gen._current_source_file or ''
src_path: str = src_file if src_file else 'unknown'
@@ -73,5 +79,6 @@ class BodyHandle(BaseHandle):
if Gen.builder and not Gen.builder.block.is_terminated:
Gen._EmitTempFrees()
def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None) -> ir.Value | None:
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType)
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)
+182 -20
View File
@@ -9,12 +9,22 @@ import llvmlite.ir as ir
from lib.core.Exportable import EnumMember as ExportEnumMember
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
from lib.core.SymbolUtils import (
IsTModule, FindStructNameInAnnotationWithContext, MangleTypeArgByRegistry)
IsTModule, FindStructNameInAnnotationWithContext, MangleTypeArgByRegistry,
EnumClassIntWidth,
EnumBodyMemberNames, EnumMemberCountMismatchError,
ClassHasFuncLevelCVTable, IsVtableSlotMethod)
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.includes.t import CTypeRegistry
# 枚举成员不允许的指针形态名:t.CPtr 家族(CPtr/CNeedPtr/CAutoPtr,后两者继承
# CPtr)、内建 rawptr(= t.CPtr)、str/bytes(= i8*)。枚举值是整数常量,
# 指针成员语义不成立,命中即明确报错(不静默当 int)。
_ENUM_PTR_ANNOTATION_NAMES: frozenset[str] = frozenset({
'CPtr', 'CNeedPtr', 'CAutoPtr', 'rawptr', 'str', 'bytes',
})
class ClassHandle(BaseHandle):
def _is_exception_class(self, Node: ast.ClassDef) -> bool:
@@ -438,11 +448,15 @@ class ClassHandle(BaseHandle):
return 'struct'
return None
def _resolve_class_flags(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> tuple[bool, bool, str | None, bool]:
def _resolve_class_flags(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> tuple[bool, bool, str | None, bool, bool]:
"""解析类的 Object/CVTable 标志和父类信息
Returns:
tuple: (IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable)
tuple: (IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable,
VtableByClassLevel)
VtableByClassLevel:**忽略方法级 CVTable 标记**算出的 vtable 状态(= 类级
CVTable / 继承链判定),供 vtable 槽筛选(SymbolUtils.IsVtableSlotMethod)
使用——与 TPV _should_method_be_virtual 第 3/4 条的判据一致。
"""
ClassName: str = Node.name
IsCpythonObject: bool = False
@@ -536,6 +550,15 @@ class ClassHandle(BaseHandle):
or ParentClassName in Gen.structs)
if parent_has_vtable or not parent_known:
IsCVTable = True
# 方法级 virtual(`virtual def m(...)` ⇒ 方法装饰器 @t.CVTable)触发类级 vtable:
# 与 TPV ClassGenBase._detect_vtable_status 的三条 `_has_any_func_cvtable`
# 分支逐条对齐(类级 NoVTable / 父类 NoVTable / 无真实父类)。
# 注意"父类已知且无 vtable" **不**触发(TPV 同分支返回 0)——
# 父类按无 vptr 布局而子类插 vptr 会让继承方法字段错位。
VtableByClassLevel: bool = IsCVTable
if (not VtableByClassLevel and ClassHasFuncLevelCVTable(Node)
and (IsNoVTable or ParentIsNoVTable or not HasParentClass)):
IsCVTable = True
if IsCVTable:
Gen.class_vtable.add(ClassName)
# 沿继承链向上传播,将所有祖先类加入 vtable
@@ -557,7 +580,7 @@ class ClassHandle(BaseHandle):
else:
# 类图中没有,尝试从 class_parent 查询
ancestor = Gen.class_parent.get(ancestor)
return IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable
return IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable, VtableByClassLevel
def _RegisterGenericClassTemplate(self, Node: ast.ClassDef) -> None:
"""注册泛型类模板到 `_generic_class_templates`(判据唯一:`_is_generic_class`)。
@@ -651,7 +674,8 @@ class ClassHandle(BaseHandle):
IsCVTable: bool
ParentClassName: str | None
IsNoVTable: bool
IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable = self._resolve_class_flags(Node, Gen)
VtableByClassLevel: bool
IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable, VtableByClassLevel = self._resolve_class_flags(Node, Gen)
# 跟踪正在发射的类,防止递归特化时 _generate_structs 过早为空成员类设置 body
Gen._classes_in_progress.add(ClassName)
# 标记正在收集字段,阻止继承处理(_ResolveGenericSlice → _specialize_generic_class)
@@ -685,7 +709,9 @@ class ClassHandle(BaseHandle):
_ck = (id(Gen), ClassName)
if _ck in _ih._struct_Load_cache:
del _ih._struct_Load_cache[_ck]
Gen.class_members[ClassName] = []
Gen.class_members[ClassName] = []
# 本地定义是权威重建点:先撤销"已展平"登记,重建完成(且父类齐全)后再登记。
Gen._members_flattened.discard(ClassName)
Gen.class_member_defaults[ClassName] = {}
Gen.class_member_signeds[ClassName] = {}
Gen.class_member_bitfields[ClassName] = {}
@@ -693,6 +719,9 @@ class ClassHandle(BaseHandle):
Gen.class_member_bitoffsets[ClassName] = {}
ParentClass: str | None = None
ParentClasses: list[str] = []
# 本类展平是否完整(父类若在此刻尚未就绪会被跳过 ⇒ 不登记已展平,
# 留待查询侧 HandlesImports.EnsureClassMembersFlattened 补齐,顺序无关)。
_local_flat_complete: bool = True
if Node.bases:
class_graph: dict = Gen._class_graph
for base in Node.bases:
@@ -784,6 +813,9 @@ class ClassHandle(BaseHandle):
_flat: list[tuple] = []
for ParentClass in ParentClasses:
if ParentClass not in class_graph and ParentClass not in Gen.class_members:
# 父类尚未就绪(跨模块父类在此刻未加载 / 特化表未生成)⇒
# 本类展平不完整,不登记 _members_flattened。
_local_flat_complete = False
continue
_FlattenInherited(ParentClass, _seen_cls, _flat, Gen)
if _flat:
@@ -871,6 +903,10 @@ class ClassHandle(BaseHandle):
if parsed:
VarName, MemberType, TypeInfo = parsed
Gen.class_members[ClassName].append((VarName, MemberType))
# const / volatile 类字段登记(统一登记入口,见 BaseGen)。
# 键 `类名.字段名` + 裸字段名;与 TransPyV 侧 ClassGenDef 的
# FieldEntry.IsConst/IsVolatile/PointeeVolatile 同口径。
Gen.RegisterFieldQualifiers(ClassName, VarName, TypeInfo, item.annotation)
if TypeInfo:
if isinstance(MemberType, ir.PointerType) and isinstance(MemberType.pointee, ir.IntType) and MemberType.pointee.width == 8:
if isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr):
@@ -1025,14 +1061,30 @@ class ClassHandle(BaseHandle):
# 构造函数(__new__/__init__)不放入 vtable,避免子类与基类
# vtable 大小不一致导致虚分派索引偏移。__before_init__ 由 TPC
# 自动生成,不会出现在 FunctionDef 中,此处一并防御。
#
# 方法级修饰符(`novirtual def m(...)` ⇒ @t.NoVTable /
# `virtual def m(...)` ⇒ @t.CVTable):是否占槽按
# SymbolUtils.IsVtableSlotMethod 判定——与 TPV
# ClassGenBase._should_method_be_virtual 同判据(**唯一规则源**),
# 故 TPC/TPV 的虚表槽位顺序与数量逐槽一致(含"类级 NoVTable +
# 方法级 CVTable ⇒ 仍生成虚表且只有该方法进槽"这一组合形态)。
# 仅在本类确有 vtable(IsCVTable)时筛选:无 vtable 的类不进
# Vtables,class_methods 仍保留全部方法供静态解析/继承包装使用。
if MethodName not in ('__new__', '__init__', '__before_init__'):
if FullMethodName not in Gen.class_methods[ClassName]:
_IsSlot: bool = (not IsCVTable) or IsVtableSlotMethod(item, VtableByClassLevel)
if _IsSlot and FullMethodName not in Gen.class_methods[ClassName]:
Gen.class_methods[ClassName].append(FullMethodName)
Gen.register_method(ClassName, FullMethodName)
Gen.register_method(ClassName, FullMethodName, _IsSlot)
else:
Gen.register_method(ClassName, FullMethodName)
if IsCVTable:
Gen.class_vtable.add(ClassName)
Gen._generate_structs()
Gen._create_Vtable_globals()
if _local_flat_complete:
# 登记"该表已按父类展平":供 _TryLoadClassMembersFromPyi 的 guard 与
# FuncGen._member_base 的查询前补齐使用(与跨模块路径同一口径)。
Gen._members_flattened.add(ClassName)
# _create_Vtable_globals 会跳过正在发射中的类(方法列表可能不完整),
# 当前类的方法已全部注册(L559-571),此处显式创建其 vtable
if IsCVTable:
@@ -1333,11 +1385,10 @@ class ClassHandle(BaseHandle):
if union_member_types:
max_size: int = 0
for member_type in union_member_types:
try:
size: int = member_type.get_abi_size(ir.DataLayout(Gen.module.data_layout))
except Exception as e: # 回退:获取类型大小失败时使用默认值 8
_vlog().warning(f"HandlesClassDef: 忽略异常 {e}", exc_info=e)
size = 8
# 统一走 Gen._get_struct_size(与 __sizeof__ / memhub alloc 同源)。
# llvmlite 并无 ir.DataLayout 类,原先那行必然抛 AttributeError,
# 再被 except 吞成 size=8 —— 静默少算,union 载荷区被开得过小。
size: int = Gen._get_struct_size(member_type)
max_size = max(max_size, size)
union_type: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, ClassName)
union_type.set_body(ir.ArrayType(ir.IntType(8), max_size))
@@ -1498,19 +1549,94 @@ class ClassHandle(BaseHandle):
# 更新预注册的 REnum 类型信息(填充变体列表)
PreTypeInfo.RenumVariants = variant_names
def _RegisterEnumMembers(self, Node: ast.ClassDef) -> None:
ClassName: str = Node.name
EnumTypeInfo: CTypeInfo = CTypeInfo()
EnumTypeInfo.Name = ClassName
EnumTypeInfo.BaseType = t.CEnum(ClassName)
EnumTypeInfo.IsEnum = True
def _EnumMemberAnnotationIsPtr(self, annotation: ast.AST | None) -> bool:
"""枚举成员注解是否含指针形态(逐层下标 + 联合类型逐边),True=含指针
与 TPV 侧 ClassGenEnum._enum_member_annotation_is_ptr 同口径。枚举值是
整数常量,指针成员语义不成立,必须明确报错(fail-fast)而非静默接受:
此前 TPC 完全忽略成员注解,`Name: t.CPtr` 会被当成普通 i32 成员放行。
指针标记既按名字匹配(含 str/bytes = i8*、rawptr = t.CPtr),也按
from t import CPtr as P 这类别名经 _t_type_symbols 还原后判定。
"""
if annotation is None:
return False
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
return (self._EnumMemberAnnotationIsPtr(annotation.left)
or self._EnumMemberAnnotationIsPtr(annotation.right))
if isinstance(annotation, ast.Subscript):
return self._EnumMemberAnnotationIsPtr(annotation.value)
name: str | None = None
if isinstance(annotation, ast.Attribute):
name = annotation.attr
elif isinstance(annotation, ast.Name):
name = annotation.id
if name is None:
return False
if name in _ENUM_PTR_ANNOTATION_NAMES:
return True
symtab: object = self.Trans.SymbolTable
resolved: type | None = None
if hasattr(symtab, '_t_type_symbols'):
resolved = symtab._t_type_symbols.get(name)
if resolved is None:
resolved = getattr(t, name, None)
if isinstance(resolved, type) and issubclass(resolved, t.CPtr):
return True
return False
def _EnumClassIntWidth(self, Node: ast.ClassDef) -> int:
"""按"最宽成员类型"推导枚举存储宽度(bit)。
规则本体唯一:SymbolUtils.EnumClassIntWidth(定义端 / 跨模块 `.vpi` 读取端 /
符号表提取端 / 声明端共用同一函数),此处只注入本端 SymbolTable
(覆盖库内自定义 typedef 与 builtins 表)。无任何可解析为整数的成员
→ 默认 32——既有枚举全部落在此支,与改前的恒 i32 逐字一致(零行为变化)。
"""
return EnumClassIntWidth(Node.body, self.Trans.SymbolTable)
def _EnumSiteLoc(self, Node: ast.AST) -> str:
"""枚举 fail-fast 站点:`文件:行[:列]`(行号经 `_line_offset` 还原)+ 源码片段。
口径与 `_FailFastFixedArrayKeyword` / `SymbolUtils._DefineSiteInfo` 一致。
"""
Gen: object = getattr(self.Trans, 'LlvmGen', None)
lineno: int = int(getattr(Node, 'lineno', 0) or 0)
Offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if Offset > 0 and lineno > Offset:
lineno -= Offset
col: int = int(getattr(Node, 'col_offset', -1))
Loc: str = getattr(Gen, '_current_source_file', '') or ''
Loc += f":{lineno}"
if col >= 0:
Loc += f":{col}"
Source: str = ''
if lineno > 0 and Gen is not None and hasattr(Gen, '_get_source_line'):
try:
Source = (Gen._get_source_line(lineno) or '').strip()
except Exception:
Source = ''
return Loc + (f",源码: {Source}" if Source else '')
def _RegisterEnumMembers(self, Node: ast.ClassDef) -> None:
ClassName: str = Node.name
# 枚举存储宽度 = 最宽成员类型(先推导再登记;成员注册顺序无关)。
# 登记到枚举条目与每个成员条目上,供常量发射点与类型解析点同点读取
# (两侧 ABI:常量宽度必须与枚举类型宽度一致)。
EnumWidth: int = self._EnumClassIntWidth(Node)
EnumTypeInfo: CTypeInfo = CTypeInfo()
EnumTypeInfo.Name = ClassName
EnumTypeInfo.BaseType = t.CEnum(ClassName)
EnumTypeInfo.IsEnum = True
EnumTypeInfo.set('enum_width', EnumWidth)
self.Trans.SymbolTable.insert(ClassName, EnumTypeInfo)
enum_export = self.Trans.Exportable.add_enum(
name=ClassName,
lineno=Node.lineno,
is_public=True
)
next_enum_value: int = 0
next_enum_value: int = 0
# 已登记成员名(用于"可见名数 vs 登记数"兜底判据,见 SymbolUtils.EnumBodyMemberNames)
RegisteredNames: list[str] = []
for item in Node.body:
if isinstance(item, ast.Assign):
for target in item.targets:
@@ -1541,7 +1667,9 @@ class ClassHandle(BaseHandle):
MemberNode.EnumName = ClassName
MemberNode.Lineno = item.lineno
MemberNode.IsEnumMember = True
MemberNode.set('enum_width', EnumWidth)
self.Trans.SymbolTable.insert(VarName, MemberNode)
RegisteredNames.append(VarName)
enum_export.members.append(ExportEnumMember(
name=VarName,
value=value,
@@ -1550,6 +1678,19 @@ class ClassHandle(BaseHandle):
elif isinstance(item, ast.AnnAssign):
if isinstance(item.target, ast.Name):
VarName = item.target.id
# [枚举规格 fail-fast] 成员不允许指针类型(t.CPtr 家族 /
# rawptr[...] / str / bytes / 联合里夹带指针)——此前成员注解
# 被整体忽略,指针成员被静默当 i32 放行。
if self._EnumMemberAnnotationIsPtr(item.annotation):
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(item, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise TypeError(
f"[fast-fail] 枚举 '{ClassName}' 成员 '{VarName}' 不能是指针类型"
f"(枚举值只支持整数成员类型),line {_lineno}"
)
value = None
if item.value:
if isinstance(item.value, ast.Constant):
@@ -1575,12 +1716,33 @@ class ClassHandle(BaseHandle):
MemberNode.EnumName = ClassName
MemberNode.Lineno = item.lineno
MemberNode.IsEnumMember = True
MemberNode.set('enum_width', EnumWidth)
self.Trans.SymbolTable.insert(VarName, MemberNode)
RegisteredNames.append(VarName)
enum_export.members.append(ExportEnumMember(
name=VarName,
value=value,
lineno=item.lineno
))
# [兜底判据 fail-fast] "源码里可见的成员名字数" vs "实际登记成员数"。
# 任何注册器不认的成员写法(逗号组带值 `A, B = 1`、逗号组带注解
# `A, B: T`、单名尾随逗号 `A,`,以及未来出现的新形态)都会让可见名数 >
# 登记数——此前是**静默丢成员**(ASTKind 71→3、OpKind 32→2;与
# Debugger/enum-semantics.md §15、rawptr-migration-and-fastfail.md §27
# 同族,这是第三次复发),此处一律 fail-fast,不再静默跳过。
# 判据口径与 TPV ClassGenEnum._translate_enum_def 完全一致(同一规则源)。
Visible: list[tuple[str, object]] = EnumBodyMemberNames(Node.body)
if len(Visible) != len(RegisteredNames):
Rest: list[str] = list(RegisteredNames)
BadNode: object = Node
for Nm, Nd in Visible:
if Nm in Rest:
Rest.remove(Nm)
elif BadNode is Node:
BadNode = Nd
raise TypeError(EnumMemberCountMismatchError(
ClassName, Visible, RegisteredNames, self._EnumSiteLoc(BadNode),
int(getattr(self.Trans, '_line_offset', 0) or 0)))
def _EmitNewFunctionLlvm(self, ClassName: str, Gen: LlvmGeneratorMixin, IsCVTable: bool = False, IsCpythonObject: bool = False) -> None:
NewFuncName: str = f'{ClassName}.__before_init__'
+88 -19
View File
@@ -9,6 +9,19 @@ import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle
from lib.constants.config import mode as _config_mode
from lib.includes.t import CTypeRegistry as _CR
from lib.core.SymbolUtils import RequireDefineUseWidthAt, RequireIntLiteralStorable, RequireNameDefined
def DefineUseWidthFromLLVMType(Ty: object) -> int | None:
"""LLVM 类型 → CDefine 常量的"使用处位宽"提示;其余 → None。
只认 i8/i16/i32/i64(与 SymbolUtils.DEFINE_WIDTH_LADDER 同集合):
i1(比较/逻辑结果)与指针不构成"整数使用处宽度",必须落回"取不到 ⇒
默认 32/64",否则 `C == 0` 之类会拿到位宽 1 而误报"使用处宽度不足"。
"""
if isinstance(Ty, ir.IntType) and Ty.width in (8, 16, 32, 64):
return int(Ty.width)
return None
class ExprHandle(BaseHandle):
@@ -45,14 +58,18 @@ class ExprHandle(BaseHandle):
def _get_llvm_member_offset(self, field_name: str, ClassName: str, Gen: LlvmCodeGenerator) -> int | None:
return self.Trans.ExprAttrHandle._get_llvm_member_offset(field_name, ClassName, Gen)
def HandleExprLlvm(self, Node: ast.AST, VarType: ir.Type | str | None = None) -> ir.Value | None:
def HandleExprLlvm(self, Node: ast.AST, VarType: ir.Type | str | None = None,
ConstWidthHint: int | None = None) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
if not Gen or not Gen.builder:
return None
if isinstance(Node, ast.Constant):
return self._HandleConstantLlvm(Node, VarType)
elif isinstance(Node, ast.Name):
return self._HandleNameLlvm(Node, VarType)
# ConstWidthHint 是**窄通道**:只有 Name(CDefine 常量物化)读取它,
# 其余分支(字面量/列表/运算…)逐字沿用既有 VarType 语义 ⇒ 非常量
# 路径的既有 IR 不变。
return self._HandleNameLlvm(Node, VarType, ConstWidthHint)
elif isinstance(Node, ast.BinOp):
return self.Trans.ExprOpsHandle._HandleBinOpLlvm(Node, VarType)
elif isinstance(Node, ast.BoolOp):
@@ -161,25 +178,30 @@ class ExprHandle(BaseHandle):
elif elem_type == 'double':
byte_offset = i * 8
ptr_as_int: Any = Gen.builder.ptrtoint(cast_ptr, ir.IntType(64))
new_ptr_val: Any = Gen.builder.add(ptr_as_int, ir.Constant(ir.IntType(64), byte_offset))
# 元素地址:以 i8* 基址按字节 GEP,再转成元素指针类型
# (不再 ptr→uint64 手动加偏移再 inttoptr,见新规范第 10 条)
elem_addr: Any = Gen.builder.gep(cast_ptr, [ir.Constant(ir.IntType(64), byte_offset)], name=f"elem_addr_{i}")
if elem_type == 'i32':
elem_ptr: Any = Gen.builder.inttoptr(new_ptr_val, ir.PointerType(ir.IntType(32)))
elem_ptr: Any = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.IntType(32)))
elif elem_type == 'i16':
elem_ptr = Gen.builder.inttoptr(new_ptr_val, ir.PointerType(ir.IntType(16)))
elem_ptr = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.IntType(16)))
elif elem_type == 'float':
elem_ptr = Gen.builder.inttoptr(new_ptr_val, ir.PointerType(ir.FloatType()))
elem_ptr = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.FloatType()))
elif elem_type == 'double':
elem_ptr = Gen.builder.inttoptr(new_ptr_val, ir.PointerType(ir.DoubleType()))
elem_ptr = Gen.builder.bitcast(elem_addr, ir.PointerType(ir.DoubleType()))
else:
elem_ptr = Gen.builder.inttoptr(new_ptr_val, ir.PointerType(ir.IntType(8)))
elem_ptr = elem_addr
if elem_type == 'i8':
if isinstance(elem_val.type, ir.IntType) and elem_val.type.width > 8:
elem_val = Gen.builder.trunc(elem_val, ir.IntType(8), name=f"trunc_elem_{i}")
elif isinstance(elem_val.type, ir.PointerType):
elem_val = Gen.builder.ptrtoint(elem_val, ir.IntType(64), name=f"ptrtoint_elem_{i}")
elem_val = Gen.builder.trunc(elem_val, ir.IntType(8), name=f"trunc_elem_{i}")
# 指针 → 整数:隐式转换一律禁止(元素值是指针、元素类型是整数)
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:数组字面量第 {i} 个元素是指针类型 {elem_val.type},"
f"但目标元素类型是 i8;如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
elif elem_type == 'i32':
if isinstance(elem_val.type, ir.IntType):
if elem_val.type.width < 32:
@@ -187,7 +209,12 @@ class ExprHandle(BaseHandle):
elif elem_val.type.width > 32:
elem_val = Gen.builder.trunc(elem_val, ir.IntType(32), name=f"trunc_elem_{i}")
elif isinstance(elem_val.type, ir.PointerType):
elem_val = Gen.builder.ptrtoint(elem_val, ir.IntType(32), name=f"ptrtoint_elem_{i}")
# 指针 → 整数:隐式转换一律禁止(ptrtoint 到 i32 还会截断地址)
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:数组字面量第 {i} 个元素是指针类型 {elem_val.type},"
f"但目标元素类型是 i32;如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
elif elem_type == 'i16':
if isinstance(elem_val.type, ir.IntType):
if elem_val.type.width < 16:
@@ -221,6 +248,10 @@ class ExprHandle(BaseHandle):
elif isinstance(Value, bool):
return ir.Constant(ir.IntType(32), 1 if Value else 0)
elif isinstance(Value, int):
# 整数字面量越界 fail-fast(字面量本身无符号,0..2^64-1):与 TPV 词法层
# `_lex_int_exceeds_u64` 同口径。负数侧由"一元减 + 字面量"的联合判定负责
# (HandlesExprOps._HandleUnaryOpLlvm),此处只判字面量本身。
RequireIntLiteralStorable(Gen, self.Trans, Node, Value)
if VarType is not None:
if isinstance(VarType, ir.IntType):
return ir.Constant(VarType, Value & ((1 << VarType.width) - 1))
@@ -327,7 +358,8 @@ class ExprHandle(BaseHandle):
return ptr
return ir.Constant(ir.IntType(32), 0)
def _HandleNameLlvm(self, Node: ast.Name, VarType: ir.Type | str | None = None) -> ir.Value:
def _HandleNameLlvm(self, Node: ast.Name, VarType: ir.Type | str | None = None,
ConstWidthHint: int | None = None) -> ir.Value:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
VarName: str = Node.id
# 治本修复:不把 module.globals 的 initializer 注入 _define_constants。
@@ -343,6 +375,9 @@ class ExprHandle(BaseHandle):
# 跳过 _define_constants 查询,直接走下方 variables/global_vars/module.globals 的 load。
if VarName in define_constants and not (hasattr(Gen, 'module') and VarName in Gen.module.globals):
val: Any = define_constants[VarName]
# CDefine 常量注册值(用于使用处宽度判据;下方 _gvar 覆盖是"同名全局变量"
# 的既有特例,不应把变量值当常量做宽度判据)
_cdefine_val: Any = val
# 治本修复:本模块全局变量初始化值优先,避免跨模块同名 CDefine 通过
# 共享 _define_constants 覆盖本模块常量
# (VpiReader.SHA1_LEN=16 被 Phase1.SHA1_LEN=17 覆盖导致双点符号 bug)
@@ -357,9 +392,28 @@ class ExprHandle(BaseHandle):
if isinstance(_init, ir.Constant) and isinstance(_init.constant, (int, float, str)):
val = _init.constant
if isinstance(val, int):
# Use VarType hint if available to avoid unnecessary type mismatch
if VarType is not None and isinstance(VarType, ir.IntType):
return ir.Constant(VarType, val & ((1 << VarType.width) - 1))
# 使用处宽度(唯一判据 SymbolUtils.RequireDefineUseWidth):
# 上下文(赋值目标 / 形参 / 返回注解 / 运算对象…)给出的位宽更宽 ⇒
# 就地按该宽度物化(立即数升宽无损);更窄 ⇒ fail-fast(替换此前的
# `val & mask` 静默截断)。
# 宽度来源优先级:**窄通道 ConstWidthHint**(仅常量物化读取,由调用
# 装配处按形参/返回类型下传)> 既有尽宽通道 VarType(赋值目标 /
# 二元运算对象)。两者都取不到 ⇒ 落到下方既有 32/64 二值口径。
_use_ty: ir.Type | None = None
# 报文的"使用处"标签按**通道**取真实站点种类(不再写笼统的
# "由调用方下传的类型提示"):窄通道 ConstWidthHint 只由
# 调用装配处(形参)与返回处下传;既有 VarType 通道承载的是
# 赋值目标 / 二元运算对象类型。
_use_ctx: str = '赋值目标 / 运算对象类型'
if ConstWidthHint in (8, 16, 32, 64):
_use_ty = ir.IntType(int(ConstWidthHint))
_use_ctx = '形参 / 返回注解'
elif VarType is not None and isinstance(VarType, ir.IntType):
_use_ty = VarType
if isinstance(_use_ty, ir.IntType):
RequireDefineUseWidthAt(Gen, self.Trans, Node, _cdefine_val,
int(_use_ty.width), VarName, _use_ctx)
return ir.Constant(_use_ty, val & ((1 << _use_ty.width) - 1))
if VarName in Gen.var_signedness and Gen.var_signedness[VarName]:
if val < -(1 << 31) or val > 0xFFFFFFFF:
return ir.Constant(ir.IntType(64), val & 0xFFFFFFFFFFFFFFFF)
@@ -384,8 +438,20 @@ class ExprHandle(BaseHandle):
if VarName in all_dc:
val: Any = all_dc[VarName]
if isinstance(val, int):
if VarType is not None and isinstance(VarType, ir.IntType):
return ir.Constant(VarType, val & ((1 << VarType.width) - 1))
# 跨模块 CDefine 常量同口径:窄通道 ConstWidthHint(形参/返回
# 类型下传)优先,其次既有 VarType;更宽 ⇒ 按该宽度物化,
# 更窄 ⇒ fail-fast(判据唯一源 SymbolUtils.RequireDefineUseWidth)
_use_ty2: ir.Type | None = None
_use_ctx2: str = '赋值目标 / 运算对象类型'
if ConstWidthHint in (8, 16, 32, 64):
_use_ty2 = ir.IntType(int(ConstWidthHint))
_use_ctx2 = '形参 / 返回注解'
elif VarType is not None and isinstance(VarType, ir.IntType):
_use_ty2 = VarType
if isinstance(_use_ty2, ir.IntType):
RequireDefineUseWidthAt(Gen, self.Trans, Node, val,
int(_use_ty2.width), VarName, _use_ctx2)
return ir.Constant(_use_ty2, val & ((1 << _use_ty2.width) - 1))
if val < -(1 << 31) or val > 0xFFFFFFFF:
return ir.Constant(ir.IntType(64), val & 0xFFFFFFFFFFFFFFFF)
return ir.Constant(ir.IntType(32), val & 0xFFFFFFFF)
@@ -460,7 +526,7 @@ class ExprHandle(BaseHandle):
SymInfo: Any = self.Trans.SymbolTable.lookup(VarName)
if SymInfo:
if SymInfo.IsEnumMember and isinstance(SymInfo.value, int):
return ir.Constant(ir.IntType(32), SymInfo.value)
return ir.Constant(Gen._enum_int_type(SymInfo.EnumName, SymInfo), SymInfo.value)
if SymInfo.IsFunction or SymInfo.IsFuncPtr:
MangledName: str = Gen._mangle_func_name(VarName)
if MangledName in Gen.module.globals:
@@ -487,4 +553,7 @@ class ExprHandle(BaseHandle):
)
ast.copy_location(virtual_attr, Node)
return self.Trans.ExprAttrHandle._HandleAttributeLlvm(virtual_attr)
# fail-fast: 未定义名称禁止静默物化为 i32 0(会把常量名写错 / define 丢失 /
# 变量漏声明这类上游缺陷藏到运行期,见 RequireNameDefined 的头注释)。
RequireNameDefined(Gen, self.Trans, Node, VarName)
return ir.Constant(ir.IntType(32), 0)
+93 -65
View File
@@ -201,6 +201,34 @@ class ExprAsmHandle(BaseHandle):
stripped.append('')
return '\n'.join([first_line] + stripped)
@staticmethod
def _intrinsic_marker(expr: ast.AST) -> str | None:
"""识别内建操作数标记调用,返回规范名或 None
支持两种等价写法(裸名写法省去 c. 前缀):
- 内联汇编:c.AsmInp / c.AsmOut ↔ asminp / asmout
- LLVM IR:c.LInp / c.LOut ↔ irinp / irout
"""
if not isinstance(expr, ast.Call):
return None
func: ast.AST = expr.func
if isinstance(func, ast.Name):
if func.id == 'asminp':
return 'AsmInp'
if func.id == 'asmout':
return 'AsmOut'
if func.id == 'irinp':
return 'LInp'
if func.id == 'irout':
return 'LOut'
return None
if isinstance(func, ast.Attribute):
if isinstance(func.value, ast.Name) and func.value.id == 'c':
if func.attr in ('AsmInp', 'AsmOut', 'LInp', 'LOut'):
return func.attr
return None
return None
def _process_joined_str_template(self, node: ast.AST, out_input_ops: list[Any], out_input_constraints: list[str],
out_output_ops: list[Any], out_output_constraints: list[str]) -> str:
if not isinstance(node, ast.JoinedStr):
@@ -214,39 +242,40 @@ class ExprAsmHandle(BaseHandle):
raw_parts.append(('text', value.value))
elif isinstance(value, ast.FormattedValue):
expr: ast.AST = value.value
if isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute):
if isinstance(expr.func.value, ast.Name) and expr.func.value.id == 'c':
if expr.func.attr == 'AsmInp':
if expr.args:
v: Any = self.HandleExprLlvm(expr.args[0])
if v:
c: str = 'r'
if len(expr.args) >= 2:
c = self._parse_asm_descr(expr.args[1])
if not c:
c = 'r'
out_input_ops.append(v)
out_input_constraints.append(c)
raw_parts.append(('input', len(out_input_ops) - 1))
elif expr.func.attr == 'AsmOut':
if expr.args:
target_ptr: Any = self._get_var_ptr_for_asm(expr.args[0])
if target_ptr:
v = target_ptr
else:
v = self.HandleExprLlvm(expr.args[0])
if v:
c: str = '=r'
if len(expr.args) >= 2:
c = self._parse_asm_descr(expr.args[1])
if not c:
c = '=r'
if not c.startswith('='):
c = '=' + c
out_output_ops.append(v)
out_output_constraints.append(c)
raw_parts.append(('output', len(out_output_ops) - 1))
else:
_marker: str | None = self._intrinsic_marker(expr)
if _marker == 'AsmInp':
if expr.args:
v: Any = self.HandleExprLlvm(expr.args[0])
if v:
c: str = 'r'
if len(expr.args) >= 2:
c = self._parse_asm_descr(expr.args[1])
if not c:
c = 'r'
out_input_ops.append(v)
out_input_constraints.append(c)
raw_parts.append(('input', len(out_input_ops) - 1))
elif _marker == 'AsmOut':
if expr.args:
target_ptr: Any = self._get_var_ptr_for_asm(expr.args[0])
if target_ptr:
v = target_ptr
else:
v = self.HandleExprLlvm(expr.args[0])
if v:
c: str = '=r'
if len(expr.args) >= 2:
c = self._parse_asm_descr(expr.args[1])
if not c:
c = '=r'
if not c.startswith('='):
c = '=' + c
out_output_ops.append(v)
out_output_constraints.append(c)
raw_parts.append(('output', len(out_output_ops) - 1))
elif not (isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute)):
# 非 c.* 属性调用的表达式按普通输入操作数处理(保持原行为:
# c.* 属性调用中非 AsmInp/AsmOut 的形态不收集)
fallback: Any = self.HandleExprLlvm(expr)
if fallback and isinstance(fallback.type, ir.IntType):
out_input_ops.append(fallback)
@@ -316,25 +345,24 @@ class ExprAsmHandle(BaseHandle):
if output_arg and isinstance(output_arg, (ast.List, ast.Tuple)):
for item in output_arg.elts:
if isinstance(item, ast.Call) and isinstance(item.func, ast.Attribute):
if item.func.value.id == 'c' and item.func.attr == 'AsmOut':
if len(item.args) >= 1:
target_ptr: Any = self._get_var_ptr_for_asm(item.args[0])
if target_ptr:
output_ops.append(target_ptr)
else:
value: Any = self.HandleExprLlvm(item.args[0])
if value:
output_ops.append(value)
constraint: str = '=r'
if len(item.args) >= 2:
base_constraint: str = self._parse_asm_descr(item.args[1])
if base_constraint:
if base_constraint.startswith('='):
constraint = base_constraint
else:
constraint = '=' + base_constraint
output_constraints.append(constraint)
if self._intrinsic_marker(item) == 'AsmOut':
if len(item.args) >= 1:
target_ptr: Any = self._get_var_ptr_for_asm(item.args[0])
if target_ptr:
output_ops.append(target_ptr)
else:
value: Any = self.HandleExprLlvm(item.args[0])
if value:
output_ops.append(value)
constraint: str = '=r'
if len(item.args) >= 2:
base_constraint: str = self._parse_asm_descr(item.args[1])
if base_constraint:
if base_constraint.startswith('='):
constraint = base_constraint
else:
constraint = '=' + base_constraint
output_constraints.append(constraint)
raw_template: str
if isinstance(asm_template_arg, ast.JoinedStr):
@@ -360,18 +388,17 @@ class ExprAsmHandle(BaseHandle):
if input_arg and isinstance(input_arg, (ast.List, ast.Tuple)):
for item in input_arg.elts:
if isinstance(item, ast.Call) and isinstance(item.func, ast.Attribute):
if item.func.value.id == 'c' and item.func.attr == 'AsmInp':
if len(item.args) >= 1:
value: Any = self.HandleExprLlvm(item.args[0])
if value:
input_ops.append(value)
constraint: str = 'r'
if len(item.args) >= 2:
constraint = self._parse_asm_descr(item.args[1])
if not constraint:
constraint = 'r'
input_constraints.append(constraint)
if self._intrinsic_marker(item) == 'AsmInp':
if len(item.args) >= 1:
value: Any = self.HandleExprLlvm(item.args[0])
if value:
input_ops.append(value)
constraint: str = 'r'
if len(item.args) >= 2:
constraint = self._parse_asm_descr(item.args[1])
if not constraint:
constraint = 'r'
input_constraints.append(constraint)
elif isinstance(item, (ast.List, ast.Tuple)) and len(item.elts) >= 2:
value_node: ast.AST = item.elts[0]
descr_node: ast.AST = item.elts[1]
@@ -565,7 +592,8 @@ class ExprAsmHandle(BaseHandle):
expr.value.attr == 'ASM_DESCR'):
AttrName: str = expr.attr
return getattr(t.ASM_DESCR, AttrName, "")
elif isinstance(expr.value, ast.Name) and IsTModule(expr.value.id, self.Trans.SymbolTable):
elif isinstance(expr.value, ast.Name) and (expr.value.id == 'ASM_DESCR' or IsTModule(expr.value.id, self.Trans.SymbolTable)):
# 裸名 ASM_DESCR.XXX:省去 t. 前缀的等价写法
AttrName: str = expr.attr
return getattr(t.ASM_DESCR, AttrName, "")
+63 -29
View File
@@ -199,6 +199,32 @@ class ExprAttrHandle(BaseHandle):
return self._make_define_constant(Gen, define_constants[key])
return None
def _NormalizeModuleGlobalValue(self, Gen: LlvmCodeGenerator, Val: Any, AttrName: str) -> Any:
"""模块限定全局读取归一化:类指针全局的「多一层」元素类型还原为对象指针
`Mod.g`(g 为模块级全局)的取值统一经 _lookup_module_global:注解为
`X | t.CPtr` 的模块级全局(如 GenApi._g_gen: rawptr[Generator.Generator]、
VLogger.logger: Logger | t.CPtr)在本编译器里物化成「比对象多一层」的
元素类型 T**(写入端见 main.vp `GenApi._g_gen = gen`:bitcast T*→T** 后
直接存值,未取地址)⇒ 其位形就是对象指针 T*,静态类型多了一层。
裸名形态(`g.Field`)由 _handle_attr_name 的既有同款补偿 bitcast 处理;
模块限定形态(`Mod.g.Field`)此前无补偿 ⇒ 载入值停在 T**,其 pointee 是
T*(指针而非结构体)⇒ _handle_attr_nested 的 char* 分支与 struct 分支都
不命中而返回 None ⇒ 带注解赋值 RHS 求值为空 fail-fast
(App/lib/core/Repl.vp:763 `GenApi._g_gen.TypeGenCDef.CDefineNames`)、
调用接收者 None 而静默丢调用(VpiReader.vp:4066
`GenApi._g_gen.ClassGenBase._collect_parents(...)`)。此处做同款逆运算:
只改静态类型、不改位形(bitcast 不产指令)。
判据限定「pointee 仍是指向结构体的指针」⇒ `str | t.CPtr` 一类真双层指针
全局(@_argv / @_g_pyi_cdef_names = i8**,实参直传 i8** 形参)不受影响。
"""
if not isinstance(Val, ir.Value) or not isinstance(Val.type, ir.PointerType):
return Val
Inner: Any = Val.type.pointee
if isinstance(Inner, ir.PointerType) and isinstance(Inner.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
return Gen.builder.bitcast(Val, Inner, name=f"cast_{AttrName}_global")
return Val
def _lookup_module_global(self, Gen: LlvmCodeGenerator, VarName: str, AttrName: str) -> ir.Value | None:
"""在模块全局变量中查找 import 的属性"""
imported_modules: Any = getattr(self.Trans, '_ImportedModules', None)
@@ -220,7 +246,7 @@ class ExprAttrHandle(BaseHandle):
return GVar
if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
return GVar
return Gen._load(GVar, name=AttrName)
return self._NormalizeModuleGlobalValue(Gen, Gen._load(GVar, name=AttrName), AttrName)
# SHA1 前缀查找
ModuleSha1Map: Any = getattr(Gen, 'ModuleSha1Map', {})
sha1_candidates: list[str] = [VarName, resolved_mod]
@@ -238,7 +264,7 @@ class ExprAttrHandle(BaseHandle):
return GVar2
if isinstance(GVar2.type, ir.PointerType) and isinstance(GVar2.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
return GVar2
return Gen._load(GVar2, name=AttrName)
return self._NormalizeModuleGlobalValue(Gen, Gen._load(GVar2, name=AttrName), AttrName)
if AttrName in Gen.module.globals:
gv: Any = Gen.module.globals[AttrName]
if AttrName in Gen.variables and Gen.variables[AttrName] is None:
@@ -246,7 +272,7 @@ class ExprAttrHandle(BaseHandle):
if str_gv_name in Gen.module.globals:
str_gv: Any = Gen.module.globals[str_gv_name]
return Gen.builder.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{AttrName}_str_ptr")
return Gen.builder.load(gv, name=AttrName)
return self._NormalizeModuleGlobalValue(Gen, Gen.builder.load(gv, name=AttrName), AttrName)
return None
def _resolve_var_classname(self, Gen: LlvmCodeGenerator, VarName: str) -> str | None:
@@ -373,6 +399,7 @@ class ExprAttrHandle(BaseHandle):
def _handle_attr_enum(self, Node: ast.Attribute, VarName: str) -> ir.Value | None:
"""处理枚举成员访问 (VarName 是枚举类型名)"""
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
SymInfo: Any = self.Trans.SymbolTable.lookup(VarName)
if not SymInfo or not SymInfo.IsEnum:
return None
@@ -387,10 +414,10 @@ class ExprAttrHandle(BaseHandle):
for key, info in self.Trans.SymbolTable.items():
if info.IsEnumMember and info.EnumName == VarName and key == Node.attr:
if isinstance(info.value, int):
return ir.Constant(ir.IntType(32), info.value)
return ir.Constant(Gen._enum_int_type(VarName, info), info.value)
return None
if isinstance(MemberInfo.value, int):
return ir.Constant(ir.IntType(32), MemberInfo.value)
return ir.Constant(Gen._enum_int_type(VarName, MemberInfo), MemberInfo.value)
return None
def _handle_attr_name(self, Node: ast.Attribute, VarName: str) -> ir.Value | None:
@@ -520,12 +547,12 @@ class ExprAttrHandle(BaseHandle):
for qname in (f"{ClassName}.{AttrName}", f"{ClassName}_{AttrName}"):
info: Any = self.Trans.SymbolTable.lookup(qname)
if info and info.IsEnumMember and isinstance(info.value, int):
return ir.Constant(ir.IntType(32), info.value)
return ir.Constant(Gen._enum_int_type(ClassName, info), info.value)
for key, info in self.Trans.SymbolTable.items():
if key == AttrName:
if info.IsEnumMember and info.EnumName == ClassName:
if isinstance(info.value, int):
return ir.Constant(ir.IntType(32), info.value)
return ir.Constant(Gen._enum_int_type(ClassName, info), info.value)
break
# CUnion 处理
@@ -1105,6 +1132,11 @@ class ExprAttrHandle(BaseHandle):
def _HandleSubscriptLlvm(self, Node: ast.Subscript) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
# pointee-volatile(rawptr[t.CVolatile | T])基址:下标 GEP 名带基址名,
# 顶层 _load/_store 经 BaseGen._IsPtrVolatile 回查登记名,使其 load/store
# 带 LLVM volatile。基址名取不到时后缀为空 ⇒ GEP 名仍是 "subscript",
# 既有代码 IR 逐字节不变。
_PVSuffix: str = Gen._SubscriptSuffix(Node.value)
# 查询数组元素字节序
arr_byte_order: str = ''
if isinstance(Node.value, ast.Name) and Node.value.id in Gen.local_var_byteorders:
@@ -1178,7 +1210,7 @@ class ExprAttrHandle(BaseHandle):
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")
ElemPtr: Any = Gen.builder.gep(ValueVal, [IndexVal], name="subscript")
ElemPtr: Any = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
if isinstance(ValueVal.type.pointee.pointee, ir.IntType) and ValueVal.type.pointee.pointee.width == 8:
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
@@ -1191,7 +1223,7 @@ class ExprAttrHandle(BaseHandle):
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 = Gen.builder.gep(ValueVal, [zero, IndexVal], name="subscript")
ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}")
elem_type: Any = pointee.element
if isinstance(elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
return ElemPtr
@@ -1210,14 +1242,14 @@ class ExprAttrHandle(BaseHandle):
CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str")
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index")
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name="subscript")
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}")
return Gen._load(ElemPtr, name="str_ptr_load")
elem_class: str | None = self._infer_element_struct_class(Node.value, Gen)
if elem_class and elem_class in Gen.structs:
CastedPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(Gen.structs[elem_class]), name=f"cast_{elem_class}_arr")
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_arr_index")
ElemPtr = Gen.builder.gep(CastedPtr, [IndexVal], name="subscript")
ElemPtr = Gen.builder.gep(CastedPtr, [IndexVal], name=f"subscript{_PVSuffix}")
if isinstance(pointee, ir.IntType):
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
return ElemPtr
@@ -1230,7 +1262,7 @@ class ExprAttrHandle(BaseHandle):
CastedPtrPtr = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str")
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index")
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name="subscript")
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}")
return Gen._load(ElemPtr, name="str_ptr_load")
arr_type: Any = self._infer_array_member_type(Node.value, Gen)
if arr_type and isinstance(arr_type, ir.ArrayType):
@@ -1241,12 +1273,12 @@ class ExprAttrHandle(BaseHandle):
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 = Gen.builder.gep(arr_ptr, [zero, IndexVal], name="subscript")
ElemPtr = Gen.builder.gep(arr_ptr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
arr_elem_type: Any = arr_type.element
if isinstance(arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
return ElemPtr
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript")
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
return ElemPtr
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
@@ -1262,7 +1294,7 @@ class ExprAttrHandle(BaseHandle):
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 = Gen.builder.gep(VarPtr, [zero, IndexVal], name="subscript")
ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
var_arr_elem_type: Any = VarPtr.type.pointee.element
if isinstance(var_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
return ElemPtr
@@ -1275,7 +1307,7 @@ class ExprAttrHandle(BaseHandle):
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 = Gen.builder.gep(AttrPtr, [zero, IndexVal], name="subscript")
ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
attr_arr_elem_type: Any = AttrPtr.type.pointee.element
if isinstance(attr_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
return ElemPtr
@@ -1290,14 +1322,14 @@ class ExprAttrHandle(BaseHandle):
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 = Gen.builder.gep(InnerPtr, [zero, IndexVal], name="subscript")
ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
inner_elem_type: Any = inner_pointee.element
if isinstance(inner_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
return ElemPtr
return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order)
arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
Gen._store(ValueVal, arr_alloc)
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name="subscript")
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}")
alloc_arr_elem_type: Any = ValueVal.type.element
if isinstance(alloc_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)):
return ElemPtr
@@ -1306,6 +1338,8 @@ class ExprAttrHandle(BaseHandle):
def HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
# 同 _HandleSubscriptLlvm:pointee-volatile 基址名后缀(空后缀 ⇒ 名不变)
_PVSuffix: str = Gen._SubscriptSuffix(Node.value)
ValueVal: Any = self.HandleExprLlvm(Node.value)
if not ValueVal:
return None
@@ -1324,12 +1358,12 @@ class ExprAttrHandle(BaseHandle):
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: Any = Gen.builder.gep(ValueVal, [zero, IndexVal], name="subscript_ptr")
ElemPtr: Any = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr
elif isinstance(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_ptr")
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript_ptr")
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr
else:
# 指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr, bytes | t.CPtr)
@@ -1341,11 +1375,11 @@ class ExprAttrHandle(BaseHandle):
CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_ptr")
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_ptr")
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name="subscript_ptr")
ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index")
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript_ptr")
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr
elif isinstance(ValueVal.type, ir.IntType):
var_ptr: Any = self._get_int_ptr(Node.value)
@@ -1367,7 +1401,7 @@ class ExprAttrHandle(BaseHandle):
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 = Gen.builder.gep(VarPtr, [zero, IndexVal], name="subscript_ptr")
ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr
if isinstance(Node.value, ast.Subscript):
InnerPtr: Any = self.HandleSubscriptPtrLlvm(Node.value)
@@ -1377,7 +1411,7 @@ class ExprAttrHandle(BaseHandle):
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 = Gen.builder.gep(InnerPtr, [zero, IndexVal], name="subscript_ptr")
ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr
if isinstance(Node.value, ast.Attribute):
AttrPtr: Any = self._get_attr_ptr(Node.value)
@@ -1387,11 +1421,11 @@ class ExprAttrHandle(BaseHandle):
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 = Gen.builder.gep(AttrPtr, [zero, IndexVal], name="subscript_ptr")
ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr
arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
Gen._store(ValueVal, arr_alloc)
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name="subscript_ptr")
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}")
return ElemPtr
return None
@@ -1705,17 +1739,17 @@ class ExprAttrHandle(BaseHandle):
for qname in (f"{enum_key}.{member_name}", f"{enum_key}_{member_name}"):
info: Any = self.Trans.SymbolTable.lookup(qname)
if info and info.IsEnumMember and isinstance(info.value, int):
return ir.Constant(ir.IntType(32), info.value)
return ir.Constant(Gen._enum_int_type(enum_key, info), info.value)
for key, info in self.Trans.SymbolTable.items():
if info.IsEnumMember and info.EnumName == enum_key and key == member_name:
if isinstance(info.value, int):
return ir.Constant(ir.IntType(32), info.value)
return ir.Constant(Gen._enum_int_type(enum_key, info), info.value)
for key, info in self.Trans.SymbolTable.items():
if info.IsEnumMember and key == member_name:
if isinstance(info.value, int):
for enum_key in enum_keys:
if info.EnumName == enum_key:
return ir.Constant(ir.IntType(32), info.value)
return ir.Constant(Gen._enum_int_type(enum_key, info), info.value)
return None
def _lookup_member_elem_class(self, Gen: LlvmCodeGenerator, OwnerClass: str | None, FieldName: str) -> str | None:
+13 -6
View File
@@ -727,7 +727,12 @@ class ExprBuiltinHandle(BaseHandle):
elif isinstance(Node, ast.Attribute):
type_name = Node.attr
else:
return ir.Constant(ir.IntType(64), 0)
# 非 名称/属性 的 sizeof 目标(如 sizeof(a + b)、sizeof(t.CArray[int,4])):
# 曾静默返回 0 → 下游 alloc(0) 返回 NULL。fail-fast。
raise ValueError(
f"[FATAL][__sizeof__] sizeof 的目标 {type(Node).__name__}"
f"({ast.unparse(Node)})不是类型名(Name/Attribute),拒绝静默返回 0"
)
if type_name not in Gen.structs:
# str/bytes 是指针类型 (char*/void*),sizeof = 指针大小
@@ -758,7 +763,11 @@ class ExprBuiltinHandle(BaseHandle):
if _config_mode == "strict":
raise
_vlog().warning(f"解析类型失败: {_e}", "Exception")
return ir.Constant(ir.IntType(64), 0)
# 所有查找策略均失败:曾静默返回 0 → 下游 elem_size=0 / alloc(0)。fail-fast。
raise ValueError(
f"[FATAL][__sizeof__] 类型 '{type_name}' 无法解析"
f"(不在 Gen.structs、CTypeRegistry 也找不到),拒绝静默返回 0"
)
struct_type = Gen.structs[type_name]
if isinstance(struct_type, ir.PointerType):
@@ -768,10 +777,8 @@ class ExprBuiltinHandle(BaseHandle):
struct_type = Gen.structs.get(type_name, struct_type)
# 递归解析所有嵌套的 opaque struct,确保 sizeof 计算正确
self._resolve_nested_opaque_structs(struct_type, Gen)
size = Gen._get_struct_size(struct_type)
if size > 0:
return ir.Constant(ir.IntType(64), size)
return ir.Constant(ir.IntType(64), 0)
# _get_struct_size 已在尺寸不可得时 fail-fast(不返回 0),此处直接采用。
return ir.Constant(ir.IntType(64), Gen._get_struct_size(struct_type))
def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
+495 -162
View File
@@ -14,7 +14,9 @@ from lib.includes import t
from lib.includes.t import CTypeRegistry
from lib.core.SymbolUtils import (
IsTModule, ExtractTypeNameFromBinOp, IsPtrSubscriptNode, DesugarPtrSubscriptToUnion,
ResolveTypeArgStr, GenericArgRawName)
ResolveTypeArgStr, GenericArgRawName,
ClassHasFuncLevelCVTable, IsVtableSlotMethod)
from lib.core.Handles.HandlesExpr import DefineUseWidthFromLLVMType
def _safe_ptr_type(ty: ir.Type) -> ir.Type:
@@ -236,6 +238,71 @@ class ExprCallHandle(BaseHandle):
except (AssertionError, TypeError):
CallArgs.append(ir.Constant(ir.IntType(32), 0))
def _LookupGlobalDefaults(self, func_name: str | None) -> list | None:
"""按函数名查询全局默认参数表(跨模块调用点用)。
查找顺序:精确键 → 去 mangled 前缀后的裸名 → 以 '.<裸名>' 结尾的键
(如 {sha1}.name)。精确键优先,避免"同名不同模块"的默认值互相乱配。
"""
if not func_name:
return None
table: dict = getattr(self.Trans, '_global_function_default_args', None) or {}
if not table:
return None
hit = table.get(func_name)
if hit:
return hit
base: str = func_name.split('.')[-1]
if base != func_name:
hit = table.get(base)
if hit:
return hit
for key, value in table.items():
if key.endswith('.' + base):
return value
return None
def _LookupGlobalParamNames(self, func_name: str | None) -> list | None:
"""按函数名查询全局参数名表;查找顺序同 _LookupGlobalDefaults。"""
if not func_name:
return None
table: dict = getattr(self.Trans, '_global_function_param_names', None) or {}
if not table:
return None
hit = table.get(func_name)
if hit:
return hit
base: str = func_name.split('.')[-1]
if base != func_name:
hit = table.get(base)
if hit:
return hit
for key, value in table.items():
if key.endswith('.' + base):
return value
return None
@staticmethod
def _ConstantToIrValue(raw: object, param_type: ir.Type, Gen: LlvmGeneratorMixin) -> ir.Value | None:
"""把全局默认参数表里的 Python 常量转成形参类型的 IR 常量。
只处理整数/布尔(跨模块默认值里最常见,如 cap: t.CSizeT = 4096)。字符串等
常量需要在调用点引用定义模块的全局对象,无法安全重建 → 返回 None,调用点
退回原有零值填充(与修复前行为一致)。
"""
if isinstance(param_type, ir.IntType):
if isinstance(raw, bool):
raw = 1 if raw else 0
if isinstance(raw, int):
max_val: int = (1 << param_type.width) - 1
if raw > max_val or raw < 0:
raw = raw & max_val
return ir.Constant(param_type, raw)
return ir.Constant(param_type, 0)
if isinstance(param_type, ir.PointerType):
return ir.Constant(param_type, None)
return None
def _ApplyKeywordArgs(self, Node: ast.Call, CallArgs: list[ir.Value], func: ir.Function | None, func_name: str | None, self_offset: int = 1) -> None:
"""在 _fill_default_args 之后调用:用 keyword args 按 param_names 覆盖 CallArgs 对应位置。
self_offset=1 表示 CallArgs[0] 是 self,参数从 CallArgs[1] 开始(__init__/__new__/__call__/method)。
@@ -274,6 +341,108 @@ class ExprCallHandle(BaseHandle):
if 0 <= target_idx < len(CallArgs) - self_offset:
CallArgs[self_offset + target_idx] = ArgVal
def _CheckFuncPtrArity(self, Node: ast.Call, FnPtr: ir.Value, Args: list[ir.Value],
FuncName: str, Gen: LlvmGeneratorMixin) -> None:
"""函数指针间接调用前的 arity 校验(fail-fast,紧邻 builder.call 之前)。
期望形参个数取自 FnPtr 的静态 LLVM 函数类型的形参表:来源是函数指针槽
(模块级/局部 t.Callable[...] 注解)的 CTypeInfo.FuncPtrParams →
CTypeInfo.ToLLVM/_ctype_to_llvm → ir.FunctionType(ret, params);变参位由
FunctionType.var_arg 承载。不校验时,实参个数少于形参个数会让 llvmlite
CallInstr 按形参个数索引 args[i] 抛裸 `IndexError: list index out of range`
(无函数名、无源位置,栈顶落在被调函数 docstring 行,无法定位真因)。
变参函数(var_arg=True,如 C printf 形态)只要求实参不少于固定形参个数:
多出的实参按变参传递,不报错;仅 len(Args) < 固定形参个数 时报错。
"""
FnType: ir.Type = FnPtr.type.pointee if isinstance(FnPtr.type, ir.PointerType) else FnPtr.type
if not isinstance(FnType, ir.FunctionType):
return
Expected: int = len(FnType.args)
Actual: int = len(Args)
IsVariadic: bool = bool(getattr(FnType, 'var_arg', False))
if IsVariadic:
if Actual >= Expected:
return
elif Actual == Expected:
return
# 源位置:按本文件既有写法取 Node 自身的 lineno/col_offset(不用
# _current_node_info/_current_lineno —— 二者可能停留在上一条语句),
# 源文件取 Gen._current_source_file,源码行取 Gen._get_source_line。
Lineno: int = getattr(Node, 'lineno', 0) or 0
# 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行,
# 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator);
# 报文行号与源码行必须同用还原后的 Lineno,二者一致(否则会显示下一行源码)。
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and Lineno > _line_offset:
Lineno -= _line_offset
Col: int = getattr(Node, 'col_offset', -1)
SrcFile: str = getattr(Gen, '_current_source_file', '') or 'unknown'
Loc: str = f"{SrcFile}:{Lineno}"
if Col >= 0:
Loc += f":{Col}"
SourceLine: str = ''
if Lineno > 0 and hasattr(Gen, '_get_source_line'):
SourceLine = (Gen._get_source_line(Lineno) or '').strip()
ExpectText: str = (f"至少 {Expected} 个形参(变参函数,仅固定形参)"
if IsVariadic else f" {Expected} 个形参")
raise Exception(
f"[TransPyC] 函数指针调用实参个数不匹配: '{FuncName}' 期望{ExpectText},"
f"实际传入 {Actual} 个实参;请检查该函数指针(t.Callable[...] 注解槽)的"
f"形参表是否与调用处实参个数一致。位置 {Loc}"
f"{(',源码: ' + SourceLine) if SourceLine else ''}"
)
def _RequireArgValue(self, ArgVal: ir.Value | None, Callee: str, ArgIndex: int,
ArgNode: ast.AST, CallNode: ast.Call) -> ir.Value:
"""fail-fast:调用实参编译期求值为空(`HandleExprLlvm` 返回 None)时终止装配。
调用装配处原写作 `ArgVal = self.HandleExprLlvm(arg); if ArgVal: 追加(ArgVal)`:
实参求值为 None 时它被**静默整条丢弃**,导致后续实参整体前移一格,最终由
`FuncGen._adjust_args` 报出误导性的"指针类型不能隐式转换为整数参数"(真因是
"少了一个实参")。实例:`ast.OpKind.BitOr` 因枚举成员未登记而求值 None ⇒
`ast.BinOp(...)` 的 4 个实参退化为 3 个 ⇒ `AST*` 落到 `op: i32`(见
`Debugger/rawptr-migration-and-fastfail.md` §27、`Debugger/enum-semantics.md`
的「逗号分组成员被静默丢弃」一节)。
`HandleExprLlvm` 返回 None 只有一种语义——该表达式编译期**求值失败**
(名称/属性链/枚举成员未登记、跨模块符号查找失败、类构造在声明期结构体不完整等);
"按类型本无值"的形态(以返回 void 的调用收尾)返回的是 CallInstr 对象而非 None,
故不存在需要跳过的合法实参。此处改为当场终止,报文含被调者、实参序号与源表达式、
根因提示与源码位置;**不丢弃该实参**。
"""
if ArgVal is not None:
return ArgVal
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
expr_text: str = self._ExprText(ArgNode)
ArgKind: str = type(ArgNode).__name__
raw_lineno: int = getattr(ArgNode, 'lineno', 0) or getattr(CallNode, 'lineno', 0) or 0
lineno: int = raw_lineno
# 行号还原:`_inject_auto_imports` 会在解析前往文件头注入 import 行,AST 行号
# 比磁盘真实行号大 N;与 HandlesBase._RaiseUnresolvedExpr 同口径。
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
col: int = getattr(ArgNode, 'col_offset', -1)
src_file: str = getattr(Gen, '_current_source_file', '') or 'unknown'
code_line: str = ''
if lineno > 0 and hasattr(Gen, '_get_source_line'):
try:
code_line = (Gen._get_source_line(lineno) or '').strip()
except Exception:
code_line = ''
loc: str = f"{src_file}:{lineno}"
if col >= 0:
loc += f":{col}"
raise Exception(
f"[TransPyC] 调用实参编译期求值为空: 被调者 '{Callee}' 的第 {ArgIndex} 个实参 "
f"'{expr_text}'({ArgKind} 形态)求值失败;已终止编译,不丢弃该实参"
f"(丢弃会使后续实参前移一格,报出与真因无关的类型不匹配)。"
f"常见根因:枚举成员/名称未登记、跨模块符号查找失败、类定义在声明期结构体不完整。"
f"位置 {loc}"
f"{(',源码: ' + code_line) if code_line else ''}"
)
def _HandleCallLlvm(self, Node: ast.Call) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
# 协程:coro.coro_run(帧) 内联翻译为 resume 循环
@@ -300,6 +469,15 @@ class ExprCallHandle(BaseHandle):
ModulePath: str | None = None
if isinstance(Node.func, ast.Name):
FuncName = Node.func.id
# __asm__(...) 语法糖:等价 c.Asm(...)(裸名调用,无需 import c)
if FuncName == '__asm__':
return self.Trans.ExprAsmHandle._HandleAsmLlvm(Node)
# __llvmir__(...) 语法糖:等价 c.LLVMIR(...)
if FuncName == '__llvmir__':
return self._HandleLLVMIRLlvm(Node)
# irinp/irout 等价 c.LInp/c.LOut(仅作模板占位符,独立调用返回 0)
if FuncName in ('irinp', 'irout'):
return ir.Constant(ir.IntType(32), 0)
# M1 协程:普通函数调用 async 函数 → create_<name>()(返回帧指针)
if hasattr(self.Trans, 'AsyncHandler') and self.Trans.AsyncHandler._IsAsyncFunc(FuncName):
return self.Trans.AsyncHandler._HandleAsyncCreateCallLlvm(Node, FuncName)
@@ -316,16 +494,19 @@ class ExprCallHandle(BaseHandle):
# 若该名字同时是类(在 Gen.structs 中或存在 __before_init__),优先作为类构造函数处理
IsAlsoClass: bool = (FuncName in Gen.structs) or Gen._has_function(f'{FuncName}.__before_init__')
if SymInfo.IsEnum and not (IsAlsoClass and getattr(SymInfo, 'IsEnumMember', False)):
# 枚举类型转换 `EnumName(v)`:结果宽度 = 该枚举的存储宽度
# (最宽成员类型),与成员常量宽度同点(ABI 一致)。
_enum_ty: ir.IntType = Gen._enum_int_type(FuncName, SymInfo)
if Node.args:
arg_val = self.HandleExprLlvm(Node.args[0])
if arg_val:
if isinstance(arg_val.type, ir.IntType) and arg_val.type.width != 32:
if arg_val.type.width < 32:
arg_val = Gen.builder.zext(arg_val, ir.IntType(32), name="zext_enum_arg")
if isinstance(arg_val.type, ir.IntType) and arg_val.type.width != _enum_ty.width:
if arg_val.type.width < _enum_ty.width:
arg_val = Gen.builder.zext(arg_val, _enum_ty, name="zext_enum_arg")
else:
arg_val = Gen.builder.trunc(arg_val, ir.IntType(32), name="trunc_enum_arg")
arg_val = Gen.builder.trunc(arg_val, _enum_ty, name="trunc_enum_arg")
return arg_val
return ir.Constant(ir.IntType(32), 0)
return ir.Constant(_enum_ty, 0)
if SymInfo.IsTypedef:
return self._HandleTypedefCastLlvm(Node, FuncName)
NewFuncName = f'{FuncName}.__before_init__'
@@ -370,6 +551,7 @@ class ExprCallHandle(BaseHandle):
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
args.append(ArgVal)
self._CheckFuncPtrArity(Node, fn_ptr, args, FuncName, Gen)
return Gen.builder.call(fn_ptr, args, name=f"call_{FuncName}")
closure_ptr = Gen._load(VarPtr, name=f"Load_closure_{FuncName}")
if isinstance(closure_ptr.type, ir.PointerType) and isinstance(closure_ptr.type.pointee, ir.FunctionType):
@@ -379,6 +561,7 @@ class ExprCallHandle(BaseHandle):
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
args.append(ArgVal)
self._CheckFuncPtrArity(Node, fn_ptr, args, FuncName, Gen)
return Gen.builder.call(fn_ptr, args, name=f"call_{FuncName}")
# 处理 t.CPtr (i8*) 函数指针调用
# 当函数被赋值给 t.CPtr 变量时,原始函数类型退化为 i8*
@@ -449,13 +632,39 @@ class ExprCallHandle(BaseHandle):
safe_env_type: ir.Type = _safe_ptr_type(expected_fn_type.args[0])
call_args[0] = Gen.builder.bitcast(call_args[0], safe_env_type, name=f"cast_env_{FuncName}")
return Gen.builder.call(fn_val, call_args, name=f"call_{FuncName}")
# 编译器内置裸指针强转 rawptr(x):rawptr 是 TPC/TPV 双端**语法保留字**
# (无 SymbolTable typedef 条目、无泛型模板;builtins.vp 已不再声明它,
# 见 TypeAnnotationResolver._BUILTIN_ROWPTR_NAME 与 TPV 同口径内置分支
# TypeGenResolve.vp:921)。落到下方 typedef 表判据必然 miss ⇒ 误报
# "Undefined function: 'rawptr'"(TransPyV/App/lib/core/Gen/
# StructGenMixin.vp `vt_ptr: rawptr = rawptr(*(uint64*)(default_val))` 根因)。
# 单独 rawptr 等价 t.CPtr(void* → i8*):目标类型取内置 rawptr 的权威
# CTypeInfo(_builtin_rawptr_info,与注解侧同源),转换复用 (rawptr[X])(x)
# / (t.CPtr)(x) 的 _emit_rawptr_cast——不新造第二套 rawptr 表示。
# 不照抄 uchar 的做法(往 builtins.vp 加 `uchar: t.CTypedef = ...`):
# rawptr 不是普通 typedef 名而是内置语法,TPV register_ctypedef 显式拒绝
# 重定义(TypeGenCType.vp:157-160)、TPC builtins_typedef_value 亦硬编码
# 返回内置值(TypeAnnotationResolver.py:313-314)⇒ 该声明不生效。
# 本分支置于下方 try 之外:解析/转换异常必须外抛,不得被吞成
# "Undefined function: 'rawptr'" 的误导报文。
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR_RP
if FuncName == _TAR_RP._BUILTIN_ROWPTR_NAME:
_rawptr_target: ir.Type = Gen._ctype_to_llvm(_TAR_RP._builtin_rawptr_info())
return self._emit_rawptr_cast(Node, Gen, _rawptr_target)
# builtins 隐式 CTypedef 作为类型转换调用(uint64(x)/int64(x)/size_t(x) 等,
# builtins.vp 声明、无需 import,不注册进 SymbolTable):常规 miss 后在此
# 尝试 typedef cast(uint64(buf) = t.CUInt64T(buf)),否则报
# "Undefined function: 'uint64'"(includes/ast/base.vp bootstrap 根因)。
_td_diag: str = ''
try:
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR_TD
if FuncName in _TAR_TD._scan_builtins()['typedefs']:
_td_table: dict = _TAR_TD._scan_builtins()['typedefs']
if FuncName not in _td_table:
# 典型成因:includes/builtins.vp 未定位到(PROJECT_INCLUDE_DIRS 尚未
# 装配)、或声明写法未被文本扫描识别 ⇒ 整表为空/缺项。
_td_diag = (f"(builtins typedef 表 {len(_td_table)} 项,无 '{FuncName}';"
f"includes 目录={_TAR_TD._builtins_include_dirs()})")
if FuncName in _td_table:
# from_type_name 对裸 typedef 名(uint64 等)解析不出真实类型类
# (BuiltinTypeMap 无此键,CreateFromTypeName 落空成空 CType),
# 因此改走权威 typedef 值:'t.CUInt64T' → CTypeRegistry.NameToLLVM
@@ -487,9 +696,11 @@ class ExprCallHandle(BaseHandle):
_tg_ptr_td: ir.Type = ir.PointerType(_tg_td)
return self._emit_rawptr_cast(Node, Gen, _tg_ptr_td)
return self._emit_rawptr_cast(Node, Gen, _tg_td)
except Exception:
pass
raise Exception(f"Undefined function: '{FuncName}'")
except Exception as _e_td:
# 不再静默:兜底链任何一步抛错都要带出来(此前 pass 让真正的根因
# 只剩一句误导性的 "Undefined function")。
_td_diag = _td_diag + f"(typedef cast 求值异常: {type(_e_td).__name__}: {_e_td})"
raise Exception(f"Undefined function: '{FuncName}'{_td_diag}")
if isinstance(Node.func, ast.Subscript):
# Handle list[int](mb) syntax — generic class constructor with explicit type args
# Handle xxx[T](x) syntax — generic function call with explicit type args
@@ -558,39 +769,12 @@ class ExprCallHandle(BaseHandle):
if ObjVal and isinstance(ObjVal.type, ir.PointerType):
pointee = ObjVal.type.pointee
if isinstance(pointee, ir.ArrayType):
# Calculate element size in bytes
elem = pointee.element
if isinstance(elem, ir.IntType):
elem_size = elem.width // 8
elif isinstance(elem, ir.PointerType):
elem_size = 8 # 64-bit pointers
elif isinstance(elem, (ir.LiteralStructType, ir.IdentifiedStructType)):
# Approximate: sum of field sizes
elem_size = 0
for field in getattr(elem, 'elements', []):
if isinstance(field, ir.IntType):
elem_size += field.width // 8
elif isinstance(field, ir.PointerType):
elem_size += 8
elif isinstance(field, ir.ArrayType):
if isinstance(field.element, ir.IntType):
elem_size += field.count * (field.element.width // 8)
else:
elem_size += field.count * 8
else:
elem_size += 8
elif isinstance(elem, ir.ArrayType):
if isinstance(elem.element, ir.IntType):
elem_size = elem.count * (elem.element.width // 8)
else:
elem_size = elem.count * 8
elif isinstance(elem, ir.FloatType):
elem_size = 4
elif isinstance(elem, ir.DoubleType):
elem_size = 8
else:
elem_size = 8 # fallback
return ir.Constant(ir.IntType(64), pointee.count * elem_size)
# 元素尺寸统一走 Gen._get_struct_size(含字段对齐/填充,与 __sizeof__ /
# memhub alloc 同源):_get_struct_size(ArrayType) = 元素尺寸 × 元素数。
# 原实现手写「字段宽度直接相加 + 未知类型兜底 8」,会漏算 struct 的填充
# ({i8,i64} 算成 9 而非 16),sizeof 偏小 → 调用方按过小尺寸 alloc →
# 越界写坏邻居块头(与 1 GiB 主池漏满同一根因链)。
return ir.Constant(ir.IntType(64), Gen._get_struct_size(pointee))
elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
# 结构体实例: 直接计算大小
self._resolve_opaque_struct_members(pointee, Gen)
@@ -649,6 +833,13 @@ class ExprCallHandle(BaseHandle):
or _base_name in Gen.global_vars)
):
_is_chained_instance = True
elif self._IsModuleQualifiedGlobalChain(_base_name, Node.func.value):
# 模块限定全局变量链:`Module.Global.field.method(...)`
# (如 GenApi._g_gen.ClassGenBase._collect_parents(...))。
# 链根是模块别名、次段是该模块的全局变量 ⇒ 接收者是对象,
# 与 self/局部变量/全局变量同一条"实例链"判据(详见
# _IsModuleQualifiedGlobalChain 的说明)。
_is_chained_instance = True
if (is_instance_var or _is_chained_instance) and not is_class_name:
return self._HandleMethodCallLlvm(Node)
FuncAttr = Node.func.attr
@@ -681,6 +872,10 @@ class ExprCallHandle(BaseHandle):
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
msg = arg.value
lineno = getattr(Node, 'lineno', 0)
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
raise Exception(f"#error: {msg} (line {lineno})")
elif FuncAttr in ('AsmInp', 'AsmOut'):
return ir.Constant(ir.IntType(32), 0)
@@ -1025,8 +1220,17 @@ class ExprCallHandle(BaseHandle):
if is_classmethod_call and ClassName in Gen.structs:
ClsPtr = Gen._alloca(Gen.structs[ClassName], name=f"cls_{ClassName}")
CallArgs.append(ClsPtr)
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
# 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度
# (更宽 ⇒ 升宽物化;更窄 ⇒ fail-fast);非常量实参走原路径。
_arg_off: int = len(CallArgs)
for _ai, arg in enumerate(Node.args):
_pt: ir.Type | None = None
if func is not None:
_ft_args: list[ir.Type] = func.function_type.args
if _ai + _arg_off < len(_ft_args):
_pt = _ft_args[_ai + _arg_off]
ArgVal = self.HandleExprLlvm(
arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt))
if ArgVal:
CallArgs.append(ArgVal)
if (ClassName not in Gen.class_vtable
@@ -1087,15 +1291,22 @@ class ExprCallHandle(BaseHandle):
if ClassName in Gen.structs:
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
CallArgs = [ObjVal]
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
CallArgs.append(ArgVal)
CallFuncName = f'{ClassName}.__call__'
struct_sha1_map = getattr(Gen, '_struct_sha1_map', {})
class_sha1 = struct_sha1_map.get(ClassName)
if class_sha1:
CallFuncName = f'{class_sha1}.{ClassName}.__call__'
# 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。
# CallArgs[0] 为 self ⇒ 第 i 个用户实参对应形参 i+1。
_cfunc: ir.Function | None = (Gen._get_function(CallFuncName)
if Gen._has_function(CallFuncName) else None)
_ptypes: list[ir.Type] = _cfunc.function_type.args if _cfunc is not None else []
for _ai, arg in enumerate(Node.args):
_pt: ir.Type | None = _ptypes[_ai + 1] if _ai + 1 < len(_ptypes) else None
ArgVal = self.HandleExprLlvm(
arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt))
if ArgVal:
CallArgs.append(ArgVal)
if not Gen._has_function(CallFuncName):
raise Exception(f"Class '{ClassName}' has no __call__ method")
func = Gen._get_function(CallFuncName)
@@ -1131,6 +1342,66 @@ class ExprCallHandle(BaseHandle):
parts.reverse()
return '.'.join(parts) if parts else None
def _IsModuleQualifiedGlobalChain(self, base_name: str, receiver: ast.AST) -> bool:
"""判定"模块限定全局变量"接收者链(链根=模块别名、次段=该模块全局变量)。
与调用点既有的"链根是已知变量(self / 局部变量 / 全局变量 / struct 变量)"
判据互补,补的是**模块限定全局变量**这一形态:
`import lib.core.Gen.GenApi as GenApi` 后写
`GenApi._g_gen.ClassGenBase._collect_parents(...)`——链根 `GenApi` 是模块
别名(不在 var_struct_class / variables / global_vars 中),次段 `_g_gen`
是该模块的全局变量。缺此判定时该接收者被当作"模块.类"引用,落到下方
`inner_attr in Gen.structs` 分支走 _HandleStaticMethodCallLlvm——该路径按
静态方法口径处理(**不前置接收者 self**),实参整体错位一格
(trans: Translator* 对上 max_parents: i32),触发 fail-fast
"指针类型 ... 不能隐式转换为整数参数 i32";不报错的同形调用(实参全为指针)
则静默生成缺 self 的错误调用。
判据只有两段:链根是模块(SymbolTable.import_aliases / IsModuleAlias 符号 /
ModuleSha1Map / _ImportedModules 任一命中),且**紧邻根名的那一段属性**不是
类型/函数类符号(类 / 枚举 / REnum / 枚举成员 / 函数 / CDefine / 异常类 /
模块别名)——即该段是"对象"(全局变量;跨模块全局在本符号表里登记为
IsTypedef 的空类型条目,故 IsTypedef 不在排除集内)。紧随段是类
(`Module.Class.static_method(...)`)时返回 False,静态方法口径保持不变。
"""
symbol_table: Any = self.Trans.SymbolTable
# 紧邻根名的那一段属性:GenApi._g_gen.ClassGenBase → '_g_gen'
first_attr: str | None = None
cursor: ast.AST = receiver
while isinstance(cursor, ast.Attribute):
first_attr = cursor.attr
cursor = cursor.value
if not first_attr or not isinstance(cursor, ast.Name):
return False
# 链根是不是模块:优先别名表(import ... as X 的 X → 模块全名),
# 其次符号表里的模块别名标记,最后模块表(其键含短名)兜底。
module_path: str = symbol_table.resolve_alias(base_name)
if module_path != base_name:
is_module: bool = True
else:
alias_entry: Any = symbol_table.lookup(base_name)
if alias_entry is not None and getattr(alias_entry, 'IsModuleAlias', False):
module_path = getattr(alias_entry, 'ResolvedModule', '') or ''
is_module = bool(module_path)
else:
is_module = (
base_name in getattr(self.Trans.LlvmGen, 'ModuleSha1Map', {})
or base_name in getattr(self.Trans, '_ImportedModules', set()))
if not is_module:
return False
segment: Any = symbol_table.lookup(f'{module_path}.{first_attr}') if module_path else None
if segment is None:
segment = symbol_table.lookup(first_attr)
if segment is None:
return False
# 该段是"类型/函数类"符号 ⇒ 属于 `Module.Class...` 引用,不按实例链处理
# (同名枚举成员/异常类在符号表里与类名冲突,一并排除,避免误判)。
for _type_flag in ('IsStruct', 'IsEnum', 'IsRenum', 'IsUnion', 'IsEnumMember',
'IsFunction', 'IsDefine', 'IsExceptionClass', 'IsModuleAlias'):
if getattr(segment, _type_flag, False):
return False
return True
def _HandleLLVMIRLlvm(self, Node: ast.Call) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
if not Node.args:
@@ -1158,36 +1429,32 @@ class ExprCallHandle(BaseHandle):
parts.append(value.value)
elif isinstance(value, ast.FormattedValue):
expr = value.value
if isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute):
if isinstance(expr.func.value, ast.Name) and expr.func.value.id == 'c':
if expr.func.attr == 'LInp':
if expr.args:
val = self.HandleExprLlvm(expr.args[0])
if val:
input_ops.append(val)
all_ops.append(('inp', len(input_ops) - 1))
parts.append(f'%__OP{operand_seq}__')
operand_seq += 1
elif expr.func.attr == 'LOut':
if expr.args:
target_ptr = self.Trans.ExprAsmHandle._get_var_ptr_for_asm(expr.args[0])
if target_ptr:
val = target_ptr
else:
val = self.HandleExprLlvm(expr.args[0])
if val:
output_targets.append(val)
all_ops.append(('out', len(output_targets) - 1))
parts.append(f'%__OP{operand_seq}__')
operand_seq += 1
else:
fallback = self.HandleExprLlvm(expr)
if fallback:
input_ops.append(fallback)
# c.LInp/c.LOut 与裸名 irinp/irout 等价(见 _intrinsic_marker)
_marker: str | None = self.Trans.ExprAsmHandle._intrinsic_marker(expr)
_is_c_qualified: bool = (isinstance(expr, ast.Call) and isinstance(expr.func, ast.Attribute)
and isinstance(expr.func.value, ast.Name) and expr.func.value.id == 'c')
if _marker == 'LInp':
if expr.args:
val = self.HandleExprLlvm(expr.args[0])
if val:
input_ops.append(val)
all_ops.append(('inp', len(input_ops) - 1))
parts.append(f'%__OP{operand_seq}__')
operand_seq += 1
else:
elif _marker == 'LOut':
if expr.args:
target_ptr = self.Trans.ExprAsmHandle._get_var_ptr_for_asm(expr.args[0])
if target_ptr:
val = target_ptr
else:
val = self.HandleExprLlvm(expr.args[0])
if val:
output_targets.append(val)
all_ops.append(('out', len(output_targets) - 1))
parts.append(f'%__OP{operand_seq}__')
operand_seq += 1
elif not _is_c_qualified:
# c.* 中非 LInp/LOut 的形态不收集(保持原行为)
fallback = self.HandleExprLlvm(expr)
if fallback:
input_ops.append(fallback)
@@ -1551,6 +1818,14 @@ class ExprCallHandle(BaseHandle):
if sym_info_exact:
exact_params = sym_info_exact.FuncPtrParams
exact_param_names = [pn for pn, _ in exact_params]
# stub/LLVM 侧形参名多为 arg0/arg1 占位(SymbolTable._InsertFuncSymbol),
# 用它们无法把命名实参 `f(cap = 8, x = 1)` 绑到正确位置,会误报"缺少必需
# 参数"。优先替换为源码/vpi 扫描得到的语义参数名(长度一致才替换,
# 避免 shape 不符时错配)。
semantic_names = (self._LookupGlobalParamNames(mangled_name)
or self._LookupGlobalParamNames(func_name))
if semantic_names and len(semantic_names) == len(exact_param_names):
exact_param_names = semantic_names
if exact_param_names:
provided = len(Node.args)
kw_provided = set()
@@ -1558,17 +1833,9 @@ class ExprCallHandle(BaseHandle):
if kw.arg and kw.arg in exact_param_names:
kw_provided.add(exact_param_names.index(kw.arg))
# 获取默认参数信息,计算必需参数数量(与 _fill_default_args 一致的查找逻辑)
global_defaults: dict = self.Trans._global_function_default_args
func_defaults: list | None = None
if mangled_name in global_defaults:
func_defaults = global_defaults[mangled_name]
elif func_name in global_defaults:
func_defaults = global_defaults[func_name]
else:
for gk, gv in global_defaults.items():
if func_name in gk or gk.endswith(func_name) or gk.endswith('.' + func_name):
func_defaults = gv
break
func_defaults: list | None = self._LookupGlobalDefaults(mangled_name)
if func_defaults is None:
func_defaults = self._LookupGlobalDefaults(func_name)
total_params: int = len(exact_param_names)
if func_defaults is None:
# 默认参数信息缺失(跨模块 stub 加载:vpi/stub 只含参数
@@ -1578,6 +1845,9 @@ class ExprCallHandle(BaseHandle):
# err_type/col_start/col_end 默认值未同步时误报"缺 arg3")。
# 同文件函数的参数检查由 FunctionDef 路径(L1983)覆盖。
pass
elif len(func_defaults) > total_params:
# 表里的条目与当前函数长度不符(同名不同模块)→ 不做严格检查
pass
else:
num_defaults: int = len(func_defaults)
min_args: int = total_params - num_defaults
@@ -1645,6 +1915,10 @@ class ExprCallHandle(BaseHandle):
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
msg = arg.value
lineno = getattr(Node, 'lineno', 0)
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
raise Exception(f"#error: {msg} (line {lineno})")
elif func_name in ('AsmInp', 'AsmOut'):
return ir.Constant(ir.IntType(32), 0)
@@ -2124,7 +2398,10 @@ class ExprCallHandle(BaseHandle):
if not is_variadic and len(Node.args) > regular_param_count:
raise Exception(f"调用 {FuncName}() 传入了 {len(Node.args)} 个参数,但函数只需要 {regular_param_count} 个参数")
max_args = len(Node.args) if is_variadic else regular_param_count
# 变参函数:固定形参(含带默认值的)之外才是可变部分。被省略的固定形参
# 也必须补进调用实参,否则实参个数少于固定形参 → 被调函数读到寄存器/栈
# 垃圾(VET json.write 缺省 cap 读成指针量级 → pool.alloc 巨量 → OOM 的形态)。
max_args = max(regular_param_count, len(Node.args)) if is_variadic else regular_param_count
resolved = [None] * max_args
for i, arg in enumerate(Node.args):
@@ -2153,31 +2430,44 @@ class ExprCallHandle(BaseHandle):
resolved[idx] = ArgVal
FuncDef_args_defaults = getattr(getattr(FuncDef, 'args', None), 'defaults', []) if FuncDef else []
if FuncDef_args_defaults:
defaults = FuncDef.args.defaults
num_defaults = len(defaults)
if num_defaults > 0:
min_args = regular_param_count - num_defaults
for i in range(regular_param_count):
if resolved[i] is None and i >= min_args:
default_idx = i - min_args
if default_idx < len(defaults):
default_node = defaults[default_idx]
default_val = self.HandleExprLlvm(default_node)
if default_val:
param_type = func.function_type.args[i]
if default_val.type != param_type:
if isinstance(param_type, ir.IntType) and isinstance(default_val.type, ir.IntType):
if default_val.type.width < param_type.width:
default_val = Gen.builder.zext(default_val, param_type, name=f"zext_default_{i}")
elif default_val.type.width > param_type.width:
default_val = Gen.builder.trunc(default_val, param_type, name=f"trunc_default_{i}")
elif isinstance(param_type, ir.PointerType) and isinstance(default_val.type, ir.IntType):
default_val = Gen.builder.inttoptr(default_val, param_type, name=f"inttoptr_default_{i}")
resolved[i] = default_val
# 跨模块调用(被调函数不在本模块 FunctionDefCache:其它 App 模块 / includes
# 模块 / .vpi 声明)时 FuncDef 为 None,本模块没有被调方 AST,AST 默认值不可用。
# 此时从全局默认参数表(Phase2 预扫描 App 与 includes 源码得到)取值,按
# "尾部默认参数"语义补齐被省略的实参;缺失该表时保持原有零值填充。
Global_defaults_vals: list | None = None
if not FuncDef_args_defaults:
Global_defaults_vals = self._LookupGlobalDefaults(FuncName)
if Global_defaults_vals is None and func is not None:
Global_defaults_vals = self._LookupGlobalDefaults(getattr(func, 'name', None))
Num_defaults: int = len(FuncDef_args_defaults) if FuncDef_args_defaults else (
len(Global_defaults_vals) if Global_defaults_vals else 0)
if Num_defaults > 0:
Min_args: int = regular_param_count - Num_defaults
for i in range(regular_param_count):
if resolved[i] is not None or i < Min_args:
continue
default_idx = i - Min_args
if default_idx >= Num_defaults:
continue
if FuncDef_args_defaults:
default_val = self.HandleExprLlvm(FuncDef_args_defaults[default_idx])
else:
default_val = self._ConstantToIrValue(Global_defaults_vals[default_idx],
func.function_type.args[i], Gen)
if default_val:
param_type = func.function_type.args[i]
if default_val.type != param_type:
if isinstance(param_type, ir.IntType) and isinstance(default_val.type, ir.IntType):
if default_val.type.width < param_type.width:
default_val = Gen.builder.zext(default_val, param_type, name=f"zext_default_{i}")
elif default_val.type.width > param_type.width:
default_val = Gen.builder.trunc(default_val, param_type, name=f"trunc_default_{i}")
elif isinstance(param_type, ir.PointerType) and isinstance(default_val.type, ir.IntType):
default_val = Gen.builder.inttoptr(default_val, param_type, name=f"inttoptr_default_{i}")
resolved[i] = default_val
if FuncDef and param_names:
min_args = regular_param_count - num_defaults if FuncDef_args_defaults else regular_param_count
min_args = regular_param_count - Num_defaults
provided = len(Node.args)
kw_provided = set()
for kw in Node.keywords:
@@ -2361,7 +2651,7 @@ class ExprCallHandle(BaseHandle):
return cmap
with open(pyi_path, 'r', encoding='utf-8') as f:
pyi_code = f.read()
tree = ast.parse(pyi_code)
tree = ast.parse(pyi_code, pyi_path)
cmap: dict[str, ast.ClassDef] = {}
for node in ast.iter_child_nodes(tree):
if isinstance(node, ast.ClassDef):
@@ -2397,17 +2687,18 @@ class ExprCallHandle(BaseHandle):
is_cvtable = self._has_cvtable_decorator(target_node)
is_novtable = self._has_novtable_decorator(target_node)
# 收集自身方法
self_methods: list[str] = []
# 收集自身方法(方法级修饰符的槽位过滤放在父类状态确定之后:
# 判据 SymbolUtils.IsVtableSlotMethod 需要 VtableByClassLevel)
self_method_pairs: list[tuple] = []
for item in target_node.body:
if isinstance(item, ast.FunctionDef):
method_name = item.name
if method_name in ('__new__', '__init__', '__before_init__'):
continue
if method_name == '__call__':
self_methods.append(f"{class_name}.__call__")
self_method_pairs.append((item, f"{class_name}.__call__"))
else:
self_methods.append(f"{class_name}.{method_name}")
self_method_pairs.append((item, f"{class_name}.{method_name}"))
# 收集基类方法(多继承:遍历所有父类,声明顺序,短名去重,先声明父类优先)
parent_methods: list[str] = []
@@ -2466,6 +2757,24 @@ class ExprCallHandle(BaseHandle):
if first_parent_vtable:
is_cvtable = True
# VtableByClassLevel:**忽略方法级 CVTable 标记**算出的 vtable 状态
# (= 类级 CVTable / 继承链判定)——供槽位筛选与"方法级触发"共用。
_VtByClassLevel: bool = is_cvtable
# 方法级修饰符筛选:`novirtual def`(⇒ @t.NoVTable)不进槽;
# `virtual def`(⇒ @t.CVTable)进槽。判据唯一源 = IsVtableSlotMethod
# (与定义端 HandlesClassDef / TPV _should_method_be_virtual 同口径),
# 故跨模块虚表槽序与定义模块逐槽一致。
self_methods: list[str] = []
for _nd, _full in self_method_pairs:
if (not is_cvtable) or IsVtableSlotMethod(_nd, _VtByClassLevel):
self_methods.append(_full)
# 方法级 virtual 触发类级 vtable(与 HandlesClassDef._resolve_class_flags /
# TPV _detect_vtable_status 的 `_has_any_func_cvtable` 分支对齐:
# 类级 NoVTable / 无真实父类这两种可判定形态;父类 vtable 已在上面处理)
if not is_cvtable and ClassHasFuncLevelCVTable(target_node):
if is_novtable or parent_name is None:
is_cvtable = True
# 合并方法列表:基类方法在前,子类方法在后
# 子类覆盖的方法放在父类的位置;
# 父类独有方法直接用父类方法原名(实际定义类.方法,与 TPV 一致)
@@ -2603,9 +2912,9 @@ class ExprCallHandle(BaseHandle):
if st is None or not isinstance(st, ir.IdentifiedStructType) or st.elements is None:
return None
members: list = Gen.class_members.get(lookup_cls, [])
has_vtable: bool = (ClassName in Gen.class_vtable
or ClassName in Gen._cross_module_vtable_classes)
base: int = 1 if has_vtable else 0
# base(vtable 占位槽数)统一走 Gen._member_base(唯一真源),
# 不再直查 class_vtable/_cross_module_vtable_classes(会漏掉跨模块 AST 节点类)。
base: int = Gen._member_base(ClassName)
idx: int | None = None
for i, (name, _) in enumerate(members):
if name == field_name:
@@ -2628,7 +2937,7 @@ class ExprCallHandle(BaseHandle):
try:
with open(r'd:\Users\TermiNexus\Desktop\TransPyC\_diag_offset.log', 'a', encoding='utf-8') as _f:
_f.write(f"{lookup_cls}|{field_name}|members={len(members)}|struct={len(st.elements)}"
f"|vtable={int(has_vtable)}|idx={idx}|diff={diff_elems}\n")
f"|base={base}|idx={idx}|diff={diff_elems}\n")
except Exception:
pass
offset: int = 0
@@ -2870,6 +3179,10 @@ class ExprCallHandle(BaseHandle):
raise
_vlog().warning(f"处理函数调用失败: {_e}", "Exception")
lineno = getattr(Node, 'lineno', 0)
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
raise Exception(f"#error: {msg} (line {lineno})")
elif MethodName in ('CElse', 'CEndif', 'CPragma', 'CUndef'):
return ir.Constant(ir.IntType(1), 1)
@@ -2904,12 +3217,16 @@ class ExprCallHandle(BaseHandle):
if (ModuleName in imported or actual_module in imported) and ModuleName not in Gen.var_struct_class:
mangled_name = Gen._mangle_func_name(MethodName, actual_module)
if Gen._has_function(mangled_name):
func = Gen._get_function(mangled_name)
CallArgs = []
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
# 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。
_ptypes: list[ir.Type] = func.function_type.args if func is not None else []
for _ai, arg in enumerate(Node.args):
_pt: ir.Type | None = _ptypes[_ai] if _ai < len(_ptypes) else None
ArgVal = self.HandleExprLlvm(
arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt))
if ArgVal:
CallArgs.append(ArgVal)
func = Gen._get_function(mangled_name)
self._fill_default_args(CallArgs, func, MethodName)
func_ft = getattr(func, 'function_type', None)
is_variadic = getattr(func_ft, 'var_arg', False) if func_ft else False
@@ -2961,8 +3278,12 @@ class ExprCallHandle(BaseHandle):
_mf = Gen._get_function(MethodName)
if '.' not in _mf.name:
CallArgs = []
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
# 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。
_ptypes: list[ir.Type] = _mf.function_type.args
for _ai, arg in enumerate(Node.args):
_pt: ir.Type | None = _ptypes[_ai] if _ai < len(_ptypes) else None
ArgVal = self.HandleExprLlvm(
arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt))
if ArgVal:
CallArgs.append(ArgVal)
func = _mf
@@ -3026,8 +3347,12 @@ class ExprCallHandle(BaseHandle):
if stub_func_type:
func = self.GetOrCreateStubFuncDecl(mangled_name, stub_func_type, Gen)
CallArgs = []
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
# 形参类型下传(窄通道):仅影响 CDefine 常量实参的物化宽度。
_ptypes: list[ir.Type] = func.function_type.args if func is not None else []
for _ai, arg in enumerate(Node.args):
_pt: ir.Type | None = _ptypes[_ai] if _ai < len(_ptypes) else None
ArgVal = self.HandleExprLlvm(
arg, ConstWidthHint=DefineUseWidthFromLLVMType(_pt))
if ArgVal:
CallArgs.append(ArgVal)
self._fill_default_args(CallArgs, func, mangled_name)
@@ -3419,8 +3744,8 @@ class ExprCallHandle(BaseHandle):
member_idx = mi
break
if member_idx >= 0 and isinstance(ObjVal.type, ir.PointerType):
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes
base = 1 if has_vtable else 0
# base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。
base = Gen._member_base(ClassName)
member_ptr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), base + member_idx)], name=f"funcptr_{MethodName}_ptr")
raw_ptr = Gen._load(member_ptr, name=f"funcptr_{MethodName}_raw")
func_ptr_type = ir.PointerType(ir.FunctionType(ir.IntType(32), [i8ptr] + [a.type for a in CallArgs]))
@@ -3845,9 +4170,19 @@ class ExprCallHandle(BaseHandle):
# 所有元素存取同槽(get(0)==get(1),list[对象] join 误报根因)。
if _BinOpContainsCPtr(Node):
return ir.Constant(ir.IntType(64), 8)
return ir.Constant(ir.IntType(64), 0)
# 不带 t.CPtr 的联合(A | B)无法确定尺寸:曾静默返回 0 →
# 下游 elem_size=0 → alloc(0) 返回 NULL / 所有元素存取同槽。fail-fast。
raise ValueError(
f"[FATAL][__sizeof__] 联合类型 '{ast.unparse(Node)}' 不含 t.CPtr,"
f"无法确定尺寸,拒绝静默返回 0(会导致 alloc(0)/元素同槽)"
)
else:
return ir.Constant(ir.IntType(64), 0)
# 非 名称/属性/联合 的 sizeof 目标(如表达式、调用):曾静默返回 0。fail-fast。
raise ValueError(
f"[FATAL][__sizeof__] 不支持的 sizeof 目标 {type(Node).__name__}"
f"({ast.unparse(Node)}),"
f"拒绝静默返回 0(会导致 alloc(0))"
)
if type_name not in Gen.structs:
# SHA1 前缀查找:如 ClassDef → 657e182b27c2a022.ClassDef
@@ -3941,10 +4276,8 @@ class ExprCallHandle(BaseHandle):
Gen.var_signedness = _saved_var_signedness
Gen.global_vars = _saved_global_vars
self.Trans.VarScopes = _saved_var_scopes
size = Gen._get_struct_size(struct_type)
if size > 0:
return ir.Constant(ir.IntType(64), size)
return ir.Constant(ir.IntType(64), 0)
# _get_struct_size 已在尺寸不可得时 fail-fast(不返回 0),此处直接采用。
return ir.Constant(ir.IntType(64), Gen._get_struct_size(struct_type))
def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None:
Gen = self.Trans.LlvmGen
@@ -4034,7 +4367,8 @@ class ExprCallHandle(BaseHandle):
if enum_member_found:
enum_type_name = enum_class_name
if enum_type_name:
type_str = f"<enum '{enum_type_name}' size=4 signed=true, ptr=false>"
_enum_bits: int = self.Trans.LlvmGen._enum_int_type(enum_type_name).width
type_str = f"<enum '{enum_type_name}' size={_enum_bits // 8} signed=true, ptr=false>"
else:
llvm_type = self.Trans.ExprUtils._infer_expr_llvm_type_full(arg)
if isinstance(llvm_type, ir.PointerType):
@@ -4382,8 +4716,8 @@ class ExprCallHandle(BaseHandle):
if not find_func:
return
members = Gen.class_members.get(ClassName, [])
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes
base = 1 if has_vtable else 0
# base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。
base = Gen._member_base(ClassName)
field_map: dict[str, tuple[int, ir.Type]] = {}
for i, (mname, mtype) in enumerate(members):
if mname in requires_all:
@@ -4447,10 +4781,9 @@ class ExprCallHandle(BaseHandle):
NewMethodFunc = Gen._find_function(NewMethodFuncName)
if NewMethodFunc:
NewMethodArgs = [result]
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
NewMethodArgs.append(ArgVal)
for ArgIdx, arg in enumerate(Node.args, start=1):
ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg), f"{ClassName}.__new__", ArgIdx, arg, Node)
NewMethodArgs.append(ArgVal)
self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName)
self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1)
NewMethodArgs = Gen._apply_auto_addr(NewMethodFuncName, NewMethodArgs)
@@ -4479,10 +4812,9 @@ class ExprCallHandle(BaseHandle):
if Gen._has_function(InitFuncName):
InitFunc = Gen._get_function(InitFuncName)
InitArgs = [result]
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
InitArgs.append(ArgVal)
for ArgIdx, arg in enumerate(Node.args, start=1):
ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg), f"{ClassName}.__init__", ArgIdx, arg, Node)
InitArgs.append(ArgVal)
self._fill_default_args(InitArgs, InitFunc, InitFuncName)
self._ApplyKeywordArgs(Node, InitArgs, InitFunc, InitFuncName, self_offset=1)
self._append_eh_msg_out_arg(InitArgs, InitFunc, Gen)
@@ -4522,10 +4854,9 @@ class ExprCallHandle(BaseHandle):
NewMethodFunc = Gen._find_function(NewMethodFuncName)
if NewMethodFunc:
NewMethodArgs = [result]
for arg in Node.args:
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
NewMethodArgs.append(ArgVal)
for ArgIdx, arg in enumerate(Node.args, start=1):
ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg), f"{ClassName}.__new__", ArgIdx, arg, Node)
NewMethodArgs.append(ArgVal)
self._fill_default_args(NewMethodArgs, NewMethodFunc, NewMethodFuncName)
self._ApplyKeywordArgs(Node, NewMethodArgs, NewMethodFunc, NewMethodFuncName, self_offset=1)
NewMethodArgs = Gen._apply_auto_addr(NewMethodFuncName, NewMethodArgs)
@@ -4563,9 +4894,8 @@ class ExprCallHandle(BaseHandle):
init_param_types = InitFunc.function_type.args if hasattr(InitFunc, 'function_type') else []
for i, arg in enumerate(Node.args):
arg_var_type = init_param_types[i + 1] if i + 1 < len(init_param_types) else None
ArgVal = self.HandleExprLlvm(arg, VarType=arg_var_type)
if ArgVal:
InitArgs.append(ArgVal)
ArgVal = self._RequireArgValue(self.HandleExprLlvm(arg, VarType=arg_var_type), f"{ClassName}.__init__", i + 1, arg, Node)
InitArgs.append(ArgVal)
self._fill_default_args(InitArgs, InitFunc, InitFuncName)
self._ApplyKeywordArgs(Node, InitArgs, InitFunc, InitFuncName, self_offset=1)
self._append_eh_msg_out_arg(InitArgs, InitFunc, Gen)
@@ -4613,8 +4943,8 @@ class ExprCallHandle(BaseHandle):
return
if not isinstance(StructType, ir.IdentifiedStructType):
return
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes
base = 1 if has_vtable else 0
# base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。
base = Gen._member_base(ClassName)
for i, (member_name, member_type) in enumerate(members):
if member_name in defaults:
default_val = defaults[member_name]
@@ -4627,8 +4957,8 @@ class ExprCallHandle(BaseHandle):
if not isinstance(StructType, ir.IdentifiedStructType):
return
members = Gen.class_members.get(ClassName, [])
has_vtable = ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes
base = 1 if has_vtable else 0
# base(vtable 占位槽数)统一走 Gen._member_base(唯一真源)。
base = Gen._member_base(ClassName)
for i, arg in enumerate(Node.args):
if i >= len(members):
break
@@ -4674,6 +5004,9 @@ class ExprCallHandle(BaseHandle):
def _HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
# pointee-volatile 基址名后缀(同 HandlesExprAttr._HandleSubscriptLlvm);空后缀
# ⇒ 下标 GEP 名仍是 "subscript",既有代码 IR 逐字节不变。
_PVSuffix: str = Gen._SubscriptSuffix(Node.value)
ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.value, Gen)
if ClassName and ClassName in Gen.structs and Gen._has_function(f'{ClassName}.__getitem__'):
obj_val = self.HandleExprLlvm(Node.value)
@@ -4706,7 +5039,7 @@ class ExprCallHandle(BaseHandle):
if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType):
LoadedValueVal = Gen._load(ValueVal, name="Load_subscript")
if isinstance(LoadedValueVal.type, ir.PointerType):
ElemPtr = Gen.builder.gep(LoadedValueVal, [IndexVal], name="subscript")
ElemPtr = Gen.builder.gep(LoadedValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
return ElemPtr
if isinstance(ValueVal.type, ir.PointerType):
pointee = ValueVal.type.pointee
@@ -4717,12 +5050,12 @@ class ExprCallHandle(BaseHandle):
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 = Gen.builder.gep(ValueVal, [zero, IndexVal], name="subscript")
ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}")
return Gen._load(ElemPtr, name="arr_subscript_val")
else:
if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64:
IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index")
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name="subscript")
ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}")
if isinstance(pointee, ir.IntType):
return Gen._load(ElemPtr, name="subscript_val")
return ElemPtr
@@ -4738,11 +5071,11 @@ class ExprCallHandle(BaseHandle):
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 = Gen.builder.gep(VarPtr, [zero, IndexVal], name="subscript")
ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}")
return ElemPtr
arr_alloc = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp")
Gen._store(ValueVal, arr_alloc)
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name="subscript")
ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}")
return ElemPtr
return None
+20 -4
View File
@@ -194,8 +194,18 @@ class ExprLambdaHandle(BaseHandle):
result_type = ir.IntType(8)
# 重新处理 Else 分支的类型转换
elif isinstance(BodyVal.type, ir.PointerType) and isinstance(result_type, ir.IntType):
# 指针转整数
BodyVal = Gen.builder.ptrtoint(BodyVal, result_type, name="ifexp_body_ptr2int")
if isinstance(BodyVal.type.pointee, ir.IntType) and BodyVal.type.pointee.width == 8:
# 1 字符字符串字面量(i8*)与 char(i8)混用(如
# `p if p is not None else '?'`):取首字符。与 else 侧对称
# (旧代码仅 else 侧这样处理,body 侧 ptrtoint 截断地址是错的)
BodyVal = Gen._load(BodyVal, name="ifexp_body_Load_char")
else:
# 其余指针 → 整数:隐式转换一律禁止(两分支类型不一致须显式 cast)
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:条件表达式两分支类型不一致({BodyVal.type} / {result_type});"
f"如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
Gen.builder.branch(MergeBB)
# 在 ElseBB 末尾添加类型转换(如果需要)和分支
if not ElseEndBB.is_terminated:
@@ -213,11 +223,17 @@ class ExprLambdaHandle(BaseHandle):
result_type = ir.IntType(8)
# 重新处理 BodyVal 分支的类型转换(已经在上面处理过了)
elif isinstance(OrelseVal.type, ir.PointerType) and isinstance(result_type, ir.IntType):
# 如果指针指向 i8,加载字符值
if isinstance(OrelseVal.type.pointee, ir.IntType) and OrelseVal.type.pointee.width == 8:
# 1 字符字符串字面量(i8*)与 char(i8)混用(原语义,保留):
# 取首字符;只有非 i8* 的指针转整数才 fail-fast
OrelseVal = Gen._load(OrelseVal, name="ifexp_orelse_Load_char")
else:
OrelseVal = Gen.builder.ptrtoint(OrelseVal, result_type, name="ifexp_orelse_ptr2int")
# 其余指针 → 整数:隐式转换一律禁止(两分支类型不一致须显式 cast)
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:条件表达式两分支类型不一致({OrelseVal.type} / {result_type});"
f"如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
Gen.builder.branch(MergeBB)
Gen.builder.position_at_start(MergeBB)
result_phi: Any = Gen.builder.phi(result_type, name="ifexp.result")
+11
View File
@@ -6,6 +6,7 @@ if TYPE_CHECKING:
from lib.core.Handles.HandlesBase import BaseHandle
import vast as ast
import llvmlite.ir as ir
from lib.core.SymbolUtils import RequireIntLiteralStorable
class ExprOpsHandle(BaseHandle):
@@ -108,6 +109,10 @@ class ExprOpsHandle(BaseHandle):
vname: str = Node.left.id
if vname in Gen.var_signedness and Gen.var_signedness[vname]:
is_unsigned = True
# 双指针算术 fail-fast 已**下沉到单一收敛点** `Gen.emit_binary_op`
# (LLVMCG/ExprGen.py):BinOp 与 AugAssign(+=、-=、*= …)都经该函数,
# 判据(运算符属算术/位运算/移位集合 且 两操作数都是 LLVM 指针)与报文
# 在那里一次收口。此处不再保留副本,避免两处判据/报文分叉。
result = Gen.emit_binary_op(Op, LeftVal, RightVal, is_unsigned=is_unsigned)
return result
@@ -244,6 +249,12 @@ class ExprOpsHandle(BaseHandle):
elif OpSymbol == '+':
return OperandVal
elif OpSymbol == '-':
# 一元减 + 整数字面量的**联合越界判定**(与 TPV `_parse_factor` 同口径):
# `-` 不属于字面量,幅值合法(≤ 2^64-1)的字面量取负后可能 < -2^63。
if isinstance(Node.operand, ast.Constant) \
and isinstance(Node.operand.value, int) \
and not isinstance(Node.operand.value, bool):
RequireIntLiteralStorable(Gen, self.Trans, Node, -Node.operand.value)
if isinstance(OperandVal.type, ir.IntType):
return Gen.builder.neg(OperandVal, name="neg_result")
elif isinstance(OperandVal.type, (ir.FloatType, ir.DoubleType)):
+3 -1
View File
@@ -339,7 +339,9 @@ class ExprUtils:
attr_name: str = node.attr
AttrInfo: Any = self.Trans.SymbolTable.lookup(attr_name)
if AttrInfo and getattr(AttrInfo, 'IsEnumMember', None):
return ir.IntType(32)
# 枚举成员表达式的 LLVM 类型 = 该枚举的存储宽度(最宽成员类型)
return self.Trans.LlvmGen._enum_int_type(
getattr(AttrInfo, 'EnumName', None), AttrInfo)
return ir.IntType(32)
elif isinstance(node, ast.BinOp):
left_type: ir.Type = self._infer_expr_llvm_type_full(node.left)
+4 -10
View File
@@ -360,16 +360,10 @@ class ForHandle(BaseHandle):
old_var: ir.Value = Gen.variables[var_name]
if isinstance(old_var.type, ir.PointerType):
elem_type: ir.Type = old_var.type.pointee
# 计算元素大小(字节)
elem_size: int = 8 # 默认指针大小
if isinstance(elem_type, ir.IntType):
elem_size = max(1, elem_type.width // 8)
elif isinstance(elem_type, ir.FloatType):
elem_size = 4
elif isinstance(elem_type, ir.DoubleType):
elem_size = 8
elif isinstance(elem_type, ir.PointerType):
elem_size = 8
# 尺寸统一走 Gen._get_struct_size(与 __sizeof__ / alloc 同源,含对齐/填充):
# 原实现只认 Int/Float/Double/Pointer,struct/array 落到「默认 8」,
# 于是 malloc(8) 后整块 store → 堆越界写坏相邻块。
elem_size: int = Gen._get_struct_size(elem_type)
# malloc 堆存储
heap_raw: ir.Value = Gen.builder.call(malloc_func, [ir.Constant(ir.IntType(64), elem_size)], name=f"heap_{var_name}")
heap_ptr: ir.Value = Gen.builder.bitcast(heap_raw, ir.PointerType(elem_type), name=f"heap_{var_name}_ptr")
+10 -4
View File
@@ -898,7 +898,7 @@ class FunctionHandle(BaseHandle):
if '[' in replacement:
SubNode: ast.AST | None = None
try:
SubNode = ast.parse(replacement, mode='eval').body
SubNode = ast.parse(replacement, '<stub:type_expr>', mode='eval').body
except Exception:
SubNode = None
if not isinstance(SubNode, ast.Subscript):
@@ -936,7 +936,7 @@ class FunctionHandle(BaseHandle):
slice_node = self._make_replacement_node(inner_name)
else:
try:
slice_node = ast.parse(inner_name, mode='eval').body
slice_node = ast.parse(inner_name, '<stub:type_expr>', mode='eval').body
except Exception:
slice_node = self._make_name_node(inner_name)
if slice_node is None:
@@ -1627,8 +1627,10 @@ class FunctionHandle(BaseHandle):
if Arg.annotation:
try:
PInfo = self.ResolveAnnotationType(Arg.annotation)
if PInfo and (PInfo.DataConst or PInfo.VarConst):
Gen.var_const_flags[Arg.arg] = True
# const/volatile 形参登记(统一登记入口)。const 判据沿用形参历史
# 条件(IsParam=True,见 BaseGen.RegisterVarQualifiers 说明);
# volatile / pointee-volatile 与 TransPyV 侧同口径。
Gen.RegisterVarQualifiers(Arg.arg, PInfo, Arg.annotation, IsParam=True)
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
@@ -2140,6 +2142,10 @@ class FunctionHandle(BaseHandle):
Gen.var_struct_class = {}
Gen.var_ptr_element = {}
Gen.var_const_flags = {}
# const/volatile 的**函数作用域层**随函数出栈清空(模块级层 global_* 与
# 类字段层 field_* 是持久的,不在此清;见 BaseGen 的登记说明)。
Gen.var_volatile_flags = {}
Gen.var_pointee_volatile_flags = {}
# 恢复保存的 var_type_info
Gen.var_type_info = saved_var_type_info
Gen.var_type_assignments = saved_var_type_assignments
+369 -20
View File
@@ -14,7 +14,9 @@ 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.includes.t import CTypeRegistry
from lib.core.SymbolUtils import ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName, FindStructNameInAnnotationWithContext
from lib.core.SymbolUtils import ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName, FindStructNameInAnnotationWithContext
from lib.core.SymbolUtils import EnumClassIntWidth, ExpandSoftKeywords
from lib.core.SymbolUtils import ClassHasFuncLevelCVTable, IsVtableSlotMethod
# 注解里的存储类 / 限定符标记:这些名字**不是类名**(联合注解里与指针类混写,
@@ -25,6 +27,107 @@ _MARKER_ANNOT_NAMES: frozenset[str] = frozenset({
'State', 'TLS',
})
# 标记基类名(不是真实父类):与 HandlesClassDef._EmitClassLlvm 的 base 跳过表同口径。
_MARKER_BASE_NAMES: frozenset[str] = frozenset({
'Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum',
'Exception', 'Enum',
})
def _HasRealParentClass(Node: ast.ClassDef) -> bool:
"""类是否声明了**真实父类**(排除 Object/CVTable/NoVTable/CEnum/... 标记基类)。"""
for base in getattr(Node, 'bases', None) or []:
base_name: str | None = None
if hasattr(base, 'id'):
base_name = base.id
elif hasattr(base, 'attr'):
base_name = base.attr
elif isinstance(base, ast.Subscript):
if hasattr(base.value, 'id'):
base_name = base.value.id
elif hasattr(base.value, 'attr'):
base_name = base.value.attr
if base_name and base_name not in _MARKER_BASE_NAMES:
return True
return False
def _VtableByClassLevelForImport(Node: ast.ClassDef, Gen: object) -> bool:
"""跨模块导入类的 VtableByClassLevel(忽略方法级 CVTable 标记的 vtable 状态)。
与 HandlesClassDef._resolve_class_flags 的口径一致:类级 @t.CVTable 或
"有真实父类且父类不是 NoVTable" ⇒ True。供 SymbolUtils.IsVtableSlotMethod
判定方法是否占虚表槽(唯一的判据源),使导入侧的虚表槽序与定义模块逐槽相同。
"""
if getattr(Node, 'decorator_list', None):
for decorator in Node.decorator_list:
if isinstance(decorator, ast.Attribute) and decorator.attr == 'CVTable':
return True
if isinstance(decorator, ast.Name) and decorator.id == 'CVTable':
return True
if not _HasRealParentClass(Node):
return False
_novtable_set: set = getattr(Gen, '_cross_module_novtable', None) or set()
for base in getattr(Node, 'bases', None) or []:
base_name: str | None = None
if hasattr(base, 'id'):
base_name = base.id
elif hasattr(base, 'attr'):
base_name = base.attr
if base_name and base_name not in _MARKER_BASE_NAMES:
if base_name in _novtable_set:
return False
return True
def _ParseFailureSite(Exc: BaseException) -> str:
"""解析异常 → `行[:列]` 文本;定位信息完全取不到时返回 `'?'`。
CPython/vast 的 SyntaxError 若按 (msg, (file, lineno, offset, text)) 构造则带
`.lineno`/`.offset`;而 TPC 自研 vast 的 Parser/ExprParser 只把行号写进报文
(`f"... at line {tok.Line}"`,见 lib/vast/Parser.py:826、ExprParser.py:640),
字段为空 ⇒ 必须正则兜底,否则诊断退化成"无行号",无法直接跳到失败行。
"""
Line: int = int(getattr(Exc, 'lineno', 0) or 0)
Col: int = int(getattr(Exc, 'offset', 0) or 0)
if not Line:
Matched: re.Match | None = re.search(r'at line (\d+)', str(Exc))
if Matched:
Line = int(Matched.group(1))
if not Line:
return '?'
return f"{Line}:{Col}" if Col else str(Line)
def _ReportDeclarationParseFailure(FilePath: str, Exc: BaseException, Consequence: str) -> None:
"""声明文件"读 → parse"失败的统一诊断口径(strict fail-fast / 非严格告警)。
诊断必带四要素:**文件路径**、**失败行[:列]**、**异常类型与文本**、**后果说明**
(后果由调用方按点位给出,如"该模块声明将全部缺失,后续可能表现为
`Undefined function`")。原实现(`_LoadModuleDeclarationsFromFile:364-367`)只在
strict 模式调 `Trans.LogWarning`——而 `Trans.Warnings` 全仓只被 append、无人消费
(BaseTranslator.py:55/137),**连 strict 模式都是无声的**;非严格模式更是直接
`return`。于是 `includes/ast/base.vp:168` 的 `enum ASTFlag:` 让整个 base.vp 的
声明(含顶层 `_init_ast`)一个都不注册,下游只报 `Undefined function: '_init_ast'`,
浪费了一轮排查。
fail-fast 依据:仓库有严格模式开关 `lib/constants/config.py:59 mode = "strict"`,
且既有多处同口径 `if _config_mode == "strict": raise`(本文件
`_RegisterSubModuleSha1:547`、`_build_stub_index:2443`、`_TryLoadStructFromStub:2539`
等)——本函数与它们一致,未放宽任何既有 fail-fast。
`FileNotFoundError` 例外:属"isfile 与 open 之间文件被删/被 `--clean` 清掉"的
竞态(先例见本文件 `_build_stub_index:2439-2441`),不是"源码解析失败",
故只告警、不触发 fail-fast。
"""
Site: str = _ParseFailureSite(Exc)
Message: str = (
f"声明解析失败 {FilePath}:{Site} — {type(Exc).__name__}: {Exc};{Consequence}"
)
if _config_mode == "strict" and not isinstance(Exc, FileNotFoundError):
raise ImportError(Message) from Exc
_vlog().warning(Message, "Exception")
class ImportHandle(BaseHandle):
_struct_Load_cache = {}
@@ -353,10 +456,21 @@ class ImportHandle(BaseHandle):
try:
with open(pyi_path, 'r', encoding='utf-8') as f:
ModuleCode = f.read()
ModuleTree = ast.parse(ModuleCode)
# Viper 软关键字层(与 TPC 其余前端入口同一层):`.vp` 源码可含
# `enum X:` / `fn` 等语法糖,直接 vast.parse 会在糖行 SyntaxError →
# 被下面的 except 吞掉并 return → **该模块的全部声明(函数/类/全局)
# 一个都不注册**,调用方随后报 "Undefined function"
# (includes/ast/base.vp 的 `enum ASTFlag:` → 六处 `from .base import …`
# 全部拿不到 `_init_ast`,即本次现场)。`.vpi`/`.pyi` 无糖,展开是空操作。
ModuleTree = ast.parse(ExpandSoftKeywords(ModuleCode), pyi_path)
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
# 统一诊断口径(strict fail-fast / 非严格告警),必带文件+行号+异常+后果。
# 原实现"吞异常 + return"(strict 也只写进无人消费的 Trans.Warnings),
# 是本次 `Undefined function: '_init_ast'` 被伪装成下游症状的直接原因。
_ReportDeclarationParseFailure(
pyi_path, _e,
"该模块的全部声明(函数/类/全局/枚举)不会注册,"
"后续引用方可能表现为 Undefined function / Undefined struct")
return
# 计算源模块名(从 .pyi 文件路径推导)。
@@ -715,7 +829,9 @@ class ImportHandle(BaseHandle):
try:
with open(FullModulePath, 'r', encoding='utf-8') as f:
ModuleCode = f.read()
ModuleTree = ast.parse(ModuleCode)
# 同上:`.vp` 源码须先过 Viper 软关键字层再 vast.parse(否则含
# `enum X:` / `fn` 的模块声明整体丢失,落到 except 的 stub 兜底)。
ModuleTree = ast.parse(ExpandSoftKeywords(ModuleCode), FullModulePath)
if module_name == 'pyzlib':
func_names = [n.name for n in ModuleTree.body if isinstance(n, ast.FunctionDef)]
has_functions_or_classes = False
@@ -854,6 +970,21 @@ class ImportHandle(BaseHandle):
elif 'includes' in FullModulePath and module_name:
self._LoadDeclarationsFromStubLlvm(module_name, Gen)
except Exception as e:
# 同族口径:读/parse 失败必须显式(文件+行号+异常+后果),不允许静默降级。
# 非严格模式仍保留既有 stub 兜底(StubGen 产物声明仍可用);strict 模式
# 由 _ReportDeclarationParseFailure 直接 fail-fast,与 :412 主点一致。
#
# **限定条件**:只有"确实有源码文件却读/解析失败"才算声明解析失败。本函数
# 的 `FullModulePath` 可为 None——TPV 自举工程 includes 为合并形态,源码模块
# (如 lib.Stub.StubMerger 包)不提供裸 .vp 文件,声明由上方
# `_LoadDeclarationsFromStubLlvm` 从 temp/*.stub.ll 加载,属**正常路径**
# (见本函数 :750 附近的长注释);此时 `open(None)` 的 TypeError 与"文件恰好
# 不在"都不应触发 fail-fast。
if FullModulePath and os.path.isfile(FullModulePath):
_ReportDeclarationParseFailure(
FullModulePath, e,
"该模块的源码声明全部未注册,"
"仅能依赖 stub 兜底(非严格模式);声明可能与源码不一致")
if module_name:
self._LoadDeclarationsFromStubLlvm(module_name, Gen)
@@ -1597,6 +1728,13 @@ class ImportHandle(BaseHandle):
if isinstance(arg.value, ast.Attribute):
if getattr(arg.value.value, 'id', None) == 't' and arg.value.attr == 'attr' and arg.attr == 'packed':
IsPacked = True
# 方法级 virtual(`virtual def m(...)` ⇒ 方法装饰器 @t.CVTable)触发类级 vtable:
# 与 HandlesClassDef._resolve_class_flags / TPV _detect_vtable_status 的
# `_has_any_func_cvtable` 分支对齐(跨模块这里只能判"类级 NoVTable /
# 无真实父类"两种可判定形态;父类 vtable 状态由 .pyi 按需注册路径解析)。
if not IsCVTable and ClassHasFuncLevelCVTable(Node):
if IsNoVTable or not _HasRealParentClass(Node):
IsCVTable = True
return (IsGenericClass, IsCenum, IsRenum, IsCpythonObject,
IsCVTable, IsNoVTable, IsPacked)
@@ -1642,6 +1780,14 @@ class ImportHandle(BaseHandle):
EnumTypeNode.Name = ClassName
EnumTypeNode.BaseType = t.CEnum(ClassName)
EnumTypeNode.IsEnum = True
# [跨模块枚举宽度] 读侧重推导:.vpi/.pyi 保留成员注解(`Name: T`),
# 故在本处按"最宽成员类型"重算(与定义端 HandlesClassDef 同一规则源
# EnumClassIntWidth),使跨模块使用同一枚举时宽度一致。本函数会以短名
# 覆盖共享符号表(_shared_sym.json pickle)中的同名条目,若不在此登记
# 宽度,覆盖后即退化为默认 i32(= 上一轮遗留缺口)。旧产物缺注解时
# 自然回落 32(Default),不崩。
EnumWidth: int = EnumClassIntWidth(Node.body, self.Trans.SymbolTable)
EnumTypeNode.set('enum_width', EnumWidth)
self.Trans.SymbolTable.insert(ClassName, EnumTypeNode)
next_enum_value: int = 0
for item in Node.body:
@@ -1672,6 +1818,7 @@ class ImportHandle(BaseHandle):
MemberNode.EnumName = ClassName
MemberNode.Lineno = item.lineno
MemberNode.IsEnumMember = True
MemberNode.set('enum_width', EnumWidth)
self.Trans.SymbolTable.insert(VarName, MemberNode)
self.Trans.SymbolTable.insert(f"{ClassName}.{VarName}", MemberNode)
self.Trans.SymbolTable.insert(f"{ClassName}_{VarName}", MemberNode)
@@ -1776,6 +1923,26 @@ class ImportHandle(BaseHandle):
next_tag = value + 1
RenumTypeNode.RenumVariants = variant_names
return
# 【声明发射重入标记】本函数主体会重入自身:成员收集(CollectAnnAssignMember
# → ResolveAnnotationType → 泛型特化 list[T]/GSListNode[T] → _EmitClassLlvm
# → StructGen._generate_structs → _TryLoadStructFromStub(X)
# → _TryLoadClassMembersFromPyi(X) → 本函数(X))。首字段即指针/容器的类必现
# (llvmlite.Param 的 Attrs、match.MatchOr 的 patterns、stmts.Interactive 的
# body…),且每轮仅 4 帧:本函数 → _TryLoadStructFromStub → 成员加载 → 本函数。
# 两条加载路径的缓存键(带/不带 source_sha1 前缀)互不相同,失败时键还会被
# 清除(_TryLoadStructFromStub 的"仍 opaque ⇒ 删键重试"),缓存守卫挡不住
# ⇒ 无限递归(RecursionError 被上层 except 吞掉,表现为类型解析静默退化)。
# 登记 (ClassName, source_sha1);由 `_TryLoadStructFromStub` 里的"顺带加载成员"
# 那一步短路(该边是递归环的唯一闭合边:结构体 body 加载会连带成员加载 → 又回到
# 本函数);本函数自身**不**短路——重入的成员收集是"把表填起来"的唯一途径,
# 消费者(重入编译的其他模块方法体按名查字段)必须能拿到它。
# 放在 CEnum/REnum 早退(上方 return)之后:那两个分支不加载成员/结构体。
_emit_inprog: set | None = getattr(Gen, '_extern_decl_emit_in_progress', None)
if _emit_inprog is None:
_emit_inprog = set()
Gen._extern_decl_emit_in_progress = _emit_inprog
_emit_key: tuple = (ClassName, source_sha1)
_emit_inprog.add(_emit_key)
# 先初始化 class_methods/class_members 等容器,确保 _TryLoadStructFromStub
# 和 _get_or_create_struct 中的 IsEnum 守卫(clean_name not in class_methods)
# 能正确识别 AST 节点类名(避免 ASTKind 枚举名冲突时把节点类当枚举返回 i32)
@@ -1814,6 +1981,11 @@ class ImportHandle(BaseHandle):
# 会被解析为 opaque struct,LLVM 报 "invalid type for function argument"。
# 字段(AnnAssign)仍正常加载,确保继承字段(如 GSListNode.Next)可被展平。
if not IsGenericClass:
# 虚表槽筛选:方法级 `novirtual def`(⇒ @t.NoVTable)不进槽、方法级
# `virtual def`(⇒ @t.CVTable)进槽——判据为 SymbolUtils.IsVtableSlotMethod
# (唯一规则源,与定义端 HandlesClassDef / TPV _should_method_be_virtual 同口径),
# 保证跨模块虚表槽序与定义模块逐槽一致。
_VtByClassLevel: bool = _VtableByClassLevelForImport(Node, Gen)
for _item in Node.body:
if isinstance(_item, ast.FunctionDef):
_has_methods_prescan = True
@@ -1825,7 +1997,8 @@ class ImportHandle(BaseHandle):
if _MethodName in ('__new__', '__init__', '__before_init__'):
continue
_FuncFullName = f"{ClassName}.__init__" if _MethodName == "__init__" else f"{ClassName}.__call__" if _MethodName == "__call__" else f"{ClassName}.{_MethodName}"
if _FuncFullName not in Gen.class_methods[ClassName]:
_IsSlot: bool = (not IsCVTable) or IsVtableSlotMethod(_item, _VtByClassLevel)
if _IsSlot and _FuncFullName not in Gen.class_methods[ClassName]:
Gen.class_methods[ClassName].append(_FuncFullName)
if _has_methods_prescan and IsCVTable:
Gen._cross_module_vtable_classes.add(ClassName)
@@ -2016,10 +2189,14 @@ class ImportHandle(BaseHandle):
# 构造函数(__new__/__init__/__before_init__)不放入 vtable,
# 避免子类与基类 vtable 大小不一致导致虚分派索引偏移。
# register_method 内部也会过滤,此处仅跳过 class_methods.append。
# 方法级修饰符(virtual def/novirtual def)⇒ 是否占槽按
# SymbolUtils.IsVtableSlotMethod(唯一规则源)判定,与定义模块逐槽一致。
_IsSlot2: bool = (not IsCVTable) or IsVtableSlotMethod(
item, _VtableByClassLevelForImport(Node, Gen))
if MethodName not in ('__new__', '__init__', '__before_init__'):
if FuncFullName not in Gen.class_methods[ClassName]:
if _IsSlot2 and FuncFullName not in Gen.class_methods[ClassName]:
Gen.class_methods[ClassName].append(FuncFullName)
Gen.register_method(ClassName, FuncFullName)
Gen.register_method(ClassName, FuncFullName, _IsSlot2)
is_item_static = any(isinstance(d, ast.Name) and d.id == 'staticmethod' for d in item.decorator_list)
is_item_property = any(isinstance(d, ast.Name) and d.id == 'property' for d in item.decorator_list)
is_item_classmethod = any(isinstance(d, ast.Name) and d.id == 'classmethod' for d in item.decorator_list)
@@ -2158,6 +2335,13 @@ class ImportHandle(BaseHandle):
# 同时处理父类 class_members 全为占位符的情况(清除后重新加载)。
_any_parent_loaded: bool = False
_all_inherited: list[tuple] = []
# 展平完整性判定(供 `_members_flattened` 登记):仅当"每个声明父类都已在
# class_members 中就绪且其自身已展平",或"占位符路径已按 struct 槽位补齐
# 继承前缀"时才为 True。未完成(典型:声明期 struct 尚 opaque / 父类所在
# 模块 stub 未就绪)时不登记,后续查询会重跑本节把继承前缀补齐
# —— 使展平结果与"父类/struct 就绪的先后顺序"无关。
_flat_complete: bool = not _ParentClasses
_placeholder_applied: bool = False
# 按继承源去重的跨父类共享 seen 集合(必须在 for 循环外,跨所有父类
# 共享——否则每个父类新建 seen,菱形祖先(如 ClassGenBase)被每个
# 继承它的 ClassGen* 父类各展平一次 → 重复(Generator 曾 707 字段)
@@ -2270,6 +2454,14 @@ class ImportHandle(BaseHandle):
_inherited_ph: list[tuple] = [(f'__inherited_{i}', _st.elements[i]) for i in range(_num_inherited)]
# 占位符路径同样整体前置(继承字段在 struct 头部),与主路径保持一致
Gen.class_members[ClassName] = _inherited_ph + Gen.class_members[ClassName]
_placeholder_applied = True
if _ParentClasses:
# 完整展平的判据:占位符路径已按 struct 槽位补齐继承前缀,或
# 每个声明父类都已在 class_members 中就绪且其自身已展平。
_flat_complete = _placeholder_applied or all(
(_pc in Gen.class_members and _pc in Gen._members_flattened)
for _pc in _ParentClasses if _pc
)
# 成员收集 + 继承展平完成 → 解除收集中保护,并用完整 class_members 设置 body。
# 必须在此显式触发一次 _generate_structs():保护期内(上方 add)所有重入的
# _generate_structs 都跳过了本类,若此处不再触发,本类 struct 将保持 opaque
@@ -2277,6 +2469,10 @@ class ImportHandle(BaseHandle):
# 本保护要消除的目标。
Gen._collecting_members_set.discard(ClassName)
Gen._generate_structs()
if _flat_complete:
# 登记"该表已按父类展平",供 _TryLoadClassMembersFromPyi 的 guard 与
# _member_base 的查询前补齐使用。未登记 ⇒ 后续任一查询都可重跑本节补齐。
Gen._members_flattened.add(ClassName)
# ============================================================
# 跨模块 vtable 槽布局与定义模块一致:父类方法在前(子类覆盖放父类槽位),
# 子类新增方法在后。定义模块(HandlesClassDef)已按此合并,跨模块若只
@@ -2301,12 +2497,18 @@ class ImportHandle(BaseHandle):
for _m in Gen.class_methods.get(ClassName, []):
_mn: str = _m.split('.')[-1] if '.' in _m else _m
_child_method_map[_mn] = _m
# 从 .vpi 的 FunctionDef 补充子类方法映射(覆盖方法可能尚未注册)
# 从 .vpi 的 FunctionDef 补充子类方法映射(覆盖方法可能尚未注册)。
# 方法级 `novirtual def`(⇒ @t.NoVTable)不占槽,**不得**用它覆盖父类槽位
# ——与 TPV _collect_vtable_methods 的 child_nvt 分支同口径(子类标记
# NoVTable 时槽位仍指向实际定义类的实现)。
_VtByClassLevelC: bool = _VtableByClassLevelForImport(Node, Gen)
for _item_m in getattr(Node, 'body', []):
if isinstance(_item_m, ast.FunctionDef):
_mn2: str = _item_m.name
if _mn2 in ('__new__', '__init__', '__before_init__'):
continue
if not IsVtableSlotMethod(_item_m, _VtByClassLevelC):
continue
_child_method_map[_mn2] = f"{ClassName}.{_mn2}"
_new_methods: list[str] = []
_covered_names: set[str] = set()
@@ -2348,6 +2550,8 @@ class ImportHandle(BaseHandle):
bw = bitfields.get(name, 0)
Gen.class_member_bitoffsets[ClassName][name] = current_bit_offset
current_bit_offset += bw
# 声明发射结束:解除重入标记(与上方 add 配对)。
_emit_inprog.discard(_emit_key)
def _build_stub_index(self) -> None:
"""扫描所有 .stub.ll 文件构建 class_name → 条目列表索引
@@ -2408,6 +2612,28 @@ class ImportHandle(BaseHandle):
_vlog().warning(f"构建 stub 索引失败: {_e}", "Exception")
self._stub_index_scanned.add(stub_path)
@staticmethod
def _ExternDeclEmitting(Gen: LlvmGeneratorMixin, class_name: str, source_sha1: str | None) -> bool:
"""该类(同名同源)的跨模块声明发射是否**正在进行中**(递归重入判定)。
标记由 `_EmitExternalClassDeclLlvm` 主体开头登记、末尾解除(键
`(ClassName, source_sha1)`)。定义模块 sha1 未知(None)时按类名保守判定:
None 与 '<sha1>' 两种形态可能指同一个类,宁可本次漏加载(由最外层收尾)
也不递归——`_EmitExternalClassDeclLlvm` → `_TryLoadStructFromStub(X)`
→ `_TryLoadClassMembersFromPyi(X)` → `_EmitExternalClassDeclLlvm(X)`
每轮仅 4 帧,且两条加载路径的缓存键(带/不带 sha1 前缀)互不相同、
失败时键还会被清除("仍 opaque ⇒ 删键重试")⇒ 缓存守卫挡不住,
会一直递归到 RecursionError(被上层 except 吞掉 ⇒ 类型解析静默退化)。
"""
_inprog: set | None = getattr(Gen, '_extern_decl_emit_in_progress', None)
if not _inprog:
return False
if (class_name, source_sha1) in _inprog:
return True
if source_sha1 is None:
return any(_k[0] == class_name for _k in _inprog)
return False
def _TryLoadStructFromStub(self, class_name: str, Gen: LlvmGeneratorMixin, source_sha1: str | None = None) -> None:
# 缓存键包含 Gen 实例标识,确保每个模块的 Gen 都能独立加载 struct 定义,
# 同时仍能防止同一 Gen 内的递归重入。原先使用纯 class_name 作为键会导致
@@ -2503,7 +2729,17 @@ class ImportHandle(BaseHandle):
raise
_vlog().warning(f"处理导入失败: {_e}", "Exception")
stub_filename = actual_sha1 + '.stub.ll'
self._TryLoadClassMembersFromPyi(class_name, stub_filename, Gen)
# 【递归环闭合边 · 重入短路】该类的声明发射尚未结束(`_EmitExternalClassDeclLlvm`
# 正在跑:见其主体开头的 `_extern_decl_emit_in_progress` 登记)⇒ 本调用是环内一环:
# 发射(X) → _TryLoadStructFromStub(X) →(此处)成员加载(X) → 发射(X) …
# 每轮仅 4 帧,且两条加载路径的缓存键(带/不带 source_sha1 前缀)互不相同、
# 失败时键还会被清除("仍 opaque ⇒ 删键重试")⇒ 缓存守卫挡不住 ⇒ 无限递归。
# 短路对象**只限"顺带加载成员"这一步**:上面的 struct body 解析照常执行
# (发射主体依赖它提前 set_body —— 否则 llvmlite `__len__` 的
# `assert self.elements is not None` 直接炸,见 _EmitExternalFuncDeclLlvm
# 的 `if _new_struct:`);成员由外层发射收尾统一收集。
if not ImportHandle._ExternDeclEmitting(Gen, class_name, source_sha1):
self._TryLoadClassMembersFromPyi(class_name, stub_filename, Gen)
# 根本修复:加载后 struct 仍为 opaque 时(如选中 'type opaque' 占位条目、
# 或 set_body 未生效),不保留成功缓存。保留会导致同 Gen 内后续调用永久
# 跳过补载,opaque 状态被固化,vtable 分派 GEP 直接崩溃。
@@ -2515,6 +2751,82 @@ class ImportHandle(BaseHandle):
except KeyError:
pass
def EnsureClassMembersFlattened(self, class_name: str, Gen: LlvmGeneratorMixin) -> None:
"""查询前补齐:确保 `class_members[class_name]` 已按父类展平(幂等、顺序无关)。
背景:`_EmitExternalClassDeclLlvm` 的展平只在"父类成员表 + 声明期 struct
body 都已就绪"时才能算出继承前缀;若当时 struct 仍 opaque(定义模块 stub
尚未生成)或父类未就绪,表就停在"自有字段",而长度随之与 struct 元素数
不再自洽(delta = 缺失槽数)。此后 `_TryLoadClassMembersFromPyi` 的原
guard(`len>0` 即短路)会把这份残缺表永久冻结 ⇒ 该类**自有字段**的 GEP
下标缺了继承前缀而整体前移(`ExceptHandler.type` 返回 1 而非 8)。
本方法是该冻结的解除点(与 `FuncGen._member_base` 的查询侧调用配套):
只有"未登记已展平 且 该类有父类 且 struct 与成员表长度不自洽(含 struct
尚未登记)"才重跑展平;对已展平类、无父类类(含 REnum 变体等非继承键)
是纯内存判断直接返回,真正重跑的类另按依赖签名去重(见下)。
以 `(struct 元素数, 成员表长度)` 作为"依赖状态签名":同一签名下补齐无效
时不再重复尝试(避免依赖长期不就绪时在热路径反复重建);依赖一旦变化
(struct 被 `_generate_structs` 修正、或表被别处改动)签名变化即重试。
"""
if Gen is None:
return
_flat: set | None = getattr(Gen, '_members_flattened', None)
if _flat is None or class_name in _flat:
return
_inprog: set | None = getattr(Gen, '_members_flatten_in_progress', None)
if _inprog is None or class_name in _inprog:
return
if class_name in (getattr(Gen, '_collecting_members_set', None) or ()):
# 该类的字段正在收集中(重入路径)⇒ 不在此处重跑展平,
# 交由收集完成后的 `_EmitExternalClassDeclLlvm` 正常收尾。
return
_parents: list = (getattr(Gen, 'class_parents', {}) or {}).get(class_name) or []
if not _parents:
# 无父类 ⇒ 展平平凡成立。表非空时登记(后续查询直接短路);表为空时
# 不登记、也不在此补齐(那是"表未加载",由 _get_member_offset 既有的
# 懒加载路径负责,不在本专项范围内)。
if Gen.class_members.get(class_name):
_flat.add(class_name)
return
_tbl: list = Gen.class_members.get(class_name) or []
_st = Gen.structs.get(class_name)
_num_elements: int = -1
if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None:
_num_elements = len(_st.elements)
if (_num_elements - len(_tbl)) in (0, 1):
# struct 布局与成员表长度自洽(无 vtable 槽 / 恰一个 vtable 槽)
# ⇒ 已展平:登记,后续查询直接短路。
_flat.add(class_name)
return
_sig: tuple[int, int] = (_num_elements, len(_tbl))
_sigs: dict = getattr(Gen, '_members_flatten_repair_sig', None)
if _sigs is None:
_sigs = {}
Gen._members_flatten_repair_sig = _sigs
if _sigs.get(class_name) == _sig:
# 同一依赖状态下补齐无效(父类/struct 仍未就绪)⇒ 不重复尝试。
return
_sigs[class_name] = _sig
if not hasattr(self, 'Trans') or self.Trans is None:
return
_sha1: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(class_name)
_cands: list[str] = [f"{_sha1}.stub.ll"] if _sha1 else []
if not _cands:
_sha1_map: dict = getattr(Gen, 'ModuleSha1Map', {}) or {}
_cands = [f"{_s}.stub.ll" for _s in _sha1_map.values()]
_inprog.add(class_name)
try:
for _stub in _cands:
try:
if self._TryLoadClassMembersFromPyi(class_name, _stub, Gen):
break
except Exception:
continue
finally:
_inprog.discard(class_name)
def _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen: LlvmGeneratorMixin,
force: bool = False) -> bool:
"""从 .vpi/.pyi 声明文件加载类成员。返回 True 表示在 pyi 中找到该类并完成处理。
@@ -2526,19 +2838,42 @@ class ImportHandle(BaseHandle):
"""
ProjectRoot = self._find_project_root()
temp_dir = os.path.join(ProjectRoot, 'temp')
if (not force) and class_name in Gen.class_members and len(Gen.class_members[class_name]) > 0:
# guard 命中:class_members 已存在(可能从别处预置、不完整),但
# class_parents 可能未设置 → 多继承展平(_FlattenExt)的递归/anc_names
# guard(2026-xx 收紧):只在"表非空 **且已登记为已展平**"时短路。原实现
# 只看 `len>0`,于是"停在自有字段"的残缺表(展平未完成)一旦生成就再也不能
# 补齐(顺序敏感)——这正是自有字段下标缺继承前缀的成因。未展平的表允许
# 重跑 `_EmitExternalClassDeclLlvm`(其自有字段收集按名去重、继承前缀按名
# 去重,重跑幂等),从而与父类/struct 的就绪顺序无关。
# 【重入短路 · 与成员表长度解耦】该类**正在**被 `_EmitExternalClassDeclLlvm`
# 收集字段(`_collecting_members_set` 置位于本文件 :2054、解除于 :2481)时,
# 本次调用必是递归重入,直接返回、交由最外层收尾。
# 为什么判据不能带 `len(class_members)>0`:类内**第一个**字段的注解解析就会
# 重入本方法 —— CollectAnnAssignMember → ResolveAnnotationType → 泛型特化
# (list[T] 等)→ _EmitClassLlvm → StructGen._generate_structs
# → _TryLoadStructFromStub(X) → 本方法(X)。此刻 class_members[X] 仍为空
# (第一个字段尚未收集完),旧判据(要求 len>0)放行 ⇒ 无限递归(每轮 4 帧):
# _TryLoadClassMembersFromPyi(X) → _EmitExternalClassDeclLlvm(X)
# → _TryLoadStructFromStub(X) → _TryLoadClassMembersFromPyi(X) …
# 现场(2026-09-24 深度探针):match.MatchOr 的 patterns、stmts.Interactive 的
# body 等"首字段即指针/容器"的类;RecursionError 被上层 except 吞掉后表现为
# `WARN: … 忽略异常 maximum recursion depth exceeded` + 类型解析静默退化
# (失败的 typedef 报文、注解赋值 RHS 求值为空等次生现象)。
_collecting: set | None = getattr(Gen, '_collecting_members_set', None)
if ((not force) and (
(_collecting is not None and class_name in _collecting)
or (class_name in Gen.class_members
and len(Gen.class_members[class_name]) > 0
and class_name in Gen._members_flattened))):
# guard 命中:class_members 已存在(可能从别处预置、不完整)或该类正在
# 收集中;但 class_parents 可能未设置 → 多继承展平(_FlattenExt)的递归/anc_names
# 失效 → 菱形去重失败 → class_members 不完整(Generator 89/83 vs 114)
# → this_adjust 错位崩溃。此处补解析 .vpi 设置 class_parents(不重载
# class_members,避免覆盖可能更完整的已有数据)。
_path0: str = os.path.join(temp_dir, stub_filename.replace('.stub.ll', '.vpi'))
try:
_pyi_b0: str = stub_filename.replace('.stub.ll', '.vpi')
_path0: str = os.path.join(temp_dir, _pyi_b0)
_tree0: ast.AST | None = getattr(self, '_pyi_cache', {}).get(_path0)
if _tree0 is None and os.path.isfile(_path0):
with open(_path0, 'r', encoding='utf-8') as _fh0:
_tree0 = ast.parse(_fh0.read())
_tree0 = ast.parse(_fh0.read(), _path0)
getattr(self, '_pyi_cache', {})[_path0] = _tree0
if _tree0 is not None:
for _n0 in _tree0.body:
@@ -2553,8 +2888,14 @@ class ImportHandle(BaseHandle):
Gen.class_parents = {}
Gen.class_parents[class_name] = _b0
break
except Exception:
pass
except Exception as _e0:
# 同族口径:此处原为 `except Exception: pass`(静默)。class_parents
# 未回填 ⇒ 多继承/菱形展平不完整 ⇒ class_members 长度不确定 ⇒
# this_adjust 错位。改为显式诊断(strict fail-fast / 非严格告警)。
_ReportDeclarationParseFailure(
_path0, _e0,
f"类 '{class_name}' 的 class_parents 未能回填,"
"多继承展平可能不完整(字段下标/this_adjust 可能错位)")
return False
pyi_basename = stub_filename.replace('.stub.ll', '.vpi')
pyi_path = os.path.join(temp_dir, pyi_basename)
@@ -2576,9 +2917,15 @@ class ImportHandle(BaseHandle):
return False
try:
with open(pyi_path, 'r', encoding='utf-8') as f:
self._pyi_cache[pyi_path] = ast.parse(f.read())
self._pyi_cache[pyi_path] = ast.parse(f.read(), pyi_path)
except Exception as e:
_vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e)
# 同族口径:原告警不带文件名("忽略异常"),改为显式诊断
# (strict fail-fast / 非严格告警)。.vpi 是编译器无糖产物,
# 解析失败属内部不变量被破坏,非严格模式仍缓存 None 并返回 False。
_ReportDeclarationParseFailure(
pyi_path, e,
f"类 '{class_name}' 的成员/父类信息不会加载,"
"class_members 可能残缺(字段下标/this_adjust 可能错位)")
self._pyi_cache[pyi_path] = None
return False
@@ -2621,6 +2968,8 @@ class ImportHandle(BaseHandle):
# 强制重载:清空按类缓存的成员数据,重建出与定义模块一致的完整布局。
# 不清空则 dedup 只在末尾追加缺失字段 → 字段顺序与 struct elements 错位。
Gen.class_members[class_name] = []
# 同步撤销"已展平"登记:重建若因依赖未就绪而不完整,须允许后续查询再补齐。
Gen._members_flattened.discard(class_name)
for _gd_attr in ('class_member_defaults', 'class_member_signeds', 'class_member_bitfields',
'class_member_byteorders', 'class_member_bitoffsets'):
_gd: dict | None = getattr(Gen, _gd_attr, None)
+11 -3
View File
@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo
from lib.core.Handles.HandlesExpr import DefineUseWidthFromLLVMType
import vast as ast
import llvmlite.ir as ir
@@ -96,7 +97,11 @@ class ReturnHandle(BaseHandle):
IsRetPtr = True
ExpectedType: ir.Type = ReturnTypeInfo.ToLLVM(Gen) if ReturnTypeInfo and ReturnTypeInfo.BaseType else Gen._CType2LLVM('int', IsRetPtr)
expected_types.append(ExpectedType)
Val: ir.Value | None = self.HandleExprLlvm(val)
# 返回注解即"使用处宽度":CDefine 常量在此按返回注解的位宽物化
# (更宽 ⇒ 升宽物化;更窄 ⇒ fail-fast);非常量返回值不受影响
# (ConstWidthHint 只被常量物化分支读取)。
Val: ir.Value | None = self.HandleExprLlvm(
val, ConstWidthHint=DefineUseWidthFromLLVMType(ExpectedType))
if Val is None:
if isinstance(ExpectedType, ir.PointerType):
Val = ir.Constant(ExpectedType, None)
@@ -131,9 +136,12 @@ class ReturnHandle(BaseHandle):
Gen.emit_return(result)
return
if Node.value:
Val: ir.Value | None = self.HandleExprLlvm(Node.value)
# 本函数返回类型即"使用处宽度":先取回返回类型再翻译值,供 CDefine 常量
# 按该位宽物化(更宽 ⇒ 升宽物化;更窄 ⇒ fail-fast)。
ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None
Val: ir.Value | None = self.HandleExprLlvm(
Node.value, ConstWidthHint=DefineUseWidthFromLLVMType(ret_type))
if Val:
ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None
if isinstance(ret_type, ir.IdentifiedStructType) and isinstance(Val, ir.Constant) and isinstance(Val.type, ir.PointerType) and Val.constant is None:
if ret_type.elements:
zero_val: ir.Constant = ir.Constant(ret_type, [
+7 -90
View File
@@ -5,37 +5,13 @@ import llvmlite.ir as ir
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin
from lib.core.Handles.HandlesBase import BaseHandle, BuiltinTypeMap
from lib.core.Handles.HandlesBase import BaseHandle
from lib.includes.t import CTypeRegistry
from lib.core.SymbolUtils import IsTModule
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
def _IsTypeExpr(node: ast.AST | None) -> bool:
"""判断 AST 节点是否为类型表达式(类型转换的目标,如 Name/Attribute/Subscript/BinOp|)"""
return (
isinstance(node, ast.Name)
or isinstance(node, ast.Attribute)
or isinstance(node, ast.Subscript)
or (isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr))
)
def _IsCastWrappingDeref(rhs: ast.AST | None, deref_node: ast.Call) -> bool:
"""判断 rhs 是否为"类型转换链包裹 c.Deref"(如 (T | t.CPtr)(t.CPtr(c.Deref(p))))。
允许在 c.Deref 处于类型转换调用链中时仍从赋值注解推断加载类型;
ifexp/compare/binop 包裹(非转换调用)不放行,避免把 i8* 错 bitcast 成
其他位宽加载(见 _infer_deref_target_type 文档)。
"""
if rhs is deref_node:
return True
if isinstance(rhs, ast.Call) and len(rhs.args) == 1 and _IsTypeExpr(rhs.func):
return _IsCastWrappingDeref(rhs.args[0], deref_node)
return False
class CSpecialCallHandle(BaseHandle):
def _HandleCIfLlvm(self, Node: ast.Call) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
@@ -251,7 +227,6 @@ class CSpecialCallHandle(BaseHandle):
if arg_val is None:
return ir.Constant(ir.IntType(32), 0)
if isinstance(arg_val.type, ir.PointerType):
pointee: ir.Type = arg_val.type.pointee
# 嵌套 Deref 的内层:加载为指针 (i8*),不推断目标类型
# 因为结果会被外层 Deref 再次解引用
NestedLoadPtr: bool = getattr(Gen, '_nested_deref_load_ptr', False)
@@ -261,75 +236,17 @@ class CSpecialCallHandle(BaseHandle):
Loaded = Gen._load(casted, name="deref")
if Loaded is not None:
return Loaded
# void* (i8*) 需要根据赋值目标类型推断加载类型
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
target_type: ir.Type | None = self._infer_deref_target_type(Gen, Node)
if target_type is not None and not isinstance(target_type, ir.IntType):
# bitcast i8* -> target_type* then load
casted: ir.Value = Gen.builder.bitcast(arg_val, ir.PointerType(target_type), name="deref_cast")
Loaded = Gen._load(casted, name="deref")
if Loaded is not None:
return Loaded
elif target_type is not None and isinstance(target_type, ir.IntType):
# 目标是整数类型 (如 i32/i64): bitcast 并加载
if target_type.width != 8:
casted: ir.Value = Gen.builder.bitcast(arg_val, ir.PointerType(target_type), name="deref_cast")
Loaded = Gen._load(casted, name="deref")
if Loaded is not None:
return Loaded
# 读取宽度只由指针的 pointee 类型决定(_load 按 arg_val.type.pointee
# 加载一个元素):pointee 是 i8(t.CPtr / void* / str / bytes)即恒读
# 1 字节,不再按赋值目标注解反推宽度——那会让同一处 c.Deref(i8*) 的
# 读取宽度取决于它是否恰好出现在带注解赋值的 RHS 里。需要 4/8 字节等
# 更宽读取时,由调用点显式给出元素类型指针后再解引用
# (c.Deref((int | t.CPtr)(p)) / c.Deref(t.CUInt64T(p, t.CPtr)))。
Loaded = Gen._load(arg_val, name="deref")
if Loaded is not None:
return Loaded
return arg_val
def _infer_deref_target_type(self, Gen: LlvmGeneratorMixin, DerefNode: ast.Call | None = None) -> ir.Type | None:
"""从当前赋值上下文推断 c.Deref 的目标加载类型。
当用户写 v: int = c.Deref(ptr) 时,从赋值注解推断目标类型。
注意:仅当 c.Deref 是赋值的直接 RHS,或 RHS 是类型转换链包裹
(如 `(T | t.CPtr)(t.CPtr(c.Deref(p)))`)时才推断;若 c.Deref 嵌套在
ifexp/compare/binop 中(如 `a = 1 if c.Deref(p) == 65 else 0`),
不应推断,否则会把 i8* 错误 bitcast 为 i32* 加载 4 字节。"""
assign_node: ast.AST | None = getattr(self.Trans, '_current_assign_node', None)
if not assign_node:
return None
# 仅当 c.Deref 是赋值的直接 RHS 或处于类型转换链中时才推断类型
if DerefNode is not None:
rhs: ast.AST | None = getattr(assign_node, 'value', None)
if rhs is not DerefNode and not _IsCastWrappingDeref(rhs, DerefNode):
return None
ann: ast.AST | None = getattr(assign_node, 'annotation', None)
if not ann:
return None
# 收集候选类型名:Name / Attribute / 联合 BinOp| 的所有 side。
# 逐个尝试解析,取第一个能解析为 LLVM 类型的(如
# `vthreading.Thread | t.CPtr` → 'Thread' 未注册 → 'CPtr' → i8*)。
candidates: list[str] = []
if isinstance(ann, ast.Name):
candidates.append(ann.id)
elif isinstance(ann, ast.Attribute):
candidates.append(ann.attr)
elif isinstance(ann, ast.BinOp) and isinstance(ann.op, ast.BitOr):
# 处理 int | t.CPtr 形式的联合类型注解
for side in (ann.left, ann.right):
if isinstance(side, ast.Name):
candidates.append(side.id)
elif isinstance(side, ast.Attribute):
candidates.append(side.attr)
for ann_name in candidates:
# 使用 BuiltinTypeMap 统一查询(同时支持 Python 类名和字符串速记)
entry: tuple[type, int] | None = BuiltinTypeMap.Get(ann_name)
if entry is not None:
ctype_cls: type = entry[0]
llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls)
if llvm_str:
resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str)
if resolved:
return resolved
# 检查是否是已注册的结构体类型
if ann_name in Gen.structs:
return Gen.structs[ann_name]
return None
def _HandleCPtrToIntLlvm(self, Node: ast.Call) -> ir.Value:
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
if not Node.args:
+6 -1
View File
@@ -141,8 +141,13 @@ class WithHandle(BaseHandle):
current_depth: int = sum(len(pl) for pl in Gen._with_provides_stack)
new_fields: int = len(Gen.class_provides[ClassName])
if current_depth + new_fields > 32:
# 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行
_lineno: int = int(getattr(Node, 'lineno', 0) or 0)
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _line_offset > 0 and _lineno > _line_offset:
_lineno -= _line_offset
raise SyntaxError(
f"[TransPyC] with provider stack overflow at line {Node.lineno}: "
f"[TransPyC] with provider stack overflow at line {_lineno}: "
f"current depth {current_depth} + new {new_fields} fields > max 32. "
f"Class '{ClassName}' provides too many fields or with nesting too deep."
)
+12 -12
View File
@@ -257,10 +257,10 @@ class BaseGenMixin:
# True=每个 define 发 DISubprogram + 入口首条指令发 DILocation。
self.dwarf_enabled: bool = False
self.DebugInfo: Any = None
# Ctrl+C 调用栈(--ctrlc-stack):仅在翻译 includes/dbgstack.vp 这一个模块时
# Ctrl+C 调用栈(--traceback):仅在翻译 includes/traceback.vp 这一个模块时
# 置 True —— 落成 .ll 文本前为本模块注入 @llvm.global_ctors 构造器调用其
# Install()(见 _RegisterDbgStackInstallViaCtor)。默认 False(零影响)。
self.ctrlc_stack_ctor: bool = False
# Install()(见 _RegisterTracebackInstallViaCtor)。默认 False(零影响)。
self.traceback_ctor: bool = False
pi: dict[str, bool | int] = self._platform_info
# 根据目标平台设置宏(声明式,从三元组推导)
if pi['is_windows']:
@@ -2100,12 +2100,12 @@ class BaseGenMixin:
gv.linkage = 'appending'
gv.initializer = ir.Constant(arr_ty, [entry_val])
def _RegisterDbgStackInstallViaCtor(self) -> None:
"""用 @llvm.global_ctors 在进程启动期调用本模块的 Install()(--ctrlc-stack)。
def _RegisterTracebackInstallViaCtor(self) -> None:
"""用 @llvm.global_ctors 在进程启动期调用本模块的 Install()(--traceback)。
仅在"本翻译单元就是 includes/dbgstack.vp"时由 StructGen.finalize() 调用
(ctrlc_stack_ctor 由 Phase2Translator._translate_file 按源文件名置位)。
生成 internal void @__dbgstack_install_ctor()(entry 块直接 call 本模块的
仅在"本翻译单元就是 includes/traceback.vp"时由 StructGen.finalize() 调用
(traceback_ctor 由 Phase2Translator._translate_file 按源文件名置位)。
生成 internal void @__traceback_install_ctor()(entry 块直接 call 本模块的
Install() 并忽略其 int 返回值),再写入 appending linkage 的
@llvm.global_ctors —— 每模块一份,链接器按 appending 语义拼进 .init_array,
进程启动时执行一次。
@@ -2121,22 +2121,22 @@ class BaseGenMixin:
install_fn: ir.Function | None = self._find_function('Install')
if install_fn is None:
raise RuntimeError(
'--ctrlc-stack: 在 dbgstack 模块内找不到 Install() 函数,'
'--traceback: 在 traceback 模块内找不到 Install() 函数,'
'无法注入启动期构造器(不静默降级)')
i32: ir.IntType = ir.IntType(32)
i8p: ir.PointerType = ir.PointerType(ir.IntType(8))
void_fnptr: ir.PointerType = ir.PointerType(ir.FunctionType(ir.VoidType(), []))
ctor: ir.Function = ir.Function(
self.module, ir.FunctionType(ir.VoidType(), []),
name=self._UniqueGlobalName('__dbgstack_install_ctor'))
name=self._UniqueGlobalName('__traceback_install_ctor'))
ctor.linkage = 'internal'
cb: ir.IRBuilder = ir.IRBuilder(ctor.append_basic_block(name='entry'))
cb.call(install_fn, []) # Install() 无参;返回值(int)有意忽略
cb.ret_void()
if 'llvm.global_ctors' in self.module.globals:
raise RuntimeError(
'--ctrlc-stack: 模块内已存在 @llvm.global_ctors,'
'无法注入 dbgstack 启动期构造器(不静默降级)')
'--traceback: 模块内已存在 @llvm.global_ctors,'
'无法注入 traceback 启动期构造器(不静默降级)')
elem_ty: ir.LiteralStructType = ir.LiteralStructType([i32, void_fnptr, i8p])
arr_ty: ir.ArrayType = ir.ArrayType(elem_ty, 1)
entry_val: ir.Constant = ir.Constant(elem_ty, [
+54 -6
View File
@@ -46,7 +46,7 @@ class ExprGenMixin:
return self.builder.bitcast(global_var, ir.PointerType(ir.IntType(8)), name="str")
return ir.Constant(ir.IntType(32), int(value))
def emit_binary_op(self, op: str, left: ir.Value, right: ir.Value, is_unsigned: bool = False) -> ir.Value:
def emit_binary_op(self, op: str, left: ir.Value, right: ir.Value, is_unsigned: bool = False, is_aug: bool = False) -> ir.Value:
if isinstance(left.type, ir.PointerType) and isinstance(right.type, ir.IntType):
# 数组指针需要先 decay 为元素指针,否则 GEP 会以整个数组为步长
# 例如 [64 x i8]* + 1 会前进 64 字节而非 1 字节
@@ -65,6 +65,46 @@ class ExprGenMixin:
left = self.builder.zext(left, ir.IntType(64), name="int2ptrint2")
if op == '+' or op == 'Add':
return self.builder.gep(right, [left], name="ptr_add_rev")
# ============================================================
# 双指针算术全域 fail-fast(**单一收敛点**)
#
# BinOp(HandlesExprOps._HandleBinOpLlvm)与 AugAssign
# (HandlesAugAssign._HandleAugAssignLlvm,+=、-=、*= …)都经本函数,
# 故在此一次收口,避免多点各写一份判据/报文。
# 判据:运算符属二元算术/位运算/移位集合,且**两操作数都是 LLVM 指针**。
# 本语言的 str 是 i8*,没有 `+` 拼接语义;指针 ± 指针也不是 GEP 语义
# (GEP 只接受「一指针一整数」,已在上方两个 ptr/int 分支处理并返回)。
# 落到下方 ops/shift_ops/bit_ops 会直出非法 IR(`add i8*, i8*` ⇒ opt 报
# invalid operand type for instruction),故在 IR 生成前 fail-fast。
# 判据与报文逐句对齐 TPV ExprGenOps.translate_binop / AssignGenMixin._apply_aug_op。
# ============================================================
if op in ('+', 'Add', '-', 'Sub', '*', 'Mult', '/', 'Div', '//', 'FloorDiv',
'%', 'Mod', '**', 'Pow', '&', 'BitAnd', '|', 'BitOr', '^', 'BitXor',
'<<', 'LShift', '>>', 'RShift') \
and isinstance(left.type, ir.PointerType) \
and isinstance(right.type, ir.PointerType):
LeftTy: str = str(left.type)
RightTy: str = str(right.type)
if LeftTy == 'i8*':
LeftTy = 'str(i8*)'
if RightTy == 'i8*':
RightTy = 'str(i8*)'
OpCh: str = op
AugNote: str = ''
if is_aug:
# 增量赋值(+= 等)要能自报家门,便于定位(不写成裸 '+')
OpCh = f'{op}='
AugNote = '(增量赋值)'
raise TypeError(
f"[TransPyC] 双指针运算类型非法: op = '{OpCh}'{AugNote},"
f'左操作数类型 = {LeftTy},右操作数类型 = {RightTy}。'
f'本语言的 str 是 i8*,没有 `+` 拼接语义(`str + str` 与指针 ± 指针'
f'都会退化成裸指针 add/sub ⇒ 非法 IR: opt 报 '
f'invalid operand type for instruction)。'
f'禁止静默降级,已终止编译。'
f'修法: 用 f-string 插值、string.strcat 或 viperlib.snprintf 构建字符串。'
+ self._get_node_info()
)
is_float: bool = isinstance(left.type, (ir.FloatType, ir.DoubleType)) or \
isinstance(right.type, (ir.FloatType, ir.DoubleType))
if is_float:
@@ -291,10 +331,14 @@ class ExprGenMixin:
if isinstance(ret_type, ir.PointerType) and isinstance(value.type, ir.PointerType):
value = self.builder.bitcast(value, ret_type, name="ret_cast")
elif isinstance(value.type, ir.PointerType) and not isinstance(ret_type, ir.PointerType):
if isinstance(value.type.pointee, ir.IntType) and isinstance(ret_type, ir.IntType) and value.type.pointee.width == ret_type.width:
value = self._load(value, name="ret_Load")
elif isinstance(ret_type, ir.IntType):
value = self.builder.ptrtoint(value, ret_type, name="ret_ptrtoint")
if isinstance(ret_type, ir.IntType):
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)。
# 原先"同宽 load 所指物 / 其余 ptrtoint"都是静默转换。
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:函数 {self.func.name} 声明返回 {ret_type},"
f"但 return 的是指针类型 {value.type};如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
elif isinstance(value.type.pointee, ret_type.__class__) and not isinstance(value.type.pointee, ir.IntType):
value = self._load(value, name="ret_Load")
elif isinstance(value.type.pointee, ir.PointerType):
@@ -302,7 +346,11 @@ class ExprGenMixin:
if Loaded.type == ret_type:
value = Loaded
elif isinstance(ret_type, ir.IntType):
value = self.builder.ptrtoint(Loaded, ret_type, name="ret_ptrtoint")
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:函数 {self.func.name} 声明返回 {ret_type},"
f"但 return 的是指针类型 {Loaded.type};如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
elif isinstance(ret_type, ir.IntType) and isinstance(value.type, ir.IntType):
if value.type.width < ret_type.width:
if value.type.width == 1:
+106 -62
View File
@@ -21,14 +21,14 @@ import re
# `!N = ` 形式的编号元数据定义行
_Definition = re.compile(r'^!(\d+) = ')
# 编号元数据引用
_Reference = re.compile(r'!(\d+)')
# `!name = ...` 形式的命名元数据(首字符为字母,故不会命中 `!N = `)
_NamedDefinition = re.compile(r'^!([A-Za-z][\w.]*)\s*=\s*(.*)$')
# `!{ ... }` 列表形式
_ListForm = re.compile(r'^!\{(.*)\}$', re.DOTALL)
# 纯 id 引用 `!N`
_PureId = re.compile(r'^!(\d+)$')
# Decimal digit set for the no-regex fast rewrite path.
_DIGITS: frozenset[str] = frozenset('0123456789')
def _SplitTopLevel(Content: str) -> list[str]:
@@ -78,47 +78,69 @@ def _ListItems(Rhs: str) -> list[str] | None:
return _SplitTopLevel(m.group(1))
def _RewriteReferences(Line: str, Mapping: dict[int, int]) -> str:
"""改写一行里对编号元数据的引用,跳过字符串字面量与 `;` 注释。"""
if '!' not in Line:
def _RewritePlain(Line: str, StrMap: dict[str, str]) -> str:
"""Rewrite `!N` references in a line that has no `"` and no `;`.
Equivalent to a regex substitution of `!` + digits but without the regex
engine callback: split on '!' (C-level), map each digit run by full token,
keep unknown ids verbatim.
"""
Parts: list[str] = Line.split('!')
if len(Parts) == 1:
return Line
if '"' not in Line and ';' not in Line:
# 元数据行绝大多数不含字符串/注释:整体正则替换,避免逐字符扫描
return _Reference.sub(
lambda m: '!' + str(Mapping.get(int(m.group(1)), int(m.group(1)))), Line)
Out: list[str] = []
Out: list[str] = [Parts[0]]
Ap = Out.append
Get = StrMap.get
for Part in Parts[1:]:
d: int = 0
n: int = len(Part)
while d < n and Part[d] in _DIGITS:
d += 1
if not d:
Ap('!')
Ap(Part)
continue
Digits: str = Part[:d]
# Leading zeros normalise the way int()/str() did in the old path.
Tok: str = '!' + Digits if (d == 1 or Digits[0] != '0') else '!' + str(int(Digits))
New: str | None = Get(Tok)
Ap(Tok if New is None else New)
Ap(Part[d:])
return ''.join(Out)
def _RewriteQuoted(Line: str, StrMap: dict[str, str]) -> str:
"""Rewrite `!N` refs in a line that contains `"` and/or `;`.
String literals and the trailing `;` comment are copied verbatim; the
remaining (quote/comment free) chunks go through `_RewritePlain`. Uses
C-level str.find instead of the old per-character Python scan.
"""
Out: list[str] = []; Ap = Out.append
i: int = 0
n: int = len(Line)
while i < n:
Ch: str = Line[i]
if Ch == '"':
j: int = i + 1
while j < n:
if Line[j] == '\\':
j += 2
continue
if Line[j] == '"':
j += 1
break
j += 1
Out.append(Line[i:j])
i = j
continue
if Ch == ';':
Out.append(Line[i:])
q: int = Line.find('"', i)
s: int = Line.find(';', i, q if q >= 0 else n)
if s >= 0:
Ap(_RewritePlain(Line[i:s], StrMap))
Ap(Line[s:]) # ';' comment: copy verbatim to end of line
break
if Ch == '!':
m = _Reference.match(Line, i)
if m:
New: int | None = Mapping.get(int(m.group(1)))
Out.append('!' + str(New) if New is not None else m.group(0))
i = m.end()
if q < 0:
Ap(_RewritePlain(Line[i:], StrMap))
break
Ap(_RewritePlain(Line[i:q], StrMap))
j: int = q + 1
while j < n:
if Line[j] == '\\':
j += 2
continue
Out.append(Ch)
i += 1
continue
Out.append(Ch)
i += 1
if Line[j] == '"':
j += 1
break
j += 1
Ap(Line[q:j]) # string literal: copy verbatim
i = j
return ''.join(Out)
@@ -139,7 +161,7 @@ def SanitizeIRMetadata(text: str) -> str:
i: int = 0
while i < LineCount:
if Segments[i].startswith('!'):
j: int = i
j: int = i + 1
while j < LineCount and Segments[j].startswith('!'):
j += 1
Blocks.append((i, j))
@@ -161,7 +183,13 @@ def SanitizeIRMetadata(text: str) -> str:
# 3) 块内按定义出现顺序重新编号(局部 id -> 全局 id)
Lines: list[str] = list(Segments)
BlockMaps: list[dict[int, int]] = []
BlockStrMaps: list[dict[str, str]] = []
BlockIsIdentity: list[bool] = []
# Definition line index -> (new global id, length of its rewritten "!N = " prefix).
# Lets step 4/5 skip a per-line regex to re-detect definition lines.
DefInfo: dict[int, tuple[int, int]] = {}
# Named metadata lines are collected here (only block lines can start with '!').
NamedLines: dict[str, list[int]] = {}
Counter: int = 0
for Start, End in Blocks:
Mapping: dict[int, int] = {}
@@ -169,14 +197,21 @@ def SanitizeIRMetadata(text: str) -> str:
for k in range(Start, End):
m = _Definition.match(Lines[k])
if not m:
nm = _NamedDefinition.match(Lines[k])
if nm is not None:
NamedLines.setdefault(nm.group(1), []).append(k)
continue
Local: int = int(m.group(1))
Mapping[Local] = Counter
if Local != Counter:
Identity = False
Lines[k] = '!' + str(Counter) + ' = ' + Lines[k][m.end():]
Prefix: str = '!' + str(Counter) + ' = '
DefInfo[k] = (Counter, len(Prefix))
Lines[k] = Prefix + Lines[k][m.end():]
Counter += 1
BlockMaps.append(Mapping)
BlockStrMaps.append({'!' + str(LocalId): '!' + str(GlobId)
for LocalId, GlobId in Mapping.items()})
BlockIsIdentity.append(Identity)
# 4) 改写全文引用(定义体右值、define/指令行的 !dbg、命名元数据列表)
@@ -185,36 +220,40 @@ def SanitizeIRMetadata(text: str) -> str:
# 旧 id 843 → Mapping[843]=1686),一旦改写左侧就会出现两个定义共用同一
# id,opt/llc 随即报 "Metadata id is already used"(本 bug 实测残留 276 个
# 重复 id)。故定义行只改写 '=' 之后的右值。
GetDef = DefInfo.get
for k in range(LineCount):
Index: int = Owner[k]
if Index < 0 or BlockIsIdentity[Index] or not BlockMaps[Index]:
continue
Line: str = Lines[k]
Head: re.Match | None = _Definition.match(Line)
if Head:
Fixed: str = Line[:Head.end()] + _RewriteReferences(Line[Head.end():], BlockMaps[Index])
if '!' not in Line:
continue
StrMap: dict[str, str] = BlockStrMaps[Index]
Def: tuple[int, int] | None = GetDef(k)
if Def is not None:
Split: int = Def[1]
Tail: str = Line[Split:]
Fixed: str = Line[:Split] + (_RewritePlain(Tail, StrMap)
if ('"' not in Tail and ';' not in Tail)
else _RewriteQuoted(Tail, StrMap))
else:
Fixed = _RewriteReferences(Line, BlockMaps[Index])
Fixed = (_RewritePlain(Line, StrMap)
if ('"' not in Line and ';' not in Line)
else _RewriteQuoted(Line, StrMap))
if Fixed != Line:
Lines[k] = Fixed
# 5) 定义体索引(新 id -> 右值文本),供命名元数据判重
# 5) 定义体索引(新 id -> 右值文本),供命名元数据判重。
# 只遍历定义行(步骤 3 已登记),不再逐行做正则匹配。
BodyOf: dict[int, str] = {}
for k in range(LineCount):
m = _Definition.match(Lines[k])
if m:
BodyOf[int(m.group(1))] = Lines[k][m.end():].strip()
for k, (NewId, Split) in DefInfo.items():
BodyOf[NewId] = Lines[k][Split:].strip()
# 6) 命名元数据:同名多份合并成一份。
# - !llvm.dbg.cu:全部条目聚合(每个 DICompileUnit 都必须被列出,不能去重)
# - 其它(!llvm.module.flags、!llvm.ident ...):条目内容(id 引用解析成
# 节点正文)完全相同的多份只留一份,不同则聚合。
NamedLines: dict[str, list[int]] = {}
for k in range(LineCount):
m = _NamedDefinition.match(Lines[k])
if m:
NamedLines.setdefault(m.group(1), []).append(k)
# NamedLines 已在步骤 3 随块扫描一并收集(命名行的名字不受引用改写影响)。
Dropped: set[int] = set()
for Name, Indices in NamedLines.items():
if len(Indices) == 1 and Name != 'llvm.dbg.cu':
@@ -225,20 +264,25 @@ def SanitizeIRMetadata(text: str) -> str:
SeenItems: set[str] = set()
SeenBodies: set[tuple[str, ...]] = set()
Single: bool = True
IsDbgCu: bool = Name == 'llvm.dbg.cu'
for k in Indices:
Rhs: str = _NamedDefinition.match(Lines[k]).group(2).strip()
NamedMatch = _NamedDefinition.match(Lines[k])
Rhs: str = (NamedMatch.group(2) if NamedMatch is not None else '').strip()
ListItems: list[str] | None = _ListItems(Rhs)
if ListItems is None:
Single = False
break
Bodies: tuple[str, ...] = tuple(
BodyOf.get(int(_PureId.match(Item).group(1)), Item) if _PureId.match(Item) else Item
for Item in ListItems
)
if Name != 'llvm.dbg.cu':
if Bodies in SeenBodies:
if not IsDbgCu:
# !llvm.dbg.cu never de-dupes by body, so skip resolving bodies for it.
Bodies: list[str] = []
for Item in ListItems:
PureMatch = _PureId.match(Item)
Bodies.append(BodyOf.get(int(PureMatch.group(1)), Item)
if PureMatch else Item)
Key: tuple[str, ...] = tuple(Bodies)
if Key in SeenBodies:
continue
SeenBodies.add(Bodies)
SeenBodies.add(Key)
for Item in ListItems:
if Item not in SeenItems:
SeenItems.add(Item)
+93 -17
View File
@@ -2,6 +2,50 @@ from __future__ import annotations
import llvmlite.ir as ir
class _VolatileLoadInstr(ir.LoadInstr):
"""带 LLVM `volatile` 限定符的 load 指令。
llvmlite 0.47 的 `ir.LoadInstr` / `ir.StoreInstr` 不支持 volatile(无该属性,
`descr` 也不输出该关键字),故按本仓库既有做法(BaseGen.VaArgInstruction
自定义 `descr`)派生子类,只把 `load` 关键字扩成 `load volatile`。
输出格式与 llvmlite 原始实现逐字段一致(类型、指针、align、元数据),
仅多一个 `volatile` 关键字;语义与 TransPyV 侧 `build_load(..., vol)` 对齐。
"""
def descr(self, buf: list[str]) -> None:
[val] = self.operands
if self.align is not None:
align = ', align %d' % (self.align)
else:
align = ''
buf.append("load volatile {0}, {1} {2}{3}{4}\n".format(
self.type,
val.type,
val.get_reference(),
align,
self._stringify_metadata(leading_comma=True),
))
class _VolatileStoreInstr(ir.StoreInstr):
"""带 LLVM `volatile` 限定符的 store 指令(与 _VolatileLoadInstr 同款理由)。"""
def descr(self, buf: list[str]) -> None:
val, ptr = self.operands
if self.align is not None:
align = ', align %d' % (self.align)
else:
align = ''
buf.append("store volatile {0} {1}, {2} {3}{4}{5}\n".format(
val.type,
val.get_reference(),
ptr.type,
ptr.get_reference(),
align,
self._stringify_metadata(leading_comma=True),
))
class MemoryOpsMixin:
def _register_local_heap_ptr(self, val: ir.Value, ClassName: str | None = None, VarName: str | None = None) -> None:
@@ -126,12 +170,42 @@ class MemoryOpsMixin:
self.builder.position_at_end(saved_block)
return instr
def _load(self, ptr: ir.Value, name: str = '', align: int | None = None) -> ir.LoadInstr:
def _EmitLoad(self, ptr: ir.Value, name: str = '', align: int | None = None,
volatile: bool = False) -> ir.Value:
"""发射一条 load;volatile=True 时走 _VolatileLoadInstr(LLVM `load volatile`)。
类型推断完全交给 LoadInstr 自身(与 llvmlite 原始路径同一套逻辑),
不额外兜底:推不出类型时与原生 `builder.load` 一样直接抛错。
"""
if not volatile:
return self.builder.load(ptr, name=name, align=align)
instr: ir.LoadInstr = _VolatileLoadInstr(self.builder.block, ptr, name=name)
instr.align = align
self.builder._insert(instr)
return instr
def _EmitStore(self, val: ir.Value, ptr: ir.Value, align: int | None = None,
volatile: bool = False) -> ir.StoreInstr:
"""发射一条 store;volatile=True 时走 _VolatileStoreInstr(LLVM `store volatile`)。"""
if not volatile:
return self.builder.store(val, ptr, align=align)
instr: ir.StoreInstr = _VolatileStoreInstr(self.builder.block, val, ptr)
instr.align = align
self.builder._insert(instr)
return instr
def _load(self, ptr: ir.Value, name: str = '', align: int | None = None,
volatile: bool | None = None) -> ir.LoadInstr:
if align is None:
if isinstance(ptr.type, ir.PointerType):
align = self._get_align(ptr.type.pointee)
else:
align = 0
# volatile=None ⇒ 按登记名自动判定(变量自身/所指物/字段,见 _IsPtrVolatile);
# 显式传入 True/False 时以调用方为准。判定必须先于下方 bitcast(bitcast
# 结果的名字不再是变量名,会丢登记名)。
if volatile is None:
volatile = self._IsPtrVolatile(ptr)
if isinstance(ptr.type, ir.PointerType):
pointee_type: ir.Type = ptr.type.pointee
if isinstance(pointee_type, ir.IdentifiedStructType) and pointee_type.name in self.typedef_int_widths:
@@ -139,8 +213,8 @@ class MemoryOpsMixin:
if width > 0:
actual_type: ir.IntType = ir.IntType(width)
bitcast_ptr: ir.Value = self.builder.bitcast(ptr, ir.PointerType(actual_type), name="typedef_bitcast_Load")
return self.builder.load(bitcast_ptr, name=name, align=align)
return self.builder.load(ptr, name=name, align=align)
return self._EmitLoad(bitcast_ptr, name=name, align=align, volatile=volatile)
return self._EmitLoad(ptr, name=name, align=align, volatile=volatile)
def _Load_var(self, name: str) -> ir.Value | None:
if name in self._direct_values:
@@ -199,17 +273,14 @@ class MemoryOpsMixin:
# (此前 pointee.width == target.width 时直接 load null,
# 生成 `load i32, i32* null` → ASAN access-violation)
return ir.Constant(target_type, 0)
if isinstance(val.type.pointee, ir.IntType) and val.type.pointee.width == target_type.width:
return self.builder.load(val, name="load_ptr_as_int_store")
if target_type.width <= 8 and isinstance(val.type.pointee, ir.IntType):
Loaded: ir.Value = self.builder.load(val, name="load_ptr_byte_store")
if Loaded.type != target_type:
if isinstance(Loaded.type, ir.IntType) and isinstance(target_type, ir.IntType):
if target_type.width < Loaded.type.width:
return self.builder.trunc(Loaded, target_type, name="trunc_ptr_byte_store")
return self.builder.zext(Loaded, target_type, name="zext_ptr_byte_store")
return Loaded
return self.builder.ptrtoint(val, target_type, name="ptr_to_int_store")
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)。
# 原先三条路径都是静默的:同宽直接 load 指针所指物的位模式、≤8 位 load
# 首字节、其余 ptrtoint 截断地址——把"类型写错"藏到运行期。
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:不能把指针类型 {val.type} 赋给整数类型 {target_type};"
f"如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))"
)
if isinstance(target_type, ir.FloatType) and isinstance(val.type, ir.DoubleType):
return self.builder.fptrunc(val, target_type, name="fptrunc_store")
if isinstance(target_type, ir.DoubleType) and isinstance(val.type, ir.FloatType):
@@ -242,12 +313,17 @@ class MemoryOpsMixin:
return Loaded
return None
def _store(self, val: ir.Value, ptr: ir.Value, align: int | None = None) -> ir.StoreInstr:
def _store(self, val: ir.Value, ptr: ir.Value, align: int | None = None,
volatile: bool | None = None) -> ir.StoreInstr:
if align is None:
if isinstance(ptr.type, ir.PointerType):
align = self._get_align(ptr.type.pointee)
else:
align = 0
# volatile=None ⇒ 按登记名自动判定(见 _IsPtrVolatile)。判定先于下方任何
# ptr 重建(null_struct_ptr_cast 等),否则会丢登记名。
if volatile is None:
volatile = self._IsPtrVolatile(ptr)
if isinstance(ptr.type, ir.PointerType):
target_type: ir.Type = ptr.type.pointee
if isinstance(target_type, ir.IdentifiedStructType) and target_type.name in self.typedef_int_widths:
@@ -261,7 +337,7 @@ class MemoryOpsMixin:
val = self.builder.trunc(val, actual_type, name="trunc_typedef")
else:
val = self.builder.zext(val, actual_type, name="zext_typedef")
return self.builder.store(val, bitcast_ptr, align=align)
return self._EmitStore(val, bitcast_ptr, align=align, volatile=volatile)
if val.type != target_type:
if isinstance(val, ir.Constant) and val.constant is None:
if isinstance(target_type, ir.PointerType):
@@ -280,7 +356,7 @@ class MemoryOpsMixin:
else:
src_info: str = self._get_node_info()
raise TypeError(f"cannot store {val.type} to {ptr.type}: mismatching types{src_info}")
return self.builder.store(val, ptr, align=align)
return self._EmitStore(val, ptr, align=align, volatile=volatile)
def _get_var_ptr(self, name: str) -> ir.Value | None:
if name in self._reg_values:
+75 -23
View File
@@ -10,6 +10,14 @@ from lib.includes import t as _t
class StructGenMixin:
def _get_align(self, llvm_type: ir.Type) -> int:
"""类型自然对齐(字节)。
与 `_get_struct_size` 同一约定:**不允许静默近似**。对齐猜错会让字段字节偏移
与 LLVM 实际布局不一致(本函数是 `_get_struct_size` 的字段偏移累加与
`_compute_field_byte_offset` 的输入),故拿不到对齐时一律响亮失败:
- opaque/未解析的 IdentifiedStructType → 曾按 ptr_size 猜;
- 未识别类型 → 曾按 4 猜。
"""
if isinstance(llvm_type, ir.IntType):
return max(1, llvm_type.width // 8)
if isinstance(llvm_type, ir.FloatType):
@@ -20,15 +28,28 @@ class StructGenMixin:
return self.ptr_size
if isinstance(llvm_type, ir.ArrayType):
return self._get_align(llvm_type.element)
if isinstance(llvm_type, ir.VectorType):
# 与 LLVM 一致:向量对齐 = 元素对齐(同 _get_struct_size 的向量口径)
return self._get_align(llvm_type.element)
if isinstance(llvm_type, ir.LiteralStructType):
return max((self._get_align(f) for f in llvm_type.elements), default=1)
if isinstance(llvm_type, ir.IdentifiedStructType):
resolved = self._resolve_opaque_struct(llvm_type)
if resolved is not llvm_type:
return self._get_align(resolved)
if llvm_type.elements is None or len(llvm_type.elements) == 0:
return self.ptr_size # opaque struct, assume pointer alignment
raise ValueError(
f"[FATAL][__sizeof__] 结构体 '{getattr(llvm_type, 'name', llvm_type)}' "
f"布局未就绪(opaque / 未解析到定义),拒绝按指针对齐兜底 —— "
f"对齐猜错会让字段字节偏移与 LLVM 实际布局不一致(读写落到错误字段)。"
f"请先确保该结构体已注册/可从 stub 加载后再取对齐。"
)
return max((self._get_align(f) for f in llvm_type.elements), default=1)
if isinstance(llvm_type, ir.VoidType):
return 0
return 4
raise ValueError(
f"[FATAL][__sizeof__] 不支持的类型 {llvm_type}({type(llvm_type).__name__})"
f"无法计算对齐,拒绝以常数 4 兜底 —— 对齐猜错会让字段字节偏移与 LLVM 实际布局不一致。")
def _resolve_opaque_struct(self, llvm_type: ir.Type) -> ir.Type:
"""尝试从 Gen.structs 中查找已解析的同名 struct,解决跨模块 struct 类型对象不一致的问题"""
@@ -77,6 +98,17 @@ class StructGenMixin:
return llvm_type
def _get_struct_size(self, llvm_type: ir.Type) -> int:
"""类型字节尺寸(含字段对齐/填充)。
**不允许静默近似**:本函数的返回值会被 `X.__sizeof__()` 直接采用
(HandlesExprCall._HandleSizeofLlvm),而 memhub 的 alloc 又直接吃该值。
此前两处静默兜底会**少算尺寸**,进而按过小尺寸分配 → 越界写坏邻居块的
8 字节块头(块头 `(order<<1)|1` 被写成偶数 ⇒ reset_to 视为"已释放"跳过、
free 被 _is_valid_ptr 判无效而静默 no-op)⇒ 该块永久丢失 ⇒ 1 GiB 主池漏满:
- opaque/未解析的 IdentifiedStructType → 曾一律返回 ptr_size;
- 未识别类型(如 SIMD VectorType)→ 曾一律返回 4。
故尺寸拿不到时一律响亮失败(项目约定:类型信息缺失立即终止,不兜底)。
"""
if isinstance(llvm_type, ir.IntType):
return max(1, llvm_type.width // 8)
if isinstance(llvm_type, ir.FloatType):
@@ -87,10 +119,20 @@ class StructGenMixin:
return self.ptr_size
if isinstance(llvm_type, ir.ArrayType):
return self._get_struct_size(llvm_type.element) * llvm_type.count
if isinstance(llvm_type, ir.VectorType):
# 与 LLVM 一致:元素尺寸 × 元素数,再向上取到 2 的幂(<3 x i32> = 16 字节)
elem_size: int = self._get_struct_size(llvm_type.element)
lanes: int = llvm_type.count
padded: int = 1
while padded < lanes:
padded = padded << 1
return elem_size * padded
if isinstance(llvm_type, ir.LiteralStructType):
total: int = 0
max_align: int = 1
for f in llvm_type.fields:
# llvmlite 的 LiteralStructType 只有 `.elements`(**没有** `.fields`):
# 旧代码写 `.fields` 必然 AttributeError,尺寸/对齐链在此静默断掉。
for f in llvm_type.elements:
fa: int = self._get_align(f)
fs: int = self._get_struct_size(f)
max_align = max(max_align, fa)
@@ -102,22 +144,32 @@ class StructGenMixin:
return total
if isinstance(llvm_type, ir.IdentifiedStructType):
# 尝试解析 opaque struct 为已注册的版本
llvm_type = self._resolve_opaque_struct(llvm_type)
resolved = self._resolve_opaque_struct(llvm_type)
if resolved is not llvm_type:
return self._get_struct_size(resolved)
if llvm_type.elements is None or len(llvm_type.elements) == 0:
return self.ptr_size # opaque struct, assume pointer size
total: int = 0
max_align: int = 1
raise ValueError(
f"[FATAL][__sizeof__] 结构体 '{getattr(llvm_type, 'name', llvm_type)}' "
f"布局未就绪(opaque / 未解析到定义),拒绝按指针大小兜底 —— "
f"近似会少算尺寸,调用方(memhub alloc)按过小尺寸分配并越界写坏邻居块头。"
f"请先确保该结构体已注册/可从 stub 加载后再取尺寸。"
)
total2: int = 0
max_align2: int = 1
for f in llvm_type.elements:
fa: int = self._get_align(f)
fs: int = self._get_struct_size(f)
max_align = max(max_align, fa)
if fa > 1:
total = (total + fa - 1) // fa * fa
total += fs
if max_align > 1:
total = (total + max_align - 1) // max_align * max_align
return total
return 4
fa2: int = self._get_align(f)
fs2: int = self._get_struct_size(f)
max_align2 = max(max_align2, fa2)
if fa2 > 1:
total2 = (total2 + fa2 - 1) // fa2 * fa2
total2 += fs2
if max_align2 > 1:
total2 = (total2 + max_align2 - 1) // max_align2 * max_align2
return total2
raise ValueError(
f"[FATAL][__sizeof__] 不支持的类型 {llvm_type}({type(llvm_type).__name__})"
f"无法计算尺寸,拒绝以常数兜底 —— 近似会少算尺寸并越界写坏内存池块头。"
)
@staticmethod
def _strip_unnecessary_quotes(ir_str: str) -> str:
@@ -140,12 +192,12 @@ class StructGenMixin:
_DbgInfo = self._GetDebugInfo()
if _DbgInfo is not None:
_DbgInfo.Finalize()
# Ctrl+C 调用栈(--ctrlc-stack):同样是"落成 .ll 文本之前"的模块级收尾——
# 当本模块就是 includes/dbgstack.vp 时,注入 @llvm.global_ctors 构造器调用
# 其 Install()(见 BaseGen._RegisterDbgStackInstallViaCtor)。未启用/非该模块
# 时 ctrlc_stack_ctor=False,不做任何事(零影响)。
if self.ctrlc_stack_ctor:
self._RegisterDbgStackInstallViaCtor()
# Ctrl+C 调用栈(--traceback):同样是"落成 .ll 文本之前"的模块级收尾——
# 当本模块就是 includes/traceback.vp 时,注入 @llvm.global_ctors 构造器调用
# 其 Install()(见 BaseGen._RegisterTracebackInstallViaCtor)。未启用/非该模块
# 时 traceback_ctor=False,不做任何事(零影响)。
if self.traceback_ctor:
self._RegisterTracebackInstallViaCtor()
ir_text: str
try:
ir_text = str(self.module)
+21 -4
View File
@@ -56,6 +56,16 @@ class TypeConvertMixin:
if resolved is not None:
return resolved
# 枚举(CEnum)类型注解 `x: Tri`:宽度 = 该枚举的存储宽度(最宽成员类型),
# 与成员常量发射点同点(ABI:两侧必须同宽)。默认宽度 32 时与改前逐字一致。
# 走 IsTypedef 之前的分支不受影响(typedef 形式枚举先被上方解析)。
if type_info.IsEnum and type_info.Name and not type_info.IsRenum:
_enum_ty: ir.Type = self._enum_int_type(type_info.Name)
_enum_result: ir.Type = _enum_ty
for _ in range(type_info.PtrCount):
_enum_result = ir.PointerType(_enum_result)
return _enum_result
# REnum 类型:通过 Name 查找或创建结构体(BaseType 可能为 None)
# 这对 BinOp `LLVMType | t.CPtr` 合并后的 CTypeInfo 至关重要
if type_info.IsRenum and type_info.Name:
@@ -146,6 +156,9 @@ class TypeConvertMixin:
enum_name: str = type_info.Name if type_info else ''
if enum_name and enum_name in self.structs:
return self.structs[enum_name]
if isinstance(BaseType, _t.CEnum):
# CEnum 存储宽度 = 最宽成员类型(REnum 仍走上方结构体路径)
return self._enum_int_type(enum_name)
return ir.IntType(32)
if isinstance(BaseType, _t.CUnion):
@@ -510,14 +523,16 @@ class TypeConvertMixin:
return ir.PointerType(_cls_struct)
return _cls_struct
if _Entry and _Entry.IsEnum:
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
_enum_ty: ir.Type = self._enum_int_type(type_str, _Entry)
return ir.PointerType(_enum_ty) if IsPtr else _enum_ty
if _Entry and _Entry.IsExceptionClass:
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
if _Entry and _Entry.IsFunction:
return ir.PointerType(ir.IntType(8))
if _Entry and _Entry.BaseType:
if (isinstance(_Entry.BaseType, type) and issubclass(_Entry.BaseType, _t.CEnum)) or _Entry.BaseType is _t.CEnum:
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
_enum_ty2: ir.Type = self._enum_int_type(type_str, _Entry)
return ir.PointerType(_enum_ty2) if IsPtr else _enum_ty2
for ClassName in self.structs:
if type_str == ClassName:
if IsPtr:
@@ -564,14 +579,16 @@ class TypeConvertMixin:
return ir.PointerType(opaque_lp)
return opaque_lp
if _Entry and _Entry.IsEnum:
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
_enum_ty3: ir.Type = self._enum_int_type(LastPart, _Entry)
return ir.PointerType(_enum_ty3) if IsPtr else _enum_ty3
if _Entry and _Entry.IsExceptionClass:
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
if _Entry and _Entry.IsFunction:
return ir.PointerType(ir.IntType(8))
if _Entry and _Entry.BaseType:
if (isinstance(_Entry.BaseType, type) and issubclass(_Entry.BaseType, _t.CEnum)) or _Entry.BaseType is _t.CEnum:
return ir.IntType(32) if not IsPtr else ir.PointerType(ir.IntType(32))
_enum_ty4: ir.Type = self._enum_int_type(LastPart, _Entry)
return ir.PointerType(_enum_ty4) if IsPtr else _enum_ty4
if LastPart in self.structs:
if IsPtr:
return ir.PointerType(self.structs[LastPart])
+6
View File
@@ -4,6 +4,8 @@ from dataclasses import dataclass, field
from typing import Dict, List, Optional, Tuple, TYPE_CHECKING
import vast as ast
from lib.core.SymbolUtils import _ENUM_DEFAULT_INT_WIDTH
if TYPE_CHECKING:
from lib.core.Handles.HandlesBase import CTypeInfo
@@ -18,6 +20,10 @@ class ClassSymbolData:
is_cpython_object: bool = False
is_packed: bool = False
enum_members: List[Tuple[str, int, int]] = field(default_factory=list) # (name, value, lineno)
# 枚举存储宽度(bit):按"最宽成员类型"推导(SymbolUtils.EnumClassIntWidth)。
# 仅 type_kind == 'enum' 有意义;非枚举 / 无可解析整数成员 → 默认 32。
# 该属性随符号表条目序列化(_shared_sym.json pickle 的 _sm._extra)⇒ 跨模块同宽。
enum_width: int = _ENUM_DEFAULT_INT_WIDTH
@dataclass
+12 -3
View File
@@ -10,7 +10,8 @@ from lib.core.SymbolData import (
FuncSymbolData, DefineSymbolData, AnonymousTypeData
)
from lib.core.Handles.HandlesBase import CTypeInfo
from lib.core.SymbolUtils import AnnotationContainsTType, CheckAnnotationHasCInline, IsTModule, AnnotationContainsName
from lib.core.SymbolUtils import AnnotationContainsTType, CheckAnnotationHasCInline, IsTModule, AnnotationContainsName
from lib.core.SymbolUtils import EnumClassIntWidth, _ENUM_DEFAULT_INT_WIDTH
from lib.includes import t
@@ -24,7 +25,7 @@ class ASTSymbolExtractor:
"""提取文件中的所有符号信息"""
with open(file_path, 'r', encoding='utf-8') as f:
code: str = f.read()
tree: ast.Module = ast.parse(code)
tree: ast.Module = ast.parse(code, file_path)
result: ModuleSymbols = ModuleSymbols(file_path=file_path)
@@ -192,9 +193,16 @@ class ASTSymbolExtractor:
type_kind = 'exception'
break
# 提取枚举成员
# 枚举成员提取 + 存储宽度推导("最宽成员类型",与定义端
# HandlesClassDef._RegisterEnumMembers / TPV ClassGenEnum 同一规则源)。
# 本条通路(LoadModuleSymbols → 消费 .vpi/.pyi)是**跨模块符号表**的来源
# (Phase2Translator._build_shared_symbol_data → _shared_sym.json pickle),
# 宽度在此推导后随条目序列化,跨模块读取端(HandlesImports 的 .vpi 直读
# 路径 + _enum_int_type)即得到同一宽度;旧产物/老 pickle 无该字段 → 退 32。
enum_members: list[tuple[str, int, int]] = []
enum_width: int = _ENUM_DEFAULT_INT_WIDTH
if type_kind == 'enum':
enum_width = EnumClassIntWidth(node.body, self._symtab)
next_enum_value: int = 0
for item in node.body:
if isinstance(item, ast.Assign):
@@ -257,6 +265,7 @@ class ASTSymbolExtractor:
is_cpython_object=is_cpython_object,
is_packed=is_packed,
enum_members=enum_members,
enum_width=enum_width,
)
# ------------------------------------------------------------------
+7 -3
View File
@@ -27,9 +27,13 @@ class SymbolInserter:
# 插入类符号
for cls in module_symbols.classes:
# 枚举存储宽度("最宽成员类型")随条目登记:本条通路是跨模块符号表的
# 来源(.vpi/.pyi → LoadModuleSymbols),宽度必须一路带到序列化产物。
_enum_width: int | None = cls.enum_width if cls.type_kind == 'enum' else None
self._symtab._InsertClassSymbol(
cls.name, cls.type_kind, cls.lineno, file_path,
cls.members, cls.is_cpython_object, cls.is_packed
cls.members, cls.is_cpython_object, cls.is_packed,
EnumWidth=_enum_width
)
loaded.append(cls.name)
if self._namespace:
@@ -38,10 +42,10 @@ class SymbolInserter:
# 插入枚举成员(短名 + 类名.成员名)—— CEnum 和 REnum 共用
if cls.type_kind in ('enum', 'renum'):
for member_name, member_value, member_lineno in cls.enum_members:
self._symtab._InsertEnumMemberSymbol(member_name, cls.name, member_lineno, file_path, value=member_value)
self._symtab._InsertEnumMemberSymbol(member_name, cls.name, member_lineno, file_path, value=member_value, EnumWidth=_enum_width)
loaded.append(member_name)
class_member_name: str = f"{cls.name}.{member_name}"
self._symtab._InsertEnumMemberSymbol(class_member_name, cls.name, member_lineno, file_path, value=member_value)
self._symtab._InsertEnumMemberSymbol(class_member_name, cls.name, member_lineno, file_path, value=member_value, EnumWidth=_enum_width)
loaded.append(class_member_name)
# REnum: 记录变体名列表到类符号(供 HandlesExprCall 跨模块变体构造识别)
if cls.type_kind == 'renum':
+7 -3
View File
@@ -28,9 +28,13 @@ class PackageReexporter:
# 插入类符号(前缀.类名)
for cls in module_symbols.classes:
full_name: str = f"{prefix}.{cls.name}"
# 枚举宽度同样带到"模块限定名"条目上(跨模块类型注解按
# <sha1>.Enum / module.Enum 形态查表,与短名条目同宽)。
_enum_width: int | None = cls.enum_width if cls.type_kind == 'enum' else None
self._symtab._InsertClassSymbol(
full_name, cls.type_kind, cls.lineno, file_path,
cls.members, cls.is_cpython_object, cls.is_packed
cls.members, cls.is_cpython_object, cls.is_packed,
EnumWidth=_enum_width
)
loaded.append(full_name)
if self._namespace:
@@ -40,11 +44,11 @@ class PackageReexporter:
if cls.type_kind in ('enum', 'renum'):
for member_name, member_value, member_lineno in cls.enum_members:
full_member_name: str = f"{prefix}.{member_name}"
self._symtab._InsertEnumMemberSymbol(full_member_name, cls.name, member_lineno, file_path, value=member_value)
self._symtab._InsertEnumMemberSymbol(full_member_name, cls.name, member_lineno, file_path, value=member_value, EnumWidth=_enum_width)
loaded.append(full_member_name)
# 同时注册 prefix.ClassName.member_name,以支持 module.Class.MEMBER 三层访问
qualified_member_name: str = f"{prefix}.{cls.name}.{member_name}"
self._symtab._InsertEnumMemberSymbol(qualified_member_name, cls.name, member_lineno, file_path, value=member_value)
self._symtab._InsertEnumMemberSymbol(qualified_member_name, cls.name, member_lineno, file_path, value=member_value, EnumWidth=_enum_width)
loaded.append(qualified_member_name)
# REnum: 记录变体名列表到前缀类符号
if cls.type_kind == 'renum':
+10 -2
View File
@@ -417,7 +417,7 @@ class SymbolTable:
# 符号插入方法(供 SymbolInserter / PackageReexporter 调用)
# ==================================================================
def _InsertClassSymbol(self, FullName: str, TypeKind: str, lineno: int, FilePath: str, members: dict[str, CTypeInfo], IsCpythonObject: bool, IsPacked: bool = False) -> None:
def _InsertClassSymbol(self, FullName: str, TypeKind: str, lineno: int, FilePath: str, members: dict[str, CTypeInfo], IsCpythonObject: bool, IsPacked: bool = False, EnumWidth: int | None = None) -> None:
info: CTypeInfo = CTypeInfo()
info.Lineno = lineno
info.file = FilePath
@@ -429,6 +429,10 @@ class SymbolTable:
info.IsUnion = True
elif TypeKind == 'enum':
info.IsEnum = True
# 枚举存储宽度("最宽成员类型"):登记到条目上 → 随符号表序列化
# (ToDict/pickle 的 _sm._extra)→ 跨模块读回同宽;未登记 → 默认 32。
if EnumWidth:
info.set('enum_width', int(EnumWidth))
elif TypeKind == 'renum':
info.IsStruct = True
info.IsRenum = True
@@ -444,7 +448,7 @@ class SymbolTable:
self._symbols[FullName] = info
def _InsertEnumMemberSymbol(self, FullName: str, EnumName: str, lineno: int, FilePath: str, value: int | None = None) -> None:
def _InsertEnumMemberSymbol(self, FullName: str, EnumName: str, lineno: int, FilePath: str, value: int | None = None, EnumWidth: int | None = None) -> None:
info: CTypeInfo = CTypeInfo()
info.IsEnumMember = True
info.EnumName = EnumName
@@ -452,6 +456,10 @@ class SymbolTable:
info.file = FilePath
if value is not None:
info.value = value
# 成员条目同样携带枚举宽度:读取端(_enum_int_type)优先用调用点手头的
# 成员条目取宽,跨模块下成员 key 可能是模块限定名,不依赖枚举条目名可达。
if EnumWidth:
info.set('enum_width', int(EnumWidth))
self._symbols[FullName] = info
def _InsertTypedefSymbol(self, FullName: str, OriginalType_kind: str | None, OriginalClass: CTypeInfo | str | None, lineno: int, FilePath: str, members: dict[str, CTypeInfo] | None = None) -> None:
+1543 -9
View File
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -198,7 +198,7 @@ class AnnotationLoaderMixin:
with open(ModuleName, 'r', encoding='utf-8') as f:
ann_content: str = f.read()
ann_content = ExpandSoftKeywords(ann_content)
ann_tree: ast.Module = ast.parse(ann_content)
ann_tree: ast.Module = ast.parse(ann_content, ModuleName)
for node in ann_tree.body:
if isinstance(node, ast.ClassDef):
ClassName: str = node.name
@@ -239,7 +239,7 @@ class AnnotationLoaderMixin:
with open(ModuleName, 'r', encoding='utf-8') as f:
ann_content: str = f.read()
ann_content = ExpandSoftKeywords(ann_content)
ann_tree: ast.Module = ast.parse(ann_content)
ann_tree: ast.Module = ast.parse(ann_content, ModuleName)
for node in ann_tree.body:
# 检测 xxx: t.CEnum 形式的枚举成员变量
if isinstance(node, ast.AnnAssign):
@@ -286,7 +286,7 @@ class AnnotationLoaderMixin:
with open(ModuleName, 'r', encoding='utf-8') as f:
ann_content: str = f.read()
ann_content = ExpandSoftKeywords(ann_content)
ann_tree: ast.Module = ast.parse(ann_content)
ann_tree: ast.Module = ast.parse(ann_content, ModuleName)
for node in ann_tree.body:
# 检测 xxx: t.CTypedef = xxx 形式的 typedef 语句
if isinstance(node, ast.AnnAssign):
+13
View File
@@ -32,6 +32,19 @@ class ConstEvalMixin:
_fallback: ir.Constant = _zv if _zv is not None else ir.Constant(ElemType, ir.Undefined)
if not isinstance(value_node, (ast.List, ast.Set)):
return None
# 显式空元素判据(与 TPV AssignGenMixin._init_global_array_from_list 的裸空指针
# 判据两侧一致):Python 侧的空 AST 元素就是 None(TPV 侧是裸 0)。列表元素计数
# 与实际填充不一致(存在 None 槽)时,按下标取元素会静默产出错误常量/回退值;
# 故在此响亮失败(不静默跳过、不静默补 fallback),并指名下标与位置。
for _gi, _elt in enumerate(value_node.elts):
if _elt is None:
raise TypeError(
f'[TransPyC] 数组初始化列表字面量第 {_gi} 个元素为空 AST 元素(None)。'
f'上层某处产生了空 AST 元素(元素计数与实际填充不一致);'
f'拒绝静默补零生成残缺初始化,已终止编译。'
f'位置 line {getattr(value_node, "lineno", 0)}'
f':{getattr(value_node, "col_offset", 0)}'
)
if isinstance(ElemType, ir.ArrayType):
constants: list[ir.Constant] = []
for elt in value_node.elts:
+4 -4
View File
@@ -125,7 +125,7 @@ class LlvmGeneratorMixin:
TypeInfo: CTypeInfo | None = None
if Node.type_comment:
try:
tc_ast: ast.Expression = ast.parse(Node.type_comment, mode='eval')
tc_ast: ast.Expression = ast.parse(Node.type_comment, '<LlvmGenerator:type_comment>', mode='eval')
TypeInfo = self.TypeMergeHandler.GetCTypeInfo(tc_ast.body)
except Exception as _e:
if _config_mode == "strict":
@@ -701,10 +701,10 @@ class LlvmGeneratorMixin:
# LlvmCodeGenerator;未启用时 dwarf_enabled=False → 不构造 DI、不挂 !dbg,
# 生成的 .ll 与改动前逐字节一致(零影响)
self.LlvmGen.dwarf_enabled = bool(getattr(self, 'DwarfEnabled', False))
# Ctrl+C 调用栈(--ctrlc-stack):透传"本模块需注入 Install() 启动期构造器"
# 标记(仅 includes/dbgstack.vp 的翻译上下文为 True);未启用时为 False
# Ctrl+C 调用栈(--traceback):透传"本模块需注入 Install() 启动期构造器"
# 标记(仅 includes/traceback.vp 的翻译上下文为 True);未启用时为 False
# → finalize() 不做任何事,生成的 .ll 与改动前逐字节一致(零影响)
self.LlvmGen.ctrlc_stack_ctor = bool(getattr(self, 'CtrlcStackCtor', False))
self.LlvmGen.traceback_ctor = bool(getattr(self, 'TracebackCtor', False))
# --- 修复:共享 vtable 状态跨模块 ---
# 根因:LlvmGen 每模块重新创建,class_vtable/class_methods 等是实例级属性,
+1 -1
View File
@@ -80,7 +80,7 @@ class PythonParserMixin:
self.CurrentFile = FilePath
# 解析Python代码为AST
tree: ast.Module = ast.parse(content)
tree: ast.Module = ast.parse(content, FilePath)
# 保存 AST 树供后续使用
self.tree = tree
+189 -41
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
import os
import re
import threading
import vast as ast
import warnings
from typing import TYPE_CHECKING
@@ -96,13 +97,68 @@ class TypeAnnotationResolver:
# ============================================================
# builtins 最后匹配(对齐 TPV TypeGenResolve._lookup_builtins_typedef):
# 所有常规注解解析路径 miss 后,扫描 includes/builtins.vp 的
# "NAME: t.CTypedef = TYPE" 声明作为最终兜底(rawptr/size_t 等)。
# 所有常规注解解析路径 miss 后,扫描 includes/builtins.vp 的 typedef
# 声明作为最终兜底(rawptr/sizet/uint64 等)。
# builtins 始终纳入编译链但不在 App 文件 import 闭包,其 typedef 不随
# from_imports 注入,故需按源文件扫描兜底。
# 必须同时兼容两种等价写法:
# - 语法糖 `typedef 名字 = 类型`(builtins.vp 现用形态)
# - 注解形态 `名字: t.CTypedef = 类型`(stdint.vp 等库模块)
# 只认注解形态时语法糖声明全被跳过 → 兜底表为空 → 裸名退化为
# `%"{mod}.名字"*` 假结构体(sizet 曾被当成结构体,BinOp 报
# "Operands must be the same type, got (%"…sizet"*, i64)")。
# ============================================================
_builtins_scan_cache: dict | None = None
# builtins 解析链的**全链**重入深度上限与线程局部计数。
# 既有 `_resolve_builtins_type_str` 的 `depth`/`visited` 只在自身递归内传递;
# 而复合值(含 `|` / `[`)分支会经 `from_node` 重新进入
# `_from_node_name → _lookup_builtins_typedef`,把 depth/visited 归零 ⇒ 该方向
# 可绕过环检测无限递归(A = B | t.CPtr、B = A 一类声明)。此计数器覆盖
# `_lookup_builtins_typedef → _resolve_builtins_type_str → from_node → …` 全程,
# 超上限即判为成环并 fail-fast(不再沿链继续)。合法链通常 ≤ 4 层。
_BUILTINS_CHAIN_MAX_DEPTH: int = 32
_chain_state: threading.local = threading.local()
# 解析失败(成环/悬空)的 builtins typedef 名 → 错误报文缓存:
# 同一失败不再被反复重试(每次重试都会重新扫描/解析整条链)。
# builtins.vp 在一次编译进程内不变,故无需失效。
_builtins_failure_cache: dict[str, str] = {}
@classmethod
def _chain_depth_enter(cls) -> int:
depth: int = getattr(cls._chain_state, 'depth', 0)
cls._chain_state.depth = depth + 1
return depth
@classmethod
def _chain_depth_exit(cls, prev: int) -> None:
cls._chain_state.depth = prev
# `typedef 名字 = 类型` 语法糖(对应 SymbolUtils._ExpandTypedefs 的展开形态)
_TYPEDEF_SUGAR_RE: re.Pattern = re.compile(
r'^typedef\s+([A-Za-z_]\w*)\s*=(?!=)\s*(.+?)\s*$')
@staticmethod
def _parse_typedef_line(stripped: str) -> tuple[str, str] | None:
"""单行源码文本 → typedef 声明 (名字, 类型值);非 typedef 行返回 None
兼容 `typedef 名字 = 类型`(语法糖)与 `名字: t.CTypedef = 类型`(注解)。
"""
m: re.Match | None = TypeAnnotationResolver._TYPEDEF_SUGAR_RE.match(stripped)
if m is not None:
return m.group(1), m.group(2)
if ':' not in stripped or 't.CTypedef' not in stripped:
return None
name, _, rest = stripped.partition(':')
if 't.CTypedef' not in rest:
return None
value: str = rest.partition('=')[2].strip()
name = name.strip()
if not name or not value:
return None
return name, value
@staticmethod
def _builtins_include_dirs() -> list[str]:
"""includes 目录列表(builtins.vp 定位用)——只认项目配置 includes,
@@ -150,23 +206,23 @@ class TypeAnnotationResolver:
if m:
aliases[m.group(2)] = m.group(1)
continue
if ':' not in stripped:
continue
name: str
rest: str
name, _, rest = stripped.partition(':')
name = name.strip()
if 't.CTypedef' not in rest:
continue
value: str = rest.partition('=')[2].strip()
if value:
typedefs[name] = value
td: tuple[str, str] | None = TypeAnnotationResolver._parse_typedef_line(stripped)
if td is not None:
typedefs[td[0]] = td[1]
# 只缓存"扫到了东西"的结果:includes 目录尚未装配(PROJECT_INCLUDE_DIRS 为空)
# 或 builtins.vp 未定位时,缓存空表会让本进程内后续所有 builtins typedef 解析
# (sizet(x)/uint64(x) 这类 cast 调用)永久 miss,只在真正扫到内容后落缓存。
if not typedefs and not aliases:
return {'typedefs': typedefs, 'aliases': aliases}
cls._builtins_scan_cache = {'typedefs': typedefs, 'aliases': aliases}
return cls._builtins_scan_cache
@classmethod
def _scan_library_typedef(cls, mod: str, attr: str) -> str | None:
"""查库模块源中 "attr: t.CTypedef = TYPE",返回 TYPE(权威定义)或 None"""
"""查库模块源中 typedef 声明,返回类型值(权威定义)或 None
兼容 `typedef 名字 = 类型`(语法糖)与 `名字: t.CTypedef = 类型`(注解)。
"""
path: str | None = TypeAnnotationResolver._find_include_file(mod)
if path is None:
return None
@@ -180,29 +236,24 @@ class TypeAnnotationResolver:
stripped: str = line.strip()
if not stripped or stripped.startswith('#'):
continue
if ':' not in stripped:
continue
name: str
rest: str
name, _, rest = stripped.partition(':')
if name.strip() != attr:
continue
if 't.CTypedef' not in rest:
continue
value: str = rest.partition('=')[2].strip()
if value:
return value
td: tuple[str, str] | None = TypeAnnotationResolver._parse_typedef_line(stripped)
if td is not None and td[0] == attr:
return td[1]
return None
@classmethod
def _resolve_builtins_type_str(cls, type_str: str, aliases: dict[str, str],
SymbolTable, depth: int = 0) -> CTypeInfo | None:
SymbolTable, depth: int = 0,
visited: frozenset[str] | None = None) -> CTypeInfo | None:
"""解析 builtins typedef 值(支持 t.XXX / mod.attr / 裸名;mod 支持 import 别名)
Args:
type_str: typedef 值(如 t.CPtr / __stdint.size_t / CSizeT)
type_str: typedef 值(如 t.CPtr / __stdint.size_t / CSizeT / sizet)
aliases: builtins.vp 的 import 别名表(__stdint → stdint)
depth: 递归深度守卫(防 typedef 值自引用成环)
visited: 本次展开链上已展开的 typedef 名(环检测)。命中环/自引用返回
None(而不是退化成 %"{mod}.{名}"* 假结构体),由调用方
_lookup_builtins_typedef fail-fast 报出根因。
"""
if not type_str or depth > 8:
return None
@@ -225,8 +276,13 @@ class TypeAnnotationResolver:
mod = mod.strip()
attr = attr.strip()
real_mod: str = aliases.get(mod, mod)
# t/c 内建命名空间:走常规 Attribute 解析(含 CPtr/State 特殊语义)
# t/c 内建命名空间:**优先按内建类名直接解析**,不依赖调用方 SymbolTable
# 的装配进度(import 注册阶段 t 尚未入表 ⇒ 原先返回 None,被上层误归因为
# "自引用/循环引用";实测 builtins typedef 'sizet' → 't.CSizeT' 即卡在此处)。
if real_mod in ('t', 'c'):
_bi: CTypeInfo | None = TypeAnnotationResolver.from_type_name(attr)
if _bi is not None:
return _bi
node = TypeAnnotationResolver._parse_type_expr(type_str)
if node is not None:
return TypeAnnotationResolver.from_node(node, SymbolTable)
@@ -234,20 +290,58 @@ class TypeAnnotationResolver:
# 库模块权威 typedef(stdint.size_t → 读 stdint.vp 扫描)
lib_val: str | None = TypeAnnotationResolver._scan_library_typedef(real_mod, attr)
if lib_val is not None:
return TypeAnnotationResolver._resolve_builtins_type_str(lib_val, aliases, SymbolTable, depth + 1)
return TypeAnnotationResolver._resolve_builtins_type_str(
lib_val, aliases, SymbolTable, depth + 1, visited)
# 库源未命中:回退常规解析(模块可能已由 import 闭包注入 SymbolTable)
node = TypeAnnotationResolver._parse_type_expr(type_str)
if node is not None:
return TypeAnnotationResolver.from_node(node, SymbolTable)
return None
# 裸名:按 BuiltinTypeMap/t 类名解析
return TypeAnnotationResolver.from_type_name(type_str)
# 裸名:名字本身即类型(t/C 类名、BuiltinTypeMap 的 C 速记名、裸 LLVM 标量名)
# → 直接解析;否则它可能是**另一个 typedef 的引用**,沿 builtins typedef 表
# 继续展开到基础类型。
# 依据:stdint.vp 的 `size_t/SIZE_T: t.CTypedef = sizet` 与 builtins.vp 的
# `size_t/uzt: t.CTypedef = sizet` 都是"别名 → 权威名 → t 类型"的链式写法,
# 与 TPV resolve_typedef_or_map 的 CTypedef 表递归(TypeGenResolve.vp:723-734)
# 同口径。此前裸名一律走 from_type_name:链中名字命中不到基础类型时退化成
# %"{mod}.{名}"* 假结构体(sizet/size_t → "Type of #N arg mismatch:
# %…sizet"* != i32" 的根因)。
if TypeAnnotationResolver._is_direct_type_name(type_str):
return TypeAnnotationResolver.from_type_name(type_str)
chain: str | None = TypeAnnotationResolver._scan_builtins()['typedefs'].get(type_str)
if chain is None:
# 非 builtins typedef 名的裸名(str/bytes/None 等):保留既有兜底
return TypeAnnotationResolver.from_type_name(type_str)
seen: frozenset[str] = visited or frozenset()
if type_str in seen or chain == type_str:
# 自引用/环(如 `sizet: t.CTypedef = sizet`):无信息可展开,返回 None
return None
return TypeAnnotationResolver._resolve_builtins_type_str(
chain, aliases, SymbolTable, depth + 1, seen | {type_str})
@staticmethod
def _is_direct_type_name(name: str) -> bool:
"""名字本身即类型(非"指向另一个 typedef 的别名")
覆盖 t 的 CType 子类名(CInt/CChar/CSizeT/CInt64T 等)、BuiltinTypeMap 的
C 速记名(int/char/bool)与裸 LLVM 标量名(i64/double…,泛型推导产物)。
"""
if not name:
return False
if BuiltinTypeMap.Get(name) is not None:
return True
if name in CTypeInfo._get_llvm_primitive_map():
return True
cls: type | None = CTypeHelper.GetTModuleCType(name)
if cls is None:
cls = getattr(t, name, None)
return isinstance(cls, type) and issubclass(cls, t.CType) and cls is not t.CType
@staticmethod
def _parse_type_expr(text: str) -> ast.AST | None:
"""vast mode='eval' 解析单个表达式,返回表达式节点或 None"""
try:
expr_tree: ast.AST = ast.parse(text, mode='eval')
expr_tree: ast.AST = ast.parse(text, '<TypeAnnotationResolver:type_expr>', mode='eval')
body = getattr(expr_tree, 'body', None)
if body is not None and not isinstance(body, list):
return body
@@ -278,18 +372,52 @@ class TypeAnnotationResolver:
"""builtins 最后匹配:查 includes/builtins.vp 的 CTypedef 声明
命中(该名字确为 builtins typedef)返回解析出的 CTypeInfo,
miss 返回 None(交由调用方走原有兜底)。
miss(该名字不是 builtins typedef)返回 None(交由调用方走原有兜底)。
rawptr 为编译器内置语法:优先于一切外部声明(不可重定义)。
名字在表内却解析不出基础类型 = 声明本身无信息/成环(自引用)⇒ fail-fast,
不再静默退化为 %"{mod}.{名}"* 假结构体(下游仅报 "Type of #N arg
mismatch: %…* != …",无法归因到 typedef 声明)。
"""
if not TypeName:
return None
if TypeName == cls._BUILTIN_ROWPTR_NAME:
return cls._builtin_rawptr_info()
data: dict = cls._scan_builtins()
type_str: str | None = data['typedefs'].get(TypeName)
if type_str is None:
return None
return cls._resolve_builtins_type_str(type_str, data['aliases'], SymbolTable)
# 同一失败不再反复解析:命中失败缓存则直接按既有报文 fail-fast,
# 不再重扫 builtins.vp / 重走整条解析链。
_cached_err: str | None = cls._builtins_failure_cache.get(TypeName)
if _cached_err is not None:
raise ValueError(_cached_err)
# 全链重入深度守卫:覆盖 `_lookup_builtins_typedef → _resolve_builtins_type_str
# → from_node → _from_node_name → _lookup_builtins_typedef` 的所有回路
# (含 `|`/`[` 复合值分支导致的 depth/visited 归零绕过)。超上限即成环。
_prev_depth: int = cls._chain_depth_enter()
try:
if _prev_depth >= cls._BUILTINS_CHAIN_MAX_DEPTH:
_err: str = (
f"builtins typedef 解析链成环或过深(>{cls._BUILTINS_CHAIN_MAX_DEPTH} 层),"
f"解析 '{TypeName}' 时终止:includes/builtins.vp 中相关 typedef 声明"
f"(含 `|` / `[...]` 复合值)互相引用成环。请改为指向真实基础类型"
f"(如 t.CSizeT),或断开环上引用。")
cls._builtins_failure_cache[TypeName] = _err
raise ValueError(_err)
data: dict = cls._scan_builtins()
type_str: str | None = data['typedefs'].get(TypeName)
if type_str is None:
return None
info: CTypeInfo | None = cls._resolve_builtins_type_str(type_str, data['aliases'], SymbolTable)
if info is None:
# 不再一律归因"自引用/循环引用"(那正是把 t.CSizeT 解析失败误报成环、
# 把人引向错误方向的根因):把声明值与该值解析失败的形态如实报出。
_err = (
f"builtins typedef '{TypeName}' 无法解析到基础类型:includes/builtins.vp "
f"声明 '{TypeName}: t.CTypedef = {type_str}',其值 '{type_str}' 解析失败"
f"(既不是内建类型名,也不是链上可展开的 typedef)。"
f"请把该声明指向真实类型(如 t.CSizeT),或确认该类型名存在。")
cls._builtins_failure_cache[TypeName] = _err
raise ValueError(_err)
return info
finally:
cls._chain_depth_exit(_prev_depth)
@staticmethod
def _from_node_constant(Node: ast.AST, SymbolTable: dict | SymbolTable) -> CTypeInfo:
@@ -742,6 +870,17 @@ class TypeAnnotationResolver:
Result.ArrayDims.insert(0, str(count_val))
else:
Result.PtrCount += 1
else:
# 单参数 t.CArray[T](= t.CArray[T, None]):长度自动推导数组。
# 指针位置语义为"元素指针" Ptr(T),需在元素类型上再加一层指针。
# 与裸名 CArray[T](HandlesTypeMerge.CollectAll 的
# `ElementType.PtrCount + 1`)、stub 侧(LLVMTypeMapper
# IsListAnnotation 的 `elem_type + '*'`)、声明端
# (DeclarationGenerator `[0 x T]` → _decay_array_to_ptr = T*)
# 以及 TPV(TypeGenResolve 单参 → Ptr(elem_ty))同口径。
# 此前缺这一层:t.CArray[str] 得 i8*(即 str 本身),
# result[k] 退化成 i8 → 写字符串指针触发 ptr→int fail-fast。
Result.PtrCount += 1
if ElemInfo.IsPtr and not ElemInfo.ArrayDims:
Result.PtrCount = max(Result.PtrCount, 1)
return Result
@@ -789,12 +928,21 @@ class TypeAnnotationResolver:
params_list = slice_node.elts[0]
return_node = slice_node.elts[1]
ParamTypes = []
if isinstance(params_list, ast.List):
_ParamsIsList: bool = isinstance(params_list, ast.List)
if _ParamsIsList:
for elt in params_list.elts:
ParamTypeInfo = TypeAnnotationResolver.from_node(elt, SymbolTable)
if ParamTypeInfo:
ParamTypes.append(('', ParamTypeInfo))
if not ParamTypes:
# 空参数表 `t.Callable[[], R]` 语义 = 零形参:LLVM 端即 `R ()*`,
# 调用点按零实参间接 call。此前在此注入 void 占位形参 → 得
# `R (void)*`(void 作形参本身不是合法 LLVM 函数类型),调用点
# 实参个数(0) 与形参个数(1) 不符 → llvmlite CallInstr
# `arg = args[i]` IndexError(Repl.vp 模块级 t.Callable 槽
# `ReplFragFn()` 翻译崩溃根因);与本文件另三处 Callable 形态
# (:378 / :422 / :471)的 `FuncPtrParams = []` 口径统一。
# 非 List 形态(遗留写法 t.Callable[A, R])保留原 void 占位行为。
if not ParamTypes and not _ParamsIsList:
ParamTypes.append(('', TypeAnnotationResolver.from_type_name('void')))
Result.FuncPtrParams = ParamTypes
ReturnTypeInfo = TypeAnnotationResolver.from_node(return_node, SymbolTable)