Files
TransPyC/lib/core/Handles/HandlesBase.py
T
TermiNexus 00edd90c28 fix(Handles*): GEP 基址 opaque pointee 收口统一为单一判据源,补齐同族 12 处缺口
【a 编译器缺陷】7aa12263 修好 `_get_attr_ptr` 的 self 分支后,同族其余 GEP 点仍会在
"基址 pointee 是另一份 opaque 同名实例"时抛裸 TypeError(被 IR 边界守卫拦下,报文为
"GEP 的基址指向**未具化**的具名结构体")。根因是同一套收口逻辑被各点各写一份、且
**多数点根本没写**:真正需要的判据只有一条。

新增唯一判据源(HandlesExprAttr):
  · `_ConcretePointeeFor(Gen, ClassName, Pointee)`:取该类的**已具化实例** ——
    ① 类键那份先 `_TryLoadStructFromStub` 补 body;② 否则 `Gen._FindConcreteTwin`
    (同短名、槽数最多);③ 都取不到 ⇒ None(**不兜底**,交守卫 fail-fast 报同短名
    实例清单 + TPC 发射点)。
  · `_ConcretizeGepBase(Gen, Ptr, ClassName, Tag)`:pointee 非 opaque ⇒ 原样返回
    (对既有正确路径零变化);opaque ⇒ bitcast 到已具化实例并**同时返回该实例**,
    供 `_get_member_offset` 作 StructHint(偏移与 GEP 必须同源,与 a43513c9 同口径)。

接入点(全部改为调用上述判据源,逐点行为只在"原本必崩"的情形下变化):
  · HandlesExprAttr._get_attr_ptr — self / 变量 / 属性链 / i8* / 下标 / 强转基座 六条分支
    (self 分支已由 7aa12263 修,此处并入统一判据;下标分支此前**完全没有**收口);
  · HandlesAnnAssign._HandleAttributeStoreLlvm — self / 变量 / 属性链 三条分支;
  · HandlesBase._get_int_ptr — 属性分支;
  · HandlesExprBuiltin(len() 的两个接收者 GEP:`obj.len` 与 list 特化字段);
  · HandlesMatch — REnum 变体 match 的载荷 GEP(变体 struct 可能未 set_body)。

冒烟(临时脚本已删,鸭子类型 Gen + 真实方法):
  · `_get_attr_ptr` self 分支 ⇒ 基址 `%"fef783da4d24330d.Keyword"*`(经 cast_self_Keyword)PASS;
  · `_get_attr_ptr` i8* 分支 ⇒ 同上 PASS;
  · `HandlesAnnAssign` self 分支 ⇒ GEP 基址为已具化实例 PASS;
  · 回归:基址本就具化时必须**原样**(不得多出 bitcast)PASS。
另:`python -m compileall TransPyC/lib` 全绿。
2026-09-30 14:42:56 +08:00

1483 lines
73 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
from typing import TYPE_CHECKING, List, Any
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
import vast as ast
from enum import IntFlag, auto
from llvmlite import ir as _ir
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, IsCPtrMarkerNode,
FlattenUnionOperands, IsI8DoublePtrType, IsClassRefPtrAnnotation,
FixedArrayKeywordAnnotationError)
from lib.core.VLogger import get_logger as _vlog
EXCEPTION_CODE_MAP: dict[str, int] = {
'ValueError': 1, 'TypeError': 2, 'RuntimeError': 3,
'ZeroDivisionError': 4, 'IndexError': 5, 'KeyError': 6,
'IOError': 7, 'OSError': 8, 'AssertionError': 9, 'Exception': 99,
}
class FuncMeta(IntFlag):
NONE = 0
STATIC_METHOD = auto()
PROPERTY_GETTER = auto()
PROPERTY_SETTER = auto()
PROPERTY_DELETER = auto()
CLASS_METHOD = auto()
ABSTRACT = auto()
class _Delegated:
"""CTypeInfo 旧字段名委托描述符 — 静态委托到 _ts/_sm 的字段
替代旧的 FIELD_ROUTES + __setattr__/__getattr__ 动态路由:
描述符在类创建时绑定,访问时直接走 __get__/__set__,
无 dict 查找和字符串比较的运行时开销。
"""
__slots__ = ('_target_attr', '_field_name')
def __init__(self, target_attr: str, field_name: str) -> None:
self._target_attr: str = target_attr # '_ts' or '_sm'
self._field_name: str = field_name
def __get__(self, obj: Any, objtype: type | None = None) -> Any:
if obj is None:
return self
target: Any = object.__getattribute__(obj, self._target_attr)
return getattr(target, self._field_name)
def __set__(self, obj: Any, value: Any) -> None:
target: Any = object.__getattribute__(obj, self._target_attr)
setattr(target, self._field_name, value)
class CTypeInfo:
"""
C 类型信息对象 - 统一符号表条目和类型计算
设计原则:
- BaseType 是 CType 实例(来自 t 模块),永远不使用硬编码字符串
- 描述符分为两种:
- VarConst/VarVolatile: 变量本身(指针)的限定符,如 int * const
- DataConst/DataVolatile: 指向数据的限定符,如 const int *
属性(类型计算):
- BaseType: t.CType - 基础类型实例 (如 t.CInt(), t.CVoid())
- PtrCount: int - 指针层数
- VarConst: bool - 变量本身(指针)是否 const,如 int * const
- VarVolatile: bool - 变量本身(指针)是否 volatile
- DataConst: bool - 指向的数据是否 const,如 const int *
- DataVolatile: bool - 指向的数据是否 volatile
- ArrayDims: List[str] - 数组维度
- Storage: t.CType - 存储类 (static, extern 等)
- IsFuncPtr: bool - 是否函数指针
- FuncPtrParams: List[Tuple[str, CTypeInfo]] - 函数指针参数列表,每个元素是 (参数名, 参数类型)
- FuncPtrReturn: CTypeInfo - 函数指针返回类型
- IsBitField: bool - 是否位域
- BitWidth: int - 位域宽度
- IsTypedef/IsStruct/IsEnum/IsUnion: bool - 类型种类
属性(符号表元数据):
- Name: str - 名称(struct/enum/union/typedef 名)
- Members: Dict[str, CTypeInfo] - 成员字典(用于 struct/union)
- OriginalType: str - typedef 的原始类型
- DeclaredType: str - 声明类型
- CreturnTypes: list - C 返回类型列表
- Lineno: int - 定义行号
- IsAnonymous: bool - 是否匿名类型
- IsCpythonObject: bool - 是否 CPython 对象
- IsEnumMember: bool - 是否枚举成员
"""
def __init__(self) -> None:
object.__setattr__(self, '_ts', TypeSpec())
sm: SymbolMeta = SymbolMeta()
sm.meta_list = FuncMeta.NONE
object.__setattr__(self, '_sm', sm)
# --- 新公共 API:供新代码直接访问 TypeSpec / SymbolMeta / SymbolKind ---
@property
def ts(self) -> TypeSpec:
"""TypeSpec — 纯类型描述(只读)"""
return self._ts
@property
def sm(self) -> SymbolMeta:
"""SymbolMeta — 符号表元数据(只读)"""
return self._sm
@property
def kind(self) -> 'SymbolKind':
"""SymbolKind — 符号类型种类枚举"""
return self._sm.kind
# 注:旧字段名(PtrCount/IsStruct/Name 等)的委托通过 _Delegated 描述符实现,
# 在模块末尾从 FIELD_ROUTES 静态绑定,无需 __setattr__/__getattr__ 动态路由
@property
def BaseType(self) -> t.CType | tuple | None:
return self._ts._base_type
@BaseType.setter
def BaseType(self, value: t.CType | type | tuple | List | None) -> None:
"""设置 BaseType,接受以下类型:
- None: 清空
- t.CType 实例: 单个类型,自动设置 IsStruct/IsEnum/IsUnion 标志
- type (CType 子类): 如 t.CStruct,自动实例化
- tuple/list of t.CType: 多个组合类型
"""
ts: TypeSpec = self._ts
sm: SymbolMeta = self._sm
if value is None:
ts._base_type = None
sm.is_struct = False
sm.is_enum = False
sm.is_union = False
sm.is_renum = False
return
if isinstance(value, (tuple, list)):
ts._base_type = tuple(value)
for v in value:
if isinstance(v, t.CStruct):
sm.is_struct = True
elif isinstance(v, t.CEnum):
sm.is_enum = True
elif isinstance(v, t.CUnion):
sm.is_union = True
elif isinstance(v, t.REnum):
sm.is_renum = True
sm.is_enum = True
return
if isinstance(value, type) and issubclass(value, t.CType):
ts._base_type = value()
return
if isinstance(value, t.CType):
ts._base_type = value
if isinstance(value, t.CStruct):
sm.is_struct = True
sm.is_enum = False
sm.is_union = False
sm.is_renum = False
elif isinstance(value, t.REnum):
sm.is_renum = True
sm.is_enum = True
sm.is_struct = False
sm.is_union = False
elif isinstance(value, t.CEnum):
sm.is_enum = True
sm.is_struct = False
sm.is_union = False
sm.is_renum = False
elif isinstance(value, t.CUnion):
sm.is_union = True
sm.is_struct = False
sm.is_enum = False
sm.is_renum = False
return
raise TypeError(f"BaseType must be t.CType instance, type subclass, or tuple of t.CType, got {type(value)}")
@property
def TypeCls(self) -> type | None:
"""获取 CType 类(从 BaseType 推断)"""
bt: t.CType | tuple | None = self._ts._base_type
if bt:
return type(bt)
sm: SymbolMeta = self._sm
if sm.is_struct:
return t.CStruct
if sm.is_enum:
return t.CEnum
if sm.is_union:
return t.CUnion
if sm.is_typedef:
return t._CTypedef
return None
@property
def IsPtr(self) -> bool:
return self._ts.ptr_count > 0
@property
def IsVoid(self) -> bool:
return isinstance(self._ts._base_type, t.CVoid)
@property
def IsStr(self) -> bool:
return isinstance(self._ts._base_type, t.CChar) and self._ts.ptr_count > 0
@property
def IsInt(self) -> bool:
bt: t.CType | tuple | None = self._ts._base_type
return isinstance(bt, t.CType) and getattr(bt, 'IsSigned', None) is not None and bt.IsSigned is True and not isinstance(bt, t.CVoid)
@property
def IsUInt(self) -> bool:
bt: t.CType | tuple | None = self._ts._base_type
return isinstance(bt, t.CType) and getattr(bt, 'IsSigned', None) is False
@property
def IsFloat(self) -> bool:
bt: t.CType | tuple | None = self._ts._base_type
return isinstance(bt, t.CType) and getattr(bt, 'IsSigned', None) is None and getattr(bt, 'Size', 0) in (32, 64, 128) and not isinstance(bt, t.CVoid)
def ToString(self) -> str:
ts: TypeSpec = self._ts
sm: SymbolMeta = self._sm
bt: t.CType | tuple | None = ts._base_type
if ts.is_func_ptr:
return 'Callable'
parts: list[str] = []
if ts.storage and not isinstance(ts.storage, t.CExport):
parts.append(type(ts.storage).__name__)
if ts.data_const or ts.data_volatile:
qualifiers: list[str] = []
if ts.data_const:
qualifiers.append("const")
if ts.data_volatile:
qualifiers.append("volatile")
parts.append("_".join(qualifiers))
if sm.is_typedef and sm.name:
return sm.name
elif sm.is_renum and sm.name:
base_name: str = sm.name
elif bt:
if isinstance(bt, str):
base_name = bt
elif isinstance(bt, type) and issubclass(bt, t.CType):
base_name = bt.__name__
elif isinstance(bt, t.CType):
# 对命名的 CType(CStruct/CEnum/CUnion/REnum),优先使用其 name 属性
# 否则回退到类名(如 'CInt'、'CChar')
_named: str | None = getattr(bt, 'name', None)
base_name = _named if _named else type(bt).__name__
else:
base_name = str(bt)
else:
base_name = "void"
if ts.is_array_ptr:
ptr_str: str = ""
if ts.ptr_count > 0:
ptr_str = "*" * ts.ptr_count
if ts.array_ptr:
inner: str = ts.array_ptr.ToString()
if ptr_str:
parts.append(f"{base_name} {ptr_str}({inner})")
else:
parts.append(f"{base_name} ({inner})")
else:
if ptr_str:
parts.append(f"{base_name} {ptr_str}()")
else:
parts.append(f"{base_name} ()")
elif ts.array_ptr:
inner = ts.array_ptr.ToString()
if ts.ptr_count > 0:
parts.append(f"{base_name} {'*' * ts.ptr_count}({inner})")
else:
parts.append(f"{base_name} ({inner})")
else:
parts.append(base_name)
if ts.ptr_count > 0:
parts.append("*" * ts.ptr_count)
if ts.var_const:
parts.append("const")
if ts.var_volatile:
parts.append("volatile")
for dim in ts.array_dims:
if dim:
parts.append(f"[{dim}]")
else:
parts.append("[]")
return " ".join(parts) if parts else "void"
@staticmethod
def CreateFromTypeName(TypeName: str) -> "CTypeInfo":
"""从类型名字符串创建 CTypeInfo(兼容旧 API)"""
info: CTypeInfo = CTypeInfo()
if TypeName.startswith('struct '):
info.BaseType = t.CStruct(name=TypeName[7:].strip()) if TypeName[7:].strip() else t.CStruct()
elif TypeName.startswith('enum '):
info.BaseType = t.CEnum(name=TypeName[5:].strip()) if TypeName[5:].strip() else t.CEnum()
elif TypeName.startswith('union '):
info.BaseType = t.CUnion(name=TypeName[6:].strip()) if TypeName[6:].strip() else t.CUnion()
else:
info.BaseType = t.CStruct(name=TypeName)
return info
# LLVM primitive type name → (CTypeClass, PtrCount) mapping
# Used when generic type inference produces LLVM type names like 'i64', 'double', etc.
_LLVM_PRIMITIVE_MAP: dict[str, tuple[type, int]] | None = None
@classmethod
def _get_llvm_primitive_map(cls) -> dict[str, tuple[type, int]]:
if cls._LLVM_PRIMITIVE_MAP is not None:
return cls._LLVM_PRIMITIVE_MAP
cls._LLVM_PRIMITIVE_MAP = {
'void': (t.CVoid, 0),
'i1': (t.CInt, 0),
'i8': (t.CChar, 0),
'i16': (t.CShort, 0),
'i32': (t.CInt, 0),
'i64': (t.CLong, 0),
'i128': (t.CInt, 0),
'float': (t.CFloat, 0),
'double': (t.CDouble, 0),
'fp128': (t.CFloat128T, 0),
}
return cls._LLVM_PRIMITIVE_MAP
@staticmethod
def FromTypeName(TypeName: str) -> "CTypeInfo":
"""从简单类型名构造 CTypeInfo(不经过 FromStr 字符串解析)"""
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver
return TypeAnnotationResolver.from_type_name(TypeName)
# FromStr 已移除 - 类型解析不再经过中间字符串格式
# typedef 展开直接走 CTypeInfo 对象,不经过字符串解析
@staticmethod
def TryEvalConstExpr(node: ast.AST, SymbolTable: Any) -> Any:
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver
return TypeAnnotationResolver.try_eval_const_expr(node, SymbolTable)
@classmethod
def FromNode(cls, Node: ast.AST, SymbolTable: Any) -> "CTypeInfo":
"""从 AST 节点解析 CTypeInfo(类方法)
Args:
Node: AST 节点(如 ast.Name, ast.Attribute 等)
SymbolTable: 符号表字典
"""
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver
return TypeAnnotationResolver.from_node(Node, SymbolTable)
def Copy(self) -> "CTypeInfo":
NewInfo: CTypeInfo = CTypeInfo()
NewInfo._ts = self._ts.copy()
NewInfo._sm = self._sm.copy()
# CTypeInfo 引用字段需要深拷贝
if isinstance(NewInfo._ts.func_ptr_return, CTypeInfo):
NewInfo._ts.func_ptr_return = NewInfo._ts.func_ptr_return.Copy()
if isinstance(NewInfo._ts.original_type, CTypeInfo):
NewInfo._ts.original_type = NewInfo._ts.original_type.Copy()
if NewInfo._ts.array_ptr:
NewInfo._ts.array_ptr = NewInfo._ts.array_ptr.Copy()
return NewInfo
def get(self, key: str, default: Any = None) -> Any:
"""获取额外属性"""
return self._sm._extra.get(key, default)
def set(self, key: str, value: Any) -> None:
"""设置额外属性"""
self._sm._extra[key] = value
@staticmethod
def VoidTypeInfo() -> "CTypeInfo":
info: CTypeInfo = CTypeInfo()
info.BaseType = t.CVoid()
return info
def ToLLVM(self, Gen: LlvmCodeGenerator) -> _ir.Type:
if Gen is None:
return _ir.IntType(32)
return Gen._ctype_to_llvm(self)
def __str__(self) -> str:
return self.ToString()
def __repr__(self) -> str:
ts: TypeSpec = self._ts
sm: SymbolMeta = self._sm
return f"CTypeInfo(BaseType={ts._base_type}, PtrCount={ts.ptr_count}, IsTypedef={sm.is_typedef}, OriginalType={ts.original_type!r}, VarConst={ts.var_const}, DataConst={ts.data_const})"
# 从 FIELD_ROUTES 静态绑定 _Delegated 描述符到 CTypeInfo 类
# 这是一次性配置(类创建期执行),替代运行时 __setattr__/__getattr__ 动态路由
for _old_name, (_target, _field) in FIELD_ROUTES.items():
setattr(CTypeInfo, _old_name, _Delegated('_' + _target, _field))
del _old_name, _target, _field
class CTypeHelper:
@staticmethod
def GetTModuleCType(TypeName: str) -> type | None:
result: type | None = CTypeRegistry.GetClassByName(TypeName)
if result is not None:
return result
TypeClass: type | None = getattr(t, TypeName, None)
if TypeClass and isinstance(TypeClass, type) and issubclass(TypeClass, t.CType):
return TypeClass
FallbackClass: type | None = getattr(t, f'_{TypeName}', None)
if FallbackClass and isinstance(FallbackClass, type) and issubclass(FallbackClass, t.CType):
return FallbackClass
return None
@staticmethod
def GetCName(type_or_name: str | type) -> str:
if isinstance(type_or_name, str):
cls: type | None = CTypeRegistry.GetClassByName(type_or_name)
if cls is None:
cls = CTypeHelper.GetTModuleCType(type_or_name)
if cls:
return cls.__name__
return type_or_name
elif isinstance(type_or_name, type) and issubclass(type_or_name, t.CType):
return type_or_name.__name__
return ''
class BuiltinTypeMap:
"""
内置类型映射表 - 字符串到 (CType类, PtrCount) 的映射
用法:BuiltinTypeMap.Get('int') -> (t.CInt, 0)
BuiltinTypeMap.Get('BYTEPTR') -> (t.CUnsignedChar, 1)
"""
_map: dict[str, tuple[type, int]] | None = None
@classmethod
def _build_map(cls) -> dict[str, tuple[type, int]]:
if cls._map is not None:
return cls._map
CTypeRegistry._build()
cls._map = {}
# 仅使用 Python 类名 → (CType 类, ptr_level) 映射。
# C 类型名(如 'int'、'int32_t')不再通过 _cname_to_class 提供。
# C 头文件 stub 生成器使用独立的硬编码表(CTypeMapper._CNAME_TO_PY)。
for pyname, ctype_cls in CTypeRegistry._name_to_class.items():
pos: frozenset = getattr(ctype_cls, 'position', frozenset())
if t.CType.POINTER in pos and t.CType.BASE not in pos:
cls._map[pyname] = (ctype_cls, 1)
else:
cls._map[pyname] = (ctype_cls, 0)
# 保留一些常用的字符串速记(这些不是 CName,而是硬编码的便捷别名)。
# 包含 Python 内置类型名(int/float/bool)和 C 类型名速记(char/short/long 等)。
_SPECIAL: dict[str, tuple[type, int]] = {
# Python 内置类型名
'str': (t.CChar, 1), 'bytes': (t.CChar, 1),
'int': (t.CInt, 0), 'float': (t.CFloat, 0), 'bool': (t.CBool, 0),
# C 类型名速记
'char': (t.CChar, 0), 'short': (t.CShort, 0),
'long': (t.CLong, 0), 'long long': (t.CLongLong, 0),
'double': (t.CDouble, 0),
'unsigned': (t.CUnsigned, 0),
'signed': (t.CInt, 0), 'signed int': (t.CInt, 0),
'signed char': (t.CSignedChar, 0),
'void': (t.CVoid, 0), 'Void': (t.CVoid, 0),
}
for k, v in _SPECIAL.items():
if k not in cls._map:
cls._map[k] = v
_FLOAT_ALIASES: dict[str, tuple[type, int]] = {
'FLOAT8': (t.CFloat8T, 0), 'FLOAT16': (t.CFloat16T, 0),
'FLOAT32': (t.CFloat32T, 0), 'FLOAT64': (t.CFloat64T, 0),
'FLOAT128': (t.CFloat128T, 0),
}
for k, v in _FLOAT_ALIASES.items():
if k not in cls._map:
cls._map[k] = v
return cls._map
@classmethod
def Get(cls, type_name: str) -> tuple[type, int] | None:
"""获取类型类和指针层级"""
return cls._build_map().get(type_name)
class BaseHandle:
def __init__(self, translator: "Translator") -> None:
self.Trans: Translator = translator
self._CurrentCpythonObjectClass: str | None = None
@staticmethod
def _is_char_pointer(val: _ir.Value) -> bool:
"""检查值是否为 char* (i8*) 指针"""
if not isinstance(val.type, _ir.PointerType):
return False
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,
ConstWidthHint: int | None = None) -> _ir.Value | None:
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint)
@staticmethod
def _ExprShape(Node: ast.AST, Depth: int = 0) -> str:
"""表达式节点的**形态摘要**(类名 + 关键子节点),供 fail-fast 报文取证。
与 `_ExprText` 的分工:`_ExprText` 给"源码文本"(属性链只到 Name 为止,遇
Call/Subscript 基座会退化成裸 attr 名,如 `(ast.Expr*(node)).value` → `'value'`),
本方法给"AST 形态",用于判定强转调用是否真被 parser 产成 `Call(func=BinOp(BitOr))`。
"""
if Depth >= 4:
return '…'
if isinstance(Node, ast.Attribute):
return f"Attribute(.{Node.attr}←{BaseHandle._ExprShape(Node.value, Depth + 1)})"
if isinstance(Node, ast.Call):
return (f"Call(func={BaseHandle._ExprShape(Node.func, Depth + 1)}"
f",args={len(Node.args)})")
if isinstance(Node, ast.BinOp):
return (f"BinOp({type(Node.op).__name__}, "
f"{BaseHandle._ExprShape(Node.left, Depth + 1)}, "
f"{BaseHandle._ExprShape(Node.right, Depth + 1)})")
if isinstance(Node, ast.Name):
return f"Name({Node.id})"
if isinstance(Node, ast.Subscript):
return f"Subscript({BaseHandle._ExprShape(Node.value, Depth + 1)})"
return type(Node).__name__
def _NoteAttrNone(self, Gen: Any, ClassName: str | None, AttrName: str, Reason: str) -> None:
"""记录「属性读取返回 None」的**确切原因**(供 fail-fast 报文取证,纯记账)。
`x.attr` 求值为 None 在 `ExprAttrHandle` 里有 5 个不同来源(类名未解析 /
基座求值为空 / 结构体未匹配 / stub 无 body / 槽位越界),各自根因完全不同;
下游(_RequireArgValue / AnnAssign)只看到"求值为空",无法区分 ⇒ 报错点与
真因相隔数层。此处把 `(类名, 字段名, 原因)` 记到 Gen,由 `_DescribeAttrBase`
在报文里回显;回显时要求 `(类名, 字段名)` 与被报节点一致,避免嵌套读取留下的
旧记录误导(不改变任何判定与 IR)。
"""
try:
Gen._LastAttrNone = (ClassName or '', AttrName or '', Reason)
except Exception: # noqa: BLE001
pass
def _DescribeAttrBase(self, Gen: Any, Node: ast.Attribute) -> str:
"""属性读返回 None 时的**基址事实**(供 fail-fast 报文取证,不改变任何判定)。
两类根因:① 类名登记退化(var_struct_class 存 'rawptr' / 'rawptr[X]' / 折叠态,
或压根没登记);② 登记名与 Gen.structs / class_members 的键不同源(短名 vs
sha1 全名)。另有一条**必须验伪**的可能:基座是强转调用 `(T*(x)).attr`
(parser 统一产成 Call(func=BinOp(BitOr)))时,HandlesExprAttr 的
`_handle_attr_call_result` 自带 `_FailFastAttrCallBase` 详细 fail-fast
(FailFastError 全仓无 except,必上抛)——若仍走到本诊断,即说明那条
fail-fast 未被走到,属**另一处缺口**,故显式标注该事实。
"""
_base: ast.AST = Node.value
_parts: list[str] = [f"基座形态={BaseHandle._ExprShape(_base)}"]
_vc_head: Any = None
if isinstance(_base, ast.Name):
_bn: str = _base.id
_vc: str | None = None
_alloc: Any = None
try:
_vc = (getattr(Gen, 'var_struct_class', None) or {}).get(_bn)
if not _vc:
_vc = (getattr(Gen, 'global_struct_class', None) or {}).get(_bn)
_alloc = (getattr(Gen, 'variables', None) or {}).get(_bn)
except Exception: # noqa: BLE001
pass
_parts.append(f"'{_bn}' 的 var_struct_class={_vc!r}")
_vc_head = _vc
_parts.append(f"alloca 类型={getattr(_alloc, 'type', None)}")
_parts.append("该名在 Gen.structs="
+ ('是' if _vc and _vc in Gen.structs else ('否' if _vc else '—')))
_parts.append("class_members="
+ (f"{len(Gen.class_members.get(_vc) or [])} 个字段"
if _vc and _vc in Gen.class_members else '无该键'))
if _vc and _vc in Gen.class_members:
_parts.append(f"字段名单={[m[0] for m in Gen.class_members[_vc]][:12]}")
# struct body 元素数 与 该字段的**计算偏移**:字段名在成员表里能找到、
# 却没读到值,只可能是"偏移 ≥ body 元素数"(`_gep_and_Load_member` 的
# 越界 return None)。这两个数字是判定该情况的唯一依据,必须一并报出。
_st_attr: Any = Gen.structs.get(_vc) if _vc else None
if _st_attr is not None:
_nelem: Any = (len(_st_attr.elements)
if getattr(_st_attr, 'elements', None) is not None else 'opaque')
_parts.append(f"struct body 元素数={_nelem}")
if _vc:
try:
_off: Any = Gen._get_member_offset(Node.attr, _vc)
except Exception as _e: # noqa: BLE001
_off = f"<取偏移抛 {type(_e).__name__}>"
_parts.append(f"字段 '{Node.attr}' 计算偏移={_off}")
elif (isinstance(_base, ast.Call) and isinstance(_base.func, ast.BinOp)
and isinstance(_base.func.op, ast.BitOr)):
_cls: str | None = None
try:
_cls = self.Trans.ExprAttrHandle._extract_union_base_class(_base.func)
except Exception: # noqa: BLE001
_cls = None
_parts.append(f"联合基类名={_cls!r}")
_vc_head = _cls
_parts.append(f"短名 in structs={'是' if _cls and _cls in Gen.structs else '否'}")
_parts.append("class_members="
+ (f"{len(Gen.class_members.get(_cls) or [])} 个字段"
if _cls in Gen.class_members else '无该键'))
_parts.append("注: 强转基座形态本应走 _FailFastAttrCallBase 详细报文,"
"此处仍是本条 ⇒ 那条 fail-fast 未触发")
# 回显读取侧的**确切失败原因**(`ExprAttrHandle` 各 return None 点登记)。
# 仅在 (类名, 字段名) 与被报节点一致时输出:嵌套读取留下的旧记录不得冒充本节点。
try:
_rec: Any = getattr(Gen, '_LastAttrNone', None)
except Exception: # noqa: BLE001
_rec = None
if isinstance(_rec, tuple) and len(_rec) >= 3:
_rec_cls, _rec_attr, _rec_reason = _rec[0], _rec[1], _rec[2]
_short = lambda s: (s.split('.')[-1] if s else s) # noqa: E731
_same: bool = bool(_rec_attr) and _rec_attr == Node.attr and (
not _rec_cls or not _vc_head or _rec_cls == _vc_head
or _short(_rec_cls) == _short(_vc_head))
if _same:
_parts.append(f"读取侧原因={_rec_reason}")
return ";".join(_parts)
@staticmethod
def _ExprText(Node: ast.AST) -> str:
"""把表达式节点渲染成源码文本(如 func.Blocks.Head、list[X*] * uint64(v)),供错误信息使用"""
if isinstance(Node, ast.Attribute):
parts: list[str] = []
cur: ast.AST = Node
while isinstance(cur, ast.Attribute):
parts.append(cur.attr)
cur = cur.value
if isinstance(cur, ast.Name):
parts.append(cur.id)
if parts:
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 _FailFastFixedArrayKeyword(self, Node: ast.AST, Msg: str) -> None:
"""内建定长数组构造子 array 误用的 fail-fast 出口。
报错含:文件:行:列 + 源码片段 + 期望形态(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:
# 行号基准修正(vast AST 行号 vs 磁盘原文件行号偏差):
# Phase2Translator._inject_auto_imports 会在解析前向源码头部注入
# 1~3 行 "import _list/_dict/json",使 AST 节点 lineno 比磁盘原文件
# 真实行号大 N(实测 CallGenCBuiltin.vp 真实 1409 报 1410、
# 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:
for cand in range(lineno, max(0, lineno - 8), -1):
if 0 < cand <= len(_src_lines) and expr_text in _src_lines[cand - 1]:
lineno = cand
break
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}"
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 ''}")
# 【基址诊断】属性读返回 None 时报出**基座真实形态 + 类名登记事实**,不再从
# "求值为空"反推(同一份事实也供 _RequireArgValue 的实参求值失败路径使用,
# 见 HandlesExprCall._RequireArgValue)。
if isinstance(Node, ast.Attribute):
msg += "\n│ 基址诊断: " + self._DescribeAttrBase(Gen, Node)
_vlog().compile_error(msg, src_file, lineno, code_line)
sys.exit(1)
def HandleBodyLlvm(self, Body: ast.AST | list[ast.stmt]) -> Any:
return self.Trans.BodyHandler.HandleBodyLlvm(Body)
def _get_int_ptr(self, Node: ast.AST) -> _ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
if isinstance(Node, ast.Name):
if Node.id in Gen.variables:
return Gen.variables[Node.id]
elif isinstance(Node, ast.Attribute):
obj_val: Any = self.HandleExprLlvm(Node.value)
if not obj_val:
return None
if isinstance(obj_val.type, _ir.PointerType):
pointee: _ir.Type = obj_val.type.pointee
if isinstance(pointee, (_ir.IdentifiedStructType, _ir.LiteralStructType)):
StructName: str | None = None
if isinstance(pointee, _ir.IdentifiedStructType):
StructName = pointee.name
if not StructName:
for ClassName, struct_type in Gen.structs.items():
if struct_type == pointee:
StructName = ClassName
break
if StructName and StructName in Gen.structs:
# 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase):pointee 可能是
# **另一份 opaque 同名实例**,typed pointer 下 GEP 必须编译期算出
# 元素类型 ⇒ 先补 body / 取同短名孪生体,并以该实例算偏移(同源)。
obj_val, _STk = self.Trans.ExprAttrHandle._ConcretizeGepBase(
Gen, obj_val, StructName, f"cast_{StructName}")
offset: int = Gen._get_member_offset(Node.attr, StructName, _STk)
member_ptr: Any = Gen.builder.gep(obj_val, [_ir.Constant(_ir.IntType(32), 0), _ir.Constant(_ir.IntType(32), offset)], name=f"{Node.attr}_ptr")
return member_ptr
return None
def _IsTModuleType(self, TypeName: str) -> bool:
if TypeName in ('t', 'State', 'Bit', 'Anonymous', 'Postdefinition'):
return True
TypeClass: type | None = getattr(t, TypeName, None)
if TypeClass and isinstance(TypeClass, type):
if issubclass(TypeClass, t.CType):
return True
FallbackClass: type | None = getattr(t, f'_{TypeName}', None)
if FallbackClass and isinstance(FallbackClass, type):
if issubclass(FallbackClass, t.CType):
return True
return False
def ResolveListElementType(
self,
elem_type_node: ast.expr,
Gen: Any,
*,
check_func_ptr: bool = False,
handle_str: bool = False,
void_fallback_width: int = 32,
fallback_to_cint: bool = False,
) -> tuple[Any, CTypeInfo | None]:
"""解析 list 元素类型,返回 (LLVM类型, CTypeInfo|None)。
统一处理结构体、str、函数指针、VoidType 回退等变体。
Args:
check_func_ptr: 是否检查函数指针(全局变量路径需要)
handle_str: 是否处理 str → i8*(局部变量路径需要)
void_fallback_width: VoidType 回退宽度 (32 或 8)
fallback_to_cint: 无类型信息时回退到 CInt
"""
elem_type_info: CTypeInfo | None = CTypeInfo.FromNode(elem_type_node, self.Trans.SymbolTable)
# 结构体类型
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
if check_func_ptr:
sym_entry: CTypeInfo | None = self.Trans.SymbolTable.lookup(elem_type_node.id)
if sym_entry and isinstance(sym_entry, CTypeInfo) and sym_entry.IsFuncPtr:
return _ir.IntType(8).as_pointer(), elem_type_info
return Gen.structs[elem_type_node.id], elem_type_info
# str → i8*
if handle_str and isinstance(elem_type_node, ast.Name) and elem_type_node.id == 'str':
return _ir.PointerType(_ir.IntType(8)), elem_type_info
# 函数指针检查
if check_func_ptr and elem_type_info and elem_type_info.IsFuncPtr:
return _ir.IntType(8).as_pointer(), elem_type_info
# 回退到 CInt
if elem_type_info is None and fallback_to_cint:
elem_type_info = CTypeInfo()
elem_type_info.BaseType = t.CInt()
elem_type: Any = Gen._ctype_to_llvm(elem_type_info)
if isinstance(elem_type, _ir.VoidType):
elem_type = _ir.IntType(void_fallback_width)
return elem_type, elem_type_info
def ParseArrayCount(
self,
count_node: ast.expr | None,
Gen: Any,
value_node: ast.expr | None = None,
*,
mode: str = 'global',
) -> int:
"""解析数组计数,返回计数(默认 1)。
统一处理 Constant/Name/Attribute/BinOp/None 等形式。
None 或 Constant(None) 时从 value_node 推断(List 长度或 str 长度+1)。
Args:
count_node: 计数 AST 节点
value_node: 赋值右侧值节点(用于 count_node 为 None 时推断)
mode: 'global' 使用 _eval_global_count + SymbolTable + _define_constants;
'local' 使用 TryEvalConstExpr
"""
# None 或 Constant(None) → 从 value_node 推断
if count_node is None or (isinstance(count_node, ast.Constant) and count_node.value is None):
if value_node and isinstance(value_node, ast.List):
return len(value_node.elts)
if value_node and isinstance(value_node, ast.Constant) and isinstance(value_node.value, str):
return len(value_node.value) + 1
return 1
# Constant(int)
if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int):
return count_node.value
if mode == 'global':
# Name → SymbolTable + _define_constants
if isinstance(count_node, ast.Name):
ArrayCount: int = 1
if self.Trans.SymbolTable.has(count_node.id):
sym_info: Any = self.Trans.SymbolTable[count_node.id]
if isinstance(sym_info.value, int):
ArrayCount = sym_info.value
if ArrayCount == 1 and count_node.id in Gen._define_constants:
def_val: Any = Gen._define_constants[count_node.id]
# 治本修复:本模块全局变量初始化值优先,避免跨模块同名 CDefine 覆盖
try:
if hasattr(Gen, 'module') and count_node.id in Gen.module.globals:
_gv: Any = Gen.module.globals[count_node.id]
if getattr(_gv, 'initializer', None) is not None:
_iv: Any = _gv.initializer
if hasattr(_iv, 'constant') and isinstance(_iv.constant, int):
def_val = _iv.constant
except Exception:
pass
if isinstance(def_val, int):
ArrayCount = def_val
return ArrayCount
# Attribute / BinOp → _eval_global_count
if isinstance(count_node, (ast.Attribute, ast.BinOp)):
eval_fn: Any = getattr(self, '_eval_global_count', None)
if eval_fn is not None:
ev: int | None = eval_fn(count_node, Gen)
if ev is not None:
return ev
return 1
else:
# local mode → TryEvalConstExpr
eval_count: Any = CTypeInfo.TryEvalConstExpr(count_node, self.Trans.SymbolTable)
if eval_count is not None and isinstance(eval_count, int) and eval_count > 0:
return eval_count
return 1
return 1
def ResolveAnnotationType(self, annotation: ast.expr) -> CTypeInfo | None:
"""解析注解类型,BinOp(BitOr) 优先使用 MergeTypes,其他使用 FromNode。
统一处理函数返回类型和参数类型的 BinOp 联合类型解析。
指针下标(rawptr[T] 内置裸指针 / t.CPtr[T] / 裸名 CPtr[T],三者等价
`T | t.CPtr`)同样交给 TypeMerge 解析:内层 T 走通用类型解析(含跨模块
类经 Gen.structs 反查 SHA1 前缀),指针层数与源码 `T | t.CPtr` 一致;
若交给 CTypeInfo.FromNode(TypeAnnotationResolver)解析,
rawptr[ast.AST] 会退化成 CVoid/1(i8*)、t.CPtr[ast.AST] 亦退化成
CVoid/1(其 Subscript-CPtr 分支不带 Gen.structs 反查),与 declare 端
%"<sha1>.AST"* 分裂。
命中泛型模板的泛型容器下标(list[X] / rawptr[list[X]] 等)同样交
TypeMerge:字段(embedded)位置与参数/返回注解**同一解析**,否则
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:
info: CTypeInfo | None = merger.MergeTypes(annotation)
if info is not None and isinstance(info, CTypeInfo):
return info
if IsPtrSubscriptNode(annotation, self.Trans.SymbolTable):
merger = getattr(self.Trans, 'TypeMergeHandler', None)
if merger:
info = merger.GetCTypeInfo(annotation)
if isinstance(info, CTypeInfo) and info.BaseType is not None:
return info
if (isinstance(annotation, ast.Subscript)
and self._SubscriptHitsGenericTemplate(annotation)
and getattr(self.Trans, 'TypeMergeHandler', None) is not None):
gci: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(annotation)
if isinstance(gci, CTypeInfo) and gci.BaseType is not None:
return gci
if isinstance(annotation, ast.Attribute):
return self._ResolveAttributeAnnotationType(annotation)
Fallback: CTypeInfo | None = CTypeInfo.FromNode(annotation, self.Trans.SymbolTable)
return self._ResolveStackArrayElementFallback(annotation, Fallback)
def _ResolveStackArrayElementFallback(
self, annotation: ast.expr, Fallback: CTypeInfo | None) -> CTypeInfo | None:
"""固定数组注解(`t.CArray[T, N]` / `list[T, N]`)元素类型兜底解析。
CTypeInfo.FromNode(TypeAnnotationResolver 的 IsListAnnotation 分支)只
会用自身解析元素类型:元素命中泛型模板时(list[X] / list[rawptr[X]])解
析为空 → 整个注解 BaseType=None → 调用方(CollectAnnAssignMember)把
VoidType 换成 i8*,字段即塌成 i8*,与声明端
DeclarationGenerator._get_type_str 的 ValueIsCArray 分支给出的
`[N x <元素>]` 布局不一致(规则 4:能不变 i8* 的就不变 i8*)。
此处仅在该元素解析为空时兜底:用**与参数注解同一解析**
(ResolveAnnotationType 递归,泛型容器因此走 TypeMerge 特化,跨模块类
经 Gen.structs 反查 SHA1)解析元素,再套上数组维度(维度序与
TypeAnnotationResolver 的 `insert(0, count)` 一致)。
元素可解析的形态(t.CArray[t.CInt, 8] / t.CArray[Node, 4] / 嵌套 CArray)
保持 FromNode 结果原样,避免改动既有布局。
"""
if isinstance(Fallback, CTypeInfo) and Fallback.BaseType is not None:
return Fallback
if not (isinstance(annotation, ast.Subscript) and IsListAnnotation(annotation)):
return Fallback
SliceNode: ast.AST = annotation.slice
if not (isinstance(SliceNode, ast.Tuple) and len(SliceNode.elts) == 2):
return Fallback
ElemInfo: CTypeInfo | None = self.ResolveAnnotationType(SliceNode.elts[0])
if not isinstance(ElemInfo, CTypeInfo) or ElemInfo.BaseType is None:
return Fallback
CountVal: int | None = CTypeInfo.TryEvalConstExpr(SliceNode.elts[1], self.Trans.SymbolTable)
if not isinstance(CountVal, int) or CountVal <= 0:
return Fallback
Result: CTypeInfo = ElemInfo.Copy()
Result.ArrayDims = [str(CountVal)] + list(ElemInfo.ArrayDims)
return Result
def _ResolveAttributeAnnotationType(self, annotation: ast.Attribute) -> CTypeInfo | None:
"""Attribute 注解(`t.CInt` / `ast.AST` / `<sha1>.CLS`)解析。
先走 CTypeInfo.FromNode(TypeAnnotationResolver,覆盖 t/c 内建与已在本模块
SymbolTable 注册的类):其 BaseType 已解析时语义不变。
**仅在 FromNode 解析不出来时**(BaseType 为 None,即该类未随本模块
import/预扫描注册)按类名查 `Gen.structs` / `Gen._struct_sha1_map` 后交
TypeMerge(HandlesTypeMerge 的 Attribute 分支,与声明端
DeclarationGenerator._get_type_str 的 `attr_name in struct_names`、名称域
SymbolUtils._ResolveAttributeArgStr 同一条"类名 → SHA1"规则)。
不修此路时跨模块类 Attribute(`rawptr[<sha1>.CLS]` 脱糖后的
`<sha1>.CLS | t.CPtr`、泛型 T 替换出的裸 `<sha1>.CLS`、`list[ast.AST]`
特化的 `append(item: T)`)在定义端判为"无法识别的参数类型注解"抛
SyntaxError,被上层 broad except 吞掉后退化为 i32/i8*,而声明端给
`%"<sha1>.CLS"*`,字段/签名两侧不一致。
"""
fallback: CTypeInfo | None = CTypeInfo.FromNode(annotation, self.Trans.SymbolTable)
if fallback is not None and fallback.BaseType is not None:
return fallback
attr_name: str = getattr(annotation, 'attr', '') or ''
gen: Any = getattr(self.Trans, 'LlvmGen', None)
if not attr_name or gen is None:
return fallback
if (attr_name not in getattr(gen, 'structs', {})
and attr_name not in getattr(gen, '_struct_sha1_map', {})):
return fallback
merger: Any = getattr(self.Trans, 'TypeMergeHandler', None)
if merger is not None:
info: CTypeInfo | None = merger.GetCTypeInfo(annotation)
if isinstance(info, CTypeInfo) and info.BaseType is not None:
return info
return fallback
def ResolveParamAnnotation(self, annotation: ast.expr) -> CTypeInfo | None:
"""函数参数注解解析(阻塞 B 同根第三处修复)。
局部变量注解走 TypeMergeHandler.GetCTypeInfo(_HandleSubscript 泛型
分支),函数参数却走 ResolveAnnotationType → Subscript 非 BinOp 直接
CTypeInfo.FromNode → TypeAnnotationResolver 不识别泛型模板,容器
(list[X] / rawptr[list[X]] / GSList[X] / rawptr[GSList[X]])被折叠成
i8* → 参数无 var_struct_class → 方法调用 FakeDuck 兜底错绑兄弟类
(如 parser.vp _append_str_value 的 values.append 被编成 AST.append
虚调用,this=list* 读 vtable 崩溃)。此处对命中泛型模板的 Subscript
改走 TypeMerge 泛型识别路径,其余保持 ResolveAnnotationType 语义。
"""
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
merger: Any = getattr(self.Trans, 'TypeMergeHandler', None)
if merger:
info: CTypeInfo | None = merger.MergeTypes(annotation)
if info is not None and isinstance(info, CTypeInfo):
return info
# 指针下标参数注解(rawptr[T] / t.CPtr[T] / 裸名 CPtr[T],含
# rawptr[list[X]] 等容器:_SubscriptHitsGenericTemplate 也命中,但统一交
# ResolveAnnotationType 处理更简单——它已把指针下标路由到 TypeMerge)。
if IsPtrSubscriptNode(annotation, self.Trans.SymbolTable):
info = self.ResolveAnnotationType(annotation)
if isinstance(info, CTypeInfo) and info.BaseType is not None:
return info
if isinstance(annotation, ast.Subscript) and getattr(self.Trans, 'TypeMergeHandler', None):
if self._SubscriptHitsGenericTemplate(annotation):
try:
_gci: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(annotation)
if _gci is not None and isinstance(_gci, CTypeInfo) and _gci.BaseType is not None:
return _gci
except Exception:
pass
# Attribute 参数注解(`<sha1>.CLS` 裸引用 / 泛型 T 替换出的 `<sha1>.CLS`)
# 与 ResolveAnnotationType 共用同一条规则(_ResolveAttributeAnnotationType →
# TypeMergeHandler.GetCTypeInfo 的 Attribute 分支查 Gen.structs /
# Gen._struct_sha1_map),不在此复制第二套查表;否则 list[X] 特化方法
# `append(item: T)` 的参数 T(= 裸 `<sha1>.CLS`)解析为空 → VoidType →
# 抛"无法识别的参数类型注解" → 特化模板方法未注册。
if isinstance(annotation, ast.Attribute):
return self.ResolveAnnotationType(annotation)
Fallback: CTypeInfo | None = CTypeInfo.FromNode(annotation, self.Trans.SymbolTable)
return self._ResolveStackArrayElementFallback(annotation, Fallback)
def _SubscriptHitsGenericTemplate(self, annotation: ast.Subscript) -> bool:
"""判断 Subscript 是否命中泛型模板:裸模板名(list[X]/GSList[X]),
或 rawptr/t.CPtr 裸指针 typedef 内嵌模板名(rawptr[list[X]] 等)。"""
tpls: Any = getattr(getattr(self.Trans, 'ClassHandler', None), '_generic_class_templates', None)
if not tpls:
return False
base: ast.AST = annotation.value
if isinstance(base, ast.Name):
if base.id in tpls:
return True
if isinstance(annotation.slice, ast.Subscript):
inner: ast.AST = annotation.slice.value
if isinstance(inner, ast.Name) and inner.id in tpls:
return True
elif isinstance(base, ast.Attribute) and base.attr == 'CPtr':
if isinstance(annotation.slice, ast.Subscript):
inner = annotation.slice.value
if isinstance(inner, ast.Name) and inner.id in tpls:
return True
return False
def _EnsureRenumVariantStruct(self, Gen: Any, RenumName: str, VariantName: str) -> str:
"""确保 REnum 变体嵌套结构体 `<REnumName>_<VariantName>` 及其 class_members 就绪。
为什么需要:导入模块侧的 stub **只声明主 REnum**(如
`%"…LLVMType" = type { i32, i64, i64, i64 }`),**不含**变体嵌套结构体声明
(`LLVMType_Ptr` 等)——它们由 `_EmitExternalClassDeclLlvm` 的 REnum 分支按主
结构体布局就地生成。若本模块从未触发该 REnum 的外部声明发射,`Gen.structs` /
`Gen.class_members` 里就没有变体——于是:
- match 模式捕获(`case LLVMType.Ptr(pointee):`)绑定静默为空 ⇒ case body
里引用的绑定名随后变成"未定义名称"(现场 VpiReader.vp:1521 `pointee_cs`);
- REnum 变体构造的载荷实参静默丢弃(只写了 tag)。
故按需补发:先试 stub,再按类 sha1 找到定义模块的 `.vpi` 触发完整外部声明发射
(`_TryLoadClassMembersFromPyi` 内部即调 `_EmitExternalClassDeclLlvm`)。
返回嵌套结构体名(调用方据此查 `Gen.class_members` 判可用性)。
"""
NestedStructName: str = f"{RenumName}_{VariantName}"
if Gen.class_members.get(NestedStructName):
return NestedStructName
_sha1: str | None = None
for _map in (getattr(Gen, 'class_sha1_map', {}), getattr(Gen, '_struct_sha1_map', {})):
_sha1 = _map.get(RenumName)
if _sha1:
break
_IH: Any = getattr(self.Trans, 'ImportHandler', None)
if _IH is None:
return NestedStructName
try:
_IH._TryLoadStructFromStub(NestedStructName, Gen)
except Exception: # 与既有导入路径同口径:失败由下方判据统一暴露
pass
if not Gen.class_members.get(NestedStructName) and _sha1:
try:
_IH._TryLoadClassMembersFromPyi(RenumName, f"{_sha1}.stub.ll", Gen)
except Exception:
pass
return NestedStructName
def CollectAnnAssignMember(
self, item: ast.AST, Gen: Any
) -> tuple[str, _ir.Type, CTypeInfo | None] | None:
"""从 ast.AnnAssign 节点解析成员信息。
返回 (VarName, MemberType, TypeInfo)。
**不再静默兜底**(原实现:注解解析返回 None → i32;_ctype_to_llvm 得 void → i8*;
任何异常 → i32 + TypeInfo=None)。这些兜底会把"注解没解析出来"伪装成一个
具体类型,下游于是报出与真因无关的错——实测:`free_lists: uint64*`(脱糖为
`uint64 | t.CPtr`)在解析异常时被当成 **i32**,`self.free_lists = base`(i8*)
便误报"指针隐式转整数已禁止"。类型信息缺失一律响亮失败。
非 AnnAssign/Name 目标时返回 None。
"""
if not isinstance(item, ast.AnnAssign) or not isinstance(item.target, ast.Name):
return None
VarName: str = item.target.id
AnnotationText: str = ast.unparse(item.annotation)
try:
TypeInfo: CTypeInfo | None = self.ResolveAnnotationType(item.annotation)
except RecursionError:
# RecursionError 不是"可忽略异常":它是类型解析/声明发射链**成环**的
# 真缺陷信号。此前被下方 `except Exception` 吞成一行
# `WARN: HandlesBase: 忽略异常 maximum recursion depth exceeded` 并静默
# 退化为 i32 ⇒ 真正的环(如跨模块类声明发射重入
# _EmitExternalClassDeclLlvm ↔ _TryLoadStructFromStub ↔ _TryLoadClassMembersFromPyi)
# 被掩盖,下游只看到类型解析静默退化。此处 fail-fast 上抛(保留原始栈),
# 让环路根因暴露在错误栈顶,不再被 WARN 掩埋。
raise
except Exception as e:
raise ValueError(
f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 解析失败:"
f"{type(e).__name__}: {e} —— 拒绝静默退化为 i32"
) from e
if TypeInfo is None:
raise ValueError(
f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 解析不出类型"
f"(ResolveAnnotationType 返回 None)—— 拒绝静默退化为 i32"
)
MemberType: _ir.Type = Gen._ctype_to_llvm(TypeInfo)
if isinstance(MemberType, _ir.VoidType):
raise ValueError(
f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 解析为 void"
f"(BaseType={getattr(TypeInfo, 'BaseType', None)} "
f"PtrCount={getattr(TypeInfo, 'PtrCount', None)} "
f"IsTypedef={getattr(TypeInfo, 'IsTypedef', None)} "
f"Name={getattr(TypeInfo, 'Name', None)})—— 拒绝静默退化为 i8*"
)
# 「内层是类引用却解析成通用指针」fail-fast(降级源头定位):
# `rawptr[Mod.Class]` / `Mod.Class | t.CPtr` 的语义是"指向该类",内层若确实是
# 已定义类,解析结果必须带该类 BaseType;退化成 void 基座(i8* / 多一层 i8**)
# 就是类型信息被丢掉 —— 该值一旦进 class_members 就会被冻进 struct body
# (llvmlite set_body 只能设一次),后期只修表不修体(现场:Generator 第 3/11
# 槽 i8**)。此处响亮失败并附判据,使降级**当场暴露**而非留到赋值时报"层数不同"。
self._CheckPtrAnnotationKeepsClass(item.annotation, TypeInfo, VarName, AnnotationText)
# 形状判据(更硬的一条):注解形态是「**指针包装的类引用**」而解析结果正是
# `i8**`(= void 基座两层,T 的类型被丢掉)⇒ 必属降级,直接响亮失败并把两侧
# 证据打全。放在字段类型的**唯一漏斗**里,覆盖全部"按注解推导成员类型"的写入链
# (本地类 / 跨模块 .vpi / 嵌套类)。
if (IsClassRefPtrAnnotation(item.annotation, self.Trans.SymbolTable)
and IsI8DoublePtrType(MemberType)):
_inner_name: str = ''
_inner_node: Any = (item.annotation.slice
if isinstance(item.annotation, ast.Subscript) else None)
if isinstance(_inner_node, ast.Attribute):
_inner_name = _inner_node.attr
elif isinstance(_inner_node, ast.Name):
_inner_name = _inner_node.id
_sym: Any = None
try:
_sym = self.Trans.SymbolTable.lookup(_inner_name) if _inner_name else None
except Exception:
_sym = None
_gen: Any = getattr(self.Trans, 'LlvmGen', None)
_structs_hit: str = (
'有' if (_gen is not None and _inner_name in getattr(_gen, 'structs', {})) else '无')
_sha1_hit: Any = (
getattr(_gen, '_struct_sha1_map', {}).get(_inner_name) if _gen is not None else None)
raise ValueError(
f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 是**类引用指针**,"
f"却被解析成 {MemberType}(void 基座两层 = 类型信息丢失)—— 拒绝降级:"
f"CTypeInfo(BaseType={getattr(TypeInfo, 'BaseType', None)} "
f"PtrCount={getattr(TypeInfo, 'PtrCount', None)} "
f"IsStruct={getattr(TypeInfo, 'IsStruct', None)} "
f"IsTypedef={getattr(TypeInfo, 'IsTypedef', None)} "
f"IsEnum={getattr(TypeInfo, 'IsEnum', None)} "
f"Name={getattr(TypeInfo, 'Name', None)});内层名='{_inner_name}' "
f"符号表[该名] => IsTypedef={getattr(_sym, 'IsTypedef', None)} "
f"BaseType={getattr(_sym, 'BaseType', None)} "
f"PtrCount={getattr(_sym, 'PtrCount', None)} "
f"IsStruct={getattr(_sym, 'IsStruct', None)} "
f"IsRenum={getattr(_sym, 'IsRenum', None)} "
f"IsModuleAlias={getattr(_sym, 'IsModuleAlias', None)};"
f"Gen.structs 命中={_structs_hit} _struct_sha1_map 命中={_sha1_hit!r}"
)
return VarName, MemberType, TypeInfo
def _CheckPtrAnnotationKeepsClass(self, Annotation: ast.AST, Info: CTypeInfo | None,
MemberName: str, AnnotationText: str) -> None:
"""指针包装注解的内层是**已定义类**时,解析结果不得退化成 void 基座(fail-fast)。
只拦"必属类型信息丢失"的组合:内层节点(Name / Attribute)的名字在
`Gen.structs`(本模块已建)或 `Gen._struct_sha1_map`(预扫描的类→模块映射)
中有类定义,而解析出的 `BaseType` 是 `t.CVoid` **且没有任何类身份**
(IsStruct / IsRenum / IsEnum / Name 全空)。其余一律放行
(`rawptr[rawptr]` 这类本就 void 基座的多层指针、模块名、泛型特化实参等)。
"""
if Info is None or Info.BaseType is None:
return
if not (Info.BaseType is t.CVoid or isinstance(Info.BaseType, t.CVoid)):
return
# 只要还带着任何"类身份"(IsStruct / IsRenum / IsEnum / Name),就不是信息丢失:
# REnum 类型条目本身 BaseType=None,`T | t.CPtr` 合并后会被默认填成 CVoid,但
# IsRenum/IsStruct/Name 齐备,_ctype_to_llvm 仍能解析出 %<sha1>.T* —— 误判会
# 把合法注解(如 REnum 变体载荷 `Pointee: rawptr[LLVMType]`)打成致命错误。
if (getattr(Info, 'IsStruct', False) or getattr(Info, 'IsRenum', False)
or getattr(Info, 'IsEnum', False) or getattr(Info, 'Name', None)):
return
Inner: ast.AST | None = None
if isinstance(Annotation, ast.Subscript) and IsPtrSubscriptNode(Annotation, self.Trans.SymbolTable):
Inner = Annotation.slice
elif isinstance(Annotation, ast.BinOp) and isinstance(Annotation.op, ast.BitOr):
for Side in FlattenUnionOperands(Annotation):
if not IsCPtrMarkerNode(Side, self.Trans.SymbolTable):
Inner = Side
break
if Inner is None:
return
InnerName: str | None = None
if isinstance(Inner, ast.Name):
InnerName = Inner.id
elif isinstance(Inner, ast.Attribute):
InnerName = Inner.attr
if not InnerName or InnerName in ('rawptr', 'CPtr'):
return
Gen: Any = getattr(self.Trans, 'LlvmGen', None)
if Gen is None:
return
if (InnerName not in getattr(Gen, 'structs', {})
and InnerName not in getattr(Gen, '_struct_sha1_map', {})):
return
_e: Any = None
try:
_e = self.Trans.SymbolTable.lookup(InnerName)
except Exception:
_e = None
raise ValueError(
f"[FATAL] 成员 '{MemberName}' 的注解 '{AnnotationText}' 内层 '{InnerName}' "
f"是**已定义类**,却被解析成通用指针(类型信息丢失)—— 拒绝静默降级:"
f"解析结果 BaseType={Info.BaseType} PtrCount={getattr(Info, 'PtrCount', None)} "
f"IsStruct={getattr(Info, 'IsStruct', None)} IsTypedef={getattr(Info, 'IsTypedef', None)} "
f"Name={getattr(Info, 'Name', None)};"
f"符号表['{InnerName}'] => IsTypedef={getattr(_e, 'IsTypedef', None)} "
f"BaseType={getattr(_e, 'BaseType', None)} PtrCount={getattr(_e, 'PtrCount', None)} "
f"IsStruct={getattr(_e, 'IsStruct', None)} IsRenum={getattr(_e, 'IsRenum', None)} "
f"IsModuleAlias={getattr(_e, 'IsModuleAlias', None)} "
f"IsEnumMember={getattr(_e, 'IsEnumMember', None)};"
f"Gen.structs 命中={'有' if InnerName in getattr(Gen, 'structs', {}) else '无'} "
f"_struct_sha1_map 命中={getattr(Gen, '_struct_sha1_map', {}).get(InnerName)!r}"
)
def LookupFunctionSymbol(self, name: str, *, module_path: str | None = None) -> CTypeInfo | None:
"""查找函数符号,先尝试 module_path.name,再回退到 name。
统一处理 HandlesExprCall / HandlesAssign 中重复的二级查找模式。
返回 CTypeInfo(不限定 IsFunction),由调用方检查标志位。
"""
st = self.Trans.SymbolTable
if module_path:
sym: CTypeInfo | None = st.lookup(f'{module_path}.{name}')
if sym:
return sym
return st.lookup(name)
def BuildLLVMFuncTypeFromSig(
self,
sym_info: CTypeInfo,
Gen: Any,
*,
mangled_name: str | None = None,
use_infer_fallback: bool = False,
) -> tuple[Any, list[Any], bool] | None:
"""从 CTypeInfo 函数签名构建 LLVM 函数类型。
返回 (ret_type, llvm_param_types, is_variadic),非函数返回 None。
统一处理 FuncPtrReturn/FuncPtrParams → ToLLVM 转换、VoidType 回退。
Args:
mangled_name: 用于 _infer_return_type + stub 查找回退
use_infer_fallback: 为 True 时启用 _infer_return_type + stub 查找回退链
"""
if not sym_info.IsFunction:
return None
is_variadic: bool = sym_info.IsVariadic
ret_type_info = sym_info.FuncPtrReturn
if isinstance(ret_type_info, CTypeInfo) and ret_type_info.BaseType:
ret_type: Any = ret_type_info.ToLLVM(Gen)
elif use_infer_fallback and mangled_name:
inferred: Any = Gen._infer_return_type(mangled_name)
if isinstance(inferred, _ir.IntType) and inferred.width == 32:
try:
stub_ft: Any = self.Trans.ImportHandler._LookupStubFuncType(mangled_name, Gen)
if stub_ft and hasattr(stub_ft, 'return_type'):
inferred = stub_ft.return_type
except Exception:
pass
ret_type = inferred
else:
ret_type = Gen._CType2LLVM('i32', False)
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 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:
# 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)
return ret_type, llvm_param_types, is_variadic
def GetOrCreateFuncDecl(
self,
name: str,
ret_type: Any,
param_types: list[Any],
Gen: Any,
*,
is_variadic: bool = False,
replace_name: str | None = None,
) -> Any:
"""创建或替换 LLVM 函数声明。
若 name 已存在且类型不匹配,通过 _replace_function_decl 替换。
replace_name 传递给 _replace_function_decl(通常为未 mangle 的 func_name)。
"""
if name not in Gen.functions:
# Gen.functions 未登记、但 module.globals 已有同名函数(本模块其它调用点
# 或依赖 stub 先建过声明)→ 复用并登记,绝不重建:llvmlite 的 NameScope
# 按名字去重(GlobalValue.deduplicate_name=False),重复 ir.Function 必抛
# DuplicatedNameError。
g_existing: Any = Gen.module.globals.get(name)
if g_existing is not None:
Gen.functions[name] = g_existing
return g_existing
func_type = _ir.FunctionType(ret_type, param_types, var_arg=is_variadic)
func_decl = Gen._CreateOrReuseFuncDecl(name, func_type)
Gen.functions[name] = func_decl
else:
existing = Gen.functions[name]
existing_type = getattr(existing, 'ftype', None)
new_type = _ir.FunctionType(ret_type, param_types, var_arg=is_variadic)
if existing_type and existing_type != new_type:
replaced = self.Trans.FunctionHandler._replace_function_decl(Gen, replace_name or name, new_type)
if replaced and replaced != existing:
Gen.functions[name] = replaced
return Gen.functions[name]
def GetOrCreateStubFuncDecl(
self,
decl_name: str,
stub_func_type: Any,
Gen: Any,
*,
alias: str | None = None,
check_existing: bool = True,
) -> Any:
"""创建 stub 函数声明,可选注册别名。
check_existing=False 时跳过 Gen.functions 存在性检查(用于确定不存在的场景),
但**仍复用 module.globals 里的同名声明**——llvmlite 的 NameScope 按名字去重
(GlobalValue.deduplicate_name=False),"强制新建同名 global"在 llvmlite 下
不成立、必抛 DuplicatedNameError,故此路必须幂等。
真实事故:同一模块两处 `self.Gen.ExprGen.translate_value(...)`(VarGenMixin.vp)
都走 check_existing=False,第一处建声明成功、第二处抛 DuplicatedNameError。
"""
if check_existing and decl_name in Gen.functions:
func_decl = Gen.functions[decl_name]
else:
g_ex_decl: Any = Gen.module.globals.get(decl_name)
if g_ex_decl is not None:
func_decl = g_ex_decl
else:
func_decl = Gen._CreateOrReuseFuncDecl(decl_name, stub_func_type)
Gen.functions[decl_name] = func_decl
if alias and alias != decl_name:
if not check_existing or alias not in Gen.functions:
Gen.functions[alias] = func_decl
return func_decl
StrictMode: bool = _config.mode == 'strict'