From 875a9beb08091e78ca962741489f1e153de5e584 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Thu, 1 Oct 2026 12:38:24 +0800 Subject: [PATCH] =?UTF-8?q?refactor(TypeGenResolve):=20=E8=A3=B8=E7=B1=BB?= =?UTF-8?q?=E5=90=8D=E6=81=92=E4=B8=BA=E5=8E=9F=E5=9E=8B=EF=BC=88struct=20?= =?UTF-8?q?=E6=9C=AC=E8=BA=AB=EF=BC=89=EF=BC=8C=E5=88=A0=E9=99=A4=205=20?= =?UTF-8?q?=E5=A4=84=20HasNew=20=E8=87=AA=E5=8A=A8=E5=8D=87=E6=8C=87?= =?UTF-8?q?=E9=92=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 口径(用户 2026-10-01 定):**裸类名注解 = 原型(struct 本身,类型/布局语义)**, 堆对象引用必须显式写 `T*` / `T | t.CPtr` / `rawptr[T]`。旧行为"有 __new__ 的类 (堆对象)裸名 ⇒ Ptr(struct)"是为兼容 `d: dict = dict(bd)` 这类"裸名 + 构造"写法 而设,代价是同一类型两种写法分裂: includes/json/__init__.vp:79 `def __getitem__(...) -> 'JsonValue' | t.CPtr` 左边(引号字符串注解)先被解析成 Ptr(struct),联合分支再包一层 ⇒ JsonValue** 而 `cur: JsonValue*` 是 JsonValue* ⇒ :90 `return cur` 报 「指针层数不同(源 1 级 → 目标 2 级)」(660427f 已在联合分支做过吸收, 本次从源头统一:裸名不再自动升指针,吸收那条规则对裸名侧自然不再触发)。 改动(resolve_annotation_type 的裸名/限定名分支,共 5 处,全部去掉 HasNew 分支): 1. `{16hex}.Class` 已归一前缀名分支(原 :2768-2770) 2. 本模块自有类分支(原 :2806-2814,删掉那段"有 __new__ ⇒ Ptr"的注释与分支) 3. from-import 归属分支(原 :2832-2834) 4. 可见结构体裸名分支(原 :2842-2844) 5. `mod.Class` 限定名分支(原 :3040-3044) 影响面盘点(静态扫描 474 个 .vp):本文件内"裸堆类名注解"16 处、跨文件候选 63 处 (含同名跨模块误报),典型: includes/hashtable.vp:45 ht: HashTable = HashTable(mb) includes/_dict.vp:29 d: dict = dict(mb) Test/BuiltinDecoratorTest/App/main.vp:33/36/68/98/137/180/184/194/199/234/241/248 TransPyV/Test/App/new_test.vp:81/93/105 Test/{ClassArrayTest,StructTest,ClosureTest2,StringIterTest}/App/*.vp 若干 这些站点需按新口径显式补 `*`(或改用 `T | t.CPtr`);真实集合以编译器 fail-fast 报错为准(同名跨模块无法静态判定归属)。 TPC 侧核查结论:**无需改动,TPC 本就同口径**。证据: 1. 全仓 `HasNew|has_new` 仅 8 处,全在**构造路径**——HandlesAnnAssign.py:401 (`list[T] = [...]` 字面量构造)、HandlesExprCall.py:5105/5161/5170/5198/5235/5263 (`ClassName(...)` 构造,`_HandleClassNewLlvm`);**注解解析链上一处都没有**。 2. 规范注解解析 `TypeAnnotationResolver._from_node_name`(裸 Name)、 `_from_node_attribute`(mod.Class)、`_from_node_constant`('X' 引号注解) 全程不查 __new__,裸类名一律 PtrCount=0(原型)。 3. 局部变量路径 HandlesAnnAssign.py:678-683 显式把裸类名 PtrCount 由 1 改回 0 ("纯结构体名注解应为值类型"),仅保留 vtable 类为指针;模块级全局 HandlesAssign.py:1840-1856 亦按 struct 值类型建 GlobalVariable。 4. 符号表类条目 PtrCount 默认 0(SymbolTable._InsertClassSymbol:489-518 从不设 PtrCount;TypeSpec.py:139 默认 0)。 因此 `d: dict = dict(mb)`(dict 有 __new__、非 vtable 类)在 TPC 侧本来就是 struct 值语义,与本次 TPV 改动后的口径一致。json/__init__.vp 的 `'JsonValue' | t.CPtr`(定义端 0+1 层、声明端裸类引用吸收 1 层)两侧都已是 `%"…JsonValue"*`(1 层),本次 TPV 改动后亦对齐。 待验证(不自行执行编译命令):先重编译 TransPyV,再跑 JsonTest / DictTest / StructTest / BuiltinDecoratorTest / ClassArrayTest 预期先出现一批"注解需补 `*`"的报错——那正是本次口径变更要求的显式化。 --- App/lib/core/Gen/Type/TypeGenResolve.vp | 35 +++++++++++-------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index 988826d..bd5a307 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -2765,8 +2765,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): sp_sha_buf2[sp_len2 - 1] = '\0' sp_entry2: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(nm.id + sp_len2, sp_sha_buf2) if sp_entry2 is not None and sp_entry2.Ty is not None: - if sp_entry2.HasNew != 0: - return llvmlite.Ptr(pool, sp_entry2.Ty) + # 裸类名恒为**原型**(struct 本身,值/布局语义),不再看 HasNew: + # 堆对象引用必须显式写 `T*` / `T | t.CPtr` / `rawptr[T]` + # (用户 2026-10-01 定;TPC 侧注解链路本无 __new__ 升指针逻辑, + # 天然同口径,故 TPC 无需同步改)。 return sp_entry2.Ty # 兜底:按完整前缀名直接查(部分注册键即全名) sp_full2: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type(nm.id) @@ -2803,14 +2805,14 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): if trans is not None and trans.ModuleSha1 is not None: own_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(nm.id, trans.ModuleSha1) if own_entry is not None and own_entry.Ty is not None: - # 有 __new__ 的类(堆对象)注解 → Ptr(struct)(引用语义,对齐 TPC)。 - # 否则无 __new__ 的类(栈对象,如 Point)保持 struct 值语义: - # 构造返回栈 alloca 指针,赋给 struct 值变量时 coerce load 拷贝。 - # 背景:d: dict = dict(bd) 的 dict 是堆对象(__new__ 分配), - # 裸名注解按值解析会让 sub: dict = sub_p 把 i8* 当 struct 解引用 - # 崩溃(DictTest Test 13)。 - if own_entry.HasNew != 0: - return llvmlite.Ptr(pool, own_entry.Ty) + # 裸类名恒为**原型**(struct 本身):注解说的是"类型/布局", + # 不再按 HasNew 自动升成 Ptr(struct)。堆对象引用一律显式写 + # `T*` / `T | t.CPtr` / `rawptr[T]`(用户 2026-10-01 定; + # TPC 侧注解链路本无 __new__ 升指针逻辑,天然同口径, + # 故 TPC 无需同步改)。旧行为(有 __new__ ⇒ Ptr(struct))是 + # 为兼容 `d: dict = dict(bd)` 这类"裸名 + 构造"写法而设, + # 代价是同一类型出现"裸名=指针 / 联合=再加一层"的分裂 + # (现场 includes/json/__init__.vp:90 CastError)。 return own_entry.Ty # from-import 归属查找:注解里的裸名(或 import 别名)经 from_imports 定位到 # 定义模块,再按 (name, sha1) 精确取类——这是 stub 注入场景的正解:此刻 @@ -2829,18 +2831,12 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): if fi_sha1 is not None: fi_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(nm.id, fi_sha1) if fi_entry is not None and fi_entry.Ty is not None: - if fi_entry.HasNew != 0: - return llvmlite.Ptr(pool, fi_entry.Ty) + # 裸类名恒为原型(见上方同口径注释) return fi_entry.Ty if self.Gen.StructGen.is_struct_visible(nm.id) != 0: struct_ty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type(nm.id) if struct_ty is not None: - nm_entry_s: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(struct_ty) - nm_has_new_s: int = 0 - if nm_entry_s is not None: - nm_has_new_s = nm_entry_s.HasNew - if nm_has_new_s != 0: - return llvmlite.Ptr(pool, struct_ty) + # 裸类名恒为原型(见上方同口径注释):不再因 HasNew 升成 Ptr return struct_ty # 枚举类(t.CEnum)裸名注解 → 底层整数类型 # 如 fat32_types.vp 内部 `res: FRESULT = ...`(FRESULT 是枚举非结构体) @@ -3039,8 +3035,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): return full_ty_v mod_entry_v: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(at.attr, mod_sha1_v) if mod_entry_v is not None and mod_entry_v.Ty is not None: - if mod_entry_v.HasNew != 0: - return llvmlite.Ptr(pool, mod_entry_v.Ty) + # 裸类名(含 mod.Class 限定名)恒为原型(见上方同口径注释) return mod_entry_v.Ty # 模块可解析但类未注册:不裸名回退(防跨库抓错), # 落到下方枚举/typedef 尝试;struct 缺失由调用侧暴露