fix(transpyv): 特化名规范化下沉到唯一生成器,裸实参路径不再造第二个名字

类别:a(编译器缺陷 —— 特化名生成点不止一处,实参在"裸名/带前缀"两态并存)

背景(命名源审计,两个独立检索子代理收敛到同一清单):`_norm_type_arg_name`
是唯一规范化器,但"拼 Class[a0,a1]"的构造点有 20+ 处,其中 6 处绕过它。
本轮把规范化**下沉到唯一生成器**,让所有调用方自动同源 —— 该函数对已规范化的串
是幂等的(规则 2/3/6 原样返回,嵌套 rawptr[X] 递归同形)。

改动:
1. ClassGenGeneric._mangle_generic_class_name 增 from_imports 参数:逐实参经
   TypeGenResolve._norm_type_arg_name 归一后再拼串(长度计算与拼接同用归一后列表)。
   唯一调用点 _specialize_generic_class 传 trans._from_imports。
2. ClassGenBase._get_base_name 增 from_imports 参数:Subscript 分支的类型实参
   (原为裸 Name.id)同样归一 —— 该函数产出的父类特化名要拿去 find_struct_by_module
   查、并写入 entry.ParentName,而注册名由生成器产出,不归一必然对不上
   (父类 GSListNode[Value] vs 注册的 GSListNode[{sha1}.Value])。
   三处调用点(_get_parent_class / _collect_parents / _trigger_base_specialization)
   传 trans._from_imports;递归调用透传。
3. _trigger_base_specialization 本身无需改:它的裸实参进入 _specialize_generic_class,
   已由第 1 条统一归一。

收敛效果(连带):_specialize_generic_class 的 spec_name 与
TypeGenResolve._resolve_generic_subscript_ty 自拼的 gen_mangled 现已同形(后者本就
归一),源码注释里"gen_mangled 与特化名可能不一致 ⇒ 用 spec_name_chk 重查"那条
隐性双名自然不再触发。

遗留(未动,见审计清单):ExprGenConst.translate_list_literal /
CallGenCtor._infer_generic_ctor_type_arg 传的内建名不受影响(规则 6 原样保留),
但其"类名"实参现由生成器统一归一;CallGenCall/CallGenDispatch 从已注册名回拆
"[...]" 时用 strchr 按首个逗号切分(深度不敏感)另计。

影响面:ABI 可见(结构体名 / 方法符号后缀段);需 TPV 自举重建 + 回归验证,
重点观察 VpiReader 的泛型键匹配(其键以 T 占位符,初判不受影响)。
This commit is contained in:
TermiNexus committed 2026-09-29 18:53:58 +08:00
1 parent 868d48ae22
commit 2b8cc2a69b
2 files changed
+56 -11

No files matched your search

+21 -5
View File
@@ -888,11 +888,18 @@ novirtual class ClassGenBase:
return 1
return 0
def _get_base_name(self, base_node: ast.AST*,
pool: memhub.MemBuddy* = None) -> str:
pool: memhub.MemBuddy* = None,
from_imports: str = None) -> str:
"""从 base AST 节点提取基类名,返回 None=失败
对于 Subscript 节点,需要 pool 分配内存拼接特化名;
pool=None 时降级返回基类名(不含类型实参)
类型实参必须与**唯一生成器**(ClassGenGeneric._mangle_generic_class_name)
同口径:本函数产出的父类特化名要拿去 find_struct_by_module 查、并写入
`entry.ParentName`,而注册名由生成器(内部经 _norm_type_arg_name 归一)
产出 ⇒ 这里同样归一,否则父类 `GSListNode[Value]` 与注册的
`GSListNode[{sha1}.Value]` 不同名 ⇒ 父类查找落空/继承链断裂。
"""
if base_node is None:
return None
@@ -911,10 +918,10 @@ novirtual class ClassGenBase:
if sub is None or sub.value is None or sub.slice is None:
return None
# 提取基类名(如 "GSListNode")
base_nm: str = self._get_base_name(sub.value, pool)
base_nm: str = self._get_base_name(sub.value, pool, from_imports)
if base_nm is None:
return None
# 提取类型实参名(如 "Value")
# 提取类型实参名(如 "Value"),并与唯一生成器同口径归一
slice_k: int = sub.slice.kind()
arg_nm: str = None
if slice_k == ast.ASTKind.Name:
@@ -926,6 +933,9 @@ novirtual class ClassGenBase:
# 无 pool 时返回基类名(降级,无法拼接特化名)
if pool is None:
return base_nm
arg_norm: str = self.Gen.TypeGenResolve._norm_type_arg_name(pool, arg_nm, from_imports)
if arg_norm is not None:
arg_nm = arg_norm
# 拼接特化名 "GSListNode[Value]"
base_len: sizet = string.strlen(base_nm)
arg_len: sizet = string.strlen(arg_nm)
@@ -956,7 +966,10 @@ novirtual class ClassGenBase:
base_node: ast.AST* = bases.get(bi)
if base_node is None:
continue
bname: str = self._get_base_name(base_node, pool)
_fi_pc: str = None
if trans is not None:
_fi_pc = trans._from_imports
bname: str = self._get_base_name(base_node, pool, _fi_pc)
if bname is not None:
# 基类别名解析(from t import CStruct as cs → cs 实际是 CStruct)。
# 不解析会把别名当真实父类 → find_struct("cs") 失败 → 类 defer 永不注册。
@@ -1008,7 +1021,10 @@ novirtual class ClassGenBase:
base_node: ast.AST* = bases.get(bi)
if base_node is None:
continue
bname: str = self._get_base_name(base_node, pool)
_fi_cp: str = None
if trans is not None:
_fi_cp = trans._from_imports
bname: str = self._get_base_name(base_node, pool, _fi_cp)
if bname is not None:
# 基类别名解析(同 _get_parent_class,见 _resolve_base_alias)
bname = self._resolve_base_alias(bname, trans)
+35 -6
View File
@@ -241,23 +241,49 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
return None
def _mangle_generic_class_name(self, pool: memhub.MemBuddy*,
class_name: str,
type_args: list[str]*) -> str:
type_args: list[str]*,
from_imports: str = None) -> str:
"""生成特化类名: list + [int] -> list[int]
**唯一生成器 + 唯一规范化点**:所有特化名(结构体注册名、方法符号里的
后缀段、查找键)都由这里产出,故类型实参的 SHA1 规范化只在这里做一次 ——
每个实参经 `TypeGenResolve._norm_type_arg_name` 归一到
`{定义模块SHA1}.{Class}`(该函数对已规范化的串**幂等**:规则 2/3/6 原样
返回,嵌套 `rawptr[X]` 也递归同形)⇒ 无论调用方(注解侧 / 构造侧 /
基类侧 / 字面量推断侧)传进来的实参是裸名还是已带前缀,注册名与查找名
**必然同形**。此前裸露名与已带前缀的两侧会分裂成两个特化结构体,已出现
三种现场:形参侧/实参侧(stmts.vp:60)、字段侧/构造侧(base.vp:357)、
基类侧/注解侧(ClassGenBase._get_base_name 的裸 Name.id)。
分配新缓冲区拼接,避免原地 strcat 导致缓冲区溢出
"""
if class_name is None or type_args is None or pool is None:
return class_name
n: sizet = len(type_args)
if n == 0:
return class_name
# 逐实参规范化(幂等):先归一成新列表,长度计算与拼接都用归一后的串
norm_args: list[str]* = list[str](pool, 8)
if norm_args is None:
return class_name
i: sizet
for i in range(n):
ta_raw: str = type_args.get(i)
if ta_raw is None:
continue
ta_norm: str = self.Gen.TypeGenResolve._norm_type_arg_name(pool, ta_raw, from_imports)
if ta_norm is None:
ta_norm = ta_raw
norm_args.append(ta_norm)
n = len(norm_args)
if n == 0:
return class_name
# 计算总长度: class_name + "[" + arg0 + "," + arg1 + ... + "]" + NUL
total_len: sizet = string.strlen(class_name) + 2 # "[" 和 "]"
i: sizet
for i in range(n):
if i > 0:
total_len += 1 # ","
ta: str = type_args.get(i)
ta: str = norm_args.get(i)
if ta is not None:
total_len += string.strlen(ta)
total_len += 1 # NUL
@@ -271,7 +297,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
for i in range(n):
if i > 0:
string.strcat(mangled, ",")
ta: str = type_args.get(i)
ta: str = norm_args.get(i)
if ta is not None:
string.strcat(mangled, ta)
string.strcat(mangled, "]")
@@ -566,8 +592,11 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
self.GenericSpecDepth -= 1
return None
# 2. 生成特化名
spec_name: str = self._mangle_generic_class_name(pool, class_name, type_args)
# 2. 生成特化名(唯一生成器内部逐实参规范化:裸名/带前缀在此收敛为同一名)
_from_imports: str = None
if trans is not None:
_from_imports = trans._from_imports
spec_name: str = self._mangle_generic_class_name(pool, class_name, type_args, _from_imports)
if spec_name is None:
self.GenericSpecDepth -= 1
return None