From 2b8cc2a69b37264df0e4ee4862db51b7c59e888a Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 29 Sep 2026 18:53:58 +0800 Subject: [PATCH] =?UTF-8?q?fix(transpyv):=20=E7=89=B9=E5=8C=96=E5=90=8D?= =?UTF-8?q?=E8=A7=84=E8=8C=83=E5=8C=96=E4=B8=8B=E6=B2=89=E5=88=B0=E5=94=AF?= =?UTF-8?q?=E4=B8=80=E7=94=9F=E6=88=90=E5=99=A8=EF=BC=8C=E8=A3=B8=E5=AE=9E?= =?UTF-8?q?=E5=8F=82=E8=B7=AF=E5=BE=84=E4=B8=8D=E5=86=8D=E9=80=A0=E7=AC=AC?= =?UTF-8?q?=E4=BA=8C=E4=B8=AA=E5=90=8D=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类别: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 占位符,初判不受影响)。 --- App/lib/core/Gen/Class/ClassGenBase.vp | 26 +++++++++++--- App/lib/core/Gen/Class/ClassGenGeneric.vp | 41 +++++++++++++++++++---- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/App/lib/core/Gen/Class/ClassGenBase.vp b/App/lib/core/Gen/Class/ClassGenBase.vp index ef1db27..8ad60ee 100644 --- a/App/lib/core/Gen/Class/ClassGenBase.vp +++ b/App/lib/core/Gen/Class/ClassGenBase.vp @@ -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) diff --git a/App/lib/core/Gen/Class/ClassGenGeneric.vp b/App/lib/core/Gen/Class/ClassGenGeneric.vp index baae092..ebe5d9c 100644 --- a/App/lib/core/Gen/Class/ClassGenGeneric.vp +++ b/App/lib/core/Gen/Class/ClassGenGeneric.vp @@ -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