From 93e983f74ee663c8042acadef57dca5dab83752c Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Thu, 1 Oct 2026 17:46:53 +0800 Subject: [PATCH] =?UTF-8?q?fix(TPV):=20=E5=88=A0=E9=99=A4=E6=AE=8B?= =?UTF-8?q?=E7=95=99=E7=9A=84"|=20t.CPtr=20struct=20=E5=BC=95=E7=94=A8?= =?UTF-8?q?=E8=AF=AD=E4=B9=89=E5=90=B8=E6=94=B6"=EF=BC=88T*=20=E8=A2=AB?= =?UTF-8?q?=E9=99=8D=E4=B8=80=E5=B1=82=E7=9A=84=E7=9C=9F=E6=AD=A3=E6=A0=B9?= =?UTF-8?q?=E5=9B=A0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现场(0a81e67 之后仍复现):Test/SlotsBugTest 编译 includes/_list.vp:54 TypeError: 下标赋值的左值元素是对象 'd1ed020479454541.AST'(48 字节),右值却是指向 'd1ed020479454541.AST' 的指针 elem_ptr[0] = item 关键事实:`T*` **不是** `rawptr[T]` 下标形——parser 把尾随 '*' 解析成 BinOp(`T | t.CPtr`)(includes/ast/parser.vp:1528-1556 `_make_ptr_type`、:2700-2707)。 所以它走 resolve_annotation_type 的 **BinOp 联合分支**,0a81e67 只修了 rawptr[T] / t.CPtr[T] 两条**下标**分支,覆盖不到这里。 链路(逐段在源码上核对过): 1. 泛型实参 `rawptr[AST]` 经 _norm_type_arg_name 归一为文本 `{sha}.AST*`; 2. `T` 走 Name 分支的泛型替换 → _parse_type_string 的 `T*` 反解析(:995-1018, 按尾随 '*' 层数包 rawptr[...])⇒ other_ty = Ptr(AST),本身正确; 3. BinOp 分支尾部的"struct 引用语义吸收":other_ty 已是 Ptr(已注册 struct) ⇒ 直接 `return other_ty`,**吞掉 CPtr 这一层** ⇒ `T*` 变成 AST*(应 AST**)。 改动: - TypeGenResolve.vp BinOp 分支:删除该吸收,恒 `return Ptr(other_ty)` (与 rawptr[T] / t.CPtr[T] 下标形、_parse_type_string 的 `T*` 反解析三处同口径)。 - VpiReader.vp:1614-1621 注入侧的同源吸收一并删除(维持"定义侧 / 注入侧 / 调用侧 三处指针层数口径同步")。 - 两处口径注释同步改为"`| t.CPtr` 恒追加恰好 1 层,无例外"。 安全性(该吸收已是死补偿):它是"裸类名自动升指针"时代的产物——当时裸类名解析成 Ptr(struct),所以要吸收;自 2026-10-01 起裸类名恒为原型 struct,`X | t.CPtr` 由 Ptr(struct) 天然得到 X*。json/__init__.vp:90 的引号注解 `'JsonValue' | t.CPtr` 现在 other_ty 就是 struct ⇒ 吸收本不触发,删除后行为不变(JsonTest 已实测翻译通过并 进入 llc 阶段)。`list[int]`(i32*)、`list[ast.AST]`(struct*)、裸类名 `X | t.CPtr` (struct*)均不受影响。 待验证:重编译 TransPyV(自举两遍)→ SlotsBugTest / JsonTest / WebTest。 --- App/lib/Stub/StubGen/VpiReader.vp | 16 +++++----- App/lib/core/Gen/Type/TypeGenResolve.vp | 40 +++++++++++-------------- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index 7f45b31..987c4cd 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -1605,19 +1605,19 @@ def _resolve_vpi_ret_annot_str(pool: memhub.MemBuddy*, if first_ty is not None: # 复刻定义侧规则(N12/N13):限定名 / 白名单裸标量 / 已注册结构体 / 可解析的非指针裸 typedef 别名 → 保留 pointee; # 其余(解析失败、裸枚举名、指针别名)→ 联合坍缩为 i8*,与定义侧 resolve_annotation_type 对称(两侧同型即收敛)。 - # 指针别名(first_td 不置位,如 VOIDPTR = CVoid|CPtr → i8*):`| t.CPtr` **追加 1 层** → i8**(不吸收); - # 仅当 pointee 命中 find_struct_by_type(类引用语义)时吸收 1 层,其余 ⇒ Ptr(first_ty)(与定义侧指针层数口径同源)。 + # 指针别名(first_td 不置位,如 VOIDPTR = CVoid|CPtr → i8*):`| t.CPtr` + # **恒追加恰好 1 层** → i8**(不吸收);指针型 T 同理(见下)。 if first_qual != 0 or first_wl != 0 or first_td != 0: return llvmlite.Ptr(pool, first_ty) if first_ty.kind() == llvmlite.LLVMType.Struct: return llvmlite.Ptr(pool, first_ty) if first_ty.kind() == llvmlite.LLVMType.Ptr: - match first_ty: - case llvmlite.LLVMType.Ptr(pointee_v): - if GenApi.find_struct_by_type(pointee_v) is not None: - return first_ty - case _: - pass + # `| t.CPtr` **恒追加恰好 1 层**(与定义侧 TypeGenResolve 的 BinOp 分支同口径): + # 这里原先还做"pointee 命中 find_struct_by_type(类引用语义)就吸收 1 层", + # 那是"裸类名自动升指针"时代的补偿,自裸类名恒为**原型 struct** 后已无对象; + # 而泛型形参实例化为指针类型时(T 的实参文本 `{sha}.AST*`)first_ty 正是 + # Ptr(已注册 struct) ⇒ 误吸收把 `T*` 降成 AST*(应为 AST**), + # 现场 includes/_list.vp:54 `elem_ptr[0] = item`。 return llvmlite.Ptr(pool, first_ty) return i8_ptr_ty return i8_ptr_ty diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index d51e07c..36f1aee 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -3161,8 +3161,9 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): # 判定该裸名是否可解析,命中即保留已解析类型参与「基类型 + 指针层数」合并 # (对齐 TPC HandlesTypeMerge._MergeAllComponentTypes)。 # 指针层数口径(本处 / VpiReader 注入侧 / 调用侧三处必须同步):`| t.CPtr` - # 恒为"追加 1 层指针",唯一例外是 struct 引用语义时吸收这 1 层 - # (判据 = Ptr 的 pointee 是否为已注册 struct,find_struct_by_type 命中即吸收)。 + # **恒为"追加恰好 1 层指针",无例外**——裸类名恒为原型 struct, + # `X | t.CPtr` 由 Ptr(struct) 天然得到 X*,不再需要"pointee 是已注册 + # struct 就吸收这 1 层"的旧补偿(该补偿曾把指针型 T 的 `T*` 降一层)。 if keep_other_ty == 0 and other_node is not None and other_node.kind() == ast.ASTKind.Name: other_nm_td: ast.Name* = ast.Name*(other_node) if other_nm_td.id is not None: @@ -3205,28 +3206,21 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): # 回退 Ptr(i8) 由 AnnotClassName 恢复 + 多遍注册收敛 # 处理,禁止 fail-fast(误杀合法前向引用)。 return llvmlite.Ptr(pool, llvmlite.Int8(pool)) - # struct 引用语义吸收(本文件 :3219-3221 早已写明、此前未实现的 - # 那条规则):other_ty 若已是「指向已注册 struct 的指针」,说明 - # 该侧裸类名已按**引用语义**解析过(有 __new__ 的堆类,Name/ - # Attribute 分支直接给 Ptr(struct),见 :2806-2813、:3040-3044) - # ⇒ `| t.CPtr` 不再追加一层,否则多一层指针。 - # 三种写法必须同型(用户 2026-09-30 定:统一为 T*): + # `X | t.CPtr` **恒追加恰好 1 层**(用户口径,与 rawptr[T]/t.CPtr[T] 下标形 + # 以及 `_parse_type_string` 的 `T*` 反解析三处同源): # T* ≡ rawptr[T] ≡ t.CPtr[T] ≡ T | t.CPtr - # 现场:includes/json/__init__.vp:90 `return cur` - # `def __getitem__(...) -> 'JsonValue' | t.CPtr`(**引号字符串** - # 注解,左边是 Constant 不是 Name —— 绕过了上面 :3176 那段 - # "Name 必须已注册 struct 否则丢弃"的守卫,于是带着 Ptr(struct) - # 走到这里再包一层 ⇒ JsonValue**),而 `cur: JsonValue*` 是 - # JsonValue* ⇒ CastError「指针层数不同(源 1 级 → 目标 2 级)」。 - # 之所以只在引号写法暴露:裸名写法会在 :3222-3239 被判定 - # td_res_is_ptr=1 → other_ty 置 None → 走 :3254 回退取 struct 再包 - # 一层,恰好抵消;引号写法绕过了那条路径。 - match other_ty: - case llvmlite.LLVMType.Ptr(pointee_ab): - if self.Gen.StructGen.find_struct_by_type(pointee_ab) is not None: - return other_ty - case _: - pass + # 这里原先还有一段"struct 引用语义吸收"(other_ty 若已是 Ptr(已注册 struct) + # 就直接返回它、吞掉 CPtr 这层),那是"裸类名自动升指针"时代的补偿: + # 当时裸类名/引号注解解析成 Ptr(struct),所以要吸收;自 2026-10-01 起 + # 裸类名恒为**原型 struct**(见本文件 Name/Attribute 各分支), + # `X | t.CPtr` 由 Ptr(struct) 天然得到 X*,吸收已无正当对象 + # (json/__init__.vp:90 的引号注解 `'JsonValue' | t.CPtr` 现在 other_ty + # 就是 struct,吸收本就不触发,删除后行为不变)。 + # 而**泛型形参实例化为指针类型**时(`list[rawptr[AST]]` 里 `T` 的实参文本是 + # `{sha}.AST*`,parser 把源码 `T*` 解析成 BinOp `T | t.CPtr`)other_ty 正是 + # Ptr(已注册 struct) ⇒ 误吸收把 `T*` 降成 AST*(应为 AST**), + # 现场 includes/_list.vp:54 `elem_ptr[0] = item` 被误判为"左值元素是对象 + # AST(48 字节)、右值是指针"而 fail-fast。 return llvmlite.Ptr(pool, other_ty) # t.CPtr[...] 下标指针标记(普通 _is_cptr_marker 不识别 Subscript):