feat(struct): 嵌套 class/struct 发射为独立结构体 {外类}_{内类}

用户 2026-10-01 定:struct 内允许叠加 struct(与 class 内写 class 同口径)。
此前类体里的嵌套 ClassDef 在两个编译器里都被**静默丢弃**(字段遍历只认 AnnAssign、
方法遍历只认 FunctionDef),现按既有 REnum/union 嵌套命名先例发射为独立结构体。

命名与引用口径(双端同源):内层结构体 = {外类}_{内类}(多层递归 A_B_C);
引用两种写法都可用 —— 限定名 `外类.内类`、扁平名 `外类_内类`。

- HandlesClassDef.py:新增 _EmitNestedClassDefs(deepcopy + 改名后递归
  _EmitClassLlvm,deepcopy 避免污染源码 AST)+ _RegisterNestedClassName
  (SymbolTable 登记裸名/点号名/扁平名,对齐 REnum 变体的三键先例)。
- TypeAnnotationResolver.py:_from_node_attribute 增加嵌套分支 —— 限定名按**扁平名**
  取条目并让 Name/BaseType 都是扁平名(否则会拼出 t.CStruct(name='内类'),
  与声明端 `%"{sha}.外类_内类"` 不同源 ⇒ 布局分叉)。
- DeclarationGenerator.py:_get_type_str 支持 `外类.内类` → `%"{sha}.外类_内类"`;
  _generate_class_decl 递归产出嵌套类型的完整声明(否则跨模块只拿到 opaque,
  指针可用但字段偏移全失效)。
- Phase1Generator.py / Phase2Translator.py:跨模块注册表按扁平名登记嵌套类。
  Phase2Translator 原先用 ast.walk 把嵌套类按**短名**登记(占住平坦命名空间、
  与定义端不同源),改为递归扁平名。

实测(TPC 侧):嵌套类名字登记 {A_B, A_C, A_C_D};`x: A.B` → CTypeInfo(A_B);
声明端 `%"cafe.A" = type { i32, %"cafe.A_B" }` + `%"cafe.A_B" = type { i32 }`
+ 多层 `%"cafe.A_C_D" = type { i32 }`。

已知缺口(下一步):TransPyV 侧跨模块走 .vpi,VpiReader 只登记顶层类 ⇒ 跨模块引用
嵌套类型仍需在 VpiReader 侧补登记(同模块路径已通)。
This commit is contained in:
TermiNexus committed 2026-10-01 23:29:54 +08:00
1 parent a8f6ca006d
commit 5eef845e5b
5 files changed
+159 -3

No files matched your search

+57
View File
@@ -617,6 +617,54 @@ class ClassHandle(BaseHandle):
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 等)是"类型强转"标记,
@@ -677,6 +725,15 @@ class ClassHandle(BaseHandle):
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)
+18
View File
@@ -1208,6 +1208,24 @@ class TypeAnnotationResolver:
return _Recovered2
return CTypeInfo()
# 嵌套类 / 嵌套 struct:限定名 `外类.内类`(ModulePath 是**类名**而非模块名)。
# 命名口径见 HandlesClassDef._EmitNestedClassDefs —— 嵌套类以扁平名
# `{外类}_{内类}` 发射,并由 _RegisterNestedClassName 把扁平名与 `外类.内类`
# 一并插入 SymbolTable。本分支按**扁平名**取条目:BaseType/Name 必须是扁平名
# (若走下方 FullName 分支,会按短名拼出 `t.CStruct(name='Inner')`,与
# 声明端的 `%"{sha}.外类_内类"` 不同源 ⇒ 布局/字段偏移分叉)。
if ModulePath and TypeName:
NestedKey: str = f"{ModulePath}_{TypeName}"
if NestedKey in SymbolTable:
NestedEntry: CTypeInfo = SymbolTable[NestedKey]
if NestedEntry is not None:
Result = NestedEntry.Copy()
Result.Name = NestedKey
if Result.BaseType is None:
Result.BaseType = t.CStruct(name=NestedKey)
Result.IsStruct = True
return Result
if FullName in SymbolTable:
Info: CTypeInfo = SymbolTable[FullName]
if Info and Info.IsTypedef: