From 660427f759de26d8d49fcc85ab5eb4e6253d3f0d Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Thu, 1 Oct 2026 00:51:51 +0800 Subject: [PATCH] =?UTF-8?q?fix(TypeGenResolve):=20`X=20|=20t.CPtr`=20?= =?UTF-8?q?=E7=9A=84=20struct=20=E5=BC=95=E7=94=A8=E8=AF=AD=E4=B9=89?= =?UTF-8?q?=E5=90=B8=E6=94=B6=E8=BF=99=E5=B1=82=E6=8C=87=E9=92=88=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=20json=20=5F=5Fgetitem=5F=5F=20=E6=8A=A5=20JsonValue*?= =?UTF-8?q?*=20vs=20JsonValue*=20=E5=B1=82=E6=95=B0=E4=B8=8D=E5=90=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现象(TransPyV 编译 includes/json/__init__.vp): line 90 return cur CastError: 指针隐式转指针已禁止:指针层数不同(源 1 级 → 目标 2 级) 源 %"e966d554e91364bc.JsonValue"* → 目标 %"e966d554e91364bc.JsonValue"** 根因链(逐环取证): 1. includes/json/__init__.vp:79 def __getitem__(self, key: str) -> 'JsonValue' | t.CPtr: 注意左边是**引号字符串注解**(Constant),不是裸 Name。 2. JsonValue 是带 __new__ 的**堆类**(该文件 :34),按引用语义,裸类名注解在 resolve_annotation_type 的 Name/Attribute 分支直接给 Ptr(struct) (:2806-2813 注释「有 __new__ 的类(堆对象)注解 → Ptr(struct)(引用语义,对齐 TPC)」, Attribute/sha1 前缀/from-import 各分支同口径)。 3. 于是 BinOp(`| t.CPtr`) 分支里 other_ty 已经是 Ptr(JsonValue);而 :3176 那段 「Name 且未注册 struct 才丢弃」的守卫只认 Name —— Constant 绕过了它, 带着 Ptr(struct) 走到 :3264 `return llvmlite.Ptr(pool, other_ty)` 再包一层 ⇒ JsonValue**。 4. 裸名写法(不带引号)不会暴露:它会在 :3222-3239 被判 td_res_is_ptr=1 → other_ty 置 None → 走 :3254 回退取 struct 再包一层,恰好抵消。 ⇒ 同一类型两种写法产出两个层数(`JsonValue*` vs `JsonValue**`),与用户 2026-09-30 定的统一口径冲突:T* ≡ rawptr[T] ≡ t.CPtr[T] ≡ T | t.CPtr。 改动(TypeGenResolve.resolve_annotation_type 的 BinOp(|t.CPtr) 分支): - 在 :3264 包装前落实本文件 :3219-3221 注释里早已写明、但此前**未实现**的那条 规则:「| t.CPtr 恒为追加 1 层,唯一例外是 struct 引用语义时吸收这 1 层 (判据 = Ptr 的 pointee 是已注册 struct,find_struct_by_type 命中即吸收)」。 - 即:other_ty 已是 Ptr(已注册 struct) 时直接返回 other_ty,不再加一层。 - 副作用(同步修正同族写法):引号字符串写法、以及 Attribute 分支在 find_struct_by_module 命中 HasNew 时返回 Ptr(struct) 的那条路径 (:3040-3044),都从「多一层」收敛为 T*。 - 不影响的既有例外:VOIDPTR/CVoid|CPtr 等「指针别名」侧解析出的是 Ptr(i8) (i8 非 struct,命中不到 find_struct_by_type)⇒ `VOIDPTR | t.CPtr` 仍是 i8**(追加一层),与 VpiReader 侧口径一致。 仅 TPV 侧(TPC 侧走 SymbolUtils.AddPointerLayers 的另一套口径,本次不动)。 待验证(不自行执行编译命令):先重编译 TransPyV,再跑 TransPyV.exe --project ./Test/JsonTest/project.json --clean --clear-cache --rebuild-includes --traceback 预期 :90 不再报层数不同;若还有同族报错请把栈贴回。 --- App/lib/core/Gen/Type/TypeGenResolve.vp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index 9c8382f..988826d 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -3261,6 +3261,28 @@ 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*): + # 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 return llvmlite.Ptr(pool, other_ty) # t.CPtr[...] 下标指针标记(普通 _is_cptr_marker 不识别 Subscript):