From 5eef845e5ba4ed21a8b0ef7e2fc3d8d38f7669f9 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Thu, 1 Oct 2026 23:29:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(struct):=20=E5=B5=8C=E5=A5=97=20class/stru?= =?UTF-8?q?ct=20=E5=8F=91=E5=B0=84=E4=B8=BA=E7=8B=AC=E7=AB=8B=E7=BB=93?= =?UTF-8?q?=E6=9E=84=E4=BD=93=20{=E5=A4=96=E7=B1=BB}=5F{=E5=86=85=E7=B1=BB?= =?UTF-8?q?}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户 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 侧补登记(同模块路径已通)。 --- lib/Projectrans/DeclarationGenerator.py | 25 +++++++++++ lib/Projectrans/Phase1Generator.py | 33 ++++++++++++++ lib/Projectrans/Phase2Translator.py | 29 +++++++++++-- lib/core/Handles/HandlesClassDef.py | 57 +++++++++++++++++++++++++ lib/core/TypeAnnotationResolver.py | 18 ++++++++ 5 files changed, 159 insertions(+), 3 deletions(-) diff --git a/lib/Projectrans/DeclarationGenerator.py b/lib/Projectrans/DeclarationGenerator.py index 842d1a9..4777adf 100644 --- a/lib/Projectrans/DeclarationGenerator.py +++ b/lib/Projectrans/DeclarationGenerator.py @@ -1,6 +1,7 @@ from __future__ import annotations import vast as ast +import copy import re from typing import List @@ -581,6 +582,20 @@ class DeclarationGenerator: struct_type_name: str = f'%"{self.module_sha1}.{class_name}"' if self.module_sha1 else f'%struct.{class_name}' struct_decl: str = f'{struct_type_name} = type {{ {", ".join(member_types)} }}' decls.append(struct_decl) + # 嵌套类 / 嵌套 struct:定义端以扁平名 `{外类}_{内类}` 发射(见 HandlesClassDef. + # _EmitNestedClassDefs),此处同步产出同名完整类型声明 —— 否则跨模块引用 + # `外类.内类` 只会得到一个 opaque 前向声明(指针可用、字段偏移全失效)。 + # deepcopy 后改名再递归:本函数所有名字都派生自 node.name,改名即全链路一致; + # 判断/枚举/联合等分支在递归时会各自按内层类自身的基类重新分派。 + for _nested_item in getattr(node, 'body', None) or []: + if not isinstance(_nested_item, ast.ClassDef): + continue + _nested_inner: str | None = getattr(_nested_item, 'name', None) + if not _nested_inner: + continue + _nested_node: ast.ClassDef = copy.deepcopy(_nested_item) + _nested_node.name = f'{class_name}_{_nested_inner}' + decls.extend(self._generate_class_decl(_nested_node)) if is_cpython_object or is_cvtable: new_func_name: str = f'{class_name}.__before_init__' if self.module_sha1: @@ -1265,6 +1280,16 @@ class DeclarationGenerator: module_name = annotation.value.id elif isinstance(annotation.value, ast.Attribute) and hasattr(annotation.value, 'attr'): module_name = annotation.value.attr + # 嵌套类 / 嵌套 struct:限定名 `外类.内类`(module_name 是**类名**而非模块名)。 + # 定义端以扁平名 `{外类}_{内类}` 发射(见 HandlesClassDef._EmitNestedClassDefs), + # 故此处按扁平名拼类型;命中与否由 struct_names 里是否存在该扁平名裁决 + # (不存在即维持下方既有路径,不改变任何既有类型名)。 + if module_name and module_name != 't' and module_name != 'c': + NestedName: str = f'{module_name}_{attr_name}' + if NestedName in self.struct_names: + NestedSha1: str | None = self.struct_sha1_map.get(NestedName, self.module_sha1) + NestedStr: str = f'%"{NestedSha1}.{NestedName}"' if NestedSha1 else f'%struct.{NestedName}' + return NestedStr if embedded else f'{NestedStr}*' if (module_name == 't' and attr_name == 'State') or attr_name == 'State': return '' if (module_name == 't' and attr_name == 'Callable') or attr_name == 'Callable': diff --git a/lib/Projectrans/Phase1Generator.py b/lib/Projectrans/Phase1Generator.py index c87fb31..35bbc4c 100644 --- a/lib/Projectrans/Phase1Generator.py +++ b/lib/Projectrans/Phase1Generator.py @@ -756,6 +756,11 @@ class Phase1Generator: else: struct_names.add(node.name) struct_sha1_map[node.name] = sha1_key + # 嵌套类 / 嵌套 struct:定义端以扁平名 `{外类}_{内类}` 发射 + #(HandlesClassDef._EmitNestedClassDefs),此处同步登记, + # 供跨模块引用 `外类.内类` / 扁平名时拿到同一类型名与前缀。 + _RegisterNestedStructNames(node, node.name, sha1_key, + struct_names, struct_sha1_map, class_def_map) # 收集所有类(含枚举/异常)的 ClassDef 节点,供 DeclarationGenerator # 跨模块字段展平使用(如 exprs.py 的 Name(AST) 需查找 base.py 的 AST) class_def_map[node.name] = node @@ -775,3 +780,31 @@ class Phase1Generator: _vlog().warning(f".ll 声明生成失败: {e}") with open(ll_path, 'w', encoding='utf-8') as f: f.write(f"; declaration for {os.path.basename(src_path)}\n; error: {e}\n") + + +# ====================================================================== +# _RegisterNestedStructNames - 嵌套类/嵌套 struct 的扁平名登记 +# +# 与定义端 HandlesClassDef._EmitNestedClassDefs 的命名口径同源(也同 +# Phase2Translator._build_struct_sha1_map 的登记),使跨模块引用 `外类.内类` +# 或扁平名 `外类_内类` 时,DeclarationGenerator._get_type_str 与 +# TypeAnnotationResolver 都落到同一类型名与前缀(否则会拼出 `%"{sha}.内类"` +# 这种与定义端 `%"{sha}.外类_内类"` 不同源的幽灵名)。多层嵌套递归为 A_B_C。 +# ====================================================================== +def _RegisterNestedStructNames(Node, Prefix: str, Sha1Key: str, + StructNames: set, StructSha1Map: dict, ClassDefMap: dict) -> None: + """把类体内的嵌套 ClassDef 以扁平名 `{外类}_{内类}` 递归登记""" + Body = getattr(Node, 'body', None) + if not Body: + return + for Item in Body: + if not isinstance(Item, ast.ClassDef): + continue + InnerName: str | None = getattr(Item, 'name', None) + if not InnerName: + continue + FlatName: str = f'{Prefix}_{InnerName}' + StructNames.add(FlatName) + StructSha1Map[FlatName] = Sha1Key + ClassDefMap[FlatName] = Item + _RegisterNestedStructNames(Item, FlatName, Sha1Key, StructNames, StructSha1Map, ClassDefMap) diff --git a/lib/Projectrans/Phase2Translator.py b/lib/Projectrans/Phase2Translator.py index ca6e9e8..d44484a 100644 --- a/lib/Projectrans/Phase2Translator.py +++ b/lib/Projectrans/Phase2Translator.py @@ -1384,7 +1384,7 @@ class Phase2Translator: _, tree = parse_python_file(pyi_path) if tree is None: continue - for node in ast.walk(tree): + for node in ast.iter_child_nodes(tree): # 每类只登记一次,**先扫到的为准**:判据必须是**类名**。 # 原实现写成 `if sha1_key in struct_sha1_map` —— 用模块 SHA1 去查一张 # 按**类名**建的表,判据恒假 ⇒ 实际是"后扫到的覆盖先扫到的", @@ -1392,10 +1392,33 @@ class Phase2Translator: # includes/ast/tokens.vp 各有一个 `class Keyword`,定义端拿到的前缀 # 可能是**另一个模块**的 sha1 ⇒ 同一类出现两个 struct 实例、 # `__before_init__` 声明与调用点分叉)。 - if isinstance(node, ast.ClassDef) and node.name not in struct_sha1_map: - struct_sha1_map[node.name] = sha1_key + if isinstance(node, ast.ClassDef): + # 嵌套类按扁平名 `{外类}_{内类}` 登记(与定义端 + # HandlesClassDef._EmitNestedClassDefs、Phase1Generator. + # _RegisterNestedStructNames 同源)。此前用 ast.walk 会把嵌套类按 + # **短名** `Inner` 登记 —— 既占住平坦命名空间,又与定义端的 + # `外类_内类` 不同源(跨模块前缀分裂)。 + self._RegisterStructSha1Rec(node, node.name, sha1_key, struct_sha1_map) return struct_sha1_map + def _RegisterStructSha1Rec(self, Node, FlatName: str, Sha1Key: str, + StructSha1Map: dict) -> None: + """递归登记"类名 → 定义模块 SHA1"(嵌套类用扁平名 `{外类}_{内类}`)""" + if not FlatName: + return + if FlatName not in StructSha1Map: + StructSha1Map[FlatName] = Sha1Key + Body = getattr(Node, 'body', None) + if not Body: + return + for Item in Body: + if not isinstance(Item, ast.ClassDef): + continue + InnerName: str | None = getattr(Item, 'name', None) + if not InnerName: + continue + self._RegisterStructSha1Rec(Item, f'{FlatName}_{InnerName}', Sha1Key, StructSha1Map) + def run(self) -> None: """扫描源目录,翻译并生成 .ll 含代码文件(导入遍历)""" import sys as _sys diff --git a/lib/core/Handles/HandlesClassDef.py b/lib/core/Handles/HandlesClassDef.py index e0d3475..5110f77 100644 --- a/lib/core/Handles/HandlesClassDef.py +++ b/lib/core/Handles/HandlesClassDef.py @@ -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) diff --git a/lib/core/TypeAnnotationResolver.py b/lib/core/TypeAnnotationResolver.py index bb405cc..09a0ee2 100644 --- a/lib/core/TypeAnnotationResolver.py +++ b/lib/core/TypeAnnotationResolver.py @@ -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: