fix(TypeGenResolve): 「保留裸名」取证对 CTypedef/内置标量/泛型形参不再报 ERROR

【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 解析通过。
This commit is contained in:
TermiNexus committed 2026-09-30 13:51:22 +08:00
1 parent 0e5ebd07ba
commit d63787f8fc
1 file changed
+71 -13
+71 -13
View File
@@ -261,6 +261,50 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
# fcount=-1 = opaque 哨兵(区别于空结构体 {}:后者 fcount==0 是"完整定义、 # fcount=-1 = opaque 哨兵(区别于空结构体 {}:后者 fcount==0 是"完整定义、
# 可 alloca");与 llvmlite 的跨模块 opaque 声明同一表示。 # 可 alloca");与 llvmlite 的跨模块 opaque 声明同一表示。
return llvmlite.Struct(pool, None, -1, full) 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*, def _norm_type_arg_name(self, pool: memhub.MemBuddy*,
type_name: str, type_name: str,
from_imports: str) -> str: from_imports: str) -> str:
@@ -289,7 +333,9 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
vs 目标 %"…list[rawptr[AST]]"*;includes/ast/base.vp:357 vs 目标 %"…list[rawptr[AST]]"*;includes/ast/base.vp:357
`children = list[rawptr[AST]](self.pool, 8)` 同族反向:字段侧裸名 vs `children = list[rawptr[AST]](self.pool, 8)` 同族反向:字段侧裸名 vs
构造侧带前缀) 构造侧带前缀)
6. 仍查不到:返回原串(内置类型名等,保持原样) 6. 仍查不到:返回原串(CTypedef / 内置标量 / 泛型形参等**非类名**保持原样;
它们本就没有"带前缀形式"可分裂,故也不触发下方的"保留裸名"取证日志 ——
判据见 _is_non_class_type_name)
返回 None 表示 type_name 为 None。 返回 None 表示 type_name 为 None。
""" """
@@ -560,18 +606,18 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
string.strncpy(_sha_b, _tp_nm, _sp_l - 1) string.strncpy(_sha_b, _tp_nm, _sp_l - 1)
_sha_b[_sp_l - 1] = '\0' _sha_b[_sp_l - 1] = '\0'
_def_sha1 = _sha_b _def_sha1 = _sha_b
# ⑤ **本文件顶层类名真源**(与"窗口"、与类定义顺序无关):Translator 的文件 # ⑤ **本文件顶层类名真源**(与"窗口"、与类定义顺序无关):Translator 的文件
# 入口已把"本文件顶层 ClassDef 的类名 + 本文件模块 SHA1"登记进 StructGen # 入口已把"本文件顶层 ClassDef 的类名 + 本文件模块 SHA1"登记进 StructGen
# (register_file_classes)。模块级变量/前向声明**早于**类定义被翻译 # (register_file_classes)。模块级变量/前向声明**早于**类定义被翻译
# (Translator.vp:592-601 < :602 translate_children)时,CurDefModuleSha1 # (Translator.vp:592-601 < :602 translate_children)时,CurDefModuleSha1
# 尚为 None ⇒ ①②③④ 全落空,裸类名(如 base.vp:148 `_append_child(..., # 尚为 None ⇒ ①②③④ 全落空,裸类名(如 base.vp:148 `_append_child(...,
# lst: list[rawptr[AST]])` 的形参)会被冻结成裸名版本并与带 SHA1 形式分裂。 # lst: list[rawptr[AST]])` 的形参)会被冻结成裸名版本并与带 SHA1 形式分裂。
# 判据是**正向事实**:该名字确由本文件声明(且本文件模块 SHA1 已知)⇒ 不猜。 # 判据是**正向事实**:该名字确由本文件声明(且本文件模块 SHA1 已知)⇒ 不猜。
if _def_sha1 is None: if _def_sha1 is None:
_file_sha: str = self.Gen.StructGen.get_file_module_sha1() _file_sha: str = self.Gen.StructGen.get_file_module_sha1()
if _file_sha is not None: if _file_sha is not None:
if self.Gen.StructGen.is_file_class(pool, type_name) != 0: if self.Gen.StructGen.is_file_class(pool, type_name) != 0:
_def_sha1 = _file_sha _def_sha1 = _file_sha
if _def_sha1 is not None: if _def_sha1 is not None:
res_d: str = self._concat_sha1_class(pool, _def_sha1, type_name) res_d: str = self._concat_sha1_class(pool, _def_sha1, type_name)
if res_d is not None: 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 _def_mod is not None and _w_cls_cur is not None:
if string.strcmp(_w_cls_cur, type_name) != 0: if string.strcmp(_w_cls_cur, type_name) != 0:
_w_need_log = 1 _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: if _w_need_log != 0:
_w_mod: str = "None" _w_mod: str = "None"
if _def_mod is not None: _w_mod = _def_mod if _def_mod is not None: _w_mod = _def_mod