# TPV 类型系统问题清单 > 记录日期:2026-08-28 > 范围:TransPyV\App\lib\core\Gen\Type\* + GenTypes.vp + StructGenMixin.vp + Class\ClassGenEnum.vp + Expr\ExprGenCdef.vp > 状态:问题 2/4/7/8 已完成,问题 1 部分完成,其余待办 ## 问题总览(按影响排序) ### 1. 静默退化不统一(影响最大) 同一类型解析失败,在不同上下文产生三种不同结果: - 函数返回类型 → 静默回退 `i32`(FuncGenDef.vp L110-114) - 模块级变量/函数参数 → 静默降级 `i32`(ClassGenDef.vp L226-228) - 联合 `X | t.CPtr` 裸名未注册 → 回退 `Ptr(i8)`(TypeGenResolve.vp L1574-1597) - 泛型 T 污染 → `return None`(TypeGenResolve.vp L1267-1268,注释自称 fail-fast 实为退化) 静默退化点不落日志 → 运行时数据损坏(ListTest/DictTest/BuddyTest 根因)而非编译期报错。 **决策**:编译期报错,不退化,直接报错(fail-fast)。 > **状态:部分完成** ✅ > - ✅ 函数返回类型:FuncGenDef.vp L113-122 有返回注解但解析失败 → `fatal_error`(无注解函数保留 i32 默认) > - ✅ 泛型 T 污染:TypeGenResolve.vp L1258-1276 改为 `fatal_error`;污染检测用 `_str_contains_ident`(token 边界匹配,修复 strstr 把 `AST|t.CPtr` 中 'T' 误判为参数名的误杀) > - ⏳ 联合 `X|t.CPtr` 退化 `Ptr(i8)`:**保留**(前向引用 + 多遍收敛设计,直接 fail-fast 破坏自举);建议后续"Phase B 收敛后校验" > - ⏳ 模块级变量/参数静默降级 `i32`:未改(参与 Phase A 多遍重译,需评估 _deps_pending 交互) ### 2. sext/zext 不一致 - `coerce_to_type` 整数扩展默认 **sext**(ExprGenBase.vp L422) - `should_zext_widen` 认为 i8 几乎总是无符号走 **zext**(ExprGenOps.vp L180-181) - MatchGenMixin.vp L745-752 各自查符号性 → 无符号类型(CUInt32T 等)赋值/传参被符号扩展;同一"窄→宽"在赋值路径与比较路径产生相反结果。 **决策**:统一 zext/sext 为单一符号性判定函数。 > **状态:已完成** ✅ > - ✅ ExprGenBase.vp L417-426 `coerce_to_type` 窄→宽接入 `_zext_or_sext_val`(i1/i8 默认 zext、`_node_type_unsigned` 无符号类型名 zext、否则 sext) > - ✅ 新增 `_zext_or_sext_val` helper(ExprGenBase.vp L457-478) ### 3. 联合类型信息丢失 `X | t.CPtr` 若 X 未注册(前向引用/Phase A 依赖未就绪)→ 字段/变量固化为 `Ptr(i8)`,GEP 步长/load 宽度全错。 正确性依赖 AnnotClassName 影子类型系统补救(脆弱,Subscript 场景会丢类名)。 > **状态:待办** ⏳ ### 4. REnum 别名表指针比较契约 `class_name` 用 CSizeT **指针值**比较(ClassGenEnum.vp L978-984),注册指针与查找指针不统一即失效。 "LLVMType 字段 Pointee 未找到"四次依旧的根因;靠 `_update_renum_alias_class_ptr` 补救,契约脆弱。 建议改 sha1+类名字符串键。 > **状态:已完成** ✅ > - ✅ ClassGenEnum.vp 别名表去指针化:`class_name` 槽位改 perm_pool 字符串拷贝,3 个 lookup 改 strcmp 内容比较 > - ✅ 删除 `_update_renum_alias_class_ptr` 及调用点 ### 5. C 语义偏差 - `_Bool` = i8 vs `CBool` = i1(TypeGenResolve.vp L723 vs L534) - `BOOL` = i32 - `INTPTR` = Ptr(i32)(应为 i64 指针宽整数) > **状态:待办** ⏳(注:`_Bool`=i8 / `BOOL`=i32 可能是有意的 C ABI 兼容,需先确认;`INTPTR`=Ptr(i32) 确为 bug 应改 i64) ### 6. CNeedPtr/CAutoPtr 检查未落地 注释要求"非指针实参应在调用点报错",实际无检查(TypeGenResolve.vp L506-508),静默降级 i32 导致指针截断。 > **状态:待办** ⏳ ### 7. 泛型特化名分裂 跨模块一侧写 `list[ast.AST | t.CPtr]`、另一侧写 `list[AST | t.CPtr]` → 两个特化名 → undefined symbol。 靠 `_norm_type_arg_name` SHA1 规范化修复。 > **状态:已完成** ✅ > - ✅ `_norm_type_arg_name` 增强(TypeGenResolve.vp L233-368):顶层 `|` 分割改为括号感知(`list[A|B]` 不误分);新增嵌套泛型实参递归(`list[list[AST]]` → `list[list[{sha1}.AST]]`,内部按顶层 `,` 分割逐个 SHA1 规范化)——所有 module 表示统一为 Hash Namespace(`{SHA1}.Class`) ### 8. 窄化截断无诊断 宽→窄直接 `trunc` 静默丢位(ExprGenBase.vp L423),`buf[idx] = 0x1FF` 类写入静默截断。 > **状态:已完成** ✅ > - ✅ ExprGenBase.vp `coerce_to_type` 窄化点(L419-432)加常量溢出诊断:编译期常量正溢出目标位宽无符号范围 → `fatal_error`(OverflowError);负常量按模 2^bits 语义放行(-1 → 0xFF 合法);非常量运行时窄化保留 trunc(值域可能安全,无法静态判定) ## 历史缺陷附录 - 指针参数 alloca 退化为 i32(VarGenMixin.vp L382-384,FuncTest 根因) - 裸类形参退化为 i32(ClassGenBase.vp L774-780) - 返回类型字符串误判:struct 字面量 `{i8*, i16}` 内含 * 被当 i8*(CallGenBase.vp L829-833) - strstr 跨模块同名类劫持:sched.Thread 被 vthreading.Thread 顶掉(StructGenMixin.vp L768-791) - 字段/注解类名 pool 指针跨文件悬空(StructGenMixin.vp L370-378) - `_strappend` 越界存疑(TypeGenErr.vp L396) - `_get_annotation_field_size` 缺类型白名单,未知类型按 8 字节(ClassGenEnum.vp L593-594) - `infer_expr_type` 参数 bits 仅识别 8/16/64 三档,32 位静默落 i32(TypeGenErr.vp L295-300) --- # 方案:类型系统收敛(t/c 库内部特判 + 正常库分析) > 记录日期:2026-08-28 > 范围:TPV 侧(TPC 侧不再维护) > 架构目标:t/c 库走编译器内部特判(map_t_type 保留核心原语);用户侧/库侧别名 > 链(from-import 别名、`X: t.CTypedef = Y`、stdint.vp 声明)走正常库分析(CTypedef > 动态表递归);vpnci/vpi 存根仅供高亮插件,编译器不感知。 > **实施状态:阶段 1/2/3 已完成** ✅(2026-08-28) ## 现状缺口 1. **Name 分支无 from-import 别名反查**(TypeGenResolve.vp L1371-1502): `from t import CInt as XXX` 后裸名 `XXX` 不在 struct/CTypedef/map_t_type 任何表 → 解析 None;`YYY: t.CTypedef = XXX` 链因此断(递归到 XXX 失败)。 2. **Attribute 路径硬编码优先**(L1539 map_t_type 先于 CTypedef L1590):stdint.vp 声明的 ~70 个 typedef 名在 map_t_type 重复硬编码,限定名(stdint.INT)吃硬编码不吃库定义 → 双事实源。 3. **三张派生表未联动**:`__sizeof__`(CallGenDispatch L583-622)、REnum 尺寸 (ClassGenEnum L555-583)、无符号名单(ExprGenOps L261-287)各自重复硬编码。 ## 阶段 1:补正常库分析(已完成)✅ ### 1a. Name 分支 from-import 别名反查(TypeGenResolve.vp) 在 Name 分支 map_t_type 兜底之后插入:裸名 miss 时反查 `ImportsGen.lookup_from_import_original(from_imports, nm.id)`(from_imports 格式 `别名:模块:原始名`,返回内部指针需复制截断于空格/null)→ 用原始名 `_parse_type_string` 重建节点 → 递归 `resolve_annotation_type`。 - 效果:`from t import CInt as XXX` → XXX → CInt → map_t_type 命中; `YYY: t.CTypedef = XXX` → YYY → CTypedef → "XXX" → 别名反查 → CInt ✅ - 防递归:复用 resolve_annotation_type 的 depth 上限;别名==原名时跳过 ### 1b. Attribute 路径 CTypedef 动态表优先(TypeGenResolve.vp) 把 `map_t_type(at.attr)` 第一优先改为:先查 CTypedef 表(若 attr 是库 typedef 名且 已注册 → 递归展开),miss 再 map_t_type。 - 效果:`t.CInt` → attr="CInt" 表无 → map_t_type 特判(t/c 内部特判保留)✅; `stdint.INT` → attr="INT" 表命中 → 动态递归(正常库分析)✅;表未注册时回落 map_t_type 硬编码兜底,不破坏现状 ## 阶段 2:map_t_type 去重收敛(已完成)✅ 删除 map_t_type 中与 stdint.vp 重复的 ~70 个名字(INT/BOOL/BYTE/WORD/HANDLE 之外的 大写别名、定长、小写简写、指针别名、int8_t/size_t/_Bool 等),保留 t/c 核心原语 (CInt/CUInt8T/CPtr/CBool/CNeedPtr 等)+ 内建小写 float/double + HANDLE/LPCSTR/LPCWSTR。 前置改造:新增统一口径 `TypeGenResolve.resolve_typedef_or_map`(CTypedef 动态表优先, miss 回退 map_t_type)+ `get_type_size_bytes`(位宽/8)+ GenApi 委托;改造旁路调用点 VpiReader(_resolve_single_vpi_type_str 两处)、ClassGenEnum(_resolve_enum_member_type 两处)、TypeGenErr(参数位宽提取)为统一口径,确保删除后 BYTE 等仍能经库 typedef 解析。 ## 阶段 3:三张派生表联动(已完成)✅ - `__sizeof__`(CallGenDispatch):尺寸表前插入 `get_type_size_bytes` 动态兜底 - REnum 尺寸(ClassGenEnum `_get_annotation_field_size`):加 pool 参数 + 动态兜底 - 无符号名单(ExprGenOps `_type_name_unsigned`):末尾加 CTypedef 展开兜底 (经 `_annot_str_unsigned` 子串判断符号性) ## 风险 - 1a 递归深度:复用现有 depth 上限,别名链过长即报错(fail-fast 优于卡死) - 1b 解析来源变化:若 CTypedef 表值与 map_t_type 硬编码不一致,限定名结果改变—— 需先对比验证(预期一致,stdint.vp 为权威) - 阶段 2 删除后:纯 map_t_type 独有域(t/c 核心 CXXX、HANDLE/LPCSTR/LPCWSTR)保留, 其余靠注册,漏注册即编译期暴露(符合 fail-fast 原则)