From e1bb42fade1c5ddafd39a4f589e4f48374f747e9 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 30 Sep 2026 00:20:30 +0800 Subject: [PATCH] =?UTF-8?q?fix(ClassGenGeneric):=20=E6=B3=9B=E5=9E=8B?= =?UTF-8?q?=E7=89=B9=E5=8C=96=E4=B8=8D=E5=86=8D=E5=B9=B3=E7=A7=BB"?= =?UTF-8?q?=E7=B1=BB=E5=AE=9A=E4=B9=89=E7=AA=97=E5=8F=A3"=EF=BC=8C?= =?UTF-8?q?=E8=A3=B8=E7=B1=BB=E5=90=8D=E5=AE=9E=E5=8F=82=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E5=86=BB=E7=BB=93=E6=88=90=E8=A3=B8=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类别:a 编译器缺陷(TPV 侧) 现象(反复出现 4 轮):编译 includes/ast/base.vp 时 :359 `_append_child(self.children, node, self.pool)` 报 「指针隐式转指针已禁止(被指类型是非基本类型…):源类型 %"…list[rawptr[AST]]"* → 目标类型 %"…list[rawptr<定义模块SHA1>.AST]]"*」——同一泛型特化被写成两种拼名(实参一侧 裸类名 `AST`、另一侧带定义模块 SHA1 前缀)。 判据(结构缺陷,代码级可证): · `_norm_type_arg_name` 规则 5.5 判定"裸类名归属"依赖两个**全局窗口变量** `StructGen.CurDefModuleSha1` / `CurDefClassName`(证据 ② `is_module_class`、 ③ `CurDefClassName` 逐字相同),入口闸门是 `_def_mod is not None`。 · `ClassGenGeneric._specialize_generic_class` 在 :715-720 把 `trans.ModuleSha1` 临时切到 **泛型模板定义模块**,再于 :729 调 `ClassGenDef.translate_class_def`;后者在 `ClassGenDef.vp:50` 会 `mark_module_class(pool, trans.ModuleSha1, class_name)` ⇒ **窗口被覆盖成"模板模块 + 特化类名"**。原实现返回时只恢复 `trans.ModuleSha1` 与 `_imported_modules`(:733-735),**没有恢复这两个窗口变量**。 · 后果:调用方文件在此之后解析任何裸类名时,证据 ②③ 都在对着"模板模块 / 特化类"判 ⇒ 全落空 ⇒ 该实参以**裸名**形式被首次注册并被后续复用(首写者胜:`StructGenMixin.vp:455` 按 Ty 指针去重、`ClassGenDef.vp:100-150` existing 早退不重算)⇒ 与同一特化在别处解析出的 带 SHA1 形式分裂成两个名字 ⇒ 被"非基本类型指针不可隐式转换"拦下。 修法: 1. `ClassGenGeneric.vp`:窗口与 `ModuleSha1` **同进同出** —— 在切 ModuleSha1 之前保存 `CurDefModuleSha1`/`CurDefClassName`,:744 之后一并恢复(嵌套特化不再污染调用方窗口)。 2. `TypeGenResolve.vp` 规则 5.5 的取证日志扩展:除原有"命中 struct 条目但 entry.ModuleSha1 为空"外,**新增触发条件"窗口存在却属于另一个类"**(`_def_mod` 有值且 `CurDefClassName` 与裸名不同)——正是"窗口被特化平移/污染"或"前向引用未登记类"的签名; 报文补出 `entry_present` 与 `find_struct_by_module` 两项事实,一轮即可定位来源是否缺失。 验证:TPV 侧改动需重新自举 TransPyV.exe 后生效;本次以静态证据链(上述行号)与取证日志 自证。注:TransPyV 仓另有 7 个他人未提交文件,本次只提交本文件与 TypeGenResolve.vp。 --- App/lib/core/Gen/Class/ClassGenGeneric.vp | 16 +++++++++- App/lib/core/Gen/Type/TypeGenResolve.vp | 39 ++++++++++++++++------- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/App/lib/core/Gen/Class/ClassGenGeneric.vp b/App/lib/core/Gen/Class/ClassGenGeneric.vp index ebe5d9c..4a1f957 100644 --- a/App/lib/core/Gen/Class/ClassGenGeneric.vp +++ b/App/lib/core/Gen/Class/ClassGenGeneric.vp @@ -714,6 +714,18 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase): # 避免模板方法体内的跨模块调用被误报"模块未导入"。 saved_module_sha1: str = trans.ModuleSha1 saved_imported: str = trans._imported_modules + # 【窗口同进同出】类定义窗口(CurDefModuleSha1/CurDefClassName)必须与 + # ModuleSha1 一起保存/恢复:下方 translate_class_def(:729)内部会按 + # **模板模块 sha1 + 特化类名** 调 mark_module_class(ClassGenDef.vp:50), + # 覆盖这两个窗口变量。原实现只恢复 trans.ModuleSha1 ⇒ 调用方文件在此之后 + # 解析任何裸类名时,规则 5.5 的归属判据(证据 ② is_module_class / + # ③ CurDefClassName 逐字相同)全都在对着"模板模块 / 特化类"判 ⇒ 判不到 + # ⇒ 该实参以**裸名**形式被首次注册并永久复用,与同一特化在别处解析出的 + # 带 SHA1 形式分裂成两个名字,最终以"被指类型是非基本类型……不可隐式转换" + # 报出(现场:includes/ast/base.vp:359 `_append_child(self.children, ...)` + # 源 list[rawptr[AST]] vs 目标 list[rawptr<定义模块SHA1>.AST])。 + saved_def_mod: str = self.Gen.StructGen.CurDefModuleSha1 + saved_def_cls: str = self.Gen.StructGen.CurDefClassName template_sha1: str = self._find_generic_template_sha1(class_name) if template_sha1 is not None: trans.ModuleSha1 = template_sha1 @@ -730,9 +742,11 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase): if tpl_file_full is not None: self.Gen.TypeGenBase.pop_trace() self.Gen.TypeGenResolve.set_current_file(saved_cur_full) - # 恢复原始 ModuleSha1 与 imports + # 恢复原始 ModuleSha1 / imports / **类定义窗口**(窗口不平移,见上方说明) trans.ModuleSha1 = saved_module_sha1 trans._imported_modules = saved_imported + self.Gen.StructGen.CurDefModuleSha1 = saved_def_mod + self.Gen.StructGen.CurDefClassName = saved_def_cls # 8. 清除 type_map 上下文 trans.GenericTypeParamNames = None diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index 543c62c..f612e74 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -564,35 +564,52 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): res_d: str = self._concat_sha1_class(pool, _def_sha1, type_name) if res_d is not None: return res_d - # 取证(只加日志,不改返回值):命中了 struct 条目却一条 SHA1 证据都取不到 - # ⇒ 本次会保留裸名,可能与本类在别处解析出的"带前缀"形式分裂。把五种证据 - # 的取值一次打出,便于一轮定位是哪个来源缺失(可疑子集:entry 的 ModuleSha1 - # 为空——正是 .vpi 注入的半条目形态)。 + # 取证(只加日志,不改返回值):一条 SHA1 证据都取不到 ⇒ 本次会保留裸名, + # 可能与本类在别处解析出的"带前缀"形式分裂。把各处证据一次打出,便于一轮 + # 定位是哪个来源缺失。两个触发条件: + # ① 命中 struct 条目但 entry.ModuleSha1 为空(.vpi 注入的半条目形态); + # ② **类定义窗口存在却属于另一个类**(_def_mod 有值、CurDefClassName 与 + # 裸名不同):这是"窗口被泛型特化平移/污染"(某次 + # _specialize_generic_class 内部 translate_class_def 覆盖了窗口且未恢复) + # 或"前向引用尚未登记的类"的签名 —— 证据 ②③ 都在对着别的类判, + # 裸名一旦被首次注册就会被永久复用。 + _w_cls_cur: str = self.Gen.StructGen.CurDefClassName + _w_need_log: int = 0 if _bare_entry is not None and _bare_entry.ModuleSha1 is None: + _w_need_log = 1 + if _def_mod is not None and _w_cls_cur is not None: + if string.strcmp(_w_cls_cur, type_name) != 0: + _w_need_log = 1 + if _w_need_log != 0: _w_mod: str = "None" if _def_mod is not None: _w_mod = _def_mod _w_cls: str = "None" - if self.Gen.StructGen.CurDefClassName is not None: - _w_cls = self.Gen.StructGen.CurDefClassName + if _w_cls_cur is not None: + _w_cls = _w_cls_cur _w_fi: str = "None" if from_imports is not None: _w_fi = from_imports _w_file: str = "None" if self.CurrentFile is not None: _w_file = self.CurrentFile _w_imc: int = 0 + _w_fbm: int = 0 if _def_mod is not None: _w_imc = self.Gen.StructGen.is_module_class(pool, _def_mod, type_name) + if self.Gen.StructGen.find_struct_by_module(type_name, _def_mod) is not None: + _w_fbm = 1 + _w_ep: int = 0 + if _bare_entry is not None: _w_ep = 1 _w_ty: str = "None" - if _bare_entry.Ty is not None: + if _bare_entry is not None and _bare_entry.Ty is not None: _w_buf: str = pool.alloc(288) if _w_buf is not None: _w_buf[0] = '\0' llvmlite.TypePrint(_w_buf, 288, _bare_entry.Ty, pool) if _w_buf[0] != '\0': _w_ty = _w_buf VLogger.error( - f"[类型实参归属] 裸类名 '{type_name}' 命中 struct 条目但取不到定义模块 SHA1" - f"(本次保留裸名,可能与带前缀形式分裂):" - f"entry.Ty={_w_ty} cur_def_mod={_w_mod} cur_def_cls={_w_cls} " - f"is_module_class={_w_imc} from_imports={_w_fi} file={_w_file}", + f"[类型实参归属] 裸类名 '{type_name}' 本次保留裸名(可能与带前缀形式分裂):" + f"entry_present={_w_ep} entry.Ty={_w_ty} cur_def_mod={_w_mod} cur_def_cls={_w_cls} " + f"is_module_class={_w_imc} find_struct_by_module={_w_fbm} " + f"from_imports={_w_fi} file={_w_file}", "TypeGen") # ---- 6. 查不到:保持原样(内置类型名等)---- return type_name