refactor(TypeGenResolve): 裸类名恒为原型(struct 本身),删除 5 处 HasNew 自动升指针

口径(用户 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
预期先出现一批"注解需补 `*`"的报错——那正是本次口径变更要求的显式化。
This commit is contained in:
TermiNexus committed 2026-10-01 12:38:24 +08:00
1 parent 660427f759
commit 875a9beb08
1 file changed
+15 -20
+15 -20
View File
@@ -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 缺失由调用侧暴露