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:
1 parent
53a345a59a
commit
c83fc752c6
53 files changed
+6238
-887
No files matched your search
@@ -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'
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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__'
|
||||
|
||||
@@ -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)
|
||||
@@ -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, "")
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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, [
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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, [
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large.
Load diff
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 等是实例级属性,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user