Files
ViperC/App/lib/core/Gen/Class/ClassGenDef.vp
T
TermiNexus 15b416b527 fix(type): 统一注入期类归属与导入上下文记账,修占位表爆与属性标记误解析
- StubMergerStore 新增共享导入上下文记账(AppendImportedModule / AppendFromImport /
  ImportCtx / AllocImportCtx / NotePlainImport),ImportsGenMixin 与 VpiReader 共用同一份
  口径;顺带移除 add_from_import 的 256 缓冲静默截断,VpiReader 删除两份私有副本

- 注入窗口按「名字归属」判据解析(is_module_class + CurDefModuleSha1):
  _forward_declare_ptr_from_node 的 Name 分支、resolve_annotation_type 的 Name/Attribute
  分支均补齐,使同一 stub 内先声明/先引用的类产出**具名占位**而不是 Ptr(i8),并新增
  _is_tattr_marker 让 t.attr.* 属性标记不参与类型联合竞争

- VpiReader._inject_class_struct_declare:命中前向声明占位时不再早退,改为「占位 Ty
  原地写入」(对齐 ClassGenDef.fill_fwd),修 StructTable 爆表(table full, cannot
  register Translator)

- 清除全部一次性探针:[DBG-GEN](VpiReader / StructGenMixin / ClassGenDef)、
  [DBG-ATTR](ExprGenAttr)、[MODSHA1](CallGenMixin)

根因记录:vpi 注入期漏记 `import X as Y -> from_imports "Y:X"`,使 GenTypes.VarEntry.Spec
(TSpec.TypeSpec | t.CPtr)取不到模块 SHA1、退化为 i8* 并被永久冻结
(CallGenCtor.vp:897 .PtrLevel 属性访问失败)。
2026-10-02 17:29:38 +08:00

753 lines
47 KiB
Plaintext
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.
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.core.CompilerFactory as CompilerFactory
from . import ClassGenBase
# ============================================================
# ClassGenDef - ClassGenMixin 拆分(阶段 D):translate_class_def 主入口
# ============================================================
novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
"""translate_class_def 主入口"""
# ============================================================
# translate_class_def — 翻译 class 定义(簇 D)
# ============================================================
def translate_class_def(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""翻译 class 定义,返回 0"""
if node is None or trans is None: return 0
cd: ast.ClassDef* = ast.ClassDef*(node)
if cd is None or cd.name is None: return 0
pool: memhub.MemBuddy* = trans.Pool
class_name: str = cd.name
if trans.ModuleSha1 is None:
# fail-fast:翻译类时模块 SHA1 缺失(命名空间隔离依赖它)——
VLogger.error("翻译类时 ModuleSha1 缺失", "CLASS")
self.Gen.TypeGenErr.fatal_error(cd, "翻译类时 ModuleSha1 缺失", "TypeError")
# 命名空间隔离:标记为当前文件可见(必须在 existing 检查之前,
# 因为 Phase B 时结构体已注册会提前 return,否则永远无法标记本地类)
self.Gen.StructGen.add_visible_struct(pool, class_name)
# 登记"本模块声明了这个类"(键 "{SHA1}.{类名}",与 struct 注册状态无关):
# 泛型类型实参命名规范化(TypeGenResolve._norm_type_arg_name 规则 5.5)据此
# 判定裸类名归属 —— 类体里的**自引用**(如 includes/ast/base.vp
# `AST.children: list[rawptr[AST]]`)解析时本类尚未 register_struct,
# 只查注册表会留在裸名 ⇒ 与后续带 SHA1 的形式分裂成两个特化名。
self.Gen.StructGen.mark_module_class(pool, trans.ModuleSha1, class_name)
# 枚举类:交给 HandlesEnum 处理
# 分派歧义检测(fail-fast):REnum 优先于 CEnum/CUnion。若某类同时被判为枚举,
# 说明被 `_is_enum_class` 抢先截胡 → 走 _translate_enum_def ⇒ 变体表永不注册
# ⇒ _is_renum_class_name 恒 0、变体构造拿不到 tag。两者语义互斥,禁止静默按枚举处理。
if self.Gen.ClassGenBase._is_enum_class(cd, trans) == 1:
if self.Gen.ClassGenBase._is_renum_class(cd, trans) == 1:
self.Gen.TypeGenErr.fatal_error(
cd,
f"类 '{class_name}' 同时被判定为 CEnum 与 REnum(分派歧义),"
f"REnum 变体表将无法注册",
"TypeError")
return self.Gen.ClassGenEnum._translate_enum_def(trans, cd)
# 联合体类:交给联合体处理
if self.Gen.ClassGenBase._is_union_class(cd, trans) == 1:
return self.Gen.ClassGenEnum._translate_union_def(trans, cd)
# REnum 类:交给 REnum 处理({i32 tag, max_payload} 布局)
if self.Gen.ClassGenBase._is_renum_class(cd, trans) == 1:
return self.Gen.ClassGenEnum._translate_renum_def(trans, cd)
# 泛型类:存储为模板,不发射 IR(等实例化时特化)
# 用局部变量接收 cd.type_params,触发类型推断
tp_val: list[str]* = cd.type_params
if self.Gen.ClassGenGeneric._is_generic_class(tp_val) == 1:
self.Gen.ClassGenGeneric._register_generic_template(pool, cd, trans.ModuleSha1)
return 0
# 异常类(try-except):注册编码+继承树,不生成 struct
# (对齐 TPC HandlesClassDef.py L509-511:_is_exception_class →
# _RegisterExceptionClass → return,异常类不编译为结构体)
if self.Gen.ClassGenBase._is_exception_class_def(cd) == 1:
return self.Gen.ClassGenBase._register_exception_class_def(trans, cd)
# 嵌套类 / 嵌套 struct:类体里再写 class / struct 此前被**静默丢弃**
# (字段循环只认 AnnAssign、_translate_oop_methods 只认 FunctionDef),
# 现按既有扁平命名先例发射为独立结构体 `{外类}_{内类}`(与 TPC 的
# union/renum 嵌套命名 `{ClassName}_{Nested}` 同源)。
# 放在此处(enum/union/renum/泛型/异常分派之后、existing 检查之前):
# 位于**所有提前 return 之前**,故 declare_only=1/0 两条路径都会递归;
# 而 REnum/union 的嵌套类已由各自分支按"变体"处理并提前 return,不会重复发射。
self._translate_nested_classdefs(trans, cd)
# 检查是否已注册(用 SHA1 区分跨模块同名类)
existing: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(class_name, trans.ModuleSha1)
# 空类注册不完整检测:existing FieldCount==0 但源类体声明了字段(vpi 注入/注册缺陷)。
# 标记 _deps_pending 延迟会让空 entry 持久存在、多遍死锁不收敛 ⇒ 改为中和旧 entry
# (置空 Name + Ty=None,使 find_struct_by_module 不再命中),走下方完整注册路径重建。
needs_rereg: int = 0
# 前向声明占位命中:本类此前被"指向未注册类的指针"注解按名字前向声明过
# (StructGenMixin.ensure_forward_class 建了 {sha}.Class 不透明占位)。
# 此处**不早退**,走下方"原位填充"分支——把字段体写进同一个 Ty 对象,
# 保住指针身份,使所有已发出的 Ptr(占位.Ty) 引用(如 CallGenBase.Gen)
# 在填充后自动指向完整定义(对齐 TPC 的 opaque 占位 + set_body 填充)。
fill_fwd: int = 0
if existing is not None:
if existing.IsForwardDecl != 0:
fill_fwd = 1
elif existing.FieldCount == 0 and trans._declare_only == 0:
if self.Gen.ClassGenBase._class_body_has_field(cd) != 0:
if existing.Name is not None:
existing.Name[0] = '\0'
existing.Ty = None
existing.FieldCount = 0
needs_rereg = 1
if existing is not None and needs_rereg == 0 and fill_fwd == 0:
# 结构体已注册(Phase A-pre 多遍扫描或 Phase B 重新翻译),但仍需在当前模块中翻译方法
if existing.Ty is not None:
# Phase 1b(declare_only=0):existing.Ty 是 Phase 1a 注册的 struct_ty,
# 其 Name(如 "sha1.ClassName")指向 Phase 1a 的 pool 内存。虽然 mark1
# 之前分配不被 reset 回收,但 struct_ty 会被后续文件翻译引用(vtable/
# self 参数类型打印),部分实现下 pool 块被复用导致 Name 悬空崩溃。
# 保险起见:深拷贝 Name 到 stdlib,确保跨文件永久存活。
if trans._declare_only == 0:
# 深拷贝整个 existing.Ty 到 stdlib:1a pool Ty 跨 1b reset 悬空,
# _ensure_field_types_in_module 遍历悬空字段 ParamNode 链表
# 读悬空挂起(G3 LinkedNode.prepend 挂起根因)。DeepCopyType
# 递归深拷贝字段链表/子类型/Name 到 stdlib(深度上限+链守卫)。
ty_deep: llvmlite.LLVMType* = llvmlite.DeepCopyType(CompilerFactory.factory.PermPool, existing.Ty)
if ty_deep is not None:
existing.Ty = ty_deep
old_name: str = llvmlite.get_struct_name(existing.Ty)
if old_name is not None:
on_len: sizet = string.strlen(old_name)
on_copy: str = CompilerFactory.factory.PermPool.alloc(on_len + 1)
if on_copy is not None:
string.strcpy(on_copy, old_name)
# in-place 覆盖所指 REnum 值(保留 existing.Ty 的指针同一性:
# Phase A 已把它登记进 named-type 表,换指针旧条目仍指向悬空
# Name)。REnum 变体构造返回 **boxed 指针**,`*槽 = <boxed 变体>`
# 属「构造物化」(boxed 指针 → 值槽的整对象拷贝,语义即
# load+store),按严格规则③的构造物化例外放行——与
# c.DerefAs(ptr, <boxed 变体>) 等价,用 * 写法更直白。
*existing.Ty = llvmlite.LLVMType.Struct(llvmlite.get_struct_fields(existing.Ty), llvmlite.get_struct_fcount(existing.Ty), on_copy)
# Phase B: 将已存在的命名结构体注册到当前模块(输出类型定义行)
llvmlite.module_add_named_type(trans.Module, pool, existing.Ty)
# B5:existing 路径(Phase B 已注册类)同样需设置 packed/aligned——
# Phase A 注册时虽已设置,但 Phase B 重新 add_named_type 后节点
# IsPacked=0,打印输出 `{...}`(idt_entry packed 未生效根因)。
if self.Gen.ClassGenBase._class_has_tattr(cd.decorator_list, "packed") != 0:
llvmlite.module_set_named_type_packed(trans.Module, pool, existing.Ty, 1)
# StructTable 每 Phase 可能重建(existing 是 Phase B 新 entry),
# 必须补设 IsPacked——否则跨模块 stub 注入(ensure_struct_def
# _in_module)按 entry.IsPacked 判定 packed 时读 0 → stub 非 packed。
existing.IsPacked = 1
align_ex: int = self.Gen.ClassGenBase._class_aligned_value(cd.decorator_list)
if align_ex > 0 and existing.Align == 0:
existing.Align = align_ex
# Phase A-pre(declare_only=1)多遍扫描时,类已注册但不应翻译方法体
# Phase B(declare_only=0)时全量翻译方法体
if trans._declare_only == 1:
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, existing.Ty, class_name, 1)
else:
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, existing.Ty, class_name)
# SHA1 已在 register_struct 时设置,无需再补
return 0
# ============================================================
# 触发 base 中 Subscript 的泛型特化(如 GSListNode[Value])
# 必须在 _detect_vtable_status 和父类字段继承之前调用,
# 否则 find_struct_by_module("GSListNode[Value]") 返回 None
# ============================================================
self.Gen.ClassGenBase._trigger_base_specialization(trans, cd)
# ============================================================
# VTable 检测:判断是否启用虚表
# ============================================================
has_vtable: int = self.Gen.ClassGenBase._detect_vtable_status(cd, trans)
parent_name: str = self.Gen.ClassGenBase._get_parent_class(cd, trans)
# 多继承:收集所有真实父类名(跳过 marker base),供字段展平/vtable/MRO 使用
parents_buf: sizet* = pool.alloc(8 * GenTypes.PARENT_MAX)
parents_count: int = 0
if parents_buf is not None:
parents_count = self.Gen.ClassGenBase._collect_parents(pool, cd, parents_buf, GenTypes.PARENT_MAX, trans)
is_novtable_deco: int = 0
if cd.decorator_list is not None:
if self.Gen.ClassGenBase._has_decorator(cd.decorator_list, "NoVTable") == 1:
is_novtable_deco = 1
# ============================================================
# PhaseA 预注册延迟检查:父类未注册时延迟注册当前类
#
# 根因:AssignHandle(HandlesBase.Mixin) 有自己的字段 _CurrentClass,
# 当 Mixin 未注册时(字母序:HandlesAssign.py 在 HandlesBase.py 之前),
# field_count=1(不是0),field_count==0 静默返回不触发,
# AssignHandle 被注册为只有 _CurrentClass 字段(缺 Trans)。
# 后续遍 existing is not None,不补字段 → attribute ptr is None: self.Trans
#
# 修复:declare_only==1 且父类未注册时,延迟注册到下一遍。
# 多遍 PhaseA-pre 会在后续遍中重试(父类已注册后完整注册)。
# Phase B(declare_only==0)不延迟,避免死锁。
# ============================================================
if trans._declare_only == 1 and parent_name is not None:
parent_check: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(parent_name, trans.ModuleSha1)
if parent_check is None:
parent_check = self.Gen.StructGen.find_struct(parent_name)
if parent_check is None:
return 0
# ============================================================
# 1. 遍历 class body,收集字段信息
# ============================================================
# 用 CSizeT 数组存储指针值(64 位系统上 8 字节)
field_names_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
field_types_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
field_defaults_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
field_annot_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
# 字段 volatile 标记缓冲(1=字段注解联合含 t.CVolatile);与 field_annot_buf
# 并行,取值 0/1(sizet 数组复用,避免另设类型)。
field_volatile_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
# 字段「所指物 volatile」标记缓冲(1=字段注解为 rawptr[t.CVolatile | T],
# 等价 C 的 volatile T*);与 field_volatile_buf 并行,取值 0/1。
field_pointee_volatile_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
# 字段 const 对象标记缓冲(1=字段注解含 t.CConst 且字段类型非指针);
# 与 field_volatile_buf 并行,取值 0/1。
field_const_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
if field_names_buf is None or field_types_buf is None or field_defaults_buf is None:
return 0
if field_annot_buf is None or field_volatile_buf is None:
return 0
if field_pointee_volatile_buf is None or field_const_buf is None:
return 0
string.memset(field_names_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_types_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_defaults_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_annot_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_volatile_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_pointee_volatile_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_const_buf, 0, 8 * GenTypes.FIELD_MAX)
field_count: int = 0
# ============================================================
# 1.5 继承父类字段(字段展平)
# ============================================================
#
# 父类字段排在子类字段之前,结构体布局:
# [vtable_ptr?, parent_field1, ..., child_field1, ...]
# 跳过父类的 __vtable__ 字段(子类会有自己的 vtable 指针)
# ============================================================
# 多继承字段展平:遍历所有父类(声明顺序),字段名去重(先声明优先)
if parents_count > 0 and parents_buf is not None:
for pi in range(parents_count):
pname_addr: sizet = parents_buf[pi]
if pname_addr == 0: continue
pname: str = rawptr(pname_addr)
if pname is None: continue
# 用 SHA1 感知查找父类,规避跨模块同名找错
parent_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(pname, trans.ModuleSha1)
if parent_entry is None:
# 父类可能定义在另一个模块(不同 SHA1,如 HandlesBase.Mixin),
# find_struct_by_module 不回退全局查找,此处手动回退到 find_struct,
# 否则子类无法继承父类字段 → field_count==0 → __sizeof__ 失败
parent_entry = self.Gen.StructGen.find_struct(pname)
if parent_entry is None:
# 父类尚未注册(Phase A 按文件序,子类模块可能先于父类模块):此时
# **不能**继续注册——StructTable 在 StdlibAlloc=1 时用 perm_pool 分配,
# 残缺条目跨 pass 存活,后续 pass 的 existing 早退永远无法修复 → 子类
# __init__ 写父类字段每遍永久失败。整类 defer(不注册);模块级变量/函数
# 参数解析失败静默降级 i32,不致命。
return 0
pfc: int = parent_entry.FieldCount
for pfi in range(pfc):
pfe: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(parent_entry, pfi)
if pfe is None: continue
pfn: str = self.Gen.StructGen.get_field_name_ptr(pfe)
if pfn is None: continue
# 跳过父类的 vtable 指针字段
if string.strcmp(pfn, "__vtable__") == 0: continue
# 多继承去重:字段名已存在跳过(先声明父类优先)
if self.Gen.ClassGenBase._field_name_exists(field_names_buf, field_count, pfn) != 0: continue
if field_count < GenTypes.FIELD_MAX:
pft: llvmlite.LLVMType* = self.Gen.StructGen.get_field_type_ptr(pfe)
pfd: ast.AST* = self.Gen.StructGen.get_field_default_ptr(pfe)
pfa: str = self.Gen.StructGen.get_field_annot_class_name(pfe)
field_names_buf[field_count] = sizet(pfn)
field_types_buf[field_count] = sizet(pft)
if pfd is not None:
field_defaults_buf[field_count] = sizet(pfd)
else:
field_defaults_buf[field_count] = 0
if pfa is not None:
stored_val: sizet = sizet(pfa)
field_annot_buf[field_count] = stored_val
else:
field_annot_buf[field_count] = 0
# 继承父类字段的 volatile 标记(字段展平后语义不变)
field_volatile_buf[field_count] = sizet(pfe.IsVolatile)
# 继承父类字段的「所指物 volatile」标记(展平后语义不变)
field_pointee_volatile_buf[field_count] = sizet(pfe.PointeeVolatile)
# 继承父类字段的 const 对象标记(展平后语义不变)
field_const_buf[field_count] = sizet(pfe.IsConst)
field_count += 1
children: list[ast.AST*]* = cd.children
if children is None:
return 0
cn: sizet = len(children)
for ci in range(cn):
stmt: ast.AST* = children.get(ci)
if stmt is None: continue
sk: int = stmt.kind()
if sk != ast.ASTKind.AnnAssign: continue
# AnnAssign(target=Name(id), annotation=type, value=...)
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
if aa is None or aa.target is None: continue
# 获取字段名
if aa.target.kind() != ast.ASTKind.Name: continue
tgt: ast.Name* = ast.Name*(aa.target)
if tgt.id is None: continue
# 类体 CDefine 常量(任意位置,2026-08-10):注册到全局 CDefine 表。
# 方法内裸名(如 ISINSTANCE_CLASS_MAX)经 translate_name_value 查
# CDefine 表解析为常量值;类体开头的常量(MRO_MAX 等)由早期预注册
# 路径注册,但方法之后的 AnnAssign 不经过那些路径 → 此处统一兜底注册。
if self.Gen.AssignGen.is_cdefine_annotation(aa.annotation) != 0:
if aa.value is not None:
self.Gen.AssignGen._register_cdefine_from_value(pool, tgt.id, aa.value, aa.annotation)
# CDefine 字段类型固定 i32,直接收集后跳过通用注解解析:
# resolve_annotation_type 不识别 "CDefine"(map_t_type/CTypedef
# 表均无此名),类体尾部常量(ISINSTANCE_CLASS_MAX 是首个触发
# 该路径的实例)Phase B 会 NameError: 't.CDefine' is not defined。
# 与类头常量(MRO_MAX 等)的 i32 字段语义保持一致。
if field_count < GenTypes.FIELD_MAX:
cdef_ty_l: llvmlite.LLVMType* = llvmlite.Int32(pool)
if cdef_ty_l is not None:
field_names_buf[field_count] = sizet(tgt.id)
field_types_buf[field_count] = sizet(cdef_ty_l)
if aa.value is not None:
field_defaults_buf[field_count] = sizet(aa.value)
else:
field_defaults_buf[field_count] = 0
field_count += 1
continue
# 跳过编译期元数据字段(__provides__/__requires__/__require_must__)
# 这些字段只用于 with 上下文的静态可达性检查,不生成运行时代码
# (FakeDuck:_str.__requires__=['__mbuddy__'])。此处解析 list[str]
# 字面量注册到持久元数据表,供 requires 注入 / with provider 查询。
if string.strcmp(tgt.id, "__provides__") == 0:
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_PROVIDES, class_name, aa.value)
continue
if string.strcmp(tgt.id, "__requires__") == 0:
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_REQUIRES, class_name, aa.value)
continue
if string.strcmp(tgt.id, "__require_must__") == 0:
self.Gen.ClassGenBase._register_class_meta_list(pool, GenTypes.CLASS_META_REQUIRE_MUST, class_name, aa.value)
continue
# 解析字段类型
field_ty: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, aa.annotation, trans._imported_modules, trans._from_imports, trans, 0)
# void 不是合法字段类型(用户 2026-10-01:void 只能用于 `void*` 与
# `typedef 不透明结构体 = void`);`void*` 已在上游归并为 i8*,不会命中。
# 放任会按 LLVM void 建字段槽 ⇒ 结构体布局非法(拖到 llc 才暴露),
# 故此处编译期 fail-fast。
if field_ty is not None and self.Gen.StmtGenDecl._is_void_type(field_ty) != 0:
self.Gen.TypeGenErr.fatal_error(aa, f"字段 '{tgt.id}' 不能是 void 类型(void 只能用于 void* 与 typedef 别名)", "TypeError")
# 单参 t.CArray[elem] + 列表字面量 → 静态数组 [N x elem]
# (与局部 AnnAssign 路径一致,对齐 TPC ParseListAnnotation 语义):
# 否则字段保持 Ptr(elem)(如 t.CArray[str] → i8**),
# for x in self.field 会误走指针迭代(元素非整数 → null 终止符
# 检查失败报错)。推断为静态数组后走数组迭代(按长度 N)。
if field_ty is not None:
field_ty = self.Gen.AssignGen._infer_static_array_ty_from_literal(pool, aa.annotation, aa.value, field_ty)
if field_ty is None:
# BUG 5 方案1修改点1:消除 i32 静默回退
# Phase A-pre(declare_only=1):依赖的类可能尚未注册,延迟到下一遍
# Phase B(declare_only=0):真正的类型解析错误,fast-fail 终止该类翻译
# 不再用 i32 回退(会导致结构体布局错误 + GEP 索引错乱)
# 例外:泛型 Subscript(如 list[AST|t.CPtr])未特化时回退为 Ptr(i8),
# 因为泛型特化可能延迟到构造调用时。AnnotClassName 会携带 mangled name
# 供 translate_attribute 做后续 bitcast。
is_generic_sub: int = 0
if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Subscript:
# t.CArray[elem, N](含裸 CArray[elem, N])是定长值数组,非泛型堆对象——
# 元素类型未就绪时若回退 Ptr(i8) 会注册成退化字段布局,Phase B 复用该 entry
# 后 GEP 元素成 i8、字段访问/赋值全部失效。仅真正的泛型类(list[T]/Vector[T]
# 等堆对象)未特化才回退 Ptr(i8)(AnnotClassName 携带 mangled name 供后续恢复)。
sub_g: ast.Subscript* = ast.Subscript*(aa.annotation)
is_carray_sub_g: int = 0
if sub_g is not None and sub_g.value is not None:
if sub_g.value.kind() == ast.ASTKind.Attribute:
svg_at: ast.Attribute* = ast.Attribute*(sub_g.value)
if svg_at.attr is not None and string.strcmp(svg_at.attr, "CArray") == 0:
is_carray_sub_g = 1
elif sub_g.value.kind() == ast.ASTKind.Name:
svg_nm: ast.Name* = ast.Name*(sub_g.value)
if svg_nm.id is not None and (string.strcmp(svg_nm.id, "CArray") == 0 or string.strcmp(svg_nm.id, "rawarray") == 0 or string.strcmp(svg_nm.id, "array") == 0):
is_carray_sub_g = 1
if is_carray_sub_g == 0:
is_generic_sub = 1
if is_generic_sub != 0:
# 泛型未特化:回退为 Ptr(i8),依赖 AnnotClassName 做后续类型恢复
field_ty = llvmlite.Ptr(pool, llvmlite.Int8(pool))
else:
# Phase A-pre(declare_only=1):依赖的类可能尚未注册,
# 静默 return 0 让多遍收敛机制生效(下一遍依赖类已注册即可成功)
if trans._declare_only == 1:
return 0
# Phase A(declare_only=0,Phase2.vp L573 多遍):依赖模块按
# 文件序可能晚于本模块翻译(如 TypeGenMixin.vp G < HandlesType.vp
# H,TraceFrames 注解的 HandlesType.TraceFrame 未注册)→ 不在本遍
# 以残缺字段注册结构体。
return 0
# 旧行为:fast-fail NameError(已移除——Phase A 依赖未就绪时
# 误报真实错误,中断多遍收敛;真实错误交给收敛检查兜底)
# 存储到数组(指针转 CSizeT)
if field_count < GenTypes.FIELD_MAX:
diag_name_val: sizet = sizet(tgt.id)
field_names_buf[field_count] = diag_name_val
field_types_buf[field_count] = sizet(field_ty)
# 存储默认值 AST 指针(None=无默认值)
if aa.value is not None:
field_defaults_buf[field_count] = sizet(aa.value)
else:
field_defaults_buf[field_count] = 0
# 提取原始类型注解的类名(联合类型简化为 Ptr(i8) 时回退查找结构体)
# BUG 1 方案3:传 pool 使泛型 Subscript 返回 mangled name(如 "list[ast.AST|t.CPtr]")
if aa.annotation is not None:
annot_cn: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(aa.annotation, trans._imported_modules, pool, trans._from_imports)
if annot_cn is not None:
diag_cn_val: sizet = sizet(annot_cn)
field_annot_buf[field_count] = diag_cn_val
else:
field_annot_buf[field_count] = 0
# t.CVolatile 存储类标记 → 该字段经对象访问的 load/store 带
# volatile(如 Spinlock.locked: t.CVolatile | t.CInt)。
if self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "CVolatile", 0) != 0:
field_volatile_buf[field_count] = 1
else:
field_volatile_buf[field_count] = 0
# rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
# volatile T*)→ 经该字段解引用(self.ptr[i]/self.ptr.field)
# 的 load/store 带 volatile。
if self.Gen.TypeGenErr.has_pointee_volatile_marker(aa.annotation) != 0:
field_pointee_volatile_buf[field_count] = 1
else:
field_pointee_volatile_buf[field_count] = 0
# const 对象字段(注解含 t.CConst 且字段类型非指针)→ 经对象给该
# 字段赋值 fail-fast。判据与 TPC 的 Gen.field_const_flags 一致。
if self.Gen.TypeGenErr.is_const_object_marker(aa.annotation, field_ty) != 0:
field_const_buf[field_count] = 1
else:
field_const_buf[field_count] = 0
else:
field_annot_buf[field_count] = 0
field_volatile_buf[field_count] = 0
field_pointee_volatile_buf[field_count] = 0
field_const_buf[field_count] = 0
field_count += 1
if field_count == 0:
# PhaseA 预注册时,父类可能尚未注册(字母序单遍扫描),
# 导致子类无法继承父类字段 → field_count==0。
# 静默返回,Phase2.py 的多遍 PhaseA-pre 会在后续遍中重试。
if trans._declare_only == 1 and parent_name is not None:
return 0
# 无字段类(接口/标记类):不能整体跳过——类可能只有方法
# (如多继承中的 ShapeM/Vec2Extra),需注册类并翻译方法。
# 空 struct 在 LLVM 中合法({});@t.CVTable 类由下方补 vtable 指针字段。
# ============================================================
# 2. 创建 LLVM StructType
# ============================================================
# 构建 ParamNode 链表
first_node: rawptr[llvmlite.ParamNode] = None
prev_node: rawptr[llvmlite.ParamNode] = None
total_field_count: int = field_count
# 如果有虚表,首字段添加 i8* vtable 指针
if has_vtable == 1:
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
vtable_node: rawptr[llvmlite.ParamNode] = pool.alloc(llvmlite.ParamNode.__sizeof__())
if vtable_node is None:
VLogger.error(f"failed to alloc vtable node for {class_name}", "CLASS")
return 0
string.memset(vtable_node, 0, llvmlite.ParamNode.__sizeof__())
vtable_node.Ty = i8_ptr_ty
first_node = vtable_node
prev_node = vtable_node
total_field_count = field_count + 1
for fi in range(field_count):
fty_addr: sizet = field_types_buf[fi]
if fty_addr == 0:
continue
fty: llvmlite.LLVMType* = (llvmlite.LLVMType*)(fty_addr)
# 创建 ParamNode
pnode: rawptr[llvmlite.ParamNode] = pool.alloc(llvmlite.ParamNode.__sizeof__())
if pnode is None:
continue
string.memset(pnode, 0, llvmlite.ParamNode.__sizeof__())
pnode.Ty = fty
if first_node is None:
first_node = pnode
if prev_node is not None:
prev_node.Next = pnode
prev_node = pnode
# 构造命名结构体类型名: "sha1.ClassName" 或 "ClassName"
# 注意: Phase 1b(declare_only=0)时 struct_ty 会注册到全局表并被后续
# 文件翻译引用(vtable/self 参数类型打印),其 Name 必须跨 1b 每文件
# reset_to(mark1) 存活 → 用 stdlib 深拷贝;Phase 1a 仍用 pool(无需跨文件)。
type_name: str = class_name
if trans.ModuleSha1 is not None:
name_buf: str = pool.alloc(64)
if name_buf is not None:
viperlib.snprintf(name_buf, 64, "%s.%s", trans.ModuleSha1, class_name)
type_name = name_buf
if trans._declare_only == 0 and type_name is not None:
tn_len: sizet = string.strlen(type_name)
tn_copy: str = CompilerFactory.factory.PermPool.alloc(tn_len + 1)
if tn_copy is not None:
string.strcpy(tn_copy, type_name)
type_name = tn_copy
# 创建 StructType(命名结构体)
struct_ty: llvmlite.LLVMType* = llvmlite.Struct(pool, first_node, total_field_count, type_name)
if struct_ty is None:
VLogger.error(f"failed to create StructType for {class_name}", "CLASS")
return 0
# Phase 1b(declare_only=0)新注册的 struct_ty 会被注册到全局表并被后续文件
# 翻译引用(vtable/self 参数类型打印/find_struct_by_type 指针匹配)。LLVMType
# 对象和 Fields 链是 pool 分配的,本文件 reset_to(mark1) 后悬空。深拷贝整棵树
# 到 stdlib,并替换 struct_ty 指针,保证 entry.Ty 与调用方持有的 struct_ty
# 指向同一个 stdlib 拷贝(指针匹配不失效)且跨文件永久存活。
# StdlibAlloc=1(Phase2 mark2 后)也必须深拷贝:Phase A-pre-inc(declare_only=1)
# 预注册 includes 时若仍用 pool struct_ty,reset_to(mark2) 后 entry.Ty 悬空 →
# 后续文件字段类型引用(resolve → Ptr(entry.Ty))→ opaque 扫描遍历悬空 fields
# 崩溃(LoopTest _scan_type_for_cross_module_refs 0xFFFF 根因)。
if trans._declare_only == 0 or self.Gen.StructGen.get_stdlib_alloc() != 0:
st_copy: llvmlite.LLVMType* = llvmlite.DeepCopyType(CompilerFactory.factory.PermPool, struct_ty)
if st_copy is not None:
struct_ty = st_copy
type_name = llvmlite.get_struct_name(struct_ty)
# 前向声明占位命中:**原位填充**——保持 existing.Ty 指针身份不变,仅把刚构建
# 的字段体写进同一个类型对象(名称沿用占位的 "{sha}.Class")。此后所有已发出的
# Ptr(占位.Ty) 引用(如 CallGenBase.Gen)自动指向完整定义。这一步必须早于
# module_add_named_type:避免同一名字先以占位名注册、又以新对象重复注册。
if fill_fwd != 0 and existing is not None and existing.Ty is not None:
_fwd_name: str = llvmlite.get_struct_name(existing.Ty)
_fwd_fields: rawptr[llvmlite.ParamNode] = llvmlite.get_struct_fields(struct_ty)
_fwd_fc: int = llvmlite.get_struct_fcount(struct_ty)
*existing.Ty = llvmlite.LLVMType.Struct(_fwd_fields, _fwd_fc, _fwd_name)
struct_ty = existing.Ty
type_name = _fwd_name
existing.IsForwardDecl = 0
existing.FieldCount = 0
# 注册命名结构体到模块(输出 %"name" = type { ... } 定义行)
llvmlite.module_add_named_type(trans.Module, pool, struct_ty)
# B5:@c.Attribute(t.attr.packed) 打包结构体 → 类型定义输出 <{...}>
# (LLVM packed struct,无字段填充,GEP 按 packed 布局计算偏移)。
# idt/gdt/elf/fat32 等二进制格式结构体依赖它读写硬件/磁盘布局。
is_packed_cd: int = self.Gen.ClassGenBase._class_has_tattr(cd.decorator_list, "packed")
if is_packed_cd != 0:
llvmlite.module_set_named_type_packed(trans.Module, pool, struct_ty, 1)
# 扫描字段类型,为跨模块结构体引用添加 opaque 声明
# 例如 list[int].__pool__ 引用 memhub.MemManager,需要在当前模块添加 %"sha1.MemManager" = type opaque
for fi in range(field_count):
fty_addr_op: sizet = field_types_buf[fi]
if fty_addr_op == 0:
continue
fty_op: llvmlite.LLVMType* = (llvmlite.LLVMType*)(fty_addr_op)
llvmlite.module_ensure_opaque_for_type(trans.Module, pool, fty_op)
# ============================================================
# 3. 注册到 HandlesStruct
# ============================================================
entry: GenTypes.StructEntry* = None
if fill_fwd != 0 and existing is not None:
# 前向声明占位:字段体已原位写入 existing.Ty,登记条目沿用 existing
# (不新建条目/Ty,否则已发出的 Ptr(占位.Ty) 引用会指向旧空对象)。
entry = existing
else:
entry = self.Gen.StructGen.register_struct(pool, class_name, struct_ty, trans.ModuleSha1)
if entry is None:
VLogger.error(f"failed to register {class_name}", "CLASS")
return 0
# B5:记录 packed 标记到 StructEntry(供 __sizeof__/字段布局等派生查询)
if is_packed_cd != 0:
entry.IsPacked = 1
# B5:记录 aligned 对齐值(@c.Attribute(t.attr.aligned(N)),如 fpu_context 16 对齐),
# 供全局变量/局部 alloca 生成时施加对齐。
align_cd: int = self.Gen.ClassGenBase._class_aligned_value(cd.decorator_list)
if align_cd > 0:
entry.Align = align_cd
# 标记 VTable 状态(直接用 entry,规避跨模块同名 find_struct 找错)
if has_vtable == 1:
entry.HasVTable = 1
if is_novtable_deco == 1:
entry.IsNoVTable = 1
if parent_name is not None:
# 1b 阶段父类名是 pool 字符串,reset 后悬空;_translate_oop_methods/
# _detect_vtable_status 会在后续文件 find_struct(ParentName) strcmp 解引用
# → 用 stdlib 拷贝保证跨文件存活。
if self.Gen.StructGen.get_stdlib_alloc() != 0:
pn_len: sizet = string.strlen(parent_name)
pn_buf: str = CompilerFactory.factory.PermPool.alloc(pn_len + 1)
if pn_buf is not None:
string.strcpy(pn_buf, parent_name)
entry.ParentName = pn_buf
else:
entry.ParentName = parent_name
# 多继承:设置父类数组(供 MRO/字段/vtable 查询,stdlib 深拷贝跨文件存活)
if parents_count > 0 and parents_buf is not None:
self.Gen.StructGen.set_parent_names(pool, class_name, parents_buf, parents_count)
# 添加字段信息
# 如果有虚表,先添加 __vtable__ 字段占位(索引 0),用户字段从索引 1 开始
if has_vtable == 1:
i8_ptr_ty2: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
self.Gen.StructGen.add_field(pool, entry, "__vtable__", i8_ptr_ty2, None, None)
for fi in range(field_count):
fname_addr: sizet = field_names_buf[fi]
if fname_addr == 0:
continue
fname: str = rawptr(fname_addr)
fty_addr2: sizet = field_types_buf[fi]
if fty_addr2 == 0:
continue
fty2: llvmlite.LLVMType* = (llvmlite.LLVMType*)(fty_addr2)
# 恢复默认值 AST 指针
fdef_addr: sizet = field_defaults_buf[fi]
fdef: ast.AST* = None
if fdef_addr != 0:
fdef = (ast.AST*)(fdef_addr)
# 恢复注解类名指针
fannot_addr: sizet = field_annot_buf[fi]
fannot: str = None
if fannot_addr != 0:
fannot = str(fannot_addr)
if fname is not None and fty2 is not None:
fidx_add: int = self.Gen.StructGen.add_field(pool, entry, fname, fty2, fdef, fannot)
# t.CVolatile 字段标记(field_volatile_buf[fi] 与 add_field 顺序一一对应)
if fidx_add >= 0 and field_volatile_buf[fi] != 0:
fe_vol: GenTypes.FieldEntry* = \
self.Gen.StructGen._get_field_entry(entry, fidx_add)
if fe_vol is not None:
fe_vol.IsVolatile = 1
# rawptr[t.CVolatile | T] 字段标记(field_pointee_volatile_buf[fi] 与
# add_field 顺序一一对应):经该字段解引用时 load/store 带 volatile。
if fidx_add >= 0 and field_pointee_volatile_buf[fi] != 0:
fe_pv: GenTypes.FieldEntry* = \
self.Gen.StructGen._get_field_entry(entry, fidx_add)
if fe_pv is not None:
fe_pv.PointeeVolatile = 1
# const 对象字段标记(field_const_buf[fi] 与 add_field 顺序一一对应)
if fidx_add >= 0 and field_const_buf[fi] != 0:
fe_c: GenTypes.FieldEntry* = \
self.Gen.StructGen._get_field_entry(entry, fidx_add)
if fe_c is not None:
fe_c.IsConst = 1
# Phase 1a 声明模式:只注册 struct + 设置 OOP 标志,不翻译方法体
if trans._declare_only == 1:
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, struct_ty, class_name, 1)
return 0
# ============================================================
# 4. OOP 方法处理:扫描 FunctionDef,标记 OOP,翻译方法,生成 __before_init__
# ============================================================
self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, struct_ty, class_name)
return 0
# ============================================================
# _translate_nested_classdefs - 递归翻译类体内的嵌套 class / struct
#
# 命名与引用口径(用户 2026-10-01 定,与 TPC union/renum 嵌套命名同源):
# · 内层结构体 IR 名 = `{sha1}.{外类}_{内类}`(sha1 = 与外层相同的定义模块);
# · 引用两种写法都可:限定名 `外类.内类`(Attribute 兜底,见 TypeGenResolve)
# 与扁平名 `外类_内类`(裸名直查结构体表即可命中);
# · 多层嵌套自然递归为 `A_B_C`(递归时子节点名已被改写为 A_B)。
# 实现选择"临时改写子节点 name 再递归"而非另写发射器:本模块所有名字派生点
# (IR 名 {sha}.{名}、register_struct/add_field、AnnotClassName、方法符号名
# `{类}.{方法}` 的 mangling、mark_module_class 键)都读 cd.name ⇒ 改名即全链路
# 一致,且复用继承展平/packed/OOP/vtable 等全部既有逻辑,零新增判据。
# 两个必须的护栏:
# ① 扁平名字符串分配在 PermPool —— 它要跨 Phase(1a/1b)存活,trans.Pool 会随
# mark/reset 回收成悬空串(后续 strcmp 读悬空内存)。
# ② 递归返回后**还原"正在定义类"窗口**(CurDefModuleSha1/CurDefClassName 会被
# 内层 mark_module_class 覆盖)——不还原会让外层随后的字段注解解析误判
# "当前定义类"(规则 5.5 的自引用窗口据此判定,误判会冻结裸名票名)。
# ============================================================
def _translate_nested_classdefs(self, trans: HT.Translator*,
cd: ast.ClassDef*) -> None:
"""把本类体内的嵌套 ClassDef 逐个按 `{外类}_{内类}` 递归翻译"""
if trans is None or cd is None or cd.name is None:
return
children: list[ast.AST*]* = cd.children
if children is None:
return
cn: sizet = len(children)
ci: sizet = 0
while ci < cn:
child_ast: ast.AST* = children.get(ci)
ci += 1
if child_ast is None:
continue
if child_ast.kind() != ast.ASTKind.ClassDef:
continue
child: ast.ClassDef* = ast.ClassDef*(child_ast)
if child is None or child.name is None:
continue
# 扁平名 `{外类}_{内类}`(优先 PermPool:名字要跨 Phase 存活;
# PermPool 缺失时退回本遍池,行为与旧路径一致)
flat_pool: memhub.MemBuddy* = CompilerFactory.factory.PermPool
if flat_pool is None:
flat_pool = trans.Pool
if flat_pool is None:
continue
outer_len: sizet = string.strlen(cd.name)
inner_len: sizet = string.strlen(child.name)
flat_len: sizet = outer_len + 1 + inner_len
flat_buf: str = flat_pool.alloc(flat_len + 1)
if flat_buf is None:
continue
viperlib.snprintf(flat_buf, flat_len + 1, "%s_%s", cd.name, child.name)
# 改写子节点名 → 递归(内部一律以扁平名注册/发射)
old_name: str = child.name
child.name = flat_buf
saved_def_sha1: str = self.Gen.StructGen.CurDefModuleSha1
saved_def_cls: str = self.Gen.StructGen.CurDefClassName
self.translate_class_def(trans, child_ast)
self.Gen.StructGen.CurDefModuleSha1 = saved_def_sha1
self.Gen.StructGen.CurDefClassName = saved_def_cls
child.name = old_name