From 868d48ae22ea808a08f00e204bc61fc5c71bbd31 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 29 Sep 2026 18:47:35 +0800 Subject: [PATCH] =?UTF-8?q?fix(transpyv):=20=E8=87=AA=E5=BC=95=E7=94=A8?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E7=9A=84=E7=89=B9=E5=8C=96=E5=90=8D=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E7=95=99=E5=9C=A8=E8=A3=B8=E5=90=8D=EF=BC=88=E6=96=B0?= =?UTF-8?q?=E5=A2=9E"=E6=9C=AC=E6=A8=A1=E5=9D=97=E5=A3=B0=E6=98=8E?= =?UTF-8?q?=E7=B1=BB"=E5=BD=92=E5=B1=9E=E8=A1=A8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类别:a(编译器缺陷 —— 字段类型在类注册前被冻结成裸名版特化名) 现场(上一轮未修掉的那一处):includes/ast/base.vp:357 if self.children is None: self.children = list[rawptr[AST]](self.pool, 8) CastError:源 %"7e2b6dda6e423bfb.list[rawptr[d1ed020479454541.AST]]"* → 目标 %"7e2b6dda6e423bfb.list[rawptr[AST]]"* 根因:字段 children: list[rawptr[AST]] | t.CPtr 写在**类 AST 自己体内**(自引用), 其类型在 translate_class_def 的字段循环里解析,而本类的 register_struct 在该函数 **末尾**才执行 ⇒ 解析时 struct 表里没有 AST ⇒ 上一轮 5.5 的两条查法 (find_struct 按名 / find_struct_by_module)全部落空 ⇒ 实参仍留裸名 rawptr[AST], 字段类型被冻结成 list[rawptr[AST]](第一遍 A-pre 即发生);而 :357 的构造调用发生在 类注册之后 ⇒ 带 SHA1 ⇒ 两侧分裂。(同一分裂在 stmts.vp:60 表现为形参侧/实参侧, 本处表现为字段侧/构造侧。) 改动: 1. StructGenMixin 新增"本模块声明类"归属表 ModuleClasses(键 "{模块SHA1}.{类名}") 与 CurDefModuleSha1(正在定义类所属模块 SHA1,登记时同步记录): - mark_module_class(pool, sha1, name) / is_module_class(pool, sha1, name) - 键含 SHA1 ⇒ 与翻译顺序/模块切换无关、无需每文件重置,也不会把别的模块的类 错认成本模块。 2. ClassGenDef 类定义入口(add_visible_struct 之后、枚举/联合分派之前)登记 mark_module_class(pool, trans.ModuleSha1, class_name),覆盖 class/enum/union/ renum 全部类种类。 3. TypeGenResolve 规则 5.5 补齐证据链:① 按名取 StructEntry.ModuleSha1; ② struct 表暂无条目时用 CurDefModuleSha1 走 find_struct_by_module; ③ 仍无条目(正是自引用窗口)时查归属表 ⇒ 加 SHA1 前缀。 CurDefModuleSha1 取代 CurrentModuleSha1 作为"当前定义模块"真源——后者在 A-pre/StubMerger 路径未设置或留着上一文件旧值(只有 Translator.ModuleSha1 权威)。 影响面:ABI 可见(特化名);需 TPV 自举重建 + 回归验证。 --- App/lib/core/Gen/Class/ClassGenDef.vp | 6 +++ App/lib/core/Gen/StructGenMixin.vp | 66 +++++++++++++++++++++++++ App/lib/core/Gen/Type/TypeGenResolve.vp | 46 +++++++++-------- 3 files changed, 98 insertions(+), 20 deletions(-) diff --git a/App/lib/core/Gen/Class/ClassGenDef.vp b/App/lib/core/Gen/Class/ClassGenDef.vp index 415f963..db11130 100644 --- a/App/lib/core/Gen/Class/ClassGenDef.vp +++ b/App/lib/core/Gen/Class/ClassGenDef.vp @@ -42,6 +42,12 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase): # 命名空间隔离:标记为当前文件可见(必须在 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。若某类同时被判为枚举, diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index 4d6c150..5636eaf 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -95,6 +95,23 @@ novirtual class StructGenMixin: VisibleStructs: hashtable.HashTable* StrictVisibility: int + # --- 各模块"声明的类名"集合(键 "{模块SHA1}.{类名}")--- + # 与 StructTable 的注册状态**无关**:类定义入口处即登记(ClassGenDef:43 之后), + # 因此"类正在定义中、struct 还没有条目"时也能答出该裸类名的归属模块。 + # 用途:泛型类型实参命名规范化(TypeGenResolve._norm_type_arg_name 规则 5.5) + # 判定裸类名属于哪个模块 —— 自引用字段(如 includes/ast/base.vp + # `AST.children: list[rawptr[AST]]`)解析时本类尚未 register_struct,只按 + # 注册表查会把实参留在裸名,与同一个类在构造调用/别的模块处的带 SHA1 形式 + # 分裂成两个特化名(现场:base.vp:357 CastError,源/目标只差实参类名前缀)。 + ModuleClasses: hashtable.HashTable* + + # --- **正在定义类**所属模块的 SHA1(mark_module_class 同步记录)--- + # 为什么不复用 TypeGenBase.CurrentModuleSha1:A-pre/StubMerger 等路径只设了 + # Translator.ModuleSha1、没设 CurrentModuleSha1(或留着上一文件的旧值), + # 而类定义入口拿到的 trans.ModuleSha1 在任何路径上都是权威的 ⇒ 以它为"当前 + # 定义模块"的唯一真源。 + CurDefModuleSha1: str + # --- 名 | 类型 下标索引(查找快速路径;索引不可信时回退原线性扫描) --- StructNameIdx: sizet* # 名 → 首注册下标(槽 u64:0=空槽,否则 index+1) StructTypeIdx: sizet* # Ty 指针值 → 首注册下标(同布局) @@ -109,6 +126,8 @@ novirtual class StructGenMixin: self.StdlibAlloc = 0 self.VisibleStructs = None self.StrictVisibility = 0 + self.ModuleClasses = None + self.CurDefModuleSha1 = None self.StructNameIdx = None self.StructTypeIdx = None self.StructIdxCap = 0 @@ -159,6 +178,53 @@ novirtual class StructGenMixin: if name is None or self.VisibleStructs is None: return 0 return self.VisibleStructs.__contains__(name) + def _module_class_key(self, pool: memhub.MemBuddy*, sha1: str, name: str) -> str: + """拼 "{sha1}.{类名}" 查询/登记键(分配在给定 pool,调用方无需释放)。""" + if pool is None or sha1 is None or name is None: + return None + k_len: sizet = string.strlen(sha1) + 1 + string.strlen(name) + 1 + k_buf: str = pool.alloc(k_len) + if k_buf is None: + return None + k_buf[0] = '\0' + string.strcat(k_buf, sha1) + string.strcat(k_buf, ".") + string.strcat(k_buf, name) + return k_buf + + def mark_module_class(self, pool: memhub.MemBuddy*, sha1: str, name: str) -> None: + """登记"{sha1}.{类名}":**该模块声明了这个类**(类定义入口调用一次)。 + + 键里带 SHA1 ⇒ 天然按"声明模块"区分,且与翻译顺序/模块切换无关(不需要 + 每文件重置):即使表里残留别的模块的登记,查询方只会命中 + (它自己那一对 sha1+类名),不会把别的模块的类错认成自己的。 + """ + if pool is None or sha1 is None or name is None: + return + # 同步记录"正在定义类所属模块"(任何翻译路径上唯一的权威来源) + self.CurDefModuleSha1 = sha1 + if self.ModuleClasses is None: + self.ModuleClasses = hashtable.HashTable(pool) + if self.ModuleClasses is None: + return + k_buf: str = self._module_class_key(pool, sha1, name) + if k_buf is None: + return + if self.ModuleClasses.__contains__(k_buf) != 0: + return + self.ModuleClasses.set_int(k_buf, 1) + + def is_module_class(self, pool: memhub.MemBuddy*, sha1: str, name: str) -> int: + """裸名 name 是否由模块 sha1 声明(1=是 / 0=否;与 struct 注册状态无关)。""" + if pool is None or sha1 is None or name is None: + return 0 + if self.ModuleClasses is None: + return 0 + k_buf: str = self._module_class_key(pool, sha1, name) + if k_buf is None: + return 0 + return self.ModuleClasses.__contains__(k_buf) + def init_struct_table(self, pool: memhub.MemBuddy*) -> int: """初始化结构体注册表,返回 1 成功 diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index b8fa60e..cba4337 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -279,14 +279,16 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): 完整模块名 → SHA1;再尝试 mod 直接查 SHA1(无别名 import 的完整路径) 5. 裸名 "Class":经 from_imports 查所属模块 SHA1 5.5 裸名 "Class" 是**本模块/已注册类**但不在 from_imports 里(典型:本模块 - 自己声明的类):先按名取结构体登记项的**定义模块 SHA1**(StructEntry. - ModuleSha1);类正在定义中(自引用字段)按名查不到时,回退用与 Name - 分支同一处证据 find_struct_by_module(name, 当前模块 SHA1) 认定本模块类。 - 缺这一步会让同一类型"本模块内是裸名、被引用模块里带前缀"分裂成两个 - 特化名(现场:includes/ast/stmts.vp:60 `_set_parent_list(targets, self)` - 源 %"…list[rawptr[d1ed020479454541.AST]]"* vs 目标 %"…list[rawptr[AST]]"*; - includes/ast/base.vp:357 `children = list[rawptr[AST]](self.pool, 8)` - 同族反向:字段侧裸名 vs 构造侧带前缀) + 自己声明的类):证据依次为 ① 按名取结构体登记项的**定义模块 SHA1** + (StructEntry.ModuleSha1);② struct 表暂无条目时,用"正在定义类所属模块" + (ClassGenDef 类定义入口登记,权威来源)查 find_struct_by_module; + ③ 仍无条目(类定义尚未 register_struct —— 自引用字段的窗口)时,查 + 该类定义入口登记的"{SHA1}.{类名}"归属表。缺这一步会让同一类型"本模块内 + 是裸名、被引用模块里带前缀"分裂成两个特化名(现场:includes/ast/stmts.vp + :60 `_set_parent_list(targets, self)` 源 %"…list[rawptr[d1ed020479454541.AST]]"* + vs 目标 %"…list[rawptr[AST]]"*;includes/ast/base.vp:357 + `children = list[rawptr[AST]](self.pool, 8)` 同族反向:字段侧裸名 vs + 构造侧带前缀) 6. 仍查不到:返回原串(内置类型名等,保持原样) 返回 None 表示 type_name 为 None。 @@ -508,18 +510,22 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): _bare_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(type_name) if _bare_entry is not None: _def_sha1 = _bare_entry.ModuleSha1 - if _def_sha1 is None: - # 类**正在定义中**(自引用字段,如 base.vp `AST.children: list[rawptr[AST]]`) - # 时按名查不到/登记项尚无 ModuleSha1 ⇒ 用与 resolve_annotation_type 的 - # Name 分支**同一处证据**认定"本模块类":find_struct_by_module(name, - # 当前模块 SHA1)。缺这条回退时,字段类型留在裸名、而同类在 - # 构造调用(类已注册)处已带 SHA1 ⇒ 同一 list[rawptr[AST]] 又分裂 - # (现场:base.vp:357 `self.children = list[rawptr[AST]](self.pool, 8)`)。 - _cur_sha1: str = self.Gen.TypeGenBase.CurrentModuleSha1 - if _cur_sha1 is not None: - _own_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(type_name, _cur_sha1) - if _own_entry is not None: - _def_sha1 = _cur_sha1 + # "正在定义类所属模块"的唯一真源:ClassGenDef 在类定义入口用 + # trans.ModuleSha1 登记(不像 TypeGenBase.CurrentModuleSha1 那样存在 + # "A-pre/StubMerger 未设置或留着上一文件旧值"的窗口)。 + _def_mod: str = self.Gen.StructGen.CurDefModuleSha1 + if _def_sha1 is None and _def_mod is not None: + _own_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(type_name, _def_mod) + if _own_entry is not None: + # ① struct 表里已有该类(上一遍已注册 / 本模块更早声明的类) + _def_sha1 = _def_mod + elif self.Gen.StructGen.is_module_class(pool, _def_mod, type_name) != 0: + # ② struct 表里**还没有**该类条目:类定义入口已登记"{SHA1}.{类名}" + # 但尚未走到 register_struct —— 正是**自引用字段**的窗口 + # (如 base.vp `AST.children: list[rawptr[AST]]`)。这条与注册 + # 状态无关,故第一遍(A-pre)就能给出带 SHA1 的实参名,字段 + # 类型不会被冻结成裸名版本、与后续构造调用处分裂。 + _def_sha1 = _def_mod if _def_sha1 is not None: res_d: str = self._concat_sha1_class(pool, _def_sha1, type_name) if res_d is not None: