Commit Graph
100 Commits
Author SHA1 Message Date
TermiNexus b7e4861e6c fix(tpv): vpi 返回注解纯 marker(t.State 等)解析为 void,修 void 调用被当 i32
调用侧 `_resolve_vpi_ret_annot_str` 只把 `t.CVoid` 映射为 void,纯 `t.State`
(存储类装饰器标记)会解析失败返回 None → 调用侧回退外部函数白名单默认 i32。

现场(MLIRLiteTest):`includes/stdlib.vp` 的 `extern def exit(status: int)`
在 stdlib 的 vpi 中是 `def exit(status: int) -> t.State: pass`,于是生成
  %6 = llvm.call @exit(%c3) : (i32) -> i32
而模块声明是 `llvm.func @exit(%status: i32)`(void),mlir-opt 报
  'llvm.call' op calling function with void result must not produce values

修复:在 `_resolve_vpi_ret_annot_str` 增加"纯 marker 注解 → void"分支,与
declare 注入路径 `_inject_module_declare` 的 is_pure_marker_rt 判据同口径。
仅**纯** marker(`t.State` / `t.CExtern` / `t.CExport`,可带 t./c. 前缀)
才归 void;`HANDLE | t.State` 等含真实类型的注解仍由段循环返回真实类型。
2026-10-03 21:51:13 +08:00
TermiNexus 94b174c8a0 fix(tpv): 补齐 asm_arg_cap 声明与嵌套数组 element 预算漏改
上一条提交(3426139)中两处改动实际未落盘(编辑动作报告成功但文件内容未变),
导致 TransPyC 编译报 NameError: 未定义名称 'asm_arg_cap'。

- CallGenCBuiltin:补 asm_arg_cap 声明(按操作数个数给足内联汇编实参表容量),
  args_buf 改用该容量分配(此前 4 处用法已改为 asm_arg_cap,声明却缺席)
- TranslatorGenMixin:嵌套数组 element 预算 192 → 768(含子数组类型前缀)
2026-10-03 21:17:41 +08:00
TermiNexus 3426139d03 fix(mlir): TypePrint 输出缓冲按 MLIR 类型长度放大;初始化器文本按类型长度给足
MLIR 类型文本内联具名结构体完整字段体(可达数百字节),旧的短缓冲会截断类型
文本、产出畸形 MLIR(mlirlite 侧 TypePrint 现已对截断响亮报错,此处补齐容量)。

- 10 处 TypePrint 调用点及其缓冲统一放大到 512:
  AssignGenMixin / TranslatorGenMixin / CallGenCBuiltin(×2) / CallGenLeafKit /
  ClassGenVTable / ExprGenBase / ExprGenOps(×4) / FuncGenDef / TypeGenResolve(×2)
- TranslatorGenMixin:
  - _stg_field_zero_text 缓冲 160 → 576(元素 = 类型前缀 + 值)
  - _default_val_const_text 缓冲 48 → 576
  - 嵌套数组 element 预算 192 → 768(含子数组类型前缀)
  - storage 结构体 element 预算 160 → 576
- CallGenCBuiltin:内联汇编实参表按操作数个数分配(原固定 512,一个结构体
  操作数即溢出)、汇编行缓冲按实际长度分配
2026-10-03 21:08:30 +08:00
TermiNexus d0434bf114 fix(repl): 片段导出符号探针改用 MLIR 形态 llvm.func;布局恢复文案改指 {sha1}.meta
- Repl.vp ⑧ 探针由 "define i32 @名(i8* %__repl_ctx__" 改为
  "llvm.func @名(%__repl_ctx__: !llvm.ptr) -> i32"(单端后 dump 出的是 MLIR 文本)
- VpiReader.vp REPL 布局恢复的注释/报错/汇总文案 .stub.ll → {sha1}.meta
  (数据源早已是 meta:_repl_stub_collect_module 读 {sha1}.meta,
   _repl_meta_scan_layout 解析 STRUCT/VTABLE;本次只改文案与注释,无逻辑改动)
2026-10-03 19:58:37 +08:00
TermiNexus cbf76cd468 fix(callgen): text.ll 返回类型回退支持 MLIR 形态 llvm.func
- _CacheModuleRetTyTexts 同时认 define 行与 mlirlite 的 llvm.func 行(含缩进/void 省略 ->/变参)

- 缓存值以 '#' 前缀标记 MLIR 形态,命中时用新增 _ParseMlirRetTyStr 解析

- _ParseMlirRetTyStr 覆盖 none/iN/f16..f128/!llvm.ptr/!llvm.array/!llvm.struct(具名按登记表回查,未登记返回 None 不臆造)

- 背景:单端 MLIR 后 text.ll 无 define 行,回退恒空 ⇒ 指针返回值退回默认 i32 被 inttoptr 截断
2026-10-03 19:45:47 +08:00
TermiNexus 7b3e814524 refactor(backend): 单端 MLIR —— 发射器全树改名 mlirlite 并删除 --backend 开关
- App/Test 的 .vp 中 llvmlite 全部改名为 mlirlite(import、限定名、类型注解、注释)

- 删除后端开关:Config.set_backend/is_mlir_backend/alias_module_name、BackendTarget/BackendIsMlir、CLI --backend、options.backend/options.target 读取,以及 IncludesScanner/StubMergerIR 的模块别名调用

- BuildPipeline 的 mlir-opt -> mlir-translate 降级管线去掉开关门,改为恒执行

- backend_dir_suffix 改名 mlir_dir_suffix 并恒返回 _mlir:TempDir/OutputDir/includes 缓存继续落在 *_mlir,避免复用旧 LLVM 后端的 .ll/obj

- project.vpj 删除已失效的 options.target
2026-10-03 19:26:24 +08:00
TermiNexus 1dcd29db9c fix(phaseb): llc 并行失败按阶段定位,不再一律报 llc 编译失败
- LlcTask 追加 FailStage 字段(0=未失败/1=mlir-opt/2=mlir-translate/3=ASan opt/4=llc),worker 在各步失败时写入

- run_llc_parallel 按 FailStage 打印真正失败的阶段名与对应命令:MLIR 后端下 mlir-opt/mlir-translate 失败此前被误指为 llc(现场 .ll 含 LLVM IR 时 mlir-opt 报 unexpected character,日志却指向 llc)

- 字段追加在结尾,所有 stride/memset 均按 LlcTask.__sizeof__() 计算,既有偏移不变
2026-10-03 19:01:50 +08:00
TermiNexus cda4cca805 refactor(generic): 类型实参符号判定复用 spec_sign_of_annot
- 两处 Call 推断由 SpecSignFromTypeNameSpan 改为 TypeGenCType.spec_sign_of_annot,复用注解侧唯一真源(精确名白名单 + CTypedef 链逐层展开)

- 收益:别名(uint64/size_t/WCHAR->WORD->uint16 等)也能判出符号性,不再是裸白名单的 UNKNOWN
2026-10-03 18:20:42 +08:00
TermiNexus 2ac412c11a feat(generic): 泛型实参推断补齐全部内建标量位宽的精确票名
- 整型按 位宽×符号性 全覆盖:i8 -> t.CUInt8T/char、i32 -> t.CUInt32T/int,i16/i64 维持 t.CUInt16T/t.CInt16T、t.CUInt64T/t.CInt64T

- 浮点补 half -> t.CFloat16T、fp128 -> t.CFloat128T(原仅 float/double,其余返回 None)

- 票名与 TypeGenResolve.map_t_type 的可解析名一一对应;无符号一律不走裸名(避免静默变号),位宽无裸名别名且符号未知时返回 None(宁缺毋错)
2026-10-03 18:19:53 +08:00
TermiNexus f9ba391236 feat(generic): 无符号 i32 泛型实参改发精确票名 t.CUInt32T
- _infer_scalar_type_name_from_spec 对 i32 增加符号性分支:无符号(CUInt32T/CSizeT/CUIntPtrT 等)发 t.CUInt32T,有符号/未知保持裸名 int(位宽无误且与历史逐字一致)

- 修复 CUInt32T 实参经泛型推断被静默变号为 int 的问题
2026-10-03 18:18:21 +08:00
TermiNexus 21436b2690 feat(generic): 泛型实参推断为无裸名别名的整型位宽补精确票名
- _infer_scalar_type_name_from_spec 支持 i64/i16 -> t.CUInt64T/t.CInt64T、t.CUInt16T/t.CInt16T;符号性未知时不猜(返回 None)

- infer_scalar_type_arg_from_llvm_ty 增加 sign 参数:LLVM 类型层无法区分 iN/uN,符号性由被调名源码文本经 SpecSignFromTypeNameSpan 判定

- 泛型函数与泛型类构造两处 Call 推断同步传符号性,修复 GenericOopTest fxi2 第 3 个类型实参(CUInt64T)推断失败
2026-10-03 17:12:47 +08:00
TermiNexus ddb1e6cc2a fix(类型): get_type_size_bytes 补浮点位宽,修复浮点泛型实参的 __sizeof__ 尺寸解析
现象:Test/VectorGenericTest/App/vectortest.vp:32 vector.Vector[CFloat64T] 特化时 vector.vp:12 T.__sizeof__() 报 "NameError: 未定义名称 T(当前泛型上下文: 类型形参 1 个 / 类型实参 1 个)" —— 新增诊断证明上下文是好的(T 能换成 CFloat64T),故问题在 __sizeof__ 快径没接住。

根因:get_type_size_bytes 只用 get_llvm_type_bits 取位宽,而该函数只认整数(Float/Double/Half/FP128 一律返回 0)⇒ 浮点 typedef(CFloat64T 经 map_t_type 已正确解析为 Double)被判"尺寸未知"返回 0 ⇒ CallGenDispatch._tc_h5_sizeof_len_isinst 的尺寸表也不含 CFloat64T(只有 CInt/CDouble/CFloat 等)⇒ 落回"回退 2"把接收者 Name(T) 当实例翻译 → NameError。同文件第 15 行 Vector[CInt] 走整数路径正常,故只有浮点特化暴露(不对称现象由此解释)。

修复:整数位宽为 0 时再取 get_llvm_float_bits(浮点位宽),两者皆 0 才返回 0。
2026-10-03 16:50:42 +08:00
TermiNexus 3a4323e288 feat(generic): 特化上下文自检 fail-fast + NameError 附泛型上下文诊断
背景:VectorGenericTest 在第 32 行 vector.Vector[CFloat64T] 特化时仍报 "NameError: 未定义名称 T"(8af967d 的类模板 tp 名补缺只能覆盖 tp 名缺失那一种,这里 tp 名在场)。静态读完 6 个 _specialize_generic_class 调用点与 ClassGenDispatch 的 __sizeof__ 快径后,无法确定是"上下文没建"还是"建了装不上",故加两处诊断。

1) ClassGenGeneric._specialize_generic_class 在 set_generic_context 之后立刻自检:类型实参个数必须等于模板类型形参个数,且首形参必须能解析成具体类型名;否则 fatal_error 点出类名与特化键(含实参),替代下游 12 行之外那句脱节的未定义名称。

2) ExprGenName 未定义名称报文追加当前泛型上下文(类型形参 N 个 / 类型实参 M 个):形参 0 个=上下文没建,形参>0 且实参 0 个=实参表丢了,皆>0=上下文在但该名不在其中,一眼可分。
2026-10-03 16:38:01 +08:00
TermiNexus da18dd39a4 fix(classdef): 已注册类早退路径补字段类型完整定义
上一条(96dad30)只改了"全新定义"路径的字段扫描循环,但 includes 在 Phase A 预注册、Phase B 重新翻译时走的是 existing 早退分支(ClassGenDef 117-167,只 module_add_named_type 后即 return 0),字段循环根本不执行 ⇒ includes/condition.vp 的 Condition 仍内嵌 opaque 成员、自身 unsized,llc 仍报 base element of getelementptr must be sized。

物证:同一构建里 ThreadingTest 的 main.vp(fb785af4a2aa1a46.ll)因按需调 ensure_struct_def_in_module,已正确拷入 CRITICAL_SECTION/CONDITION_VARIABLE/SRWLOCK 的完整定义;而定义它们的 condition.vp 自身(de351f55b29d33a3.ll)没有 ⇒ 只有定义模块自身体内 GEP 时才暴露。

修复:早退分支在 module_add_named_type 后补调 _ensure_field_types_in_module(Struct/Array 值类型递归补完整定义、Ptr 补 opaque),与全新定义路径同口径。
2026-10-03 16:05:24 +08:00
TermiNexus 96dad309ac fix(classdef): 值类型字段补完整结构体定义,修复跨模块嵌套结构体 unsized 导致 llc GEP 报错
现场:Test/ThreadingTest 编译 includes/condition.vp 时 llc 报「base element of getelementptr must be sized」:getelementptr %Condition, %Condition* %1, i32 0, i32 1。

根因:ClassGenDef 定义类时对字段类型一律只调 module_ensure_opaque_for_type(仅 opaque 声明)。当字段是**值类型**跨模块结构体时,本类体内嵌的成员在 IR 中仍是 opaque ⇒ 成员 unsized ⇒ 本类 unsized ⇒ 本模块内对本类做带字段下标的 GEP 时 llc 报错。现场物证:condition.vp 的 .ll 里 CONDITION_VARIABLE/CRITICAL_SECTION = type opaque,而 Condition = type {CV, CS}。

修复:指针字段维持 opaque(指向物无需在本模块有体);非指针(值类型结构体/结构体数组)改调 ensure_struct_def_in_module 复制完整定义,并配合 _ensure_field_types_in_module 覆盖数组元素。
2026-10-03 15:58:48 +08:00
TermiNexus 28b7437e21 fix(generic): 泛型类构造 Call 实参推断同步收紧,抽出共用精确映射
CallGenCtor._infer_generic_ctor_type_arg 的 Call 分支此前与泛型函数路径同款名字子串猜测(含 Int→int、Float→double),把 CUInt64T/CInt16T 静默归成 int=i32、CFloat32T 猜成 double。改为:把被调表达式交给注解解析器,命中标量才据精确位宽映射;非标量/不可解析仍 fail-fast(保持原有"明确的类型意图却无法解析"报错)。

新增 ClassGenGeneric.infer_scalar_type_arg_from_llvm_ty 作为两处共用的唯一入口(内部走 _infer_scalar_type_name_from_spec),泛型函数路径一并改为调用它,消除重复的 arena/SpecFromLLVMType 样板,保证两处同口径。
2026-10-03 15:51:12 +08:00
TermiNexus 9a80f1df6c fix(generic): 泛型函数 Call 实参推断改为精确类型解析,移除名字子串猜测
原实现按被调名子串猜类型实参(含 Int→int、Float→double 等),把 CUInt64T/CInt16T 一律归成 int=i32,静默窄化/扩宽位宽(禁止静默默认原则);CFloat32T 也会被猜成 double。

改为:把被调表达式本身交给注解解析器 resolve_annotation_type,命中**标量**才据其精确位宽经 _infer_scalar_type_name_from_spec(唯一真源)映射;非标量(构造/普通函数调用)或不可解析一律返回 None,由调用方汇总后 fail-fast 提示显式标注。行为变化:CUInt64T(x)/CInt16T(x) 等无等位宽裸名的转换实参不再被静默猜成 int。
2026-10-03 15:49:58 +08:00
TermiNexus e196d6a28a fix(类型): 模块限定注解支持 from-import 重导出解析
现场:Test/ThreadingTest/App/main.vp:184 `lock: vthreading.Lock = vthreading.Lock()` 报「变量类型注解解析失败」。vthreading.Lock 是重导出(includes/vthreading.vp `from lock import Lock`),Lock 真定义在 lock.vp;普通模块的 from-import 重导出不像包 __init__ 那样登记别名 entry(VpiReader._inject_reexport_class_declare 仅对包 __init__ 触发),故按 vthreading sha1 查 Lock 恒落空。

resolve_annotation_type 的 Attribute 模块限定分支此前未做真定义模块校正:先按原 sha1 查(既有命中情形逐字不变),未命中时再经 _class_true_def_sha1(唯一真源,已在 _normalize_type_name 规则 4b 使用)校正后重查一次,无证据时原样返回不改变行为。
2026-10-03 15:44:49 +08:00
TermiNexus c035a82394 fix(类型): str/bytes 聚合指针检查豁免字符数组衰减,并统一判据真源
误报现场:includes/os/_win32.vp:207 `name: str = find_data.cFileName`。WIN32_FIND_DATAA.cFileName 是 [260 x i8],字段访问得到 Ptr(Array(i8,260)),其指针在 C 语义下天然衰减为 char*/str,属合法赋值,原判据把 Ptr(Array) 一律当真而误杀。

新增 ExprGenBase._str_rejects_ptr_kind(单一真源:1=结构体/类/联合指针、2=非字符数组指针、3=函数指针、0=可接受;[N x i8] 豁免)供 fail_if_str_annot_agg_ptr 与 CallGenLib 跨模块实参检查共用,消除两处重复的 kind 判定,避免同规则两处演化。
2026-10-03 15:40:01 +08:00
TermiNexus bf2f38acd2 fix(generic): 修正 SpecToLLVMText 返回值判据倒置,恢复内建标量实参的泛型类型实参推断
_infer_scalar_type_name_from_spec 把 SpecToLLVMText 的『0=成功』误判为失败(写成 == 0 就 return None),使该函数对任何内建标量都恒返回 None —— 泛型函数隐式推断中『实参来自内建标量形参/字段』的路径(Name/Attribute 分支)永远推不出,报『无法从实参推断泛型函数 ... 禁止静默默认 int』(现场:GenericOopTest fxi 体内 fxi2(a, b, ...),a: double)。判据改为 != 0(与 ClassGenVTable 同约定)。

同时收紧位宽映射:i16/i64/f16 在 Viper 没有等位宽裸名别名(裸 int=int32、裸 float=float32),原映射 i16/i64->int、half->float 会静默窄化/扩宽位宽(i64->int 即 i64->i32),违反『禁止静默默认/兜底』原则;改为只映射位宽精确对应的形态(i1->bool、i8->char、i32->int、f32->float、f64->double),其余返回 None 交给调用方 fail-fast 提示显式标注。
2026-10-03 15:28:02 +08:00
TermiNexus 09ea7283f9 fix(类型): str/bytes 不再作通用指针通配,补齐实参/赋值/返回三处隐式转换 fail-fast
规则:void*/t.CPtr/rawptr 允许隐式转为任意指针类型,str/bytes 不允许(即便两者在 IR 层同为 i8*)。此前 coerce_to_type 的规则 1(通用指针通配)会把指向结构体/类/数组/函数的指针静默 bitcast 到 str 目标(反向亦然)。

- ExprGenBase:新增 _annot_name_is_bare_str/_annot_is_bare_str(裸 str/bytes 判定,组合段如 str|t.CPtr 不受影响)、fail_if_str_annot_agg_ptr(值是指向聚合类型的指针则 fail-fast,消息 CastError)、fail_if_str_annot_ast_agg_ptr(AST 注解入口)。

- 返回路径 StmtGenCifMixin.translate_return:在类型转换分支之前用 trans.CurFuncRetAnnot 判定,避免被规则 1 静默 bitcast。

- 赋值路径 AssignGenMixin.translate_annassign:四个分支(global / nonlocal / mod_glob / 局部)在 coerce 前用 aa.annotation 判定。

- 同模块实参 CallGenMixin.translate_call:在参数循环前一次性确保本模块 .vpi 参数注解缓存就绪(同跨模块 CallGenLib 键式:模块级函数 cls=None、段下标==形参下标),循环内按 _vpi_param_annot_seg_is 判定裸 str/bytes。
2026-10-03 14:46:57 +08:00
TermiNexus 8af967db7f fix(generic): 泛型类模板类型参数名补缺 + 空上下文 fail-fast,修复特化方法体 T 变 NameError 2026-10-03 14:34:55 +08:00
TermiNexus 98ee53340f feat(call): 跨模块实参加 str/bytes 形参的 fail-fast(str 不作通用指针通配),并加 vpi 注解段等值判定 2026-10-03 14:23:12 +08:00
TermiNexus feb29fa7a3 fix(generic-func): 发射特化调用改传符号名(callee: str)而非 Function*,修复生成空 @ 的非法 IR 2026-10-03 14:18:14 +08:00
TermiNexus df1ffa4c2a refactor(subscript): 删除调用侧下标 zext 与 _zext_index_to_i32,统一交给 build_gep(单一真源) 2026-10-03 13:35:36 +08:00
TermiNexus bcbc7142be fix(subscript): 指针下标 GEP 补 zext,修复 i8 下标被符号扩展(uint8ptr[200] -> p[-56] 越界),zlib 漏计字节>=128 根因 2026-10-03 13:20:11 +08:00
TermiNexus ab0ee723b3 fix(classgen): 补 import llvmlite,修复 llvmlite.LLVMType/Int32 解析不一致导致的指针隐式转换 fast-fail 2026-10-03 13:01:42 +08:00
TermiNexus 2868798f58 fix(classgen): 补声明 GenericFunc* 五个类字段,修复 GenericFuncKeys 成员查找 fast-fail 2026-10-03 12:54:56 +08:00
TermiNexus 63acb2bf4d fix(fstring): 使用点按需补 @snprintf declare,f-string 不再依赖显式 import stdio 2026-10-03 12:46:30 +08:00
TermiNexus f2b65f57ec fix(match): case A | B 的 OR 模式注册全部备选(switch 仅取首个)
_handle_simple_match 的 MatchOr 分支此前只把 pats[0] 译成 case 值 → switch value: case 1 | 2 中 2 落 default(LogicFlowTest SwitchMultiValueTest(2) != 10)。改为按臂记录 MatchOr 节点,建 sw 链时逐备选 new_switch_case 挂同一 body(等价 C 的 case 1: case 2:);_handle_guarded_simple_match 同款:cond 改为各备选 subj==cv 的 OR 再与 guard 取与。非 MatchOr 路径逐字不变;非值备选改为 fail-fast(原为静默丢弃)。
2026-10-03 12:11:43 +08:00
TermiNexus 3b465121cd fix(call): kwargs 调用路径补齐省略形参的默认值
_translate_call_with_kwargs 此前对「位置/关键字均未提供的形参」直接跳过 → 调用实参数 < define 形参数,被调函数按 ABI 读未写寄存器/栈槽(remote.one(x = 1) 的 cap 读成 0,DefaultArgTest 断言失败)。改为:按 found_func mangled 名前缀取定义模块 SHA1,用全局默认表 find_func_defaults 取定义处默认值快照补齐(右对齐索引);取不到默认值时 fail-fast,绝不静默漏参/填零。跨模块默认值通道本就通(vpi 带默认值),无需改产物格式。
2026-10-03 12:08:32 +08:00
TermiNexus 692998820c feat(generic): 泛型函数类型实参推断与单态化
支持 def f[T](x: T) -> T 及 f(x)/f[T](x):模板注册(Phase1Scan/Phase2 顶层 FunctionDef,键 {sha1}.{name})、按形参注解对齐从实参推断类型实参(无来源即 fail-fast,不静默默认 int)、克隆模板并 set_generic_context 后翻译生成特化体(名字 {sha1}.f[int],规范化复用 _mangle_generic_class_name)、同签名去重、跨模块按模板签名补 declare。泛型函数模板不再发 vpi(模板签名含未绑定 T),也不再发射 int 兜底版本。已知限制(未静默):bound [T: bound] 解析期已被丢弃;t.CExport/t.CExtern/t.State 泛型函数、关键字/默认参数调用、mod.f[T](x) 显式下标等暂 fail-fast 或走既有报错。
2026-10-03 11:59:26 +08:00
TermiNexus 1fbebb2a62 fix(type): 普通模块 from-import 重导出类的归属模块校正
_class_true_def_sha1 在 cand 模块下查不到该类时原样返回 cand_sha1;而普通模块(非包 __init__)的 from X import Y 重导出不登记别名 entry(VpiReader._inject_reexport_class_declare 仅对包 __init__ 触发)。于是 vthreading.Lock(vthreading 仅 from lock import Lock)的前向声明被错误建在 vthreading 模块下 → 方法返回类型查询去读 vthreading 的 text.ll/vpi(无 Lock.acquire)→ fail-fast(ThreadingTest 根因)。补:先查全局 re-export 内存表 lookup_reexport,未命中再按需解析 .vpi 的 from-import 定位源模块,取其真定义 SHA1(源为包时按同一 IR 名前缀口径继续校正);无证据时行为不变。
2026-10-03 11:37:27 +08:00
TermiNexus 18f4c2326f fix(match): REnum match 变体字段绑定前确保跨模块结构体完整定义
_bind_variant_fields 对 renum_ty 做带字段下标的 GEP,LLVM 要求被指结构体 sized;而模块内对跨模块 REnum(如 llvmlite.__types.LLVMType)只有签名扫描产生的 type opaque,作 GEP base element 时 llc 报 base element of getelementptr must be sized(LLvmLiteTest 根因)。其它取字段路径在 GEP 前都会调 ensure_struct_def_in_module,唯独 REnum match 绑定漏了。补上该调用(同名原地替换 opaque,非追加;本地非跨模块类型自动跳过)。
2026-10-03 11:21:41 +08:00
TermiNexus 105456e56e feat(type): 泛型类注解必须带类型实参,裸名 fail-fast
resolve_annotation_type 入口集中守卫:注解(或其 A | t.CPtr 一侧)的基名(Name.id / Attribute.attr)是已注册泛型模板却没写 [...] 时 fatal_error,报文给出正确写法示例(vector.Vector[CInt]、GSList[G5Node])。判据复用 _find_generic_template(按项目注册),非泛型同名类(struct_test 的裸 Box、dict)不误报;Subscript 形态不受影响,仍走 _resolve_generic_subscript_ty 与 _fail_on_degenerated_generic_annotation。自举源码 includes/** 与 TransPyV/App/** 已确认无裸泛型注解。
2026-10-03 11:16:42 +08:00
TermiNexus 8da4550d79 fix(vpi): 全局变量注入补自注入守卫,消除同模块 external global 与定义重名
循环 import(os/__init__ 导入 os._win32/os._posix,而二者又导入包 os)使 imported_modules 含本模块包名 → LoadDepVpiIntoModule 命中本模块自身 sha1 → _inject_global_variable_declare 为本模块自己的模块级全局发射 @<sha1>.<name> = external global,随后定义端 handle_module_level_var 再发 = global … → llc redefinition of global(SysLibTest 根因)。补上 _inject_function_declare / _inject_method_declare 已有的同款自注入守卫(_declare_only==0 且 src_sha1==trans.ModuleSha1 时早退)。
2026-10-03 11:08:58 +08:00
TermiNexus 19d7a90c28 feat(match): 支持 match/switch 守卫(case X if cond)
通配计数只统计无守卫的 case _,允许多个带守卫的 case _ if ...;_handle_simple_match 预扫描 has_guard,有守卫时改走 _handle_guarded_simple_match(按书写顺序的 if-else 比较链,guard 归一为 i1 后与值比较取与,链尾落 default/end),无守卫时保持原 switch 分派路径逐字不变(零回归);_handle_renum_match 对带守卫臂 fail-fast,避免 switch(tag) 静默忽略 guard。
2026-10-03 10:58:47 +08:00
TermiNexus 67667b10db fix(class): __before_init__ 定义时复用同名前向声明,消除同模块 declare+define
本类方法体内构造自身(如 Vec2.__add__ 里 return Vec2(...))时,构造路径先经 _call_method_on_ptr → _ensure_method_declare 在本模块建好同名 __before_init__ 前向声明(IsDeclared=1);随后 _generate_before_init 又 create_function 追加一条同名 define,使同一模块 IR 同时出现 declare 与 define → llc 报 invalid redefinition of function(OopInheritTest 根因;④ 改用 full.ll 直喂 llc 后不再被旧 BuildCombinedIR 去重掩盖)。改为按名复用该前向声明(命名首参 %self、校准类型、清 IsDeclared 转 define),与 _generate_method/FuncGenDef 同构。
2026-10-03 10:26:57 +08:00
TermiNexus 4c114d8875 fix(call): 泛型模板的前向声明占位不再阻断构造期类型实参推断
注解 vector.Vector | CPtr(无实参)会经 TypeGenResolve 登记不透明前向声明占位 {sha}.Vector(IsForwardDecl=1)。模块限定构造 vector.Vector(...) 在 CallGenMixin 命中该占位后,gen_ctor_entry != None 使泛型推断分支(struct_ty is None and gen_ctor_entry is None)被整段跳过,静默按普通结构体/函数构造并产出裸泛型基类对象 → 后续方法调用触发 D25 接收者 fail-fast(VectorGenericTest 根因)。改为:命中项是前向声明占位且类名为已注册泛型模板时,不锁定 struct_ty/gen_ctor_entry,放行泛型推断按实参(_hint: T)特化;裸名分支与模块限定分支同口径。
2026-10-03 10:18:29 +08:00
TermiNexus 53ba4e48bd fix(expr): 修正 not 取反的 const_int 参数顺序(bits 在前)
上一提交 df9a5a9 误把 const_int 参数写成 (value, bits),实际签名为 const_int(pool, bits, val)。改为 const_int(pool, 1, 0)(i1 false),使 not x 真正降级为 (bool(x)==0),不再与 1 比较而恒等。对照本文件 663/680 行的 const_int(pool, 1, 0) 用法可确认参数顺序。
2026-10-03 09:58:35 +08:00
TermiNexus 9bd3ac3dfb refactor(sidecar): 删除 BuildCombinedIR 文本合并死代码(④-d)
在线模型方案(④-a~④-c)已由 LoadFullIR 取代文本合并,BuildCombinedIR 及其专属辅助(_Seen* 去重表、_RegisterStubLines、_LoadAndAppendStub、_LoadAndAppendTextDeclares、_IsTypeDefLine/_IsIrHeaderLine/HoistTypeDefsBeforeDeclares、_collect_stub_sha1s、_stub_scan_cache_*)已无调用方,一并删除(约 1348 行);同步改写 #35/TranslatorGenMixin 中引用旧函数的注释。
2026-10-03 09:13:17 +08:00
TermiNexus df9a5a9beb fix(expr): 修复 not 一元运算未取反(i1 条件与 0 比较)
not x 的降级此前写成 (bool(x) == 1),在 i1 上与恒等等价,导致 not r2 不取反(FuncTest Test 7 bool return 失败)。注释本意即 not x = (bool(x) == 0),改为与 i1 常量 0 比较。
2026-10-03 09:07:04 +08:00
TermiNexus cc8ea01476 feat(sidecar): 翻译点增写 {sha1}.full.ll,Phase B 改用在线 FULL 产物(④-b/④-c)
- 翻译点(Phase1Main / Phase2 PhaseA / Phase2 includes 重试)在 dump stub/text/meta 后
  追加 dump_ir(OUTPUT_FULL) → {sha1}.full.ll(FULL 缓冲 16MB = stub+text 2× 余量)
- Phase1 增量跳转新增要求 full.ll 存在(缺失即重译补写)
- 新增 StubMergerBuild.LoadFullIR(与 BuildCombinedIR 同契约:读文件到 out_buf)
- Phase B 三处调用点 BuildCombinedIR → LoadFullIR;COMBINED_IR_SIZE 4MB → 16MB
- 诊断报文/注释同步(读 full.ll 失败)

full.ll 为模型在线时产出的自足单模块 IR(类型定义在声明之前,天然满足 llc 顺序);
BuildCombinedIR 暂保留(④-d 再删)。未编译验证。
2026-10-03 08:43:03 +08:00
TermiNexus ec9e950185 fix(gen): 全局初始化器引用跨模块 vtable 时补 external 声明(④-a)
模块级对象全局的 storage 初始化器以符号名写入依赖类 vtable
(i8* @<dep_sha1>.<Cls>.vtable),此前不建 GlobalVariable ⇒ 单模块
OUTPUT_FULL 缺该符号声明,只能靠 BuildCombinedIR 合并依赖 stub 补入。

现于写初始化器前:跨模块(ModuleSha1 != 当前模块)且模块内无该全局时,
new_global_variable(..., Ptr(i8)) + module_add_global 补 external 声明
(对齐 CallGenLeafKit._make_vtable_ref_i8)。仅跨模块补,本模块类由
_generate_vtable 产出定义,避免同模块重复定义。

去 IR 化步骤④-a(生产者自足,FULL IR 自足化前置);未编译验证。
2026-10-03 08:39:13 +08:00
TermiNexus 324a22b6d1 refactor(call): CallGenFd 返回类型精确化改以 vpi 为权威
返回类型「精确化」原只读定义模块 text.ll;改为先查 vpi 返回注解缓存
(_lookup_method_ret_ty_via_vpi),未命中再回退 text.ll,与 ② 的
vpi→text.ll 口径一致。

去 IR 化遗留收尾;未编译验证。
2026-10-03 08:28:07 +08:00
TermiNexus 3b0ed04e70 chore(stubmerger): 删除 StubMergerIR 中 6 个无调用方的旧 IR 文本扫描助手
删除 _IsFuncDeclaredOrDefined / _CommentOutDeclareInBuf / _CommentOutGlobalInBuf /
_HasGlobalInBuf / _CommentOutOpaqueTypeInBuf / _HasFullTypeDefinition —— 全仓无调用方
(去重判定早已迁移到 StubMergerBuild 的 _seen_* 哈希表),保留在用中的
_is_submodule_of_imported。顺带更新本模块头注释(现为 meta sidecar 工具)。

去 IR 化收尾清理;未编译验证。
2026-10-03 08:26:02 +08:00
TermiNexus 611f49cfb6 refactor(repl): 会话函数签名改从翻译期模型取,去除片段 IR 文本解析
- 新增 _repl_ty_ptr_token(LLVMType* → tag*1000+bits)与 _repl_model_func_sig
  (按裸导出名查 tr.Module 的 Function,读 RetTy/Params;含 ctx 首形参与 EH 出参判定)
- _repl_precheck_session_funcs / _repl_register_session_funcs 增 mod 形参;
  两处调用点传 tr.Module
- 删除旧 IR 文本解析:_repl_ir_func_sig / _repl_ir_find_define_at /
  _repl_llvm_ty_token / _repl_span_str 及其 scratch ReplTySpan / ReplTySpan3 /
  ReplIrDefOff / _REPL_FN_TY_CAP

去 IR 化步骤⑤;返回码(1/2/3/4/5)与旧实现一致。未编译验证。
2026-10-03 08:23:45 +08:00
TermiNexus 35f2070d3b feat(sidecar): meta 增写 STRUCT/VTABLE,REPL 布局恢复改读 meta
- WriteModuleFuncMeta 更名 WriteModuleMeta:追加 STRUCT <name> FIELDS n PACKED 0|1
  (opaque → STRUCT <name> OPAQUE)与 VTABLE <class> SLOTS n;数据源改为
  Module.NamedTypeHead / GlobalHead,不再有任何 IR 文本反推
- VpiReader REPL 布局改读 {sha1}.meta:新增 _meta_atoi / _repl_meta_scan_layout
  按前缀精确匹配解析 STRUCT/VTABLE 行
- 删除旧 .stub.ll 文本解析辅助 _repl_stub_take_name / _repl_stub_body_count
  及其 scratch 缓冲 ReplStubNameBuf / _REPL_STUB_NAME_CAP

去 IR 化步骤③;未编译验证。
2026-10-03 08:18:15 +08:00
TermiNexus 78d3941f7d refactor(call): 跨模块返回类型解析改以 .vpi 为权威,text.ll 降为回退
将 8 处跨模块方法/函数返回类型解析的 text.ll → vpi 顺序对调为 vpi → text.ll:
- CallGenProp:属性方法调用
- CallGenMixin:跨模块裸名 CExport、from-import 函数
- CallGenCall:跨模块方法、类方法调用、self 方法调用(含 MRO 祖先链重试段整体搬移)
- CallGenDispatch:__len__ 跨模块、泛型 Mixin 方法

本模块 found_func / local_fn 取当前 IR 声明类型的路径保持不变。
去 IR 化步骤②;未编译验证。
2026-10-03 08:10:34 +08:00
TermiNexus 94d2bf5047 feat(sidecar): 落盘 {sha1}.meta 函数定义清单,声明文件判定改读结构化 sidecar
- 新增 WriteModuleFuncMeta:翻译期从 Module.FuncHead + Function.IsDeclared 产出 FUNC <mangled> DEF|DECL(与 dump_ir 的 stub/text 判据同源)
- 新增 _MetaHasDef / 重写 IsDeclOnlyModule:不再扫 text.ll 的 define 行,改读 meta
- Phase1/Phase2 落盘点紧随 text.ll 写出 meta;Phase1 增量跳转额外要求 meta 存在
- Phase2 内联 is_decl_mi 与 PhaseC 判定统一走 IsDeclOnlyModule

去 IR 化步骤①(sidecar 最小闭环),全后端统一,默认行为不变。
2026-10-03 08:06:39 +08:00
TermiNexus 0ebe2e0752 feat(backend): 新增 MLIR 后端开关/产物隔离/降级管线/模块别名(默认 llvm 逐字不变)
- Config: BackendTarget/BackendIsMlir/set_backend/is_mlir_backend/backend_dir_suffix/alias_module_name;读取 options.backend(回退 options.target)

- main.vp: 新增 --backend llvm|mlir(CLI 优先级最高,默认 llvm),加载工程后、resolve_paths 前生效

- 产物隔离: temp/output 追加 _mlir 后缀;includes 缓存追加后端后缀(.binary[_asan][_mlir])

- BuildPipeline: MLIR 降级 mlir-opt(校验) -> mlir-translate --mlir-to-llvmir,覆盖串行/并行/--phase2command,且置于 ASan opt 之前;LlcTask 增 MlirOptCmd/MlirTranslateCmd 字段

- 别名: ResolveModuleToPath 与 _FindSha1ByModName 经 Config.alias_module_name 把 llvmlite 重定向到 mlirlite(调用点零改动)
2026-10-03 07:53:04 +08:00
TermiNexus 4eec3f0908 feat(dunder): 补齐 dunder 协议(__bool__/__call__/__str__/__abs__/__del__/**/增量/反向)
- __bool__:ExprGenOps.coerce_cond_to_bool 统一 if/while/and/or/not/三元/协程条件的真值归一,优先调度 __bool__

- __call__:obj(args) 分发到 obj.__call__(CallGenMixin 裸名分支)

- __str__:str()/print/f-string 调度(CallGenDispatch/CallGenLeafKit/ExprGenFstr)

- __abs__:abs(obj) 调度(CallGenMixin)

- __del__/__delete__:del obj → __delete__;del obj.attr 无 property deleter 时回退对象 __del__

- ** / __pow__:新增 llvm.pow 内联(整数按常量指数回写),_binop_to_dunder 补 Pow

- 增量赋值:__iX__ 优先、缺失回退 __X__(新增 _aug_op_to_dunder)

- 反向运算符:新增 _rbinop_to_dunder,lhs 无 dunder 时调度 rhs.__rX__
2026-10-03 00:40:00 +08:00
TermiNexus a3bb62f2c3 feat(switch): switch 允许重复 case 标签(match 仍禁止)
- HandlesMatch / MatchGenMixin:值标签去重仅对 match 生效;switch 允许重复 case
  标签(按书写顺序,先出现者生效);通配臂多个(default / case _)仍 fail-fast
- wiki/07-control-flow.md 同步该语义
2026-10-03 00:18:55 +08:00
TermiNexus fabd92adc7 feat(switch): 新增 switch 语句并禁止 match 内 fallthrough
- MatchGenMixin:case 体按 node.is_switch 置 _in_switch_case;_has_no_break 只认 FallThrough 节点(去掉 c.NoBreak() 兼容路径)
- StmtGenDeclMixin:translate_fallthrough 只在 switch 的 case 体内合法,match/switch 之外 fail-fast
- Translator:_in_match_case 更名 _in_switch_case(语义 = 当前 case 体属于 switch)
- FuncGenDef / FuncGenMixin:函数体 / 嵌套函数 / lambda 入口隔离并恢复 _in_switch_case
- 因 match 改为硬关键字,重命名同名局部变量 match -> Matched(StubMergerStore / ImportsGenMixin / ExprGenAttr / CallGenBase)
2026-10-02 23:15:43 +08:00
TermiNexus 38860a6313 feat(match): 重名 case 标签 fail-fast
MatchGenMixin._translate_match_impl 新增前置检查(任何 IR 产出之前):新增
_case_label_same / _value_label_same 比较 switch 标签(常量 / 名字 / 属性链 /
MatchSingleton / MatchClass 变体名 / MatchOr 子模式),标签重复或出现多个
`case _` 时 fatal_error 终止(C switch 不允许重复 case 标签)。
2026-10-02 22:44:34 +08:00
TermiNexus d633a5786f fix(flow): 函数体入口清空 break/continue 目标与 match-case 标记
- FuncGenDef.translate_function_def:进函数体前保存并清空 _break_bb / _continue_bb /
  _in_match_case,退出恢复
- FuncGenMixin:嵌套函数体与 lambda 体两处同样处理——原实现下嵌套函数体内的
  break/continue 会继承外层函数的跳转目标(跨函数 br,非法 IR)
2026-10-02 22:40:04 +08:00
TermiNexus fb4175736f fix(flow): 循环/match 外的 break/continue 改为 fail-fast
- StmtGenDeclMixin:translate_break / translate_continue 增加 node 形参,目标块
  缺失(_break_bb / _continue_bb 为 None)时 fatal_error,不再静默不发跳转
- StmtGenMixin:Break / Continue 分派传入 node(供错误定位)
2026-10-02 22:35:26 +08:00
TermiNexus 43d25e58b9 feat(match): case 内 break 跳出 match,并落地 fallthrough
- Translator:新增 _in_match_case(FallThrough 合法性判定用);_break_bb 注释补充
  case 内 break 语义
- StmtGenDeclMixin:新增 translate_fallthrough(本身不产出 IR;非 case 体内 fail-fast)
- StmtGenMixin:FallThrough 语句分派
- MatchGenMixin:进出 case 体(含 default 臂)时暂存/覆盖 _break_bb = match end_bb,
  使 case 内 break 跳出 match 而非外层循环;_has_no_break 兼容 FallThrough 标记;
  REnum / simple 两条 switch 路径的 fallthrough 由"只留无终结符块"改为显式 br
  下一个 case body(原实现产出非法 IR)
2026-10-02 22:33:15 +08:00
TermiNexus 0b7f7a50c7 docs(types): 同步 list 相关注释,list[T, N] 不是定长数组
- TypeGenResolve._is_count_arg_container:说明把 list 纳入判据只是为了历史写法
  list[E, N] 的第 2 实参不被误当类名归一;list 本体是 _list.vp 的堆上 STL 容器
- AssignGenMixin:注释示例由 list[type] / list[type, N] 改为 CArray 形态
2026-10-02 22:16:30 +08:00
TermiNexus 8d01c01afd feat(viperbuild): 落地 --src 工程模式配置面,并补齐循环 else 与整型 None 比较
- 新增 lib/core/builtin/ViperBuild.vp:--src Pass 0 依赖闭包扫描 + 宏配置提取 +
  默认值填充 + 写回 Config;main.vp 路由改为工程模式并删除单文件分支
- Translator.vp:写入 triple / datalayout,翻译期擦除 viperbuild 宏语句;
  ImportsGenMixin.vp:对 viperbuild 豁免 SHA1 校验
- StmtGenCifMixin.vp:for / while 的 else 子句在 11 条循环生成路径全部接通
- ExprGenOps.vp:None 比较按 None 所在侧分派,整型 None 隐式为 == 0 / != 0
2026-10-02 22:07:01 +08:00
TermiNexus 15b416b527 fix(type): 统一注入期类归属与导入上下文记账,修占位表爆与属性标记误解析
- StubMergerStore 新增共享导入上下文记账(AppendImportedModule / AppendFromImport /
  ImportCtx / AllocImportCtx / NotePlainImport),ImportsGenMixin 与 VpiReader 共用同一份
  口径;顺带移除 add_from_import 的 256 缓冲静默截断,VpiReader 删除两份私有副本

- 注入窗口按「名字归属」判据解析(is_module_class + CurDefModuleSha1):
  _forward_declare_ptr_from_node 的 Name 分支、resolve_annotation_type 的 Name/Attribute
  分支均补齐,使同一 stub 内先声明/先引用的类产出**具名占位**而不是 Ptr(i8),并新增
  _is_tattr_marker 让 t.attr.* 属性标记不参与类型联合竞争

- VpiReader._inject_class_struct_declare:命中前向声明占位时不再早退,改为「占位 Ty
  原地写入」(对齐 ClassGenDef.fill_fwd),修 StructTable 爆表(table full, cannot
  register Translator)

- 清除全部一次性探针:[DBG-GEN](VpiReader / StructGenMixin / ClassGenDef)、
  [DBG-ATTR](ExprGenAttr)、[MODSHA1](CallGenMixin)

根因记录:vpi 注入期漏记 `import X as Y -> from_imports "Y:X"`,使 GenTypes.VarEntry.Spec
(TSpec.TypeSpec | t.CPtr)取不到模块 SHA1、退化为 i8* 并被永久冻结
(CallGenCtor.vp:897 .PtrLevel 属性访问失败)。
2026-10-02 17:29:38 +08:00
TermiNexus 2c33effee1 fix(type): 前向声明补 import-as 别名反查(LookupModNameByAlias)
_forward_declare_ptr_from_node 的 Attribute 分支此前仅走
ImportsGen.lookup_from_import 反查别名;对 `import X as Y`(from_imports 里存
"Y:X")补一道 StubMergerStore.LookupModNameByAlias 专用反查,避免别名解析不到
→ 拿不到模块 sha1 → 无法前向声明而退回 Ptr(i8)。现场候选:GenTypes.vp 的
`import lib.core.Gen.Type.TypeSpec as TSpec`,字段 `Spec: TSpec.TypeSpec*`
(ve_i.Spec.PtrLevel 属性访问失败)。
2026-10-02 11:48:31 +08:00
TermiNexus 4958fd2c14 debug(attr): [DBG-ATTR] 泛化到任意属性访问失败,打印基座/结构体/字段数(用完删除)
Generator.FuncGenBase 已由前向声明修复;新报错 ve_i.Spec.PtrLevel 属同类症状
(某字段指针的 pointee 不是可查字段的结构体)。去掉 attr=="FuncGenBase" 守卫,
任何属性访问失败都打印 obj_ptr.Ty / struct 名 / 条目 FieldCount / base_class。
仅打印,不改行为。
2026-10-02 02:50:01 +08:00
TermiNexus 87e2356eea feat(type): rawptr[T]/t.CPtr[T] 下标形也走前向声明,并覆盖同文件前向引用
承接 c0d3231(X | t.CPtr 已改前向声明):
- t.CPtr[T] 分支:inner 解析为 None 时先尝试前向声明具名占位并返回
  Ptr(占位.Ty),保住类身份;无法确定类名/模块时才回 None 交多遍收敛。
  rawptr[T] 经 builtins typedef(rawptr = t.CPtr)兜底归一到本分支,故一并覆盖。
- _forward_declare_ptr_from_node 的 Name 分支补"本文件顶层类"回退:用
  StructGen.register_file_classes 登记的"{名字 → 本文件模块 SHA1}"前向声明,
  覆盖同文件前向引用 / 自我引用(如 JsonValue | t.CPtr 自引用字段)。

无编译验证。
2026-10-02 02:48:26 +08:00
TermiNexus c0d3231e02 fix(type): 未注册类指针改前向声明(具名不透明占位)+ 定义侧原位填充
对齐 TPC 语义(未注册类 → 具名空 CStruct 占位 + 后续 set_body 填充)。
此前 TPV 对"指向尚未注册类的指针"回退匿名 Ptr(i8),丢类身份且被冻结后
无法修复:现场 CallGenBase.Gen: Generator.Generator* 与 Generator 构成
循环依赖(Gen→Generator,Generator.CallGenBase→CallGenBase),Gen 先于
Generator 注册 ⇒ 冻结成 i8* ⇒ self.Gen.FuncGenBase 属性访问失败
(1.log 取证:Generator 条目本身正确 fcount=47,但 lookup_field 收到
struct_ty=<non-struct>)。

- GenTypes.StructEntry 新增 IsForwardDecl 标记(占位条目)。
- StructGenMixin.ensure_forward_class:按"类名 + 定义模块 SHA1"登记具名
  不透明结构体 {sha}.Class 占位(名称与定义侧 ClassGenDef 同口径,持久池
  分配),幂等;定义侧据此按名定位原位填充。
- TypeGenResolve._forward_declare_ptr_from_node(Attribute/Name 两形,经
  from_imports 与模块名表求 sha1,再 _class_true_def_sha1 校正)+ BinOp
  (X|t.CPtr) 未注册回退:优先返回 Ptr(占位.Ty);仅在无法确定类名/模块时
  才退回 Ptr(i8) 由 AnnotClassName 恢复。
- ClassGenDef:命中前向声明占位时不早退,把新构建的字段体**原位写入同一
  Ty 对象**(*existing.Ty = Struct(...),指针身份不变),登记条目沿用占位
  条目;declare_only 1/0 均适用。所有已发出的 Ptr(占位.Ty) 引用在填充后
  自动指向完整定义。

TPC 侧核对:TPC 本就是"具名占位 + set_body"模型(TypeAnnotationResolver
.CreateFromTypeName / BaseGen._get_or_create_struct / StructGen op 占位),
并有 fail-fast 拒绝类引用降级为 void**,无需改动。
2026-10-02 02:40:05 +08:00
TermiNexus d1c0f6754c debug(gen): 补 [DBG-ATTR] 探针——FuncGenBase 处 obj_ptr 类型与注解类名兜底(用完删除)
已知(1.log 取证):Generator 结构体登记正确(tbl[337] fcount=47 含 FuncGenBase),
真正问题是 `self.Gen` 字段类型退化成 i8*(lookup_field 收到 struct_ty=<non-struct>)。
本探针在报错点补打印:
- obj_ptr.Ty(确认是否为 i8*)
- base_class(_resolve_attr_field_class 取到的 Gen 字段注解类名)
- 用 base_class 走 lookup_field_by_class 找 FuncGenBase 的字段下标

用于判定"注解类名未记录"还是"按类名查询也失败"。仅打印,不改行为。
2026-10-02 02:31:09 +08:00
TermiNexus 01e95b51cf debug(gen): 一次性取证插桩——Generator 结构体登记/查找全链路(用完删除)
覆盖点(全部以 [DBG-GEN] 为前缀,仅打印不改行为):
- StructGenMixin.register_struct:IN(name/sha1/ty/StructCount)、DEDUP(命中条目)
- StructGenMixin.add_field:逐字段打印写入 Generator 条目的字段名与下标
- StructGenMixin.find_struct_by_module:IN / HIT / MISS
- StructGenMixin.lookup_field(field=FuncGenBase):struct_ty 名、按 Ty 指针命中条目的
  全字段名、以及结构体表内所有含 "Generator" 的条目
- VpiReader._inject_class_struct_declare:IN / EXISTING / RENUM-SKIP / EMPTY /
  FIELD-FAIL / REGISTERED
- ClassGenDef.translate_class_def:EXISTING / REGISTER / DONE

目的:一次重建即可判定 Generator.FuncGenBase 查不到是"命中空/旧条目""Ty 错配"
还是"字段名悬空",并看清是哪条登记路径产出的条目。
2026-10-02 02:18:55 +08:00
TermiNexus 1ad7abf33d refactor(TransPyV): 统一自举写法(novirtual / define / rawptr→* / 去续行反斜杠)
- @t.NoVTable class X → novirtual class X
- t.CDefine = N → define N
- rawptr[X]、X | t.CPtr → X*
- t.CPtr → rawptr(裸指针标记)
- t.Callable[...] → callable[...]
- 去掉 if/elif 条件的行续反斜杠(改单行写法)
- w32.win32file.X → win32file.X(已 import as)
- core/__init__.vp 去掉未使用的 import t, c / from stdint import *

涉及:CompilerFactory、IncludesScanner、Repl、core/__init__、
Gen/Call/{CallGenBase,CallGenCBuiltin,CallGenCall,CallGenDispatch,CallGenLeafKit}、
Gen/Class/ClassGenGeneric。
2026-10-02 02:16:37 +08:00
TermiNexus 7e82ed6940 chore(debug): 移除 [DBG-GEN] 一次性插桩(改走静态分析)
按用户要求撤掉 lookup_field 里的临时打印,恢复原函数体。
2026-10-02 02:14:54 +08:00
TermiNexus 1d1efc6288 debug(gen): 一次性插桩打印 Generator.FuncGenBase 字段查找上下文(用完删除)
AsyncGenMixin.vp:58 `self.Gen.FuncGenBase` 报「属性访问失败」,需确认
lookup_field 收到的 struct_ty 是谁、结构体表里 Generator 条目的 FieldCount/sha1,
以判断是字段缺失、Ty 身份不一致还是按到了空/错误条目。仅打印,不改行为。
2026-10-02 02:10:58 +08:00
TermiNexus f865986675 fix(stub): App 预注册注入依赖 vpi 时跳过函数/方法 declare,避免打断多遍收敛
承接上一提交(App 预注册期开始注入依赖 vpi)。注入的依赖 vpi 里,函数/方法
declare 的形参/返回注解可能引用**本遍尚未注册**的类(跨文件注册顺序:被依赖模块
的类可能排在更后,需靠下一遍收敛),resolve 落空会在 declare 注入里 fail-fast,
直接打断 Phase A-pre 的多遍收敛。现场:App 预注册 AssignGenMixin.vp 注入
Translator.vpi 时,InjectTracebackInstallCtor 形参 rawptr[Translator] 解析失败
(Translator 结构体因字段引用尚未注册的 GenTypes 类而本遍未登记)。

改动:VpiReader._ParseVpiIntoModuleImpl 新增 pre_skip_fn_declares(仅当
trans._pre_allow_dep_inject != 0 即 App 预注册时置 1),在「第一遍 B 方法 declare」
与「模块级函数 declare」两处跳过注入。预注册的唯一目的是注册 struct/enum/union
(第一遍 A)与包重导出别名(ImportFrom 段),两者均保留;方法/函数 declare 写在
预注册的瞬时模块里、随即随 mb.reset_to 丢弃,无跨遍作用。全量翻译
(_declare_only==0)不跳过,仍 fail-fast。
2026-10-02 01:47:18 +08:00
TermiNexus 981af07d5c fix(phase): App 预注册期注入依赖 vpi,修 list[ast.ClassDef*] 票名前缀分裂
Phase A-pre(App 预注册)此前不做导入处理、不注入依赖 .vpi,导致包重导出
类的字段注解在预注册期只能保留「包前缀」(1abcd…),并被冻结进结构体布局;
阶段 A 构造函数侧却能借包别名 entry 校正为真定义前缀(e70bd…),两处票名
分裂 ⇒「非基本类型指针不可隐式转换」(现场 ClassGenGeneric.vp:92)。

改动(只放开 App 预注册,不动 includes 预注册那条防依赖环自锁的约束):
- Translator 新增 _pre_allow_dep_inject 开关,并在导入处理门拆成两道:
  ①导入处理 + 自动容器可见性 + LoadDepVpiIntoModule —— 全量翻译与 App 预注册共用;
  ②模块级变量预注册 + 函数前向声明 —— 仍只在全量翻译做。
- BuildPipeline.TranslateFileGetTrans 新增 pre_allow_dep_inject 参数并写入 Translator。
- Phase2 App 预注册调用点传 1;includes 预注册保持默认 0。
2026-10-02 01:03:31 +08:00
TermiNexus 57f8c655c8 chore(debug): 移除 [DBG-P7] 一次性插桩(根因已定位)
定位结论见 Debugger/debug-nested-class-struct.md 的 P7:不是别名登记失败,而是
Phase A-pre(预注册 struct/enum/union,declare_only)解析类字段注解时,被 import
模块的 vpi 一概尚未注入 ⇒ 包重导出别名 entry 不存在 ⇒ 字段侧票名保留包前缀并被
冻结进结构体布局,与阶段 A 构造函数侧的真定义前缀分裂。

删除 96c1102 / 3b8a2b2 引入的全部临时打印(VpiReader.vp / TypeGenResolve.vp /
CallGenCtor.vp 三处),代码回到插桩前状态(-108/+0)。
2026-10-02 00:59:44 +08:00
TermiNexus acd6c9a615 docs(expr): 去掉指向已删除的 wiki/14-oo-refactor.md 的注释引用
该文档(对象化重构计划)已完成并从 wiki 删除,注释只保留「对象化 P3」的来源标记,
避免留下悬空文档引用。
2026-10-02 00:47:17 +08:00
TermiNexus 3b8a2b225f debug(stub): 补全 [DBG-P7] 插桩覆盖所有票名产生路径(定位后删除)
在 96c1102 基础上把"可能产出该票名"的路径一次打全,保证一次重建即可定死根因:

- TypeGenResolve._class_true_def_sha1:MISS 时额外 dump 结构体表里所有名字含
  ClassDef 的条目(下标/名字/归属 sha1/Ty 的 IR 名前缀)——直接看出别名 entry
  到底有没有、挂在哪个 sha1 上。
- TypeGenResolve.extract_class_name_from_annotation:AnnotClassName 通道算出的
  泛型票名(含 ClassDef 才打)。
- TypeGenResolve._resolve_generic_subscript_ty:注解/字段侧算出的泛型特化名
  gen_mangled(含 ClassDef 才打)。
- CallGenCtor._extract_type_args_from_slice:构造函数侧实参串的 raw/norm 两态。
- VpiReader._inject_class_struct_declare:进入 + 各出口
  (already / renum_skip / field_unresolved / zero_fields / registered)。
- VpiReader._inject_reexport_class_declare:调用现场与四个出口
  (already / no_src_entry / alias_reg_fail / alias_ok)。
2026-10-02 00:34:38 +08:00
TermiNexus 96c110242e debug(stub): 一次性插桩定位 ast.ClassDef 票名前缀分裂(定位后删除)
9994d2e 改判据后现象不变,需要在一次重建里同时看到三处事实,避免反复猜:
- TypeGenResolve._class_true_def_sha1:cls 含 "ClassDef" 时打印 HIT/MISS 与候选
  sha1、命中条目的 IR 名前缀(用 strstr 以便同时捕获 "ClassDef*" 形态);
- VpiReader._inject_reexport_class_declare:src/local 为 ClassDef 时打印调用现场
  的「源 entry 是否已注册 / 别名是否已存在」(src_entry=0 ⇒ 别名必然建不起来);
- VpiReader 的 ImportFrom 分支:判定为包 __init__ 时各打印一行(模块名、包前缀、
  子模块名与解析出的 sha1),确认包判定与子模块解析是否成立。

全部带 [DBG-P7] 前缀,便于 grep 与事后清除。
2026-10-02 00:32:05 +08:00
TermiNexus 9994d2e983 fix(stub): 包 __init__ 判定改按模块名,修 ast.ClassDef 票名前缀分裂
存量缺口:VpiReader 判定"本 stub 是否包 __init__"依赖 sha1→rel_path 反查
(strstr(rel_path, "__init__.vp"))。该反查在部分运行形态下取不到条目 ⇒ 判定恒
false ⇒ 包重导出类的别名 entry 静默不登记 ⇒ `ast.ClassDef` 这类限定名在泛型实参
里保留包前缀(幽灵名 `list[1abcd5277fa442de.ClassDef*]` 全产物恒 opaque),与构造
函数侧的真定义模块前缀(`list[e70bd9b4487d921d.ClassDef*]`)分裂成两个类型,报
「指针隐式转指针已禁止」(现场 ClassGenGeneric.vp:92 的 CastError)。

改法(不再依赖 rel_path 字段,均为模块名表的既有事实):
- StubMergerStore 新增 LookupModNameBySha1:按 SHA1 反查模块名(只读指针)。
- VpiReader 的 ImportFrom 分支:取 level>0 的兄弟模块 S,用其模块名去掉末段得到包
  前缀 P;又因 Utils.PathToModuleName 把 pkg/__init__.vp 记成 "pkg",故
  「本 stub 的模块名 == P」即证明本 stub 就是包 P 的 __init__ ⇒ is_pkg_init_rt=1,
  同时直接用 P 拼出子模块全名(不再从 rel_path 反推目录名)。
- 模块名证据不足时仍回退原 rel_path 路线,行为不变。
2026-10-02 00:07:23 +08:00
TermiNexus b91dfef777 feat(stub): vpi 注入侧递归登记嵌套类为扁平名 {外类}_{内类}
VpiReader 此前只登记顶层 ClassDef,跨模块引用 `外类.内类` 拿不到类型
(TPC 侧 stub.ll 已递归声明,双端不对齐)。现按定义端
ClassGenDef._translate_nested_classdefs 的命名口径补上:

- _inject_class_struct_declare 增加 name_prefix 形参,非空时算出扁平名
  real_name;REPL 分支、已注册检查、命名结构体类型名、register_struct、
  add_visible_struct 一律改用 real_name。
- 新增 _inject_nested_class_declares:遍历类体内缩进 ClassDef 子节点,
  以 real_name 为前缀递归调用(多层嵌套自然展开为 A_B_C)。
- 嵌套登记后再次调用 mark_module_class 复原"正在定义类"窗口:否则内层
  类名会顶掉 CurDefClassName,使外层字段注解的规则 5.5 自引用证据判错、
  且 `外类.内类` 的守卫①失效。
2026-10-01 23:39:11 +08:00
TermiNexus c3f554f1f5 feat(struct): 嵌套 class/struct 发射为独立结构体 {外类}_{内类}
用户 2026-10-01 定:struct 内允许叠加 struct(与 class 内写 class 同口径)。
此前类体里的嵌套 ClassDef 被**静默丢弃**(字段循环只认 AnnAssign、
_translate_oop_methods 只认 FunctionDef),现按 TPC 侧 union/renum 的
`{ClassName}_{Nested}` 命名先例发射为独立结构体,与 TPC 提交 5eef845e 同源。

- ClassGenDef.vp:新增 _translate_nested_classdefs —— 在 enum/union/renum/泛型/
  异常分派之后、existing 检查之前(所有提前 return 之前,故 declare_only=1/0 都递归)
  遍历类体 ClassDef 子节点,把子节点 name 临时改写为扁平名后递归
  translate_class_def(本模块所有名字派生点都读 cd.name ⇒ 改名即全链路一致)。
  两个护栏:扁平名分配在 PermPool(跨 Phase 存活);递归后还原 CurDefModuleSha1/
  CurDefClassName(内层 mark_module_class 会覆盖,否则外层字段解析误判当前定义类)。
- TypeGenResolve.vp:新增 _resolve_nested_class_type 并在 Attribute 分支的
  "base 不是模块" 出口前接上 —— 限定名 `外类.内类` 按扁平名查结构体表即可命中
  (裸名 `外类_内类` 走既有 Name 分支天然命中)。判据两道:base 是已知类
  (find_struct 命中,或正处于该类的定义窗口内);扁平名条目存在且 Ty 非空。

引用两种写法都可用:`外类.内类`、`外类_内类`;多层嵌套递归为 A_B_C。
2026-10-01 23:30:15 +08:00
TermiNexus 3ccc37ca3f fix(type): 泛型实参归一先剥尾随星号,消除包前缀票名分裂
_norm_type_arg_name 规则 4 用 strchr('.') 切出 attr 时把尾随 '*' 一起带进了类名
('ast.ClassDef*' → attr='ClassDef*'),于是 _class_true_def_sha1 按名查不到
entry、真定义模块校正失效,留下包前缀:

  字段侧 ClassGenBase.vp:123 → list[1abcd5277fa442de.ClassDef*](无定义的幽灵名,
                              全部产物里只有 = type opaque,从无 = type {...})
  构造侧 ClassGenGeneric.vp:92 → list[e70bd9b4487d921d.ClassDef*]
  ⇒ ClassGenGeneric.vp:92 报「指针隐式转指针已禁止」。

TPC 侧名称域(SymbolUtils.ResolveTypeArgStr 的 BinOp 分支)本就是「先解析基类型 X,
再 AddPointerLayers 追加 '*'」→ {真定义sha}.X*,TPV 与之对齐。

新增规则 1.6:剥掉全部尾随 '*' → 递归归一基名 → 回贴同样多的 '*'。
i8** / int* / T* / 已带 SHA1 前缀的文本逐字不变。
2026-10-01 22:40:54 +08:00
TermiNexus 02b9429363 fix(type): void* 归并分支的 match 必须守 None,修 ASan 空指针崩溃
BinOp 的 CPtr 分支里,上一笔为 void* 归并新增的 match other_ty 未判空。
other_ty = resolve_annotation_type(另一边) 在 Phase 1a prescan 时**可以是 None**
(本分支下方既有代码就写着 if other_ty is None: ... 回退 Ptr(i8),正说明 None 是
期望值);对 None 做 REnum case 匹配会按其 tag 解引用空指针。

现场:TPV(ASan) 自举 Phase1 报 access-violation on address 0x0(READ),
栈 _prescan_funcdef_signatures → resolve_annotation_type;触发源是用户 WIP 里
新增的 X* 返回注解(parser 把 X* 展开成 BinOp X | t.CPtr,正好走本分支)。

改为 if other_ty is not None: 包住,None 继续走下方既有回退路径。
2026-10-01 22:07:41 +08:00
TermiNexus be14fa5f94 fix(ctor): REnum 变体构造识别取错限定名,导致构造物化豁免失效
_ctor_call_agg_ty 从被调方沿属性链一路走到链根裸名,三段式
llvmlite.LLVMType.Struct(...) 于是被判成限定名 'llvmlite'(非 REnum 类)⇒
返回 None ⇒ ClassGenDef.vp:128 的 *existing.Ty = LLVMType.Struct(...) 被
「指针 → 非基本类型值」禁令拦下。改为取「变体名左边那一段」:两段式取裸名
(EnumType.Variant)、三段式取属性名(llvmlite.LLVMType.Struct → LLVMType)。
2026-10-01 21:44:55 +08:00
TermiNexus 2f6db41b11 fix(void): 形参与字段注解禁止 void,改为编译期 fail-fast
void 只允许用于 void* 与 typedef 不透明结构体别名(用户 2026-10-01 定)。
此前 def f(p: void) / class C: x: void 不走 translate_annassign,会按 LLVM void
直接建形参/字段槽,生成非法 IR 拖到 llc 阶段才暴露。现补 6 处形参落点
(FuncGenDef 前向声明/外部声明/函数体/alloca 各一、ClassGenVTable 签名/alloca 各一)
与 1 处字段落点(ClassGenDef),统一编译期 TypeError。
void* 已在上游归并为 i8*,不受影响。
2026-10-01 21:43:11 +08:00
TermiNexus 59865b2a3a feat(TPV): void 值操作 fail-fast(声明 void 变量 / 把 void 值当值用一律报 TypeError)
承上一笔(裸名 void = t.CVoid 别名,仅 `void*` 与 `typedef 不透明结构体 = void` 可用):

- ExprGenBase.coerce_to_type 顶部:void 值被 coerce 到**非 void** 目标(赋值 / 传参 /
  返回 / 二元运算对齐)直接 fatal_error(TypeError),不再按 LLVM void 硬拼出非法 IR
  ("load void" / "bitcast ... to void");void → void 属无值传递,放行。
- AssignGenMixin.translate_annassign:注解解析结果为 void 时禁止声明该变量
  (否则会按 LLVM void 建槽)。
2026-10-01 21:39:48 +08:00
TermiNexus e0bcf4bcef feat(TPV): 裸名 void 作为 t.CVoid 的别名(void* = i8*)
用户 2026-10-01 需求:`void` 即 t.CVoid 的别名,只允许两种用法——`a: void*` 与
`typedef 不透明结构体 = void`(后者 `X*` 亦为 i8*)。

- TypeGenResolve 内置名表:新增 "void"(与 "CVoid" 同义 → llvmlite.Void)
- TypeGenResolve._is_cvoid_marker:裸名 "void" 与 "CVoid" 同判
- TypeGenResolve BinOp 联合合并:裸名 "void" 直接置 other_is_cvoid(无需 import t);
  解析结果本身是 Void 时亦按 void* = i8* 归并,覆盖 `typedef X = void` 的 `X*`
  (否则会拼出非法的 Ptr(void))
- VpiReader:注解文本里的裸 "void" 段与 "CVoid" 同判(has_cvoid → i8*);
  has_cptr 分支对解析结果为 Void 的 first_ty 同样归并为 i8*(跨模块侧同口径)

TPC 侧无需改动:HandlesBase 的内置名表已有 'void': (t.CVoid, 0),
`void*` 经既有 CVoid+CPtr 合并即 i8*。
2026-10-01 21:37:40 +08:00
TermiNexus 21d5cd1cd1 fix(ExprGenBase): REnum 变体构造纳入「构造物化」豁免;错误报文只报问题本身
1) _ctor_call_agg_ty 增加 REnum 变体构造识别:`EnumType.Variant(...)` 的变体名不是结构体名
   (find_struct 必 miss)⇒ 取属性链最右裸名,若判为基础枚举则返回该枚举的聚合类型。
   否则 `*槽 = <boxed 变体>` 会被「指针 → 非基本类型值」禁令拦下——
   现场 ClassGenDef.vp:128 `*existing.Ty = llvmlite.LLVMType.Struct(...)`。
2) 按用户要求简化 Error 输出:CastError 只报问题(原因 + 源/目标类型),去掉
   ";出错表达式: <形态>(行/列)源码: ..." 后缀(文件与行号已由 TypeGenErr 的 Traceback
   帧给出,属重复展示),并删除随之失去调用点的 _cast_fail_where / _source_snippet。
2026-10-01 21:17:49 +08:00
TermiNexus a714ec9d37 fix(VpiReader): 返回/形参注解切分只在顶层 | 处断段(括号感知)
_resolve_vpi_ret_annot_str 原按裸 `|` 切段,`list[ast.AST | t.CPtr] | t.CPtr` 会被切成
`list[ast.AST` / `t.CPtr]` / `t.CPtr`;首段再经 strrchr('.') 落到 `AST` ⇒ 定义侧类型解析成
`AST*`(元素指针),而正确答案是 list 专化结构体的指针。现场:跨模块调用 coerce 时报
"源类型 list[AST*]* → 目标类型 AST*"(CallGenLib._coerce_args_to_def_params 的 cargs 形参)。

现切分按 `[`/`]` 深度,只在顶层 `|` 处断段(与 _cache_vpi_param_annots_text 的顶层逗号
切分同口径)。改后该段解析不出 ⇒ 落 i8* 通配,与 `list[str] | t.CPtr` 等同形注解的既有
行为一致;配合 CallGen 侧的 bitcast 对齐(见上一笔)即可安全使用。
2026-10-01 20:45:21 +08:00
TermiNexus 8e3811c490 fix(CallGen): 跨模块调用的 declare 形参与实参统一以定义侧为准
问题一(declare 形参被首个调用点冻结):CallGenBase._ensure_method_declare 新建跨模块方法
declare 时形参类型取"首个调用点的实参类型",而这条 declare 被同模块所有调用点共用 ⇒ 后续
调用传入另一种具体指针类型时,被这条被冻住的形参类型 coerce,报"非基本类型指针间隐式转换"。
现场:VarGenMixin.repl_ensure_entry 的 node: ast.AST*,先被 translate_annassign 以 ast.AnnAssign*
建 declare,后 translate_assign 传 ast.AST* 即报"源类型 …AST* → 目标类型 …AnnAssign*"。
⇒ 指针形参与整数同口径,一律采用定义侧(.vpi 注解段)类型;解析不出指针时保留实参类型。

问题二(实参未对齐 declare):declare 改用定义侧类型后实参也必须对齐,否则首个调用点
(由它建立 declare、不走 coerce_to_type)原样传具体指针会与 declare 类型不一致(llc 报
argument type mismatch)。⇒ CallGenLib._coerce_arg_value_to_ty 的同层指针分支改为"类型内容
不一致即 bitcast 到定义侧类型"(原判据只覆盖一侧为 i8*、或模板注解来源两种情形)。
2026-10-01 20:29:15 +08:00
TermiNexus 93e983f74e fix(TPV): 删除残留的"| t.CPtr struct 引用语义吸收"(T* 被降一层的真正根因)
现场(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。
2026-10-01 17:46:53 +08:00
TermiNexus 0a81e67e89 fix(TPV/TypeGenResolve): rawptr[T]/t.CPtr[T] 恒追加 1 层,删除"裸类引用吸收"旧补偿
现场:Test/SlotsBugTest 编译 includes/_list.vp:54 报
  TypeError: 下标赋值的左值元素是对象 'd1ed020479454541.AST'(48 字节),右值却是指向
  'd1ed020479454541.AST' 的指针
  源码: elem_ptr[0] = item

用户判定正确:`elem_ptr: T*` 与 `item: T` 同为 T,赋值本身合法;错在编译器把 `T*`
少算了一层——T 实例化为 rawptr[AST](= AST*)时,`T*` 应为 AST**。

根因:rawptr[T] / t.CPtr[T] 分支里有"切片是裸类引用(解析结果 Ptr(已注册 struct))
就吸收这 1 层"的旧补偿(_ptr_subscript_absorbs_class)。那是"裸类名自动升指针"时代的
产物——当时裸类名解析成 Ptr(struct),于是 rawptr[X] 会多包一层,需要吸收;
自 875a9be 起裸类名恒为**原型(struct 值)**,Ptr(struct) 天然就是 X*,补偿已无对象;
而**泛型形参实例化为指针类型**时(T = rawptr[AST]),切片的解析结果同样是
Ptr(已注册 struct) ⇒ 被误吸收:`elem_ptr: T*` 变成 AST*(应为 AST**)⇒ 左值元素被判成
48 字节的 AST 对象、右值是指针 ⇒ fail-fast。

改动:
- 删除 _ptr_subscript_absorbs_class,以及 rawptr[T] / t.CPtr[T] 两处调用,改为**恒追加
  1 层**:`rawptr[T]` ≡ `t.CPtr[T]` ≡ `T*` ≡ `T | t.CPtr` 四种写法同型(用户口径),
  不再有"裸类引用吸收"例外。
- 同步修正四处引用该函数的口径注释(:407 / :433 / :567 / :1000)。
- **保留** BinOp `| t.CPtr` 分支内的同名吸收:它只对**非裸名**侧可达(Constant/Attribute;
  裸名侧在更早的"Name 必须已注册 struct"守卫处就改走其它分支),故
  includes/json/__init__.vp 的引号注解 `'JsonValue' | t.CPtr` 行为不变。
  仓库内也不存在 `rawptr['X']` / `CPtr['X']` 切片写法(仅注释提及),删除吸收无副作用。

已知遗留(本次未改,待你定夺):`T | t.CPtr` 这一写法在 T 实例化为指针类型或类类型时
仍会退化——Name 侧解析出的 Ptr(struct) 被丢弃,随后按名字 'T' 查 struct 失败 ⇒
返回 Ptr(i8)(位置:本文件 BinOp 分支的 keep_other_ty 判定,约 :3166-3194)。
当前仓库没有使用该写法的现场,故未一并改动。

待验证:重编译 TransPyV(自举两遍)→ SlotsBugTest / JsonTest / NoVTableCrossMixinTest。
2026-10-01 16:51:31 +08:00
TermiNexus f748cae562 fix(TPV/CallGenCall): 判据化重加兄弟 Mixin 方法解析(修 NoVTableCrossMixinTest 未定义符号)
现场:Test/NoVTableCrossMixinTest 链接期报
  ld.lld: error: undefined symbol: 1e19037e6cfdce36.BaseB.helper
    >>> referenced by ...(1e19037e6cfdce36.BaseB.caller)
  ld.lld: error: undefined symbol: 1e19037e6cfdce36.MixinC2.mid2
    >>> referenced by ...(1e19037e6cfdce36.MixinC2.outer2)

这是 455c760 删除"兄弟 Mixin 兜底"的必然结果:NoVTable 多继承下,Mixin 内的
self.<兄弟方法>()(Combined(BaseA, BaseB) 中 BaseB.caller 调 BaseA.helper)本类
MRO 查不到、跨模块 stub 也没有 ⇒ 调用名退化成 {sha1}.BaseB.helper(该符号不存在)。

改动(不是恢复原兜底,而是**判据化解析**,不做"同名即绑"的猜测):
- CallGenCall._translate_method_call:在自身 MRO 查找失败后新增兄弟 Mixin 解析,
  两条**正向判据**缺一不可:
    ① 接收类必须是某个已注册类的**直接父类**(它确实被组合使用,是真 Mixin)——
       挡掉 includes/w32/fileio.vp 的 FileW.__init__ → self.seek() 误绑 File.seek
       (FileW 无人继承,不是任何人的 Mixin);
    ② 候选类与接收类必须同处某个已注册**组合类的 MRO**(真·兄弟)。
  命中后 found_func / mc_anc 都指向定义类 ⇒ 调用名用定义类 SHA1 前缀(符号存在),
  this 由既有的 mc_anc this-adjust 分支按组合布局前移(该分支未改动)。
  只取**本模块内已有函数**的候选(跨模块定义类需经其 text.ll 取签名,本轮不启用);
  接收类为跨模块类时不启用(沿用原 sib_recv_local 守卫,避免误绑跨模块同名方法)。
- 新增两个判据辅助方法:_recv_is_mixin_base / _mixin_sibling_same_composite。

待验证:重编译 TransPyV(自举两遍)→ NoVTableCrossMixinTest / WebTest / FileIoTest。
2026-10-01 15:00:55 +08:00
TermiNexus dc4e06d88b fix(TPV): 本文件自己声明的 extern 裸名优先于依赖 vpi 注入的同名声明
现场:Test/WebTest 编译 includes/socket.vp:236 报
  CastError: 指针隐式转指针已禁止:源 %"56d0ee99ecf0a0b5.SocketAddr"* →
  目标 %"99c88d4cbd933ac6.WSASocketAddr"*
  源码: result = connect(self.fd, c.Addr(addr), 16)

机制(**链接期无冲突**——两边都不是 export,@connect 是同一个 C 符号,最终只有
ws2_32 里一个定义;问题在"编译 socket.vp 时的同一个模块内"):
1. extern/state/export 声明的符号**不带 SHA1 前缀**(FuncGenDef.vp:172-173
   `mangled_name = fd.name`)⇒ socket.vp 与 w32.winsock2 都产出裸名 @connect
   (前者形参 SocketAddr|t.CPtr,后者 WSASocketAddr|t.CPtr)。
2. Translator 的 prologue 先注入依赖 w32.winsock2 的 vpi
   (StubMergerBuild.LoadDepVpiIntoModule → VpiReader.ParseVpiIntoModule →
   _inject_function_declare:create_declare + module_add_function),当前模块里
   先出现了 winsock2 版的 @connect。
3. 随后 forward_declare_functions 处理 socket.vp 自己的 `extern def connect` 时,
   find_func_in_module 命中已存在 ⇒ `continue`(FuncGenDef.vp:178-182)
   ⇒ **本文件自己的声明被静默丢弃**,调用点改按 winsock2 的形参 coerce
   (报错里的目标类型 WSASocketAddr* 正是 winsock2 版的指纹)。
   (同文件 send/recv/socket/listen 亦被顶掉,只是形参是 i8*/u64,不报错——
   即 socket.vp 的 POSIX 分支本来就在调用 winsock2 的声明。)

改动(注入期跳过"本文件已声明的裸名",让本文件自己的声明优先):
- StructGenMixin:新增 FileFuncNames 表 + register_file_funcs(pool, tree) /
  is_file_func(name),与既有的 FileClassNames / register_file_classes /
  is_file_class 同规矩(键与表同池、每文件随 reset_visible_structs 重建)。
- GenApi:新增 register_file_funcs / is_file_func 委托包装。
- Translator._translate_module_level:在 register_file_classes 之后调用
  register_file_funcs(三种模式共用、早于依赖 vpi 注入)。
- VpiReader._inject_function_declare:算出 mangled name 后,若 `use_bare_name != 0`
  且 `is_file_func(fd.name) != 0` 直接返回(不注入)。判据只用**裸名**:带 SHA1
  前缀的依赖函数名与本文件同名声明不会互相顶替,避免误跳过跨模块调用。

待验证:重编译 TransPyV(自举两遍)→ WebTest / JsonTest / FileIoTest。
2026-10-01 14:46:47 +08:00
TermiNexus d979cc8c1f fix(TPV/ExprGenName): 结构体值局部变量在使用处要求同型结构体值时 load 出值
现场:Test/WebTest 编译 includes/socket.vp:121 报
  CastError: 非基本类型的值/指针隐式转换已禁止(构造物化除外):
  不能把指针类型隐式转为非基本类型值(Foo* → Foo)
  源类型 %"56d0ee99ecf0a0b5.SocketAddr"* → 目标值类型 %"56d0ee99ecf0a0b5.SocketAddr"

现场代码:
  class SocketAddr:  family: u16 / port: u16 / addr: u32 / zero: u64   # 纯结构体,无 __new__、无方法
  def _MakeAddr(host: str, port: INT) -> SocketAddr:
      addr: SocketAddr                     # 结构体值局部变量
      string.memset(c.Addr(addr), 0, 16)
      ...
      return addr                          # ← 第 121 行报错

根因:ExprGenName.translate_name_value 对"结构体值局部变量"的右值**恒返回 alloca 地址**
(原实现,注释解释为"对象即指针"语义,避免 ptrtoint %Struct 非法指令);而 _MakeAddr
的返回类型经 resolve_annotation_type 解析为**结构体值**。translate_return 会把函数返回
类型作为 target_ty 下传给 translate_value(StmtGenCifMixin.vp:557-558),于是 `addr`
求值出 %SocketAddr* → coerce_to_type 的"指针 → 非基本类型值"分支
(ExprGenBase.vp:954-970)只对"构造物化"豁免 → 报错。
标量局部变量无此问题(同函数内非 Struct 分支会 build_load 出值),属结构体值局部变量
独有的表示差异。

改动:ExprGenName.translate_name_value 的 Struct 分支增加 target_ty 判据——使用处
**明确要求同一非基本类型(非指针)**时(_same_agg_ty 判据:与"构造物化豁免"同一类型
匹配真源 AssignGen._stg_ty_equal),像标量一样 build_load 出值;否则维持既有"返回地址"。
- target_ty 为指针时不命中(裸类形参 lowering 为 Ptr(struct),见 FuncGenDef)⇒
  Test/StructTest 的 point_sum(p) 这类实参传址行为不变。
- 字段访问基座(ExprGenAttr.vp:743-744,直接 lookup_var 取地址)、c.Addr 取址
  (CallGenCBuiltin.vp:293)、方法接收者均不经本函数取地址,不受影响。
- 同时修正该处已失真的注释(原文举例 `x: Cls | t.CPtr` 实际解析为 Ptr(struct),
  并非 Struct 值局部变量)。

待验证:重编译 TransPyV(自举两遍)→ WebTest / StructTest / ClassArrayTest / JsonTest。
2026-10-01 14:26:18 +08:00
TermiNexus 25c1c9875b fix(TPV/VpiReader): 阻断 vpi 自注入(循环 import)+ __init__ 声明端与定义端同取 void
现场:Test/JsonTest 编译 includes/json/__parser.vp 后 llc 报
  06cb4f5b8fd08841.ll:259:7: error: value doesn't match function result type 'i32'
    ret void
(该函数是 define i32 @"06cb4f5b8fd08841.ParseState.__init__",函数体末尾却是 ret void)

根因两层,都在 vpi 注入侧:
1) 自注入:__parser.vp 与 json/__init__.vp 循环 import(__parser 顶部
   `from . import (JsonValue, ...)`,__init__.vp 底部 `from .__parser import parse`)。
   __parser 编译时,其**自身** vpi(06cb4f5b8fd08841.vpi 里有
   `class ParseState: def __init__(self, src: str, pool: rawptr[...]): pass`)经依赖链
   (deps: t c stdint memhub string json → json 的依赖含 json.__parser)被反向注入
   ⇒ 模块 IR 里先出现 `declare i32 @"06cb....ParseState.__init__"`(IsDeclared=1)。
   该方法随后走 _translate_method 的"复用前向声明"路径,函数体按**定义端**语义发射
   (__init__ → 末尾 ret void),而函数对象自身的 RetTy 仍是注入时的 i32。
2) 类型口径分裂:VpiReader._inject_method_declare 对"方法无返回注解"一律退 i32 默认,
   而定义端 ClassGenVTable._translate_method 对 __init__ 恒 Void ⇒ declare i32 /
   define void。此前只在跨模块出现(不同 .obj,链接器不校验类型,静默 ABI 不一致);
   自注入把两者塞进同一个模块,才被 llc 拦下。

改动:
- VpiReader._inject_method_declare / _inject_function_declare:新增自注入守卫——
  仅全量翻译阶段(trans._declare_only == 0)且 src_sha1 == trans.ModuleSha1 时直接返回。
  本模块自己的 define 由 ClassGenVTable._translate_oop_methods / FuncGenDef 负责发射,
  不需要也不允许经依赖 vpi 反向注入(注入侧类型可能与定义端不一致)。
- VpiReader._inject_method_declare:__init__ 无返回注解 → Void(与定义端一致),
  消除 declare/define 返回类型分裂。__init__ 的返回值在三处调用点
  (CallGenCtor.vp:227 / :589 / :1275)均未被使用,改 void 不影响调用方。

待验证:重编译 TransPyV(自举两遍)→ JsonTest / FileIoTest。
2026-10-01 14:11:45 +08:00
TermiNexus 2bfcb6ef6e fix(TPV): 修 455c760 遗漏——args_node 必须声明在 decl_reuse 分支之外
现场:Test/FakeDuckTest 编译 includes/viperio.vp 第 12 行
  self.data = data   →   NameError: 未定义名称 'data'
(Buf.__init__ 的第一个形参名在方法体里找不到。)

原因:455c760 把整段"参数创建块"置于 if decl_reuse == 0: 之下时,把
args_node: ast.Arguments* = fd.args 一并圈了进去;但函数体段的
"为其他参数创建 alloca 并 store"(ClassGenVTable.vp:904 起)仍要遍历
args_node。复用前向声明(decl_reuse==1,现已是常规路径)时 args_node
未定义 → 形参不进符号表 → 方法体引用形参名即报 NameError。

修法:把 args_node 的声明提到 decl_reuse 分支之前。该块内其余变量
(md_defaults/md_default_count/md_param_count/md_args_node/md_ags/
md_alist/md_dlist/max_funcs/first_pname/first_pname_buf/fd_ags/
fd_first_arg/ags/alist/an/start_idx/ai/arg/param_ty/resolved/pname/
i1_ty_si/i1_ptr_ty_si)已逐一核对,全部只在块内使用,无需外提。

待验证:重编译 TransPyV(自举两遍)→ FileIoTest / FakeDuckTest。
2026-10-01 13:57:04 +08:00
TermiNexus 455c7608b1 fix(TPV): 方法先声明后定义(消除方法查找的源顺序依赖),删除兄弟 Mixin 兜底
现场:Test/FileIoTest 编译 includes/w32/fileio.vp 报
  CastError: 源 %"b0721da228be3d55.FileW"* → 目标 %"b0721da228be3d55.File"*
  (指针隐式转指针已禁止:被指类型是非基本类型)

根因(有旧 IR 佐证,非本轮改动引起):FileW.__init__(源 304 行)里的
self.seek(0, SEEK_END) 被解析成 File.seek,而不是 FileW.seek:
  1) _translate_method 只在方法体翻译时才 create_function,于是方法查找
     (find_func_in_module)依赖方法在类体内的书写顺序:FileW.seek 在源 403 行,
     晚于 __init__ → 查不到 → None;
  2) MRO 无父类 → None;
  3) 兄弟 Mixin 兜底随即遍历全部已注册 struct,取"本模块存在同名方法"的
     第一个候选类 → 命中 File → 绑成 File.seek,self 再被 this-adjust
     bitcast 成 File*。
旧 IR(TransPyV\.tpv_build\temp\b0\b0721da228be3d55.text.ll,mtime 9/27,
早于本轮全部改动)里就是:
  %97 = bitcast %"...FileW"* %96 to %"...File"*
  %98 = call i32 @"...File.seek"(%"...File"* %97, i32 0, i32 2)
即靠一层隐式跨类 bitcast 静默通过;指针规则收紧后以 CastError 报出。
(File.__init__ 的 self.seek 同样查不到,只是兜底恰好扫到 File.seek 而"碰巧正确"。)

改动:
1. ClassGenVTable._translate_oop_methods:翻译方法体之前,先对本类**全部方法**
   跑一遍 sig_only=1 建前向声明(declare + 参数 + 函数表登记)——与
   FuncGenDef.forward_declare_functions 对模块级函数做的事完全同构,
   使方法查找不再依赖类体内的书写顺序。
2. ClassGenVTable._translate_method 新增 sig_only 形参:
   · 签名部分改用 create_declare(IsDeclared=1),已存在同名前向声明则复用;
   · 发射函数体前把 IsDeclared 清零转 define(对齐 FuncGenDef.vp:544-545),
     否则打印端按 IsDeclared 输出 declare、函数体整体丢失;
   · 复用声明时跳过重复的 add_param 与 add_func_to_table;
   · 命中同名函数且 IsDeclared==0(函数体已发射)时直接返回,杜绝重复发射
     (llvmlite.create_function 不做同名去重,旧写法会造 duplicate symbol)。
3. CallGenCall._translate_method_call:按"不兜底、fail-fast"决定,整块删除
   兄弟 Mixin 兜底(遍历 struct 表猜候选类)及其死分支
   (mixin_hit / mixin_ret_ty / mixin_anc_name / mixin_anc_sha1 的声明与消费点)。
   方法未命中即为真未定义,交下方 text.ll / vpi / 本模块豁免路径
   (CallGenCall.vp:962 起的 is_local_mod 分支,从已声明的本模块函数直接取
   返回类型)处理,不再猜候选类。
4. 同步修正 ClassGenVTable 顶部 _translate_method 的注释块与函数文档串。

TPC 侧无需同步:TPC 的声明阶段把类成员全部登记进 SymbolTable,本就不存在
"方法查找依赖源顺序",也没有这段兄弟 Mixin 扫描(HandlesExprCall.py:3877-3914
的兜底带"调用类自身未声明该方法"守卫,与本次删除的 TPV 版不同)。

待验证(不自行执行编译命令):重编译 TransPyV(自举需编两遍),再跑
FileIoTest / NoVTableCrossMixinTest / DictTest / 泛型相关测试。
2026-10-01 13:45:10 +08:00
TermiNexus 875a9beb08 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
预期先出现一批"注解需补 `*`"的报错——那正是本次口径变更要求的显式化。
2026-10-01 12:38:24 +08:00
TermiNexus 660427f759 fix(TypeGenResolve): X | t.CPtr 的 struct 引用语义吸收这层指针,修 json __getitem__ 报 JsonValue** vs JsonValue* 层数不同
现象(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 不再报层数不同;若还有同族报错请把栈贴回。
2026-10-01 00:51:51 +08:00
TermiNexus 3329c9beae fix(TPV/CallGenBase): text.ll 返回类型的命名指针不再坍缩成通用 i8*,修 FakeDuck parts[0] 报整数隐式转指针
现象(TransPyV 编译 Test/FakeDuckTest):
  main.vp:115  p0: str = parts[0]
  CastError: 整数隐式转指针已禁止…出错表达式: 下标表达式 Subscript
    (parts = s1.split(','),s1: str,split 由 includes/_fakeduck.vp 的 _str 提供)

根因链(逐环取证,均落在产物文件上):
1. _fakeduck.vp:273  `def split(self, delimiter: str) -> list[str]*`。
2. 定义侧产物正确:temp/88/884eaabef9bd161c.text.ll:1775
     define %"b0798adca340002b.list[str]"* @"884eaabef9bd161c._str.split"(...)
   (884eaabef9bd161c = includes/_fakeduck.vp,b0798adca340002b = includes/_list.vp)
3. 调用侧 declare 走了 vpi 注解:temp/88/884eaabef9bd161c.vpi:27
     def split(self, delimiter: str) -> list[str] | t.CPtr: pass
   该联合在 stub 侧解析不出 `list[str]` 这个泛型特化名 ⇒ 坍缩成 i8*(既有设计已知的
   「跨模块 declare 的 list[T] 返回类型被简化」),故 _translate_fakeduck_call 里那条
   「从定义模块 text.ll 读精确返回类型」的兜底(CallGenFd.vp:134-142)必须生效。
4. 但该兜底读到的 `%"b0798adca340002b.list[str]"*` 又被本函数坍缩回 i8*:
   CallGenBase._ParseLLVMRetTyStr 的「含 * 者一律返回通用 Ptr(Int8)」分支
   (原注释:"包括 i8*, i32*, %"name"*, { i32, i8 }* 等")。
5. 于是 parts 的 alloca 退化为 i8* ⇒ parts[0] 走字节下标取到整数 ⇒ 赋给 str 报
   「整数隐式转指针已禁止」(报文指不到真因,正是本问题最难查的地方)。

改动(CallGenBase._ParseLLVMRetTyStr):
- 新增「命名类型 `%"name"*` / `%name*`」分支:剥引号/空格与尾随 `*`(支持多级),
  先 GenApi.get_struct_type(name)、未命中再 GenApi.get_enum_base_type(name),命中即
  按星号层数包 Ptr 返回真实结构体/枚举指针;查不到登记项时才保持原来的通用 Ptr(Int8)
  行为(名字可能来自尚未登记的模块,不引入新的静默降级面)。
- 同步更新该函数顶部说明(原写「含 * 者返回通用 Ptr(Int8)」,现区分字面量结构体 /
  命名类型 / 其余)。
- 收益面不限 FakeDuck:所有经 text.ll 取返回类型的方法调用(CallGenCall / CallGenDispatch
  / CallGenProp / CallGenFd 等)都从「指针一律 i8*」变为「命名类指针精确」,
  调用方 alloca 与下标/字段语义不再退化。

待验证(不自行执行编译命令):用 TransPyV.exe 重跑
  --project ./Test/FakeDuckTest/project.json --clean --clear-cache --rebuild-includes --traceback
预期 115 行不再报错;若仍在 parts2.get(0)(131 行)等处冒新错,请把栈贴回来。
2026-09-30 23:53:18 +08:00
TermiNexus f2f3e5b7bf fix(TypeGenResolve): 裸名内建类型统一为 t.X/c.X,消 CPtr 与 t.CPtr 的分裂与误报 ERROR
内建伪模块 t/c 没有模块 SHA1(规则 5 的模块名→sha1 表查不到),于是 from t import CPtr 之后的裸名 CPtr 被原样保留,而规则 3 保留的是 t.CPtr ⇒ 同一类型两种票名(list[CPtr] 与 list[t.CPtr] 分裂),并额外刷 ERROR [类型实参归属] 裸类名 CPtr 保留裸名(现场 Test/NewAstTest/App/main.vp,log 里 from_imports 明写 CPtr:t)。新增规则 5.2:from_imports 命中模块 t/c 且 map_t_type 认该名 ⇒ 返回 t.X/c.X(规则 3 已定的规范形)。只对"本名就是 t/c 已知内建类型名"生效,别名写法不猜真实符号名、维持原样。
2026-09-30 22:59:01 +08:00