Files
TransPyC/lib/core/Handles/HandlesClassDef.py
T
TermiNexus 2514cee4c9 feat(lang): 同步 cif/celif/celse、值引用注解与 assert 改译 testcheck.check
vast 双路径(Python Parser/ExprParser 与 C 扩展 vast_c.c)新增 cif-celif-celse 语句族与 &T / &temp T 参数注解解析,_parser.pyd 已同步重编。HandlesIf 改认 @CIf 标记并做编译期常量求值,含运行时变量一律 fail-fast,同时删除 c.CIf 家族(c.py)。HandlesAssert 合成 testcheck.check(cond, None, msg) 调用,非字符串字面量消息报 TypeError。FuncGen 调用点按形参模式自动取址(ref 仅接受左值,右值 fail-fast;temp 物化临时栈),SymbolUtils/TypeAnnotationResolver 提供模式识别;t.py 删除 CNeedPtr。
2026-10-07 19:03:29 +08:00

2165 lines
133 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
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin
import vast as ast
import copy
import sys
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,
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/CAutoPtr,后者继承
# CPtr)、值引用标记(CRef/CTempRef,`&T`/`&temp T`)、内建 rawptr(= t.CPtr)、
# str/bytes(= i8*)。枚举值是整数常量,指针成员语义不成立,命中即明确报错
# (不静默当 int)。
_ENUM_PTR_ANNOTATION_NAMES: frozenset[str] = frozenset({
'CPtr', 'CAutoPtr', 'CRef', 'CTempRef', 'rawptr', 'str', 'bytes',
})
class ClassHandle(BaseHandle):
def _is_exception_class(self, Node: ast.ClassDef) -> bool:
if not Node.bases:
return False
for base in Node.bases:
if hasattr(base, 'id'):
if base.id == 'Exception':
return True
if base.id in self.Trans.exception_registry:
return True
elif hasattr(base, 'attr'):
if base.attr == 'Exception':
return True
if base.attr in self.Trans.exception_registry:
return True
return False
def _get_exception_parent(self, Node: ast.ClassDef) -> str | None:
if not Node.bases:
return None
for base in Node.bases:
if hasattr(base, 'id'):
if base.id != 'Exception' and base.id in self.Trans.exception_registry:
return base.id
elif hasattr(base, 'attr'):
if base.attr != 'Exception' and base.attr in self.Trans.exception_registry:
return base.attr
return None
def _RegisterExceptionClass(self, Node: ast.ClassDef) -> None:
ClassName: str = Node.name
if ClassName in self.Trans.exception_registry:
return
code: int = self.Trans._next_exception_code
self.Trans._next_exception_code += 1
self.Trans.exception_registry[ClassName] = code
parent: str | None = self._get_exception_parent(Node)
if parent:
self.Trans.exception_parents[ClassName] = parent
ExcTypeInfo: CTypeInfo = CTypeInfo()
ExcTypeInfo.Name = ClassName
ExcTypeInfo.IsExceptionClass = True
ExcTypeInfo.value = code
self.Trans.SymbolTable.insert(ClassName, ExcTypeInfo)
def _is_generic_class(self, Node: ast.ClassDef) -> bool:
if hasattr(Node, 'type_params') and Node.type_params:
return True
return False
def _mangle_generic_class_name(self, class_name: str, type_args: list[str]) -> str:
"""泛型类特化名:class_name + '[arg1][arg2]'。
单项 mangling 走 SymbolUtils.MangleTypeArgByRegistry(声明端
DeclarationGenerator 的泛型分支以 ResolveGenericTypeArgStr 调用同一函数,
两侧拼名规则唯一),'][' 分隔与 HandlesTypeMerge / DeclarationGenerator 一致。
"""
mangled_args: list[str] = [MangleTypeArgByRegistry(ta) for ta in type_args]
return class_name + '[' + ']['.join(mangled_args) + ']'
def _HasClassDefinition(self, Name: str, Gen: LlvmGeneratorMixin) -> bool:
"""类型名是否有类定义——泛型"类类型参数"的唯一判据。
数据来源就是"是否存在类定义"这一事实本身:Gen._struct_sha1_map
(类名 → 定义模块 SHA1,Phase2 从各 .vpi 扫描构建)与 Gen.structs
(本模块已发射的 struct 名,键形如 '{sha1}.{名}')。
因此内建值类型(int/CInt/…,LLVM 映射 i32 等)与语言内建 `str`
(LLVM 映射 i8*)都不在两张表里 → 自然判为值类型,不需要任何
内建名白名单/黑名单参与判定。
"""
if not Name:
return False
if Name in Gen._struct_sha1_map or Name in Gen.structs:
return True
for Sha1 in Gen._struct_sha1_map.values():
if f'{Sha1}.{Name}' in Gen.structs:
return True
return False
@staticmethod
def _BareClassAnnotationName(Node: ast.AST) -> str | None:
"""取注解节点的裸类名(供 class_type_names 判定),非类名形态返回 None。
T 替换后的两种形态都要认:
- ast.Name:'AST'(未注册类)或 'sha1.AST'(旧式点号名)→ 去前缀;
- ast.Attribute:'sha1.AST' 经 _make_replacement_node/_make_name_node
产出的 Attribute 链(Name('sha1').AST)→ attr 名。
其余(Union `X | t.CPtr`、Subscript `list[T]`、内建)→ None,不追加
指针层(已显式写了 `| t.CPtr` 的注解不再重复加层)。
"""
if isinstance(Node, ast.Name):
Name: str = Node.id
return Name.split('.')[-1] if '.' in Name else Name
if isinstance(Node, ast.Attribute):
return Node.attr
return None
def _specialize_generic_class(self, ClassName: str, type_args: list[str], Gen: LlvmGeneratorMixin, type_names: list[str] | None = None, declare_only: bool = False) -> str | None:
if not hasattr(self, '_generic_class_templates') or ClassName not in self._generic_class_templates:
return None
# 防御:共享符号表构建阶段 Gen 可能为 None,此时无法发射特化 IR
if Gen is None:
return None
template: dict = self._generic_class_templates[ClassName]
Node: ast.ClassDef = template['node']
type_param_names: list[str] = template['type_params']
if len(type_args) != len(type_param_names):
return None
spec_key: str = ClassName + '<' + ','.join(type_args) + '>'
# 缓存必须在 Gen 上(每个模块独立),不能在 ClassHandler 上跨模块共享,
# 否则临时 Gen(如共享符号表构建阶段)的特化会污染正式翻译阶段的缓存
if not hasattr(Gen, '_generic_class_specializations'):
Gen._generic_class_specializations = {}
if spec_key in Gen._generic_class_specializations:
cached: object = Gen._generic_class_specializations[spec_key]
# 缓存格式: (spec_name, was_declare_only)
if isinstance(cached, tuple):
cached_name: str = cached[0]
cached_declare_only: bool = cached[1]
# 完整特化请求但缓存只有 declare_only 版本 → 需要补发方法体
if not declare_only and cached_declare_only:
spec_name = cached_name
# 标记为正在完整特化,防止递归
Gen._generic_class_specializations[spec_key] = (spec_name, False)
else:
return cached_name
else:
# 旧格式(纯字符串):直接返回
return cached
else:
spec_name: str = self._mangle_generic_class_name(ClassName, type_args)
# 预注册到缓存,防止特化过程中方法体引用自身类型导致无限递归
Gen._generic_class_specializations[spec_key] = (spec_name, declare_only)
# 内建类型名集合(必须在 type_map/cptr_params 检测之前定义)
_BUILTIN_TYPE_NAMES: set = {
'CInt', 'CUInt', 'CInt8T', 'CUInt8T', 'CInt16T', 'CUInt16T',
'CInt32T', 'CUInt32T', 'CInt64T', 'CUInt64T', 'CChar', 'CShort',
'CLong', 'CDouble', 'CFloat', 'CFloat32T', 'CFloat64T', 'CBool',
'CVoid', 'CSizeT', 'CSSizeT', 'CPtr', 'CArray', 'CEnum', 'CUnion',
'CStruct', 'CDefine',
'int', 'double', 'float', 'i8', 'i16', 'i32', 'i64', 'void',
'half', 'fp128', 'char', 'bool',
}
# 值类型/类类型不再用"内建名白名单"判定:见下方 class_type_names,
# 唯一判据是"是否有类定义"(_HasClassDefinition:Gen._struct_sha1_map
# / Gen.structs)。内建(int/CInt/…)与语言内建 `str`(LLVM 映射 i8*)
# 都不在类表里 → 天然判为值类型,无需名表。
# 历史教训(名表判据的代价,保留以便回归时理解):`str` 曾漏在
# "非类类型名"集合外 → `list[str]` 的类型参数被误判为类类型,特化后
# 方法签名被额外包一层 `| t.CPtr`(append/set 形参 i8**、get/pop 返回
# i8**)→ 调用点多解一层 load(读字符串首 8 字节)→ ASan 崩溃。
# 注意 _BUILTIN_TYPE_NAMES 本身仍保留:它用于 type_map 替换
# 与 _t_c_imported_names 注册,不能把 'str' 并进去
# (否则 HandlesTypeMerge 会把注解 `str` 当 `t.str` 解析)。
type_map: dict[str, str] = {}
for i, tp_name in enumerate(type_param_names):
if type_names and i < len(type_names):
tn: str = type_names[i]
# 判断是否为 t 模块内建类型(裸名或 't.' 前缀,如 'CInt'/'t.CInt'):
# 内建类型必须用源码名(type_name)替换 T(t.CInt → i32),否则
# 用 mangled type_arg(如 'i32')替换会产生 'i32.__sizeof__' 这类
# 源码中不存在的名称([FATAL][__sizeof__] 类型 'i32' 无法解析)。
# 真正的类类型参数(如 'AST'/'ast.AST')才用 mangled type_arg
# (带 sha1 前缀 + ptr 后缀),避免模板内部嵌套注解被替换成裸短名
# 后重新特化出只有 declare、无方法体的错误特化(list[AST].__len__
# undefined symbol 根因)。_make_replacement_node 会把 mangled 名
# 还原为 'sha1.AST | t.CPtr',重新解析时拼回一致的 mangled 名。
tn_bare: str = tn.split('.')[-1] if '.' in tn else tn
if tn_bare in _BUILTIN_TYPE_NAMES:
type_map[tp_name] = tn
elif i < len(type_args):
type_map[tp_name] = type_args[i]
else:
type_map[tp_name] = tn
else:
type_map[tp_name] = type_args[i]
# 检测哪些类型参数在原始类型实参中带有指针语义(| t.CPtr / rawptr[T])
# 当 type_arg 以 '*' 结尾(mangling 把每个 CPtr 层渲染为一层 '*')且对应的
# type_name(去模块前缀后)不是内建类型名时,说明调用方写了
# list[AST | t.CPtr] / list[rawptr[AST]]。注意用 tn_bare 判断:
# 't.CPtr' 去前缀后是 'CPtr'(内建),不算类类型参数。
cptr_params: set[str] = set()
for i, tp_name in enumerate(type_param_names):
type_arg: str = type_args[i]
if type_arg.endswith('*'):
tn: str = type_names[i] if type_names and i < len(type_names) else type_args[i]
tn_bare_c: str = tn.split('.')[-1] if '.' in tn else tn
if tn_bare_c not in _BUILTIN_TYPE_NAMES:
cptr_params.add(tp_name)
# Bug 2 修复:识别特化为 class 类型的类型参数
# 这类参数在方法签名中是裸 T(无 | t.CPtr),特化后为裸 ClassName(值类型)
# 需要加 | t.CPtr 使其成为指针类型(class 实例应按指针传递)
# 判据 = "是否有类定义"(_HasClassDefinition),存**裸名**:注解侧的
# 比较也用裸名(T 替换后的 'sha1.Param' 去前缀)——此前存带模块前缀的
# tn(如 'ast.AST')而比较用裸名 'AST',前缀形态永不命中。
class_type_names: set[str] = set()
for i, tp_name in enumerate(type_param_names):
tn: str = type_names[i] if type_names and i < len(type_names) else type_args[i]
tn_bare_cls: str = tn.split('.')[-1] if '.' in tn else tn
if self._HasClassDefinition(tn_bare_cls, Gen):
class_type_names.add(tn_bare_cls)
# 仅对 t 模块内建类型名注册 _t_c_imported_names(如 CInt -> ('t','CInt'))
# 类名(如 AST)不应注册为 t.AST,否则会污染名称解析
if type_names:
if not hasattr(self.Trans, '_t_c_imported_names'):
self.Trans._t_c_imported_names = {}
for tn in type_names:
if tn in _BUILTIN_TYPE_NAMES and tn not in self.Trans._t_c_imported_names:
self.Trans._t_c_imported_names[tn] = ('t', tn)
SpecNode: ast.ClassDef = copy.deepcopy(Node)
SpecNode.type_params = []
SpecNode.name = spec_name
for item in SpecNode.body:
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
if item.annotation:
item.annotation = self.Trans.FunctionHandler._replace_type_in_annotation(item.annotation, type_map, cptr_params)
elif isinstance(item, ast.FunctionDef):
for arg in item.args.args:
if arg.annotation:
arg.annotation = self.Trans.FunctionHandler._replace_type_in_annotation(arg.annotation, type_map, cptr_params)
# class 类型参数加指针语义:T -> GNode -> GNode | t.CPtr
# T 替换后的真实形态是 Attribute 链('<sha1>.GNode' →
# Name('<sha1>').GNode,见 _make_replacement_node/_make_name_node),
# 不是 ast.Name:此前只认 ast.Name → 方法形参漏加 | t.CPtr,
# 特化后按值结构体传参(被调者栈帧副本地址入链 → 悬垂写)。
raw_name: str | None = self._BareClassAnnotationName(arg.annotation)
if raw_name is not None and raw_name in class_type_names:
arg.annotation = ast.BinOp(
left=arg.annotation,
op=ast.BitOr(),
right=ast.Attribute(value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load())
)
if item.returns:
item.returns = self.Trans.FunctionHandler._replace_type_in_annotation(item.returns, type_map, cptr_params)
# 返回类型也加指针语义(同一裸名判据,形态同上)
raw_name: str | None = self._BareClassAnnotationName(item.returns)
if raw_name is not None and raw_name in class_type_names:
item.returns = ast.BinOp(
left=item.returns,
op=ast.BitOr(),
right=ast.Attribute(value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load())
)
self.Trans.FunctionHandler._apply_type_map_to_body(item.body, type_map, cptr_params)
saved_builder: ir.IRBuilder = Gen.builder
saved_func: ir.Function = Gen.func
saved_variables: dict = dict(Gen.variables) if Gen.variables else {}
saved_direct_values: dict = dict(Gen._direct_values) if Gen._direct_values else {}
saved_var_type_info: dict = dict(Gen.var_type_info) if Gen.var_type_info else {}
saved_var_signedness: dict = dict(Gen.var_signedness) if Gen.var_signedness else {}
saved_global_vars: set = set(Gen.global_vars) if Gen.global_vars else set()
saved_var_scopes: list = [dict(s) for s in self.Trans.VarScopes] if self.Trans.VarScopes else []
saved_block: ir.Block | None = None
if Gen.builder and Gen.builder.block and not Gen.builder.block.is_terminated:
saved_block = Gen.builder.block
# 切换 module_sha1 为泛型模板定义模块的 SHA1,
# 使结构体类型名和方法定义名都用定义模块的 SHA1 前缀,
# 与调用端 _struct_sha1_map 查到的 SHA1 一致。
template_sha1: str | None = template.get('sha1')
if not template_sha1:
# 模板注册表缺 sha1 时回退到"模板类定义模块"(_struct_sha1_map 即
# 类名 → 定义模块表,Phase2 从各 .vpi 扫描构建):与声明端
# DeclarationGenerator._get_type_str 的泛型前缀、调用点
# Gen._struct_sha1_map[spec_name] 同一条规则源;否则 declare/define
# 两侧前缀分裂(使用方模块 vs 模板模块)→ 跨模块 undefined symbol。
template_sha1 = Gen._struct_sha1_map.get(ClassName)
saved_gen_sha1: str | None = getattr(Gen, 'module_sha1', None)
saved_trans_sha1: str | None = getattr(self.Trans, '_module_sha1', None)
if template_sha1:
Gen.module_sha1 = template_sha1
self.Trans._module_sha1 = template_sha1
try:
self._EmitClassLlvm(SpecNode, Gen, declare_only=declare_only)
finally:
# 确保 Gen 状态恢复,即使 _EmitClassLlvm 抛出异常
Gen.builder = saved_builder
if saved_block is not None and Gen.builder is not None:
Gen.builder.position_at_end(saved_block)
Gen.func = saved_func
Gen.variables = saved_variables
Gen._direct_values = saved_direct_values
Gen.var_type_info = saved_var_type_info
Gen.var_signedness = saved_var_signedness
Gen.global_vars = saved_global_vars
self.Trans.VarScopes = saved_var_scopes
# 恢复原始 module_sha1
Gen.module_sha1 = saved_gen_sha1
self.Trans._module_sha1 = saved_trans_sha1
Gen._generic_class_specializations[spec_key] = (spec_name, declare_only)
# 缓存 type_args/type_names,供后续 declare_only=False 完整特化补发方法体使用
# (如 _HandleMethodCallLlvm 中发现方法未定义时触发)
if not hasattr(Gen, '_generic_class_spec_type_info'):
Gen._generic_class_spec_type_info = {}
Gen._generic_class_spec_type_info[spec_key] = (list(type_args), list(type_names) if type_names else None)
if not hasattr(self.Trans, '_generic_class_specializations'):
self.Trans._generic_class_specializations = {}
self.Trans._generic_class_specializations[spec_key] = spec_name
# 用模板定义模块的 SHA1 注册到 _struct_sha1_map,与方法定义端一致
if template_sha1:
Gen._struct_sha1_map[spec_name] = template_sha1
elif hasattr(self.Trans, '_module_sha1') and self.Trans._module_sha1:
Gen._struct_sha1_map[spec_name] = self.Trans._module_sha1
return spec_name
def _resolve_base_kind(self, base_name: str, Gen: LlvmGeneratorMixin) -> str | None:
"""解析基类名称的类型种类
查询顺序:
1. _t_type_symbols(from t import CEnum as en)
2. t 模块属性(t.CEnum / t.CUnion / t.CStruct / t.REnum)
3. 返回 None 表示不是已知标记基类
Returns:
'enum' | 'union' | 'renum' | 'struct' | None
"""
# 1) 查 _t_type_symbols(别名导入)
symtab: object = self.Trans.SymbolTable
cls: type | None = symtab._t_type_symbols.get(base_name)
if cls is not None:
if issubclass(cls, (t.CEnum, t.REnum)):
return 'renum' if issubclass(cls, t.REnum) else 'enum'
if issubclass(cls, t.CUnion):
return 'union'
if issubclass(cls, t.CStruct):
return 'struct'
# CType 及其子类(CChar/CInt/CVoid/CPtr 等)是"类型强转"标记,
# 不应被编译为真实结构体。
if issubclass(cls, t.CType):
return 'ctype_marker'
# 2) 查 t 模块属性(直接引用 t.CEnum 等)
t_cls: type | None = getattr(t, base_name, None)
if isinstance(t_cls, type) and issubclass(t_cls, t.CType):
if issubclass(t_cls, (t.CEnum, t.REnum)):
return 'renum' if issubclass(t_cls, t.REnum) else 'enum'
if issubclass(t_cls, t.CUnion):
return 'union'
if issubclass(t_cls, t.CStruct):
return 'struct'
# CType 及其子类是"类型强转"标记,不编译为结构体。
return 'ctype_marker'
return None
@staticmethod
def _is_classname_ctype_marker(class_name: str) -> bool:
"""检查类名是否是 t.CType 的"类型强转"标记子类。
只有 position 不含 BASE 的纯修饰符/标记类型(CPtr/CVolatile/CConst/
CInline/CStatic/CExtern/CExport/CRegister/CAuto/_CTypedef/CTypeDefault 等)
才应跳过编译。有 BASE position 的基本类型(CInt/CChar/CSizeT/CLong 等)
有具体大小,应被正常编译。CType 基类本身无 bases,base 检测无法触发,
需要直接按类名检查。
注意:CTypeRegistry.GetClassByName 会跳过 CType 本身和 CTypeDefault,
所以这里直接用 getattr(t, name) 检查 t 模块属性。
Returns:
True 如果 class_name 是 CType 标记子类名(应跳过编译)
"""
if not class_name:
return False
# 直接查 t 模块属性(CTypeRegistry._build 会跳过 CType/CTypeDefault/BigEndian/LittleEndian)
t_cls: type | None = getattr(t, class_name, None)
if not isinstance(t_cls, type) or not issubclass(t_cls, t.CType):
return False
if issubclass(t_cls, (t.CStruct, t.CEnum, t.REnum, t.CUnion)):
return False
# 基本类型(position 含 BASE 且 Size > 0)有具体大小,不应跳过
# CVoid 虽含 BASE 但 Size=0,CType Size=None,仍需跳过
if t.CType.BASE in t_cls.position:
try:
inst = t_cls()
size_val = getattr(inst, 'Size', None)
if size_val is not None and size_val > 0:
return False
except Exception:
pass
return True
def _resolve_decorator_kind(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> str | None:
"""从装饰器列表中解析结构体类型种类
支持 @t.CStruct, @t.CUnion, @t.CEnum, @t.REnum
也支持 from t import CStruct 等别名形式
Returns:
'enum' | 'union' | 'renum' | 'struct' | None
"""
if not hasattr(Node, 'decorator_list') or not Node.decorator_list:
return None
symtab: object = self.Trans.SymbolTable
for decorator in Node.decorator_list:
attr_name: str | None = None
# @t.CStruct 形式
if isinstance(decorator, ast.Attribute) and isinstance(decorator.value, ast.Name):
if IsTModule(decorator.value.id, symtab):
attr_name = decorator.attr
# @CStruct 形式 (from t import CStruct)
elif isinstance(decorator, ast.Name):
cls: type | None = getattr(t, decorator.id, None)
if cls is None and hasattr(symtab, '_t_type_symbols'):
cls = symtab._t_type_symbols.get(decorator.id)
if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType):
attr_name = cls.__name__
if attr_name:
# 查 _t_type_symbols 和 t 模块属性以确定种类
resolved_cls: type | None = None
if hasattr(symtab, '_t_type_symbols'):
resolved_cls = symtab._t_type_symbols.get(attr_name)
if resolved_cls is None:
resolved_cls = getattr(t, attr_name, None)
if resolved_cls is not None and isinstance(resolved_cls, type):
if issubclass(resolved_cls, t.REnum):
return 'renum'
if issubclass(resolved_cls, t.CEnum):
return 'enum'
if issubclass(resolved_cls, t.CUnion):
return 'union'
if issubclass(resolved_cls, t.CStruct):
return 'struct'
return None
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,
VtableByClassLevel)
VtableByClassLevel:**忽略方法级 CVTable 标记**算出的 vtable 状态(= 类级
CVTable / 继承链判定),供 vtable 槽筛选(SymbolUtils.IsVtableSlotMethod)
使用——与 TPV _should_method_be_virtual 第 3/4 条的判据一致。
"""
ClassName: str = Node.name
IsCpythonObject: bool = False
IsCVTable: bool = False
IsNoVTable: bool = False
if hasattr(Node, 'decorator_list') and Node.decorator_list:
for decorator in Node.decorator_list:
if isinstance(decorator, ast.Attribute):
if hasattr(decorator.value, 'id') and IsTModule(decorator.value.id, self.Trans.SymbolTable):
if decorator.attr == 'Object':
IsCpythonObject = True
elif decorator.attr == 'CVTable':
IsCVTable = True
elif decorator.attr == 'NoVTable':
IsNoVTable = True
elif isinstance(decorator, ast.Name):
if decorator.id == 'Object':
IsCpythonObject = True
elif decorator.id == 'CVTable':
IsCVTable = True
elif decorator.id == 'NoVTable':
IsNoVTable = True
elif isinstance(decorator, ast.Call):
# 检测 @c.Attribute(t.attr.packed)
if isinstance(decorator.func, ast.Attribute):
if getattr(decorator.func.value, 'id', None) == 'c' and decorator.func.attr == 'Attribute':
for arg in decorator.args:
if isinstance(arg, ast.Attribute):
if isinstance(arg.value, ast.Attribute):
if IsTModule(getattr(arg.value.value, 'id', ''), self.Trans.SymbolTable) and arg.value.attr == 'attr' and arg.attr == 'packed':
Gen.class_packed.add(ClassName)
class_graph: dict = Gen._class_graph
if class_graph.get(ClassName, {}).get('has_methods', False) and not IsNoVTable:
if not IsCpythonObject:
IsCpythonObject = True
# 多继承支持(与 TPV _detect_vtable_status 对齐):不再因"作为其他类的基类"
# 自动启用 vtable。vtable 状态统一由继承链决定(见下方 HasParentClass 判定):
# 类自身无 @t.CVTable 时,父类有 vtable → 子类有;父类无 vtable → 子类静态。
# 否则基类(如 C(A,B) 中的 A/B)被自动加 vptr 后,其字段区在子类中的偏移
# 与自身布局不一致,继承方法按自身布局读 self 会字段错位。
HasParentClass: bool = False
ParentClassName: str | None = None
ParentClasses: list[str] = []
ParentIsNoVTable: bool = False
if Node.bases:
for base in Node.bases:
base_name: str | None = None
if hasattr(base, 'id'):
base_name = base.id
elif hasattr(base, 'attr'):
base_name = base.attr
if base_name:
kind: str | None = self._resolve_base_kind(base_name, Gen)
if kind is not None or base_name in t._MARKER_BASES:
continue # 标记基类,跳过
# 多继承:收集所有真实父类(声明顺序),不再 break
ParentClasses.append(base_name)
if ParentClassName is None:
ParentClassName = base_name
if base_name in class_graph or base_name in Gen.class_members or base_name in Gen.structs:
HasParentClass = True
# 检测父类是否有 @t.NoVTable 标记(非多态继承)
if class_graph.get(base_name, {}).get('has_novtable', False):
ParentIsNoVTable = True
# 跨模块父类:检查 _cross_module_novtable 集合
if base_name in Gen._cross_module_novtable:
ParentIsNoVTable = True
if HasParentClass and not IsCVTable and not IsNoVTable and not ParentIsNoVTable:
# 继承链统一 vtable(与 TPV _detect_vtable_status 一致):
# 父类有 vtable → 子类启用;父类无 vtable → 子类不启用(静态);
# 父类 vtable 状态未知(跨模块/未注册)→ 保守启用。
# 沿继承链追溯父类的 vtable 状态(支持空中间基类 Stmt/ExprNode):
# Stmt/ExprNode 无方法但 @t.CVTable,跨模块加载时 _has_methods_prescan=False
# 未注册 class_vtable,若只查直接父类会误判子类为静态类 →
# __before_init__ base=0 不写 vtable → 实例 vtable 为 null → 虚调用段错误。
# 修复:沿 class_parent 链向上查,任一祖先有 vtable 即判定子类有 vtable。
_parent_has_vtable: bool = False
_p_chain: str | None = ParentClassName
_p_visited_chain: set[str] = {ClassName}
_class_parent_map: dict = getattr(Gen, 'class_parent', {})
while _p_chain and _p_chain not in _p_visited_chain:
_p_visited_chain.add(_p_chain)
if (_p_chain in Gen.class_vtable
or _p_chain in Gen._cross_module_vtable_classes):
_parent_has_vtable = True
break
_p_chain = _class_parent_map.get(_p_chain)
parent_has_vtable: bool = _parent_has_vtable
parent_known: bool = (ParentClassName in class_graph
or ParentClassName in Gen.class_members
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
visited: set[str] = {ClassName}
ancestor: str | None = ParentClassName
while ancestor and ancestor not in visited:
visited.add(ancestor)
Gen.class_vtable.add(ancestor)
# 查询祖先类的父类
ancestor_info: dict | None = class_graph.get(ancestor)
if ancestor_info:
ancestor_bases: list = ancestor_info.get('bases', [])
next_ancestor: str | None = None
for ab in ancestor_bases:
if ab not in t._MARKER_BASES and ab not in visited:
next_ancestor = ab
break
ancestor = next_ancestor
else:
# 类图中没有,尝试从 class_parent 查询
ancestor = Gen.class_parent.get(ancestor)
return IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable, VtableByClassLevel
def _RegisterGenericClassTemplate(self, Node: ast.ClassDef) -> None:
"""注册泛型类模板到 `_generic_class_templates`(判据唯一:`_is_generic_class`)。
调用点两处:类处理阶段(_EmitClassLlvm)与前置声明之前的本模块预扫描
(PrescanModuleGenericClassTemplates)。注册内容唯一(node/type_params/
定义模块 SHA1),两个调用点不得各写一份。
"""
if not hasattr(self, '_generic_class_templates'):
self._generic_class_templates = {}
self._generic_class_templates[Node.name] = {
'node': Node,
'type_params': [tp.name for tp in Node.type_params],
# 记录泛型模板定义模块的 SHA1,用于特化时统一命名空间
'sha1': getattr(self.Trans, '_module_sha1', None),
}
def PrescanModuleGenericClassTemplates(self, ModuleBody: object) -> None:
"""前置声明之前预注册**本模块**泛型类模板。
判据与 includes 侧对泛型类的注册(HandlesImports._LoadModuleDeclarationsFromFile/
_EmitImportFromDeclarations 的 `node.type_params` 非空)**同源**,即
_is_generic_class,不看类名白名单。
类处理阶段(下方 _EmitClassLlvm)才注册本模块模板,而顶层函数前置声明
(GenerateLlvmDirect 的 _EmitFunctionForwardDeclLlvm)已需要它:模块级函数
参数/返回注解写自身泛型类(`def f(b: Box[Node])`)时
_SubscriptHitsGenericTemplate 判为不命中 → TypeAnnotationResolver 解析为空 →
VoidType → 抛"无法识别的参数类型注解";而同模块方法参数(类处理阶段之后)
却可用,二者分裂。预注册后两处走同一 _generic_class_templates。
"""
for Node in ModuleBody:
if isinstance(Node, ast.ClassDef) and self._is_generic_class(Node):
self._RegisterGenericClassTemplate(Node)
# ==================================================================
# _EmitNestedClassDefs - 递归发射类体内的嵌套 class / struct
#
# 命名与引用口径(用户 2026-10-01 定,与 union/renum 嵌套命名同源):
# · 内层结构体名 = `{外类}_{内类}`(Gen.structs / class_members 等同键);
# · 引用:限定名 `外类.内类`(TypeAnnotationResolver._from_node_attribute 的
# 扁平名回退)与扁平名 `外类_内类`(裸名直查 SymbolTable)都可用;
# · 多层嵌套自然递归为 A_B_C(deepcopy 后改名再递归)。
# 用 deepcopy 而不是临时改原名:源码 AST 节点还会被 _CollectOwnMemberTypes /
# SymbolExtractor / vpi 写出等路径读到,不得留下改名的副作用。
# ==================================================================
def _EmitNestedClassDefs(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> None:
"""把类体内的嵌套 ClassDef 逐个以 `{外类}_{内类}` 递归发射(见上方注释)"""
if Node is None:
return
Body: list | None = getattr(Node, 'body', None)
ClassName: str | None = getattr(Node, 'name', None)
if not Body or not ClassName:
return
for Item in list(Body):
if not isinstance(Item, ast.ClassDef):
continue
InnerName: str | None = getattr(Item, 'name', None)
if not InnerName:
continue
NestedName: str = f'{ClassName}_{InnerName}'
# 已发射过(多遍/重复调用)只补名字登记,不重复发射
if NestedName in Gen.structs and NestedName in Gen.class_members:
self._RegisterNestedClassName(ClassName, InnerName, NestedName)
continue
NestedNode: ast.ClassDef = copy.deepcopy(Item)
NestedNode.name = NestedName
self._EmitClassLlvm(NestedNode, Gen)
self._RegisterNestedClassName(ClassName, InnerName, NestedName)
def _RegisterNestedClassName(self, ClassName: str, InnerName: str, NestedName: str) -> None:
"""登记嵌套类名的三种写法(裸名 / `外类.内类` / 扁平名)
与 _EmitREnumLlvm 的变体三键登记同口径(见本文件 REnum 变体注册处),
供"限定名扁平回退"与"裸名直查"两条解析路径命中。
"""
MemberNode: CTypeInfo = CTypeInfo()
MemberNode.Name = NestedName
MemberNode.BaseType = t.CStruct(name=NestedName)
MemberNode.IsStruct = True
self.Trans.SymbolTable.insert(NestedName, MemberNode)
self.Trans.SymbolTable.insert(f'{ClassName}.{InnerName}', MemberNode)
def _EmitClassLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin, declare_only: bool = False) -> None:
ClassName: str = Node.name
# t.CType 及其子类(CChar/CInt/CVoid/CPtr/CTypeDefault 等)是"类型强转"标记,
# 不应被编译为真实结构体。CType 基类本身无 bases,base 检测无法触发,
# 此处在方法入口直接按类名跳过。
if self._is_classname_ctype_marker(ClassName):
return
if self._is_generic_class(Node):
self._RegisterGenericClassTemplate(Node)
return
if self._is_exception_class(Node):
self._RegisterExceptionClass(Node)
return
IsCenum: bool = False
IsCunion: bool = False
IsRenum: bool = False
# 先检查装饰器形式(@t.CStruct / @t.CEnum / @t.CUnion / @t.REnum)
deco_kind: str | None = self._resolve_decorator_kind(Node, Gen)
if deco_kind == 'enum':
IsCenum = True
elif deco_kind == 'union':
IsCunion = True
elif deco_kind == 'renum':
IsRenum = True
# 再检查基类继承形式(class X(t.CStruct):),与装饰器合并
if not IsCenum and not IsCunion and not IsRenum and Node.bases:
for base in Node.bases:
base_name: str | None = None
if hasattr(base, 'attr'):
base_name = base.attr
elif hasattr(base, 'id'):
base_name = base.id
if base_name:
kind: str | None = self._resolve_base_kind(base_name, Gen)
if kind == 'enum':
IsCenum = True
break
elif kind == 'union':
IsCunion = True
elif kind == 'renum':
IsRenum = True
elif kind == 'ctype_marker':
# CType 及其子类(CChar/CInt/CVoid/CPtr/CTypeDefault 等)是
# "类型强转"标记,不应被编译为真实结构体。直接 return 跳过编译。
return
if IsCenum:
self._RegisterEnumMembers(Node)
return
if IsCunion:
self._EmitUnionLlvm(Node, Gen)
return
if IsRenum:
self._EmitREnumLlvm(Node, Gen)
return
IsCpythonObject: bool
IsCVTable: bool
ParentClassName: str | None
IsNoVTable: bool
VtableByClassLevel: bool
IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable, VtableByClassLevel = self._resolve_class_flags(Node, Gen)
# 嵌套类 / 嵌套 struct:类体里再写 class / struct 此前被**静默忽略**(本函数的
# 字段/方法遍历只认 AnnAssign / FunctionDef,CollectAnnAssignMember 对其它节点
# 直接返回 None)。现按既有扁平命名先例发射为独立结构体 `{外类}_{内类}`
# (与 _EmitUnionLlvm:1569 / _EmitREnumLlvm:1715 的 `f"{ClassName}_{Nested}"`
# 同源),并登记 SymbolTable 三键,使 `外类.内类` 与扁平名 `外类_内类` 都能解析。
# 时机:在 flags 解析之后、class_members 初始化之前 —— 让嵌套结构体先就绪,
# 外层字段注解引用 `外类.内类` 时即可命中(_resolve_class_flags 内的名字键
# 均已按 ClassName 建立,递归改名不影响外层)。
self._EmitNestedClassDefs(Node, Gen)
# 跟踪正在发射的类,防止递归特化时 _generate_structs 过早为空成员类设置 body
Gen._classes_in_progress.add(ClassName)
# 标记正在收集字段,阻止继承处理(_ResolveGenericSlice → _specialize_generic_class)
# 和 CollectAnnAssignMember 触发的嵌套 _generate_structs 用不完整 class_members 调用 set_body
Gen._collecting_members_set.add(ClassName)
if ClassName not in Gen.class_methods:
Gen.class_methods[ClassName] = []
else:
# 防御性清除:移除导入阶段可能添加的构造函数(__new__/__init__/__before_init__)。
# 构造函数不参与虚分派,放入 vtable 会导致子类与基类 vtable 大小不一致,
# 索引偏移破坏多态分派(如 Module 有 __new__/__init__ 使 vtable=7,而 AST 无它们 vtable=5,
# 调用方按 AST 布局取 append@idx4 实际取到 Module.dump@idx4)。
Gen.class_methods[ClassName] = [
m for m in Gen.class_methods[ClassName]
if m.split('.')[-1] not in ('__new__', '__init__', '__before_init__')
]
# 当前文件定义的类是权威定义,覆盖之前导入阶段可能添加的外部声明。
# 关键:如果本地类是 @t.NoVTable 但同名导入类有 vtable(名称冲突),
# 必须清除 vtable 状态,否则 _generate_structs 会错误添加 vtable 指针。
if IsNoVTable:
Gen.class_vtable.discard(ClassName)
Gen._cross_module_vtable_classes.discard(ClassName)
Gen._cross_module_novtable.add(ClassName)
# 清除可能从导入 stub 加载的旧 struct body(含错误 vtable 指针),
# 让 _generate_structs 用本地 class_members 重新生成。
# 字段名必须是 llvmlite 的**公开属性 `elements`**(is_opaque = elements is None;
# set_body 仅在 elements is not None 时拒绝)——写 `_elements` 在本版本是空操作,
# 会让"权威重建点"永远无法刷新已冻结的错误布局。
_old_st = Gen.structs.get(ClassName)
if isinstance(_old_st, ir.IdentifiedStructType) and _old_st.elements is not None:
_old_st.elements = None
_ih = getattr(Gen, '_import_handler_ref', None)
if _ih is not None:
_ck = (id(Gen), ClassName)
if _ck in _ih._struct_Load_cache:
del _ih._struct_Load_cache[_ck]
Gen.class_members[ClassName] = []
# 本地定义是权威重建点:先撤销"已展平"登记,重建完成(且父类齐全)后再登记。
Gen._members_flattened.discard(ClassName)
Gen.class_member_defaults[ClassName] = {}
Gen.class_member_signeds[ClassName] = {}
Gen.class_member_bitfields[ClassName] = {}
Gen.class_member_byteorders[ClassName] = {}
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:
if hasattr(base, 'id'):
base_name = base.id
elif hasattr(base, 'attr'):
base_name = base.attr
elif isinstance(base, ast.Subscript):
# 泛型特化基类:GSListNode[GNode]
# 特化泛型类,用特化名作为父类名
spec_name: str | None = self.Trans.ExprCallHandle._ResolveGenericSlice(base)
if spec_name:
base_name = spec_name
else:
continue
else:
continue
if base_name == ClassName:
continue
if base_name in ('Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'):
continue
# 多继承:收集所有真实父类(声明顺序),不再 break。
# 总是记录父类名,即使父类 class_members 未加载。
# _get_member_offset 会沿 class_parent 链回退查找继承字段。
if ParentClass is None:
ParentClass = base_name
Gen.class_parent[ClassName] = ParentClass
ParentClasses.append(base_name)
if ParentClasses:
Gen.class_parents[ClassName] = ParentClasses
if ParentClasses:
# ============================================================
# 多继承字段展平:遍历所有父类(声明顺序),字段名**不去重**,
# __vtable__ 跳过(vtable 指针由 _generate_structs 按 class_vtable 统一添加)。
# 布局: [父类1字段..., 父类2字段..., 子类字段...](MRO 正序,与
# DeclarationGenerator stub / TPV 参考实现一致)。
#
# 关键修复(2026-08-10):原实现用 `inherited + class_members`(prepend)
# 产出 MRO 反序布局,且按字段名去重(如多个 Mixin 各自声明的 TempDir
# 被剔除)——导致定义模块 struct 与跨模块 stub 布局不一致,所有多继承
# 类字段运行时错位(ASan 下读到 0xbe 填充崩溃)。Mixin 方法体以自身
# 局部索引独立编译,调用侧 this_adjust + 块内索引依赖"展平块与 Mixin
# 自身逐字段一致",因此跨父类不能去重。菱形继承(A→D, B→D)会让 D
# 字段重复——当前项目 Mixin 为兄弟无共享祖先;若未来出现真菱形需改为
# "按继承源去重"而非"按字段名去重"。
# ============================================================
# 按继承源去重(2026-08-18):菱形继承(子类同时继承 A、B、D 且
# A/B 也继承 D)会让 D 的字段被重复展平。典型:Generator 同时继承
# ClassGenMixin/ClassGenEnum/ClassGenGeneric/ClassGenVTable/ClassGenDef/
# ClassGenBase,而前 5 个都继承 ClassGenBase → ClassGenBase 41 字段
# 被展平 6 次,struct 膨胀且 this_adjust/字段 GEP 全部错位。
# 修复:递归展平整个继承树,每个类只处理一次;每类只贡献
# "非祖先字段"(class_members[类] 是含祖先的展平结果,需剔除祖先
# 部分)。同名字段(如 TempDir 在 CallGenBase/TypeGenBase 各自声明)
# 因非继承关系,两个类都贡献,各自保留——符合下方 L654 注释的
# "按继承源去重"而非"按字段名去重"。
def _FlattenInherited(cls_name: str, _seen_cls: set,
_out: list, _gen) -> None:
if cls_name in _seen_cls:
return
_seen_cls.add(cls_name)
for _gp in _gen.class_parents.get(cls_name, []) or []:
_FlattenInherited(_gp, _seen_cls, _out, _gen)
# 收集该类的全部祖先字段名(含间接),剔除自身已展平结果中
# 属于祖先的部分(避免 class_members[类] 里混合的祖先字段重复)
_anc_names: set[str] = set()
_stack: list[str] = [cls_name]
_vis: set[str] = set()
while _stack:
_c = _stack.pop()
for _gp in _gen.class_parents.get(_c, []) or []:
if _gp in _vis:
continue
_vis.add(_gp)
_stack.append(_gp)
for _mn, _mt in _gen.class_members.get(_gp, []) or []:
_anc_names.add(_mn)
for _mn, _mt in _gen.class_members.get(cls_name, []) or []:
if _mn == '__vtable__':
continue
if _mn in _anc_names:
continue
_out.append((_mn, _mt))
_pd: dict = dict(_gen.class_member_defaults.get(cls_name, {}))
if _mn in _pd:
_gen.class_member_defaults[ClassName][_mn] = _pd[_mn]
_seen_cls: set[str] = set()
_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:
# 【幂等(2026-09-30)】该祖先块已作为**连续整段**存在于本表时不再追加:
# 本处是**追加**(`class_members + _flat`),与导入侧的
# `PrependInheritedBlock`(前置,自带表头判据)判据不对称,"块已在表中段/
# 尾部"的情形谁都不判 ⇒ 整块再插一次,表里多出 len(_flat) 条重复条目,
# body 随之多出同名槽(现场:output/<sha1>.stub.ll 的 TypeGenCType 73 槽
# = temp 声明 41 槽 + 其 [9:41] 的原样副本;同族 7 个类,
# 下游即 Repl.vp:802 的"基座 pointee 未匹配任何 struct")。
# 判据(名字 + IR 类型逐项连续匹配)与成因见
# HandlesImports.InheritedBlockAlreadyPresent;只跳过重复、不改其余布局。
from lib.core.Handles.HandlesImports import NormalizeAncestralCopies as _NormAnc
# 【规范化(2026-09-30)】_NormAnc 同时完成两件事:
# ① "块已整段在头/尾 ⇒ 不再追加"(等价于原 InheritedBlockAlreadyPresent);
# ② 剥掉两端挂着的"祖先块前缀的**陈旧半截副本**"——此前某次展平写入的是
# 半截祖先块(父类表当时只收到前 k 项),本次整块追加就让那 k 条留在表里
# 成重复(现场:ClassGenDef 表 50 / 去重 42 / body 42 ⇒ 8 条陈旧副本)。
# 判据与成因见 HandlesImports.NormalizeAncestralCopies。
_Trimmed: list
_HasFull: bool
_Trimmed, _HasFull = _NormAnc(list(Gen.class_members[ClassName]), _flat, InsertAtHead=False)
_Dropped: int = len(Gen.class_members[ClassName]) - len(_Trimmed)
if _Dropped > 0:
_vlog().warning(
f"[成员展平] 类 '{ClassName}' 追加祖先块({len(_flat)} 项)前剥掉 "
f"{_Dropped} 条**陈旧半截副本**(重名条目不占布局槽位,却会让 "
f"base/下标回退失准):表 {len(Gen.class_members[ClassName])} → "
f"{len(_Trimmed)} 项",
"成员展平")
Gen.class_members[ClassName] = _Trimmed
if _HasFull:
_vlog().warning(
f"[成员展平] 类 '{ClassName}' 的祖先块({len(_flat)} 项)已存在于成员表"
f"(表 {len(Gen.class_members[ClassName])} 项)⇒ 跳过重复追加"
f"(重复会让 body 多出同名槽、与 Phase 1 声明布局分叉)",
"成员展平")
else:
Gen.class_members[ClassName] = _Trimmed + _flat
# ============================================================
# 多继承方法合并:遍历所有父类(声明顺序)收集方法(短名去重,先声明父类优先),
# vtable 槽/方法名用"实际定义类.方法"(如 B.get_b,与 TPV 一致),
# 不再生成子类包装器 "ClassName.method"(旧逻辑注册了包装器但未生成函数体,
# 导致链接 undefined reference,如 C.get_b / ColoredCircle.color_code / Vec2Both.extra_val)。
# ============================================================
all_parent_methods: list[str] = []
parent_method_names_seen: set[str] = set()
for ParentClass in ParentClasses:
for pm in list(Gen.class_methods.get(ParentClass, [])):
method_name: str = pm.split('.')[-1] if '.' in pm else pm
if method_name in ('__new__', '__init__', '__before_init__'):
continue
if method_name in parent_method_names_seen:
continue # 先声明父类优先,菱形去重
parent_method_names_seen.add(method_name)
all_parent_methods.append(pm)
if all_parent_methods:
# 构建子类已有方法映射(方法短名 -> 方法全名)。
# 子类覆盖判定:既查 class_methods(prescan/导入阶段已注册),
# 也直接扫描 ClassDef 自身的方法(与 TPV _collect_vtable_methods 一致)。
child_method_map: dict[str, str] = {}
for m in Gen.class_methods[ClassName]:
method_name: str = m.split('.')[-1] if '.' in m else m
child_method_map[method_name] = m
for _item_self in Node.body:
if isinstance(_item_self, ast.FunctionDef):
_mn_self: str = _item_self.name
if _mn_self in ('__new__', '__init__', '__before_init__'):
continue
child_method_map[_mn_self] = f"{ClassName}.{_mn_self}"
# 父类方法在前(保证 vtable 布局一致性,子类多态分派索引正确),
# 子类新增方法在后。子类覆盖的方法放在父类方法的位置(用子类实现)。
new_methods: list = []
covered_names: set[str] = set()
for pm in all_parent_methods:
method_name: str = pm.split('.')[-1] if '.' in pm else pm
if method_name in child_method_map:
# 子类覆盖了父类方法 — 用子类的实现,放在父类的位置
new_methods.append(child_method_map[method_name])
covered_names.add(method_name)
else:
# 父类独有方法 — 直接用父类方法原名(定义类名,如 B.get_b)
new_methods.append(pm)
covered_names.add(method_name)
# 添加子类新增方法(不在父类中的)
for m in list(Gen.class_methods[ClassName]):
method_name: str = m.split('.')[-1] if '.' in m else m
if method_name not in covered_names:
new_methods.append(m)
Gen.class_methods[ClassName] = new_methods
# 提取首个裸字符串字面量作为结构体 __doc__ docstring
if (Node.body and isinstance(Node.body[0], ast.Expr)
and isinstance(Node.body[0].value, ast.Constant)
and isinstance(Node.body[0].value.value, str)):
Gen._create_doc_global(ClassName, Node.body[0].value.value)
else:
Gen._create_doc_global(ClassName, None)
# 解析 __provides__/__requires__/__require_must__ 编译期元数据
for item in Node.body:
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
field_name: str = item.target.id
if field_name in ('__provides__', '__requires__', '__require_must__') and item.value:
values: list[str] = []
if isinstance(item.value, ast.List):
for elt in item.value.elts:
if isinstance(elt, ast.Constant) and isinstance(elt.value, str):
values.append(elt.value)
if field_name == '__provides__':
Gen.class_provides[ClassName] = values
elif field_name == '__requires__':
Gen.class_requires[ClassName] = values
elif field_name == '__require_must__':
Gen.class_require_must[ClassName] = values
# 自有字段追加段的起点(供下方"字段名遮蔽"检查切分自有/继承两段;
# 此时 class_members[ClassName] 只含继承前缀,见 :825)。
_OwnFieldStart: int = len(Gen.class_members[ClassName])
# 【嵌套重入防重(2026-09-30)】本循环**每轮都可能重入**同一张表:
# CollectAnnAssignMember → ResolveAnnotationType → 泛型特化(list[X] 等)
# → _generate_structs / _TryLoadClassMembersFromPyi → _EmitExternalClassDeclLlvm
# → 可按 .vpi **清空并重填**本类成员表(HandlesImports.py:3408 的 force 分支,
# 由 HandlesImports.py:2647/2654 与 FuncGen.py:1057 触发;该分支不受
# "字段收集中"保护,见 HandlesImports.py:3299 的 `not force` 豁免)。
# 此时本循环后面的 `append` 会把**嵌套帧已经登记过的同一字段**再写一条 ——
# 现场:AST 的最后一个字段 `children: list[rawptr[AST]] | t.CPtr` 是全类唯一
# 触发泛型特化的注解,处理它时嵌套帧重填了 AST 表(7 项),本循环恢复后
# 又追加一次 `children` ⇒ 表 8 项 / 去重 7 项,重名条目 `children@7`;
# 该表又被祖先块展平**原样拷贝**给 AST 的每个子类(_FlattenInherited 只按
# 祖先名过滤、不按名去重)⇒ StructGen 的"[结构体布局] 成员表含重名条目"
# 在大量 AST 子类上齐发(AST/Stmt/ExprNode/MatchCase/ExceptHandler…)。
# 判据刻意收窄:**只跳过"本轮循环开始之后才出现"的同名条目**(嵌套帧刚登记
# 的那条)。循环前已在表里的同名(继承前缀/同名字段)照旧追加 ⇒ 上方
# "字段名遮蔽"fail-fast 的判据(自有段切分)与既有布局语义逐字不变。
_StartNames: set[str] = {m[0] for m in Gen.class_members[ClassName]}
for item in Node.body:
# 跳过编译期元数据字段(__provides__/__requires__/__require_must__)
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
if item.target.id in ('__provides__', '__requires__', '__require_must__'):
continue
parsed = self.CollectAnnAssignMember(item, Gen)
if parsed:
VarName, MemberType, TypeInfo = parsed
if VarName not in _StartNames \
and VarName in {m[0] for m in Gen.class_members[ClassName]}:
continue
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):
element_class: str | None = FindStructNameInAnnotationWithContext(
item.annotation, self.Trans, Gen)
# Fallback: FindStructNameInAnnotation 不处理 Subscript slice 为 BinOp 的情况
# (如 list[AST | t.CPtr] | t.CPtr),使用 TypeInfo.Name 作为回退。
# TypeInfo.Name 由 _HandleSubscript 设置为特化名(如 'list[sha1.AST*]'),
# 经 _MergeCTypeInfoInto 传播后保留。
if not element_class and TypeInfo.IsStruct and TypeInfo.Name and TypeInfo.Name in Gen.structs:
element_class = TypeInfo.Name
if element_class:
if ClassName not in Gen.class_member_element_class:
Gen.class_member_element_class[ClassName] = {}
Gen.class_member_element_class[ClassName][VarName] = element_class
# 泛型特化结构体指针字段(如 GSList[BasicBlock] | t.CPtr):
# 记录特化名,供后续 _try_access_opaque_generic_member 在所有候选特化版本
# 都有目标字段(如 Head/Tail/Count)时选择正确特化版本。
# 现有 i8* 分支不覆盖结构体指针字段,这里补充。
elif (isinstance(MemberType, ir.PointerType)
and isinstance(MemberType.pointee, ir.IdentifiedStructType)
and isinstance(item.annotation, ast.BinOp)
and isinstance(item.annotation.op, ast.BitOr)):
spec_in_anno: str | None = FindStructNameInAnnotationWithContext(
item.annotation, self.Trans, Gen)
if spec_in_anno and '[' in spec_in_anno:
if ClassName not in Gen.class_member_element_class:
Gen.class_member_element_class[ClassName] = {}
Gen.class_member_element_class[ClassName][VarName] = spec_in_anno
# Subscript 容器字段(list[X] / rawptr[list[X]]):非 BinOp 联合,
# 上面的 i8*/BinOp 分支不命中 → 容器身份丢失 → 字段方法调用
# (get/len…)分派失败 "Undefined method 'get' in class 'rawptr'"
# (Converter.vp L190)。与 HandlesImports 对称,用与特化生成端
# 同源的 _resolve_type_arg_str 记录 element_class。
if isinstance(item.annotation, ast.Subscript):
_ann_sub_c: ast.Subscript = item.annotation
_subv_c: ast.AST = _ann_sub_c.value
_subname_c: str = getattr(_subv_c, 'id', None) or getattr(_subv_c, 'attr', '')
_elem_slice_c: ast.AST | None = None
if _subname_c == 'list':
_elem_slice_c = _ann_sub_c.slice
elif _subname_c == 'rawptr':
_slice_c: ast.AST = _ann_sub_c.slice
if isinstance(_slice_c, ast.Subscript):
# rawptr[泛型容器](rawptr[list[X]] / rawptr[GSList[Param]]):
# 容器身份 = 泛型特化名。list 继续走 _elem_slice_c 统一登记;
# 其余泛型容器直接登记 element_class(阻塞 B 根因链一环:
# 原实现只认内层 list,rawptr[GSList[Param]] 在此空转并跳过
# 裸结构分支 → element_class 缺失 → 字段/局部变量方法分派
# 丢失容器身份,FakeDuck 错绑 LinkedNode.append 写穿)。
_inner_sub_c: ast.Subscript = _slice_c
_iname_c: str = getattr(_inner_sub_c.value, 'id', None) or getattr(_inner_sub_c.value, 'attr', '')
if _iname_c == 'list':
_elem_slice_c = _inner_sub_c.slice
else:
_ec_gc: str | None = None
if _iname_c:
try:
_ec_gc = f'{_iname_c}[{self.Trans.ExprCallHandle._resolve_type_arg_str(_inner_sub_c.slice)}]'
except Exception:
_ec_gc = None
if _ec_gc:
if ClassName not in Gen.class_member_element_class:
Gen.class_member_element_class[ClassName] = {}
Gen.class_member_element_class[ClassName][VarName] = _ec_gc
else:
# rawptr[裸结构](如 rawptr[Token]):跨模块 stub 会把自引用/类内
# 指针字段退化为 i8*,链式成员访问(a.b.c)需 element_class 恢复
# struct 身份走 char*→struct cast。HEAD 态 `Token | t.CPtr` 联合
# 由上方 BinOp 分支登记;rawptr 重构后此分支补齐(阻塞 A 根因)。
# 与 HandlesImports 的 rawptr 分支对称。
_slc_name_c: str | None = None
if isinstance(_slice_c, ast.Name):
_slc_name_c = _slice_c.id
elif isinstance(_slice_c, ast.Attribute):
_slc_name_c = _slice_c.attr
_ec_target_c: str | None = None
if _slc_name_c:
if _slc_name_c in Gen.structs:
_ec_target_c = _slc_name_c
else:
_st_sha1_c: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(_slc_name_c)
if _st_sha1_c and f'{_st_sha1_c}.{_slc_name_c}' in Gen.structs:
_ec_target_c = f'{_st_sha1_c}.{_slc_name_c}'
else:
# 短名模糊扫描(对齐 HandlesAnnAssign 变量侧
# 补偿 L597-602):跨模块 `mod.Class` 形态的
# rawptr[Class] 字段在 class_sha1_map 尚无该短名
# 时上面两路全 miss → element_class 静默缺失 →
# 字段方法调用(factory.PermPool.alloc)链尾类名
# 丢失 → TPC 调用分派返回 i32 0 哨兵 → 误报
# "Unknown module: ...PermPool"。
for _ec_k_c in Gen.structs:
_ec_short_c: str = _ec_k_c.split('.')[-1] if '.' in _ec_k_c else _ec_k_c
if _ec_short_c == _slc_name_c:
_ec_target_c = _ec_k_c
break
if _ec_target_c:
if ClassName not in Gen.class_member_element_class:
Gen.class_member_element_class[ClassName] = {}
Gen.class_member_element_class[ClassName][VarName] = _ec_target_c
if _elem_slice_c is not None:
_ec_c: str | None = None
try:
_ec_c = f'list[{self.Trans.ExprCallHandle._resolve_type_arg_str(_elem_slice_c)}]'
except Exception:
_ec_c = None
if _ec_c:
if ClassName not in Gen.class_member_element_class:
Gen.class_member_element_class[ClassName] = {}
Gen.class_member_element_class[ClassName][VarName] = _ec_c
if hasattr(TypeInfo.BaseType, 'IsSigned'):
Gen.class_member_signeds[ClassName][VarName] = TypeInfo.BaseType.IsSigned
else:
Gen.class_member_signeds[ClassName][VarName] = None
if TypeInfo.IsBitField:
Gen.class_member_bitfields[ClassName][VarName] = TypeInfo.BitWidth
else:
Gen.class_member_bitfields[ClassName][VarName] = 0
if TypeInfo.ByteOrder:
Gen.class_member_byteorders[ClassName][VarName] = TypeInfo.ByteOrder
else:
Gen.class_member_byteorders[ClassName][VarName] = ""
if item.value:
const: ir.Constant | None = self._BuildScalarConstant(item.value, MemberType, Gen)
if const:
Gen.class_member_defaults[ClassName][VarName] = const
else:
Gen.class_member_signeds[ClassName][VarName] = None
Gen.class_member_bitfields[ClassName][VarName] = 0
# ============================================================
# 【字段名遮蔽 fail-fast】类体声明的字段名与**任一祖先声明的字段名**重名 ⇒ 终止编译
#
# 这不是风格问题:本仓两条既有机制决定了"同名"必然产出**写错槽**,且是静默的。
# ① 子类同名声明会成为**自有字段**,与**同名继承项**在 class_members 里并存;
# 跨模块侧的前置过滤器(HandlesImports._FlattenExt 前置段 :2516-2521)按
# "名字已在表内"过滤——它本意只是防同一函数重入时重复前置,却会把**同名继承项
# 整条丢掉** ⇒ 继承段与 struct body 的继承前缀失去一一对应:被丢项**之后**的
# 继承字段表内下标整体前移 k(k = 被丢项数),而查询侧回退路径用的是
# `base + 表内下标`(FuncGen._get_member_offset)⇒ 落到**类型完全不同的槽**。
# 现场:TypeGenCType 重复声明 5 个父类已有字段后,继承字段
# LastBuiltinsTypedefResult(下标 32)前移为 27 = CTypedefNameIdxBuilt(i32)
# ⇒ `self.Gen.TypeGenCType.LastBuiltinsTypedefResult = "t.CPtr"` 报
# "指针隐式转整数已禁止:不能把指针类型 i8* 赋给整数类型 i32"。
# ② 父类方法按**父类布局**访问该名字的槽、子类方法按**自有槽**访问 ⇒ 同名两槽
# split-brain(一侧写、另一侧读),运行期静默错值。
# 唯一合法写法:字段只在其**归属类**声明一次;子类要新字段就换名字。
# 合法例外(不在本检查范围):两个**互不为祖先**的类各自声明同名字段
# (如 CallGenBase / TypeGenBase 的 TempDir)——那不属于遮蔽。
# 判据来源:自有 = 本类类体追加段(:903 快照起);祖先 = 沿 class_parents 传递收集
# 各祖先 class_members 的声明名(含仅从 .vpi 加载的祖先;祖先未就绪则该项不成判据,
# 不产生误报)。
# ============================================================
_AncestorFieldNames: dict[str, str] = {}
_AncSeen: set[str] = set()
# class_parents 可能尚未建立(HandlesImports 同族代码也用 hasattr 兜底),
# 缺失时视为"无祖先"——只可能少报、不可能误报。
_ParentsMap: dict = getattr(Gen, 'class_parents', None) or {}
_AncStack: list[str] = list(_ParentsMap.get(ClassName) or [])
while _AncStack:
_Anc: str = _AncStack.pop()
if not _Anc or _Anc in _AncSeen:
continue
_AncSeen.add(_Anc)
for _An, _ in Gen.class_members.get(_Anc) or []:
if _An != '__vtable__':
_AncestorFieldNames.setdefault(_An, _Anc)
_AncStack.extend(_ParentsMap.get(_Anc) or [])
_OwnFieldNames: list[str] = [n for n, _ in Gen.class_members[ClassName][_OwnFieldStart:]]
_Shadowed: list[str] = sorted({_n for _n in _OwnFieldNames if _n in _AncestorFieldNames})
if _Shadowed:
_Decl: ast.AST | None = None
for _it in Node.body:
if (isinstance(_it, ast.AnnAssign) and isinstance(_it.target, ast.Name)
and _it.target.id in _Shadowed):
_Decl = _it
break
_src: str = getattr(Gen, '_current_source_file', '') or ''
_ln: int = (getattr(_Decl, 'lineno', 0) or getattr(Node, 'lineno', 0) or 0)
_col: int = getattr(_Decl, 'col_offset', -1) if _Decl is not None else -1
# 行号还原:AST 行号含 _inject_auto_imports 注入的 N 行(同
# HandlesBase._FailFastFixedArrayKeyword / _ErrorStack 各站点)。
_off: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _off > 0 and _ln > _off:
_ln -= _off
_code: str = ''
try:
_code = (Gen._get_source_line(_ln) or '').strip()
except Exception:
_code = ''
_Pairs: str = '、'.join(f"'{_n}'(祖先 '{_AncestorFieldNames[_n]}' 已声明)"
for _n in _Shadowed)
_msg: str = (
f"类 '{ClassName}' 的类体字段与祖先类声明的字段**重名**:{_Pairs}。"
f"字段只能在其归属类声明一次(子类要新字段请改名;若确实要复用父类字段,"
f"删掉子类的这条声明即可)——重名会让该名字在 class_members 里变成"
f"「自有字段 + 同名继承项」两份,跨模块展平的按名过滤会丢掉继承项,"
f"使其后所有继承字段的表内下标整体前移、写入落到类型不同的槽"
f"(典型:指针值写进 i32 槽 → 「指针隐式转整数已禁止」);"
f"父类方法与子类方法还会各自访问不同槽(同一变量 split-brain)。"
f"位置 {_src}:{_ln}:{_col}")
_vlog().compile_error(_msg, _src, _ln, _code)
sys.exit(1)
# 字段收集完成,清除标志,允许后续 _generate_structs 正常设置 body
Gen._collecting_members_set.discard(ClassName)
existing_members: set[str] = {m[0] for m in Gen.class_members[ClassName]}
for item in Node.body:
if isinstance(item, ast.FunctionDef):
self._current_method_args = {}
for arg in item.args.args:
if arg.arg != 'self' and arg.annotation:
self._current_method_args[arg.arg] = arg.annotation
self._scan_method_body_for_self_members(item.body, ClassName, existing_members, Gen)
self._current_method_args = {}
for item in Node.body:
if isinstance(item, ast.FunctionDef):
MethodName: str = item.name
FullMethodName: str = f"{ClassName}.{MethodName}"
# property setter/deleter 使用不同的函数名后缀
item_meta: FuncMeta = self.Trans.FunctionHandler._ExtractFuncMeta(item.decorator_list)
if FuncMeta.PROPERTY_SETTER in item_meta:
FullMethodName = FullMethodName + '$set'
elif FuncMeta.PROPERTY_DELETER in item_meta:
FullMethodName = FullMethodName + '$del'
# 构造函数(__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__'):
_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, _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:
Gen._create_vtable_for_class(ClassName)
if ClassName in Gen.structs:
self._EmitNewFunctionLlvm(ClassName, Gen, IsCVTable=IsCVTable, IsCpythonObject=IsCpythonObject)
for item in Node.body:
if isinstance(item, ast.FunctionDef):
self.Trans.FunctionHandler._EmitFunctionForwardDeclLlvm(item, Gen, ClassName=ClassName)
if not declare_only:
for item in Node.body:
if isinstance(item, ast.FunctionDef):
self.Trans.FunctionHandler._EmitFunctionLlvm(item, Gen, ClassName=ClassName)
if IsCVTable and ClassName in Gen.Vtables:
self._fill_vtable_with_methods(ClassName, Gen)
# 多继承支持:不再生成子类包装器(_generate_inherited_method_wrappers)。
# 包装器只对 self 做 bitcast 基址转换,不调整字段偏移——对多继承中
# 第二个及之后的父类(字段区偏移 > 0)会错位。继承方法统一走
# MRO 祖先链查找 + this 调整(见 HandlesExprCall._adjust_this_for_inherited_method),
# 与 TPV 一致。
Gen._classes_in_progress.discard(ClassName)
def _generate_inherited_method_wrappers(self, ClassName: str, Gen: LlvmGeneratorMixin) -> None:
"""为子类继承但未覆写的方法生成包装函数,使跨模块调用能正确链接。
例如 Label 继承 Widget.place,生成 Label.place 函数,
内部将 self 从 Label* bitcast 为 Widget*,然后调用 Widget.place。
"""
# 收集本类方法 + 沿 MRO 继承链收集父类方法名
# 原代码只遍历 class_methods[ClassName],遗漏父类独有方法(如 __enter__),
# 导致子类调用继承的方法时链接失败(undefined reference)
methods: list = list(Gen.class_methods.get(ClassName, []))
seen_shorts: set[str] = {m.split('.')[-1] if '.' in m else m for m in methods}
mro_list: list[str] = Gen._get_mro_chain(ClassName)
for anc in mro_list[1:]: # 跳过自身,先声明父类优先(MRO 已去重)
parent_methods: list = Gen.class_methods.get(anc, [])
for pm in parent_methods:
pm_short: str = pm.split('.')[-1] if '.' in pm else pm
# 避免重复:如果子类已覆写同名方法,不添加
if pm_short not in seen_shorts:
seen_shorts.add(pm_short)
methods.append(pm)
if not methods:
return
if ClassName not in Gen.structs:
return
for method_name in methods:
method_short: str = method_name.split('.')[-1] if '.' in method_name else method_name
child_full: str = f"{ClassName}.{method_short}"
# 如果子类已经有该方法的实现,跳过
if Gen._find_function(child_full):
continue
# 沿 MRO 继承链查找父类实现(先声明父类优先)
parent_func: ir.Function | None = None
parent_class: str | None = None
for anc in Gen._get_mro_chain(ClassName)[1:]:
parent_full: str = f"{anc}.{method_short}"
parent_func = Gen._find_function(parent_full)
if parent_func:
parent_class = anc
break
if not parent_func or not parent_class:
continue
# 父类和子类都需要有 struct 定义
if parent_class not in Gen.structs:
continue
# 构造子类包装函数签名:与父类函数相同,但 self 参数类型为子类指针
parent_ftype: ir.FunctionType = parent_func.function_type
parent_ret_type: ir.Type = parent_ftype.return_type
parent_param_types: list[ir.Type] = list(parent_ftype.args)
is_vararg: bool = parent_ftype.var_arg
# 判断父类方法是否是静态方法:
# 如果第一个参数类型是父类指针,说明是实例方法;否则是静态方法
parent_struct_ptr_type: ir.PointerType | None = ir.PointerType(Gen.structs[parent_class]) if parent_class in Gen.structs else None
is_static: bool = True
if parent_param_types and parent_struct_ptr_type:
first_param: ir.Type = parent_param_types[0]
# 检查第一个参数是否是父类指针(或可以 bitcast 为父类指针的指针类型)
if isinstance(first_param, ir.PointerType):
if isinstance(first_param.pointee, ir.IdentifiedStructType):
if first_param.pointee.name == Gen.structs[parent_class].name:
is_static = False
elif first_param == ir.IntType(8):
# i8* 可能是前向声明时的 self 类型,视为实例方法
is_static = False
if is_static:
# 静态方法:签名与父类完全一致,不需要 self 参数
child_param_types: list[ir.Type] = list(parent_param_types)
child_struct_ptr: ir.PointerType | None = None
else:
# 实例方法:替换第一个参数(self)的类型为子类指针
child_struct_ptr = ir.PointerType(Gen.structs[ClassName])
if parent_param_types:
child_param_types = [child_struct_ptr] + parent_param_types[1:]
else:
child_param_types = [child_struct_ptr]
child_func_type: ir.FunctionType = ir.FunctionType(parent_ret_type, child_param_types, var_arg=is_vararg)
child_mangled: str = Gen._mangle_func_name(child_full)
child_func: ir.Function = Gen._get_or_declare_function(child_mangled, child_func_type)
Gen.functions[child_full] = child_func
# 生成函数体
entry_block: ir.Block = child_func.append_basic_block(name="entry")
saved_builder: ir.IRBuilder = Gen.builder
saved_func: ir.Function = Gen.func
Gen.builder = Gen._NewIrBuilder(entry_block)
Gen.func = child_func
# 准备参数
call_args: list[ir.Value] = []
if is_static:
# 静态方法:参数直接传递,不做 bitcast
for arg in child_func.args:
call_args.append(arg)
else:
# 实例方法:bitcast self,其余参数直接传递
for i, arg in enumerate(child_func.args):
if i == 0 and parent_param_types:
actual_self_type: ir.Type = parent_param_types[0]
casted_self: ir.Value = Gen.builder.bitcast(arg, actual_self_type, name="self_cast")
call_args.append(casted_self)
else:
call_args.append(arg)
result: ir.Value = Gen.builder.call(parent_func, call_args, name="inherited_call")
if isinstance(parent_ret_type, ir.VoidType):
Gen.builder.ret_void()
else:
Gen.builder.ret(result)
Gen.builder = saved_builder
Gen.func = saved_func
def _fill_vtable_with_methods(self, ClassName: str, Gen: LlvmGeneratorMixin) -> None:
methods: list = Gen.class_methods.get(ClassName, [])
if not methods or ClassName not in Gen.Vtables:
return
Vtable: ir.GlobalVariable = Gen.Vtables[ClassName]
VtableType: ir.Type = Vtable.type.pointee
null_i8ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None)
initializers: list[ir.Constant] = []
for mi, method_name in enumerate(methods):
# 多继承:vtable 槽直接用完整方法名(实际定义类.方法,如 B.get_b),
# 与 TPV 一致,避免"子类名.方法"包装器导致链接 undefined reference。
func: ir.Function | None = Gen._find_function(method_name)
if not func:
func = Gen._find_function(f"{method_name}__")
if not func:
method_short: str = method_name.split('.')[-1] if '.' in method_name else method_name
func = Gen._find_function(f"{ClassName}.{method_short}")
if not func:
func = Gen._find_function(f"{ClassName}.{method_short}__")
if not func:
# 沿 MRO 祖先链查找定义类(先声明父类优先)
for anc in Gen._get_mro_chain(ClassName)[1:]:
func = Gen._find_function(f"{anc}.{method_short}")
if not func:
func = Gen._find_function(f"{anc}.{method_short}__")
if func:
break
if func:
initializers.append(func.bitcast(ir.PointerType(ir.IntType(8))))
else:
initializers.append(null_i8ptr)
if initializers:
Vtable.initializer = ir.Constant(VtableType, initializers)
def _scan_method_body_for_self_members(self, body: list[ast.AST], ClassName: str, existing_members: set[str], Gen: LlvmGeneratorMixin) -> None:
for stmt in body:
self._scan_node_for_self_assign(stmt, ClassName, existing_members, Gen)
def _scan_node_for_self_assign(self, node: ast.AST, ClassName: str, existing_members: set[str], Gen: LlvmGeneratorMixin) -> None:
if isinstance(node, ast.AnnAssign):
if (isinstance(node.target, ast.Attribute)
and isinstance(node.target.value, ast.Name)
and node.target.value.id == 'self'):
VarName: str = node.target.attr
if VarName not in existing_members:
# fail-fast(项目约定:类型信息缺失立即终止,不兜底)。
# 此前三处静默 i32 兜底(FromNode 返回 None / _ctype_to_llvm 得 void /
# 解析抛异常)会把"声明没解析出来"伪装成"该成员是 i32",下游赋值/取址
# 检查于是报出与真因无关的错——实测:`free_lists: uint64*` 被当成 i32,
# `self.free_lists = base`(i8*) 因此误报"指针隐式转整数已禁止"。
AnnotationText: str = ast.unparse(node.annotation)
try:
TypeInfo: CTypeInfo | None = CTypeInfo.FromNode(node.annotation, self.Trans.SymbolTable)
if TypeInfo is None:
raise ValueError(
f"[FATAL] 类 '{ClassName}' 成员 '{VarName}' 的注解 "
f"'{AnnotationText}' 解析不出类型(CTypeInfo.FromNode 返回 None)"
f"—— 拒绝静默当作 i32"
)
MemberType: ir.Type = Gen._ctype_to_llvm(TypeInfo)
if isinstance(MemberType, ir.VoidType):
raise ValueError(
f"[FATAL] 类 '{ClassName}' 成员 '{VarName}' 的注解 "
f"'{AnnotationText}' 解析为 void(BaseType="
f"{getattr(TypeInfo, 'BaseType', None)} PtrCount="
f"{getattr(TypeInfo, 'PtrCount', None)} IsTypedef="
f"{getattr(TypeInfo, 'IsTypedef', None)} Name="
f"{getattr(TypeInfo, 'Name', None)})—— 拒绝静默当作 i32"
)
except Exception as e: # 成员类型解析失败:带注解原文与类/成员名终止
if isinstance(e, ValueError) and str(e).startswith('[FATAL]'):
raise
raise ValueError(
f"[FATAL] 类 '{ClassName}' 成员 '{VarName}' 的注解 "
f"'{AnnotationText}' 解析失败:{type(e).__name__}: {e}"
) from e
Gen.class_members[ClassName].append((VarName, MemberType))
Gen.class_member_signeds[ClassName][VarName] = None
Gen.class_member_bitfields[ClassName][VarName] = 0
Gen.class_member_byteorders[ClassName][VarName] = ""
Gen.class_member_bitoffsets[ClassName][VarName] = 0
existing_members.add(VarName)
len_name = f'{VarName}__len'
if isinstance(MemberType, ir.PointerType) and node.value and isinstance(node.value, ast.List):
if len_name not in existing_members:
Gen.class_members[ClassName].append((len_name, ir.IntType(64)))
Gen.class_member_signeds[ClassName][len_name] = None
Gen.class_member_bitfields[ClassName][len_name] = 0
Gen.class_member_byteorders[ClassName][len_name] = ""
Gen.class_member_bitoffsets[ClassName][len_name] = 0
Gen.class_member_defaults[ClassName][len_name] = ir.Constant(ir.IntType(64), len(node.value.elts))
existing_members.add(len_name)
elif isinstance(node, ast.Assign):
for target in node.targets:
if (isinstance(target, ast.Attribute)
and isinstance(target.value, ast.Name)
and target.value.id == 'self'):
VarName = target.attr
if VarName not in existing_members:
# 类型判定:只接受**能唯一确定**的取值形态(下表);推不出即
# fail-fast,绝不静默产出 i32 槽(项目约定:类型信息缺失立即
# 终止;与同函数 `self.X: T` 分支 :1356-1386 同口径)。
# 同族现场:`free_lists: uint64*` 被当成 i32 后,
# `self.free_lists = base`(i8*) 误报"指针隐式转整数已禁止"。
# 【必须与 DeclarationGenerator._CollectOwnMemberTypes 的
# 同名规则逐条一致】否则 stub 与定义端 struct 布局分叉
# (stub 少/多一个槽 ⇒ 跨模块字段偏移整体错位):
# 整型字面量 → i32;布尔字面量 → i32(Python 中 bool 是 int
# 子类,维持既有 int 分支语义);浮点字面量 → double;
# 字符串字面量 → i8*;值为**本方法带注解的形参** → 该形参类型;
# 白名单调用 float()/double() → double、char() → i8、
# int()/long()/short() → i32、`x.len()` → i32。
# 其余(None 字面量、BinOp、任意调用/属性链…)→ fail-fast:
# 这些形态推不出成员类型,静默 i32 只会把错误推到下游
# (写指针值 → "指针隐式转整数已禁止")。
MemberType: ir.Type | None = None
if node.value is not None:
try:
if isinstance(node.value, ast.Constant):
if isinstance(node.value.value, bool):
MemberType = ir.IntType(32)
elif isinstance(node.value.value, int):
MemberType = ir.IntType(32)
elif isinstance(node.value.value, float):
MemberType = ir.DoubleType()
elif isinstance(node.value.value, str):
MemberType = ir.PointerType(ir.IntType(8))
elif isinstance(node.value, ast.Name):
if hasattr(self, '_current_method_args'):
arg_info = self._current_method_args.get(node.value.id)
if arg_info:
TypeInfo = CTypeInfo.FromNode(arg_info, self.Trans.SymbolTable)
if TypeInfo:
InferredType = Gen._ctype_to_llvm(TypeInfo)
if not isinstance(InferredType, ir.VoidType):
MemberType = InferredType
elif isinstance(node.value, ast.Call):
if isinstance(node.value.func, ast.Name):
if node.value.func.id in ('float', 'double'):
MemberType = ir.DoubleType()
elif node.value.func.id == 'char':
# char() 是内建 i8 强转(与 float()/double() 并列)
MemberType = ir.IntType(8)
elif node.value.func.id in ('int', 'long', 'short'):
MemberType = ir.IntType(32)
elif isinstance(node.value.func, ast.Attribute):
if node.value.func.attr == 'len':
MemberType = ir.IntType(32)
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
if MemberType is None:
_src_f: str = getattr(Gen, '_current_source_file', '') or ''
_ln_f: int = getattr(node, 'lineno', 0) or 0
_col_f: int = getattr(node, 'col_offset', -1)
# 行号还原:AST 行号含 _inject_auto_imports 注入的 N 行
_off_f: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
if _off_f > 0 and _ln_f > _off_f:
_ln_f -= _off_f
_code_f: str = ''
try:
_code_f = (Gen._get_source_line(_ln_f) or '').strip()
except Exception:
_code_f = ''
_val_txt: str = ''
try:
_val_txt = ast.unparse(node.value) if node.value is not None else '<无值>'
except Exception:
_val_txt = '<无法反解析>'
_msg_f: str = (
f"类 '{ClassName}' 的字段 '{VarName}' 没有声明类型"
f"(既不在类体注解、也不在祖先中),且首次赋值 "
f"`self.{VarName} = {_val_txt}` 的取值形态推不出成员类型"
f"——拒绝静默当成 i32(那会把错误推到下游:写指针值时报"
f"「指针隐式转整数已禁止」)。请在类体声明该字段并给注解"
f"(如 `{VarName}: t.CInt32T` / `{VarName}: t.CPtr`),"
f"或在赋值处给出可定型的取值。"
f"位置 {_src_f}:{_ln_f}:{_col_f}")
_vlog().compile_error(_msg_f, _src_f, _ln_f, _code_f)
sys.exit(1)
Gen.class_members[ClassName].append((VarName, MemberType))
Gen.class_member_signeds[ClassName][VarName] = None
Gen.class_member_bitfields[ClassName][VarName] = 0
Gen.class_member_byteorders[ClassName][VarName] = ""
Gen.class_member_bitoffsets[ClassName][VarName] = 0
existing_members.add(VarName)
for child in ast.iter_child_nodes(node):
if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
continue
self._scan_node_for_self_assign(child, ClassName, existing_members, Gen)
def _EmitUnionLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> None:
ClassName: str = Node.name
union_member_types: list[ir.Type] = []
for item in Node.body:
if isinstance(item, ast.ClassDef):
nested_class_name: str = item.name
nested_member_types: list[ir.Type] = []
for nested_item in item.body:
parsed = self.CollectAnnAssignMember(nested_item, Gen)
if parsed:
nested_member_types.append(parsed[1])
if nested_member_types:
nested_struct_type: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, f"{ClassName}_{nested_class_name}")
nested_struct_type.set_body(*nested_member_types)
Gen.structs[f"{ClassName}_{nested_class_name}"] = nested_struct_type
union_member_types.append(nested_struct_type)
nested_class_full_name: str = f"{ClassName}_{nested_class_name}"
if nested_class_full_name not in Gen.class_members:
Gen.class_members[nested_class_full_name] = []
if nested_class_full_name not in Gen.class_member_signeds:
Gen.class_member_signeds[nested_class_full_name] = {}
for nested_item in item.body:
parsed = self.CollectAnnAssignMember(nested_item, Gen)
if parsed:
VarName, MemberType, TypeInfo = parsed
Gen.class_members[nested_class_full_name].append((VarName, MemberType))
if TypeInfo and hasattr(TypeInfo.BaseType, 'IsSigned'):
Gen.class_member_signeds[nested_class_full_name][VarName] = TypeInfo.BaseType.IsSigned
else:
Gen.class_member_signeds[nested_class_full_name][VarName] = None
if union_member_types:
max_size: int = 0
for member_type in union_member_types:
# 统一走 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))
Gen.structs[ClassName] = union_type
UnionNode: CTypeInfo = CTypeInfo()
UnionNode.Name = ClassName
UnionNode.IsUnion = True
self.Trans.SymbolTable.insert(ClassName, UnionNode)
def _EmitREnumLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> None:
ClassName: str = Node.name
variant_info: list[tuple] = []
variant_names: list[str] = []
# 预注册 REnum opaque 结构体到 Gen.structs 和 SymbolTable,
# 使自引用成员(如 LLVMType | t.CPtr)在收集成员时能正确解析为 %LLVMType*。
# 若不预注册,GetCTypeInfo 找不到 LLVMType → 回退到 IsEnum → i32 → | t.CPtr → i32*(错误)。
PreRenumType: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, ClassName)
Gen.structs[ClassName] = PreRenumType
PreTypeInfo: CTypeInfo = CTypeInfo()
PreTypeInfo.Name = ClassName
PreTypeInfo.BaseType = t.REnum(ClassName)
PreTypeInfo.IsRenum = True
PreTypeInfo.IsEnum = True
# REnum 在内存布局上是 struct({ i32 __tag, <max_payload> }),
# 必须设置 IsStruct=True 才能让 HandlesTypeMerge 的 CPtr 吸收逻辑生效:
# `REnumType | t.CPtr` 应得到 REnumType*(PtrCount=1)而非 REnumType**(PtrCount=2)。
# 否则 include-imported 函数返回类型会出现双指针,与声明单指针不匹配 → 运行时崩溃。
PreTypeInfo.IsStruct = True
PreTypeInfo.RenumVariants = []
self.Trans.SymbolTable.insert(ClassName, PreTypeInfo)
# 第一遍:收集显式赋值的 tag 值,确定起始索引
explicit_values: dict[str, int] = {}
for item in Node.body:
if isinstance(item, ast.Assign):
for target in item.targets:
if isinstance(target, ast.Name):
if item.value and isinstance(item.value, ast.Constant):
explicit_values[target.id] = item.value.value
# 类变体从 max(explicit_values, default=-1) + 1 开始
next_class_tag: int = max(explicit_values.values()) + 1 if explicit_values else 0
variant_index: int = 0
for item in Node.body:
if isinstance(item, ast.ClassDef):
VariantName: str = item.name
variant_names.append(VariantName)
member_types: list[ir.Type] = []
member_names: list[str] = []
member_annotations: list[ast.AST | None] = []
for nested_item in item.body:
parsed = self.CollectAnnAssignMember(nested_item, Gen)
if parsed:
VarName, MemberType, TypeInfo = parsed
member_types.append(MemberType)
member_names.append(VarName)
member_annotations.append(nested_item.annotation if TypeInfo else None)
# 类变体使用专用计数器,避免与显式值冲突
tag: int = next_class_tag
next_class_tag += 1
variant_info.append((VariantName, member_types, member_names, member_annotations, item.lineno, tag))
elif isinstance(item, ast.Assign):
for target in item.targets:
if isinstance(target, ast.Name):
VarName = target.id
variant_names.append(VarName)
value: int | None = None
if item.value and isinstance(item.value, ast.Constant):
value = item.value.value
else:
value = variant_index
variant_index += 1
variant_info.append((VarName, [], [], [], item.lineno, value))
# REnum 布局是所有变体的联合体。每个字段位置必须能容纳任何变体在该位置的字段。
# Bug 修复:原来只选总尺寸最大的变体,但不同变体在同一位置可能有不同尺寸的字段。
# 例如 MetaType: Concrete 位置3是 i32(4字节),但 Var 位置3是 MetaType*(8字节)。
# 用 Concrete 的布局会导致 Var.Instance 指针被截断为4字节 → 运行时崩溃。
# 修复:按每个位置的 max 字段尺寸构建布局。
def _ir_type_size(t: ir.Type) -> int:
if isinstance(t, ir.IntType):
return max(4, t.width // 8)
elif isinstance(t, ir.PointerType):
return 8
elif isinstance(t, ir.FloatType):
return 4
elif isinstance(t, ir.DoubleType):
return 8
elif isinstance(t, ir.ArrayType):
if isinstance(t.element, ir.IntType):
return t.element.width // 8 * t.count
return 8 * t.count
else:
return 8
all_variant_types: list[list[ir.Type]] = []
max_field_count: int = 0
for _, member_types, _, _, _, _ in variant_info:
if member_types:
full_types: list[ir.Type] = [ir.IntType(32)] + member_types
all_variant_types.append(full_types)
if len(full_types) > max_field_count:
max_field_count = len(full_types)
max_variant_struct: list[ir.Type] = []
for pos in range(max_field_count):
pos_max_size: int = 0
for vt in all_variant_types:
if pos < len(vt):
sz: int = _ir_type_size(vt[pos])
if sz > pos_max_size:
pos_max_size = sz
# 按最大尺寸选择 LLVM 类型:<=4 用 i32,>4 用 i64(8字节,可容纳指针)
if pos_max_size <= 4:
max_variant_struct.append(ir.IntType(32))
else:
max_variant_struct.append(ir.IntType(64))
if not max_variant_struct:
max_variant_struct = [ir.IntType(32), ir.IntType(32)]
# 复用预注册的 opaque 结构体,设置 body 使其完整
PreRenumType.set_body(*max_variant_struct)
for VariantName, member_types, member_names, member_annotations, lineno, tag_value in variant_info:
NestedStructName: str = f"{ClassName}_{VariantName}"
if member_types:
padded_member_types: list[ir.Type] = list(max_variant_struct)
nested_struct_type: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, NestedStructName)
nested_struct_type.set_body(*padded_member_types)
Gen.structs[NestedStructName] = nested_struct_type
if NestedStructName not in Gen.class_members:
Gen.class_members[NestedStructName] = []
if NestedStructName not in Gen.class_member_signeds:
Gen.class_member_signeds[NestedStructName] = {}
Gen.class_members[NestedStructName].append(('__tag', ir.IntType(32)))
Gen.class_member_signeds[NestedStructName]['__tag'] = None
for i, (mname, mtype) in enumerate(zip(member_names, member_types)):
Gen.class_members[NestedStructName].append((mname, mtype))
try:
ti: CTypeInfo | None = CTypeInfo.FromNode(member_annotations[i], self.Trans.SymbolTable)
if ti and hasattr(ti.BaseType, 'IsSigned'):
Gen.class_member_signeds[NestedStructName][mname] = ti.BaseType.IsSigned
else:
Gen.class_member_signeds[NestedStructName][mname] = None
except Exception as e: # 回退:签名信息解析失败时设为 None
_vlog().warning(f"HandlesClassDef: 忽略异常 {e}", exc_info=e)
Gen.class_member_signeds[NestedStructName][mname] = None
MemberNode: CTypeInfo = CTypeInfo()
MemberNode.Name = VariantName
MemberNode.BaseType = t.CEnum(ClassName)
MemberNode.value = tag_value
MemberNode.EnumName = ClassName
MemberNode.Lineno = lineno
MemberNode.IsEnumMember = True
self.Trans.SymbolTable.insert(VariantName, MemberNode)
# 同模块 REnum 变体也注册限定名和下划线名,
# 对齐跨模块导入(HandlesImports.py)的注册逻辑。
# 避免裸名被同名的工厂函数覆盖后,路径 2 的限定名查找失败。
self.Trans.SymbolTable.insert(f"{ClassName}.{VariantName}", MemberNode)
self.Trans.SymbolTable.insert(f"{ClassName}_{VariantName}", MemberNode)
# 更新预注册的 REnum 类型信息(填充变体列表)
PreTypeInfo.RenumVariants = variant_names
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
# 已登记成员名(用于"可见名数 vs 登记数"兜底判据,见 SymbolUtils.EnumBodyMemberNames)
RegisteredNames: list[str] = []
for item in Node.body:
if isinstance(item, ast.Assign):
for target in item.targets:
if isinstance(target, ast.Name):
VarName: str = target.id
value: int | str | None = None
if item.value:
if isinstance(item.value, ast.Constant):
value = item.value.value
next_enum_value = value + 1
elif isinstance(item.value, ast.UnaryOp) and isinstance(item.value.op, ast.USub):
if isinstance(item.value.operand, ast.Constant):
value = -item.value.operand.value
next_enum_value = value + 1
elif isinstance(item.value, ast.Name):
value = item.value.id
ref_info: CTypeInfo | None = self.Trans.SymbolTable.lookup(value)
if ref_info:
if ref_info.value is not None and isinstance(ref_info.value, int):
next_enum_value = ref_info.value + 1
else:
value = next_enum_value
next_enum_value += 1
MemberNode: CTypeInfo = CTypeInfo()
MemberNode.Name = VarName
MemberNode.BaseType = t.CEnum(ClassName)
MemberNode.value = value
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
))
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):
value = item.value.value
next_enum_value = value + 1
elif isinstance(item.value, ast.UnaryOp) and isinstance(item.value.op, ast.USub):
if isinstance(item.value.operand, ast.Constant):
value = -item.value.operand.value
next_enum_value = value + 1
elif isinstance(item.value, ast.Name):
value = item.value.id
ref_info: CTypeInfo | None = self.Trans.SymbolTable.lookup(value)
if ref_info:
if ref_info.value is not None and isinstance(ref_info.value, int):
next_enum_value = ref_info.value + 1
else:
value = next_enum_value
next_enum_value += 1
MemberNode: CTypeInfo = CTypeInfo()
MemberNode.Name = VarName
MemberNode.BaseType = t.CEnum(ClassName)
MemberNode.value = value
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__'
existing_func: ir.Function | None = Gen.functions.get(NewFuncName)
if existing_func and len(existing_func.blocks) > 0:
return
MangledName: str = Gen._mangle_name(NewFuncName)
StructType: ir.Type = Gen.structs[ClassName]
if isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0):
self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen)
StructType = Gen.structs.get(ClassName, StructType)
StructPtrType: ir.PointerType = ir.PointerType(StructType)
FuncType: ir.FunctionType = ir.FunctionType(ir.VoidType(), [StructPtrType])
if existing_func:
func: ir.Function = existing_func
else:
func = Gen._get_or_declare_function(MangledName, FuncType)
# 泛型特化类(ClassName 含 '[',如 GSList[NameEntry])的 __before_init__
# 在多模块各自特化生成同名定义(external linkage)→ ld.lld duplicate
# symbol warning。__before_init__ 由相同模板+类型参数合成,定义相同,
# 设 linkonce_odr 让链接器合并重复定义。
if '[' in ClassName:
func.linkage = 'linkonce_odr'
Gen.functions[NewFuncName] = func
EntryBlock: ir.Block = func.append_basic_block(name="entry")
saved_builder: ir.IRBuilder = Gen.builder
saved_func: ir.Function = Gen.func
Gen.builder = Gen._NewIrBuilder(EntryBlock)
Gen.func = func
StructPtr: ir.Argument = func.args[0]
base: int = 1 if IsCVTable else 0
if IsCVTable and ClassName in Gen.Vtables:
VtablePtr: ir.Value = Gen.builder.gep(StructPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="vtable_slot")
VtableAddr: ir.Value = Gen.builder.bitcast(Gen.Vtables[ClassName], ir.PointerType(ir.IntType(8)), name="vtable_addr")
Gen.builder.store(VtableAddr, VtablePtr)
members: list[tuple] = Gen.class_members.get(ClassName, [])
defaults: dict = Gen.class_member_defaults.get(ClassName, {})
struct_elems: tuple = StructType.elements if hasattr(StructType, 'elements') and StructType.elements is not None else ()
for i, (member_name, member_type) in enumerate(members):
elem_idx: int = base + i
if len(struct_elems) > 0 and elem_idx >= len(struct_elems):
break
ElemPtr: ir.Value = Gen.builder.gep(StructPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), elem_idx)], name=f"{ClassName}_{member_name}")
if member_name in defaults:
try:
val: ir.Constant = defaults[member_name]
if not isinstance(val, ir.Constant):
val = ir.Constant(ElemPtr.type.pointee, val)
Gen.builder.store(val, ElemPtr)
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
else:
try:
ZeroVal: ir.Constant = ir.Constant(ElemPtr.type.pointee, None)
Gen.builder.store(ZeroVal, ElemPtr)
except Exception:
pass
Gen.builder.ret_void()
Gen.builder = saved_builder
Gen.func = saved_func
def _PreRegisterClassMembers(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> None:
"""兜底:把一个类的**自有字段**从 ClassDef 体收集进 `Gen.class_members[类名]`。
调用点:[FuncGen._get_member_offset] 的"struct body 已存在、而
`class_members[类名]` 为空"分支——该类的成员表在本次翻译里从未被收集
(时序/跨模块窗口),而按名定位字段槽位必须有表。
此前本方法**全仓无定义**(调用点却存在)⇒ 一旦进入即
`AttributeError: 'ClassHandle' object has no attribute '_PreRegisterClassMembers'`
(现场:翻译 TransPyV/App/lib/core/Gen/Call/CallGenLeafKit.vp 终止编译)。
判据与实现刻意与既有两处成员收集段同源:
- `_EmitClassLlvm` 的自有字段段(`CollectAnnAssignMember` → 追加 `(名, 类型)`);
- `HandlesImports._EmitExternalClassDeclLlvm` 的同名段。
但**只做收集**:
- 不展平继承前缀——调用方随后经 `_member_base` →
`EnsureClassMembersFlattened` 按父类补齐(表空且有父类时会被前置);
- 不调用 `_generate_structs()`:body 已存在,重入 `set_body` 会把残缺布局
永久固化(llvmlite 的 `set_body` 只能设一次,见
HandlesImports._EmitExternalClassDeclLlvm 的 `_collecting_members_set` 说明);
- 只追加**尚无同名**的条目(幂等:重复命中不会把表撑长,也就不会让
`base + 表内下标` 整体后移)。
"""
if Node is None or Gen is None:
return
ClassName: str = getattr(Node, 'name', None)
if not ClassName:
return
if ClassName not in Gen.class_members:
Gen.class_members[ClassName] = []
# 【防重 · 实时判据(2026-09-30)】原实现循环前快照一次名字集合,循环内只更新
# 自己追加的项;而 CollectAnnAssignMember 每轮都可能重入并**清空重填**本类表
# (泛型特化 → _generate_structs → _TryLoadClassMembersFromPyi(force=True) →
# HandlesImports.py:3415)⇒ 快照失效后会重复追加同一字段。改为每轮实时读表,
# 判据语义("名字已在表里就不追加")与改前逐字相同。
Gen._collecting_members_set.add(ClassName)
try:
for item in Node.body:
# 编译期元数据字段(__provides__/__requires__/__require_must__)不是布局槽位
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
if item.target.id in ('__provides__', '__requires__', '__require_must__'):
continue
parsed = self.CollectAnnAssignMember(item, Gen)
if not parsed:
continue
VarName, MemberType, TypeInfo = parsed
if VarName in {n for n, _ in Gen.class_members[ClassName]}:
continue
Gen.class_members[ClassName].append((VarName, MemberType))
Gen.RegisterFieldQualifiers(ClassName, VarName, TypeInfo, item.annotation)
_Signeds: dict = getattr(Gen, 'class_member_signeds', {}).get(ClassName)
if isinstance(_Signeds, dict):
_Signeds[VarName] = None
_BitFields: dict = getattr(Gen, 'class_member_bitfields', {}).get(ClassName)
if isinstance(_BitFields, dict):
_BitFields[VarName] = 0
finally:
Gen._collecting_members_set.discard(ClassName)
def _BuildScalarConstant(self, node: ast.AST, llvm_type: ir.Type, Gen: LlvmGeneratorMixin | None = None) -> ir.Constant | None:
"""构建标量常量,委托给统一的 _BuildScalarConstantUnified 方法。"""
if Gen is None:
Gen = self.Trans.LlvmGen
return self.Trans._BuildScalarConstantUnified(node, llvm_type, Gen)
def _BuildStructConstant(self, call_node: ast.Call, StructName: str, Gen: LlvmGeneratorMixin) -> ir.Constant | None:
struct_type: ir.Type = Gen.structs[StructName]
members: list[tuple] = Gen.class_members.get(StructName, [])
defaults: dict = Gen.class_member_defaults.get(StructName, {})
member_values: list[ir.Constant] = []
for member_name, member_type in members:
if member_name in defaults:
val: ir.Constant = defaults[member_name]
if not isinstance(val, ir.Constant):
try:
val = ir.Constant(member_type, val)
except Exception: # 回退:常量转换失败时使用零值
val = ir.Constant(member_type, 0)
member_values.append(val)
else:
try:
if isinstance(member_type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.PointerType, ir.ArrayType)):
member_values.append(ir.Constant(member_type, None))
else:
member_values.append(ir.Constant(member_type, ir.Undefined))
except Exception: # 回退:常量创建失败时使用零值
member_values.append(ir.Constant(member_type, 0))
if call_node.args:
for i, arg in enumerate(call_node.args):
if i < len(members):
_, member_type = members[i]
const: ir.Constant | None = self._BuildScalarConstant(arg, member_type, Gen)
if const is not None:
member_values[i] = const
try:
return ir.Constant(struct_type, member_values)
except Exception: # 回退:结构体常量创建失败
return None