From d63787f8fc6dd8f1ba5904545f1515046ab20633 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 30 Sep 2026 13:51:22 +0800 Subject: [PATCH] =?UTF-8?q?fix(TypeGenResolve):=20=E3=80=8C=E4=BF=9D?= =?UTF-8?q?=E7=95=99=E8=A3=B8=E5=90=8D=E3=80=8D=E5=8F=96=E8=AF=81=E5=AF=B9?= =?UTF-8?q?=20CTypedef/=E5=86=85=E7=BD=AE=E6=A0=87=E9=87=8F/=E6=B3=9B?= =?UTF-8?q?=E5=9E=8B=E5=BD=A2=E5=8F=82=E4=B8=8D=E5=86=8D=E6=8A=A5=20ERROR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 【b 机制漏豁免】取证日志(df1244d 加入)的前提是"这是个**类名**":只有类名存在 "{定义模块SHA1}.类" 这第二个 mangling 形式,留裸名才可能与它分裂。CTypedef / 语言内置标量 / 泛型形参的名域就是它自身,保留裸名即规范形式(TPC 侧 SymbolUtils.NormalizeGenericArgName 同口径),对它报 ERROR 是纯噪声。 现场:编译 includes/traceback.vp 时,TracebackState.FrameAddr 的注解 `t.CArray[uint64, TRACEBACK_FRAME_BUF]`(traceback.vp:532)经规则 1.5 递归规范化 实参 'uint64' ⇒ 规则 5.5 取不到 SHA1 ⇒ 每次编译刷一条 "[类型实参归属] 裸类名 'uint64' 本次保留裸名…"。'uint64' 是 includes/builtins.vp 的 `typedef uint64 = t.CUInt64T`,不进类归属是预期行为,不是缺陷。 修法:新增 `_is_non_class_type_name(pool, name)`(正向白名单,不猜): ① CTypedef 动态表命中(Phase1 1b register_ctypedef_from_source 文本扫描注册); ② builtins 兜底 `_lookup_builtins_typedef`(与 resolve_typedef_or_map 同一口径: builtins 始终在编译链内,但其 typedef 不随 from_imports 注入 ⇒ 表可能 miss; 命中即幂等注册进表); ③ 语言内置标量/字符串(int/char/bool/float/double/str/bytes/void,与 _resolve_annot_type_name 的跳过清单同口径); ④ 泛型形参名(单字母大写 T/U/V…,特化前出现在实参位属正常)。 门控只对「结构体表里根本没有该名」(_bare_entry is None)豁免:若它确是已注册的类 (.vpi 半条目)只是缺定义模块 SHA1,那是真正的归属缺口,照旧报出,不被同名 typedef 掩盖。 未验证:本改动需重新自举 TransPyV.exe 才生效(TPC 侧无对应逻辑)。vast 解析通过。 --- App/lib/core/Gen/Type/TypeGenResolve.vp | 84 +++++++++++++++++++++---- 1 file changed, 71 insertions(+), 13 deletions(-) diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index bb28258..d8971aa 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -261,6 +261,50 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): # fcount=-1 = opaque 哨兵(区别于空结构体 {}:后者 fcount==0 是"完整定义、 # 可 alloca");与 llvmlite 的跨模块 opaque 声明同一表示。 return llvmlite.Struct(pool, None, -1, full) + + def _is_non_class_type_name(self, pool: memhub.MemBuddy*, name: str) -> int: + """该类型名是否**本就无类归属**(CTypedef / 语言内置标量 / 泛型形参)——不是类名。 + + 用途:`_norm_type_arg_name` 的"保留裸名"取证门控。该取证的前提是"这是个**类名**" + —— 类名有 "{定义模块SHA1}.类" 这一 mangling 形式,留在裸名才会与带前缀形式分裂; + 而下列名字的名域就是它自身,不存在第二个形式(TPC 侧 + `SymbolUtils.NormalizeGenericArgName` 同样原样保留)⇒ 对它报 ERROR 是纯噪声。 + 现场(2026-09-30):includes/traceback.vp:532 + `FrameAddr: t.CArray[uint64, TRACEBACK_FRAME_BUF]` 的实参 'uint64' 是 + includes/builtins.vp 的 `typedef uint64 = t.CUInt64T`,不进类归属是**预期**, + 却每次编译刷一条 ERROR。 + + 判据(正向白名单,逐条可查,不猜): + ① CTypedef 动态表命中 —— typedef 唯一真源(Phase1 1b 的 + register_ctypedef_from_source 文本扫描注册;表在文件边界被 clear 后重建, + 故紧跟 ② 的 builtins 兜底,与 `resolve_typedef_or_map` 的"统一口径"一致); + ② builtins.vp 的 typedef(如 `typedef uint64 = t.CUInt64T`):builtins 始终 + 在编译链内,但其 typedef 不随当前文件 from_imports 注入 ⇒ 表可能 miss, + 必须走与 `resolve_typedef_or_map` 同一兜底(命中即注册进表,幂等); + ③ 语言内置标量/字符串名 —— 与 `_resolve_annot_type_name` 的跳过清单同口径; + ④ 泛型形参名(单字母大写,T/U/V…)—— 特化前出现在实参位属正常。 + """ + if name is None or name[0] == '\0': + return 0 + if self.Gen.TypeGenCType is not None: + _ts_nc: str = self.Gen.TypeGenCType.lookup_ctypedef(name) + if _ts_nc is not None and self.Gen.TypeGenCType.CTypedefFound != 0: + return 1 + if self._lookup_builtins_typedef(pool, name) is not None: + return 1 + if string.strcmp(name, "int") == 0: return 1 + if string.strcmp(name, "char") == 0: return 1 + if string.strcmp(name, "bool") == 0: return 1 + if string.strcmp(name, "float") == 0: return 1 + if string.strcmp(name, "double") == 0: return 1 + if string.strcmp(name, "str") == 0: return 1 + if string.strcmp(name, "bytes") == 0: return 1 + if string.strcmp(name, "void") == 0: return 1 + if string.strlen(name) == 1: + if name[0] >= 'A' and name[0] <= 'Z': + return 1 + return 0 + def _norm_type_arg_name(self, pool: memhub.MemBuddy*, type_name: str, from_imports: str) -> str: @@ -289,7 +333,9 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): vs 目标 %"…list[rawptr[AST]]"*;includes/ast/base.vp:357 `children = list[rawptr[AST]](self.pool, 8)` 同族反向:字段侧裸名 vs 构造侧带前缀) - 6. 仍查不到:返回原串(内置类型名等,保持原样) + 6. 仍查不到:返回原串(CTypedef / 内置标量 / 泛型形参等**非类名**保持原样; + 它们本就没有"带前缀形式"可分裂,故也不触发下方的"保留裸名"取证日志 —— + 判据见 _is_non_class_type_name) 返回 None 表示 type_name 为 None。 """ @@ -560,18 +606,18 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): string.strncpy(_sha_b, _tp_nm, _sp_l - 1) _sha_b[_sp_l - 1] = '\0' _def_sha1 = _sha_b - # ⑤ **本文件顶层类名真源**(与"窗口"、与类定义顺序无关):Translator 的文件 - # 入口已把"本文件顶层 ClassDef 的类名 + 本文件模块 SHA1"登记进 StructGen - # (register_file_classes)。模块级变量/前向声明**早于**类定义被翻译 - # (Translator.vp:592-601 < :602 translate_children)时,CurDefModuleSha1 - # 尚为 None ⇒ ①②③④ 全落空,裸类名(如 base.vp:148 `_append_child(..., - # lst: list[rawptr[AST]])` 的形参)会被冻结成裸名版本并与带 SHA1 形式分裂。 - # 判据是**正向事实**:该名字确由本文件声明(且本文件模块 SHA1 已知)⇒ 不猜。 - if _def_sha1 is None: - _file_sha: str = self.Gen.StructGen.get_file_module_sha1() - if _file_sha is not None: - if self.Gen.StructGen.is_file_class(pool, type_name) != 0: - _def_sha1 = _file_sha + # ⑤ **本文件顶层类名真源**(与"窗口"、与类定义顺序无关):Translator 的文件 + # 入口已把"本文件顶层 ClassDef 的类名 + 本文件模块 SHA1"登记进 StructGen + # (register_file_classes)。模块级变量/前向声明**早于**类定义被翻译 + # (Translator.vp:592-601 < :602 translate_children)时,CurDefModuleSha1 + # 尚为 None ⇒ ①②③④ 全落空,裸类名(如 base.vp:148 `_append_child(..., + # lst: list[rawptr[AST]])` 的形参)会被冻结成裸名版本并与带 SHA1 形式分裂。 + # 判据是**正向事实**:该名字确由本文件声明(且本文件模块 SHA1 已知)⇒ 不猜。 + if _def_sha1 is None: + _file_sha: str = self.Gen.StructGen.get_file_module_sha1() + if _file_sha is not None: + if self.Gen.StructGen.is_file_class(pool, type_name) != 0: + _def_sha1 = _file_sha if _def_sha1 is not None: res_d: str = self._concat_sha1_class(pool, _def_sha1, type_name) if res_d is not None: @@ -592,6 +638,18 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): 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 + # 门控(2026-09-30):取证的前提是"这是个**类名**"——只有类名存在 + # "{定义模块SHA1}.类" 这第二个形式可分裂。CTypedef(如 builtins.vp 的 + # `typedef uint64 = t.CUInt64T`)/ 语言内置标量 / 泛型形参的名域就是它自身, + # 保留裸名即规范形式(TPC NormalizeGenericArgName 同口径)⇒ 不对它们报错。 + # 现场:includes/traceback.vp:532 `t.CArray[uint64, TRACEBACK_FRAME_BUF]` + # 每编译一次刷一条与真因无关的 ERROR。 + # 只对**结构体表里根本没有该名**(_bare_entry is None)的情形豁免:若它确 + # 是已注册的类(如 .vpi 半条目)只是缺定义模块 SHA1,那是真正的归属缺口, + # 照旧报出(不被同名 typedef 掩盖)。 + if _w_need_log != 0 and _bare_entry is None \ + and self._is_non_class_type_name(pool, type_name) != 0: + _w_need_log = 0 if _w_need_log != 0: _w_mod: str = "None" if _def_mod is not None: _w_mod = _def_mod