Commit Graph
117 Commits
Author SHA1 Message Date
TermiNexus f2cbb658e1 fix(stubgen): vpi 落盘入口统一重建文件级名字窗口,修复 FileFuncNames 悬垂野读崩溃
Phase1 1b-pre / 1b 兜底 / BuildPipeline / Repl 为生成 vpi 各自重新 parse 出独立 AST,但不走 register_file_funcs,导致 is_file_func(range)(_PushInductionEntry)仍读到上一个文件的表 + 已被 reset 的池 → 悬垂指针,ASan access-violation 崩在 HashTable._slot_hash。将 reset_file_name_window + register_file_func_names 收敛到 _GeneratePyiFromAst 唯一入口,覆盖全部 vpi 落盘调用点;Phase2 的重复调用随之移除。
2026-10-10 14:00:41 +08:00
TermiNexus 6da8e2e095 feat(gen): 落地 @life(ret(...)) 返回值生命期契约(A2)
- GenApi:新增 compute_life_anno / _RetKindFromTag 与 RET_KIND_* 编码,
  提供 get_ret_kind / get_ret_kind_method / lookup_ret_kind_via_vpi;
  _WillFreeMergeTag 不再拦截 ret(改由 compute_life_anno 收集校验)。
- StructGenMixin:EscDirTable 由 3 槽扩到 4 槽(新增 ret_kind 列),
  compute_escape_summary 把契约落在 box[5] 并登记。
- Stub:.vpi 行尾新增 escr=<ret_kind>(Converter 写、VpiReader 读 _g_vpi_esd_r)。
- AssignGenMixin.life_of_expr(Call):@life(ret(...)) 声明优先于任何推断;
  frame/heap/static/borrows=none 直接映射,borrows=x 取对应实参生命期,
  方法调用按接收者静态类型解析类名。
- StmtGenCifMixin:同批移除定义端 return <本帧地址> 的 fail-fast,
  返回值去向改由调用点按被调者契约判定。
2026-10-09 22:58:33 +08:00
TermiNexus 79299ec47d feat(lifetime): 声明式指令契约落 .vpi(esce=/escn=),跨模块调用点也能最终裁决
写侧 Converter:读 compute_escape_summary 落在 box[3]/box[4] 的契约掩码,随函数头行尾写 esce=<强制逃逸位> / escn=<强制不逃逸位>;
只有指令(既无 esc/ret 也无 wf)时也补一个 "# esc=0 ret=0" 注释段——读侧是"见 # 才解析"。

读侧 VpiReader:_cache_vpi_esc_clears_text 同一次行扫描里顺带收 esce=/escn= 进 _g_vpi_esd_*(与 escg 同键 sha1[.Cls].func);
新增 _lookup_vpi_escape_dir(.., noescape);CallGenBase.lookup_escape_dir_via_vpi 包装。

调用点:模块级函数 / 方法 / 构造器三条路径在本文件指令表为空(= 跨模块)时改查对方 .vpi,
仍放在所有推断/回退之后最后套用:esc_mask = (esc_mask | esce) & ~escn。
2026-10-09 20:40:30 +08:00
TermiNexus fc797da542 feat(lifetime): @life(escape/noescape) 多形参、硬逃逸位同命精化、归纳变量区间与跨模块常量特化
- GenApi:escape/noescape 标签支持多实参;新增 compute_escape_directives 与若干委托

- StructGenMixin:硬逃逸位(box[2])、常量与归纳变量区间折叠、_PackEscapeClear/compute_escape_clears

- Call 系:调用点同命裁剪(_FrameArgsEscapeClears)+ 跨模块 escg= 查询;写侧 Converter、读侧 VpiReader

- AssignGenMixin:裸 T()(无 new、无 __new__)判为帧生命期
2026-10-09 18:44:51 +08:00
TermiNexus dd24dc670f fix(gen): P2 失效状态改存独立槽表,不再给 VarEntry 加字段
现象:任何工程编译都在函数形参注册刚结束处 access-violation(pc=0,逐字节可复现)。定位:逐层探针把崩点压到 FuncGenDef 的 pre_scan 段,而该段对 Check(body 仅 if+return)应为空操作,唯一变量是 VarEntry 这个热结构被加了 Freed 字段。修法:撤销 VarEntry.Freed,改用 VarGenMixin 的独立失效槽表 _g_freed_slots(键 = 变量槽 alloca,整次翻译内唯一,故全局累积、无需按函数 reset/restore);_FreedMark/_FreedCheck/_FreedClearSlot 三个 API 替换字段读写,P2 语义不变。同时清除全部 TEMP 探针。
2026-10-09 11:51:11 +08:00
TermiNexus be2521fe46 chore(dbg): 临时阶段日志定位 Phase A 崩溃点(定位后移除)
四个探针:PhaseA translating <path> / vpi.gen <rel_path> / TR.enter sha1 / TR.prelude.done。用于把崩溃点夹在 解析→vpi生成→模块前言→函数体翻译 四段之间。
2026-10-09 10:08:43 +08:00
TermiNexus 97313e9e55 feat(gen): P2 生命期标注 @life(willFree(...)) 驱动释放后使用与二次释放检查
标注面:@life(willFree(<形参名|0起序号>)),兼容叠加式 @willFree(...),可混用累加;序号按形参表下标、self/cls 计入。GenApi.compute_willfree_mask 解析标注并对非法标注 fail-fast。本文件表 StructGenMixin.WfTable + 跨模块 .vpi 行尾 wf=<mask>(Converter 写 / VpiReader 读 / CallGenBase 查)。调用点在实参全部翻译之后把对应实参变量槽置 VarEntry.Freed;其后的读取路径报 LifetimeError。重新赋值清除失效态。
2026-10-08 21:28:49 +08:00
TermiNexus 9e55e909a8 feat(gen): LifeTag 接到 new 描述符;vpi 文本缓存收敛为单一入口
life_of_expr 新增 ASTKind.New 分支(§2.3 矩阵):stack→LIFE_FRAME;heap/memgr 的 slife 同样按帧生命期(作用域末 RAII 释放,逃逸必悬垂);hlife→LIFE_HEAP。于是 new stack slife Foo() 的结果被赋给变量/返回时会被既有汇点判据 fail_if_life_escapes 拦住(该判据本来就是生命期驱动,无需再加语法特例)。

VpiReader 新增 _cache_vpi_annots_text 单一入口(ret/param/逃逸摘要一次喂齐),两个 vpi 加载点都改调它:从结构上消除"某个加载点漏喂某类缓存"这类静默退化(本次 EscapeXmodTest 漏检的根因类别)。
2026-10-08 18:19:15 +08:00
TermiNexus 14920c0e1e fix(vpi): 逃逸摘要在所有 vpi 加载点都喂缓存,修 EscapeXmodTest 跨模块漏检
_ensure_vpi_ret_annot_loaded 开头有"本模块 ret 注解已缓存则直接 return 1"的短路:若某模块先经 _ParseVpiIntoModuleImpl(stub 注入)加载,而该处只喂了 ret/param 缓存、没喂逃逸缓存,后续跨模块逃逸查询就会永久查空。现场:EscapeXmodTest 漏检——box.vpi 里 `# esc=2 ret=0` 落盘正确,查询侧却命中不到。在该加载点补 _cache_vpi_escape_annots_text。
2026-10-08 17:58:07 +08:00
TermiNexus cd68bb62ac feat(gen): M2.3 v3 跨模块逃逸摘要 + 全局赋值判据双保险
【v3 跨模块摘要】Converter._PyiFunction 在每个 def 签名行尾写 `# esc=<mask> ret=<ret>`(仅非零时写);VpiReader 新增逃逸摘要缓存(键 {sha1}.{Class}.{method} / {sha1}.{func},值打包 mask|ret<<32),与返回注解缓存同一次加载解析,并加"模块已加载"备忘 + 键前缀过滤避免拖慢编译;StructGenMixin 抽出 compute_escape_summary 供登记与落盘共用;CallGenBase.lookup_escape_via_vpi 按本类 + MRO 祖先模块 sha1 查询,方法/构造器/模块级函数三处调用点在本地表未命中时回退查 vpi。

【修复】Converter._PyiEnsureEscScratch 补 global(漏写导致全局仍 NULL,box 算成 0+520 → ASan WRITE on 0x208);函数内写"本文件已声明的模块全局"且无 global/nonlocal 时必须 fail-fast——原判据只看 SymTab.Root.Vars,部分时序看不到模块全局而静默建局部影子;新增 FileVarNames(register_file_funcs 预扫收集顶层变量名)+ is_file_var() 兜底,普通赋值与注解式重声明两处都接上。
2026-10-08 17:28:06 +08:00
TermiNexus b03e55bb7e fix(gen): 类归属不采信星号导入候选,收口 fatal_error 返回类型与默认值补全
TypeGenResolve._class_true_def_sha1 / CallGenBase._lookup_reexport_from_vpi 新增 allow_star 参数:类归属只采信具名 re-export。此前 `from X import *` 候选被当成类的真实定义模块,而几乎每个文件都 star-import stdint,导致 TypeGenErr 前向声明被建成 "stdint.TypeGenErr";该名字在 StructTable 里只剩一个错误 sha1 的条目,fatal_error 的跨模块返回类型解析全部落空并 fail-fast。

TypeGenErr.fatal_error/fatal_type_error 显式 -> int(留空会解析出退化类型 i0,命中 CallGenCall 的 i0 守卫);_fail_method_ret_ty 增加失败上下文转储并并入同一条 VLogger.error(批处理下 error 即 sys.exit,其后代码全是死代码)。

CallGenMixin 模块级调用:默认值已知但重建不出时 fail-fast,不再静默产出操作数不足的 llvm.call(只会被 MLIR 校验拦下);AssignGenMixin 两处 VLogger.error 补上 category 实参。

M2.3 v1b 收尾:逃逸摘要表与扫描临时区改为只分配一次、传递闭包扫到不动点、u64 改 sizet;VpiReader 不再 free 跨阶段引用的 src_sha1(heap-use-after-free)。
2026-10-08 16:38:35 +08:00
TermiNexus 2fb648d68a feat(lang): 新增 cif/celif/celse 条件编译与值引用注解 &T/&temp T,assert 改译 testcheck.check
cif 三件套:StmtGenCifMixin 负责常量求值(含 defined(模块宏) 的 Attribute 形式),链式错配与含运行时变量一律 fail-fast;删掉 c.CIf 家族。值引用:TypeGenResolve 让 CRef/CTempRef 与 t.CPtr 走同一解析路径(&void 折叠为 i8*,删除 t.CNeedPtr),CallGenBase 在调用点按形参模式自动取址(ref 只接受左值,右值 fail-fast;temp 物化临时栈),跨模块经 VpiReader/StubMergerBuild 传递模式。assert:StmtGenMixin 合成 testcheck.check(cond, None, msg),Translator 检测到 assert 时自动注入 testcheck 导入(带去重与防环),深递归守卫改用 stdio.printf + stdlib.exit(Translator 无 Gen 成员)。
2026-10-07 19:03:16 +08:00
TermiNexus 7a9bc5ae75 chore(messages): TransPyV 运行时中文字符串英文化
把 TransPyV/App 下 57 个文件的运行时中文字符串(编译器报错/告警/日志、CLI 帮助与横幅、REPL 提示、Phase1/Phase2 流程日志、Codegen 各 Mixin 的类型与调用报错、Stub/Vpi 诊断等)共 1091 行译为英文。

口径:只改字符串字面量内部文字,行数零变化(+1092/-1092);printf 格式符与转义序列多重集合校验不变(_strcheck 异常 0);译文不引入撇号以免干扰单引号字面量解析。验证:三重重编 SUCCESS(136.20s)+ 全量 77/77 顺序回归通过 + VET 编译 SUCCESS / --search-paths 7 条 exit 0;新产物 help/日志输出已为英文。
2026-10-07 00:03:58 +08:00
TermiNexus cd55a7e214 refactor: rawptr[T] -> T*(R14)+ 修 REnum 前向占位未就地填充
R14:rawptr[X] -> X*(56 文件 / 834 行改动);保留 6 处 rawptr[t.CPtr](存纯裸指针的规范例外)。 修复:class X(t.REnum) 的变体字段写成自引用 X* 时,字段解析在变体扫描期经 ensure_forward_class 建了一个 opaque 前向占位;末尾 register_struct 按 Ty 指针去重会再建第二个同名 entry,而 find_struct 取首个(占位,0 字段)→ X.__sizeof__() == 0,报「已注册但大小为 0(字段链表未填充)」。改为注册前若命中同模块前向占位则就地填充(与 ensure_forward_class 文档口径一致)。TPC 无此问题,属 TPV 侧缺口。 验证:自举三重重编 SUCCESS;全量 77/77 顺序回归通过(修复前卡在 LLvmLiteTest)。
2026-10-05 17:38:28 +08:00
TermiNexus fb53f88893 refactor(gen): R12/R13 同步——下标 while 循环改 for range、去 c.Set
与 includes 同步执行两批源改写:R12 把「i: int = 0 与 while i < N: … i += 1」形式的计数循环改写为「for i in range(N):」;R13 把 c.Set(*p, v) 迁移为 *p = v、c.Set(a, b) 迁移为 a = b(c.Set 是正在退化的路径)。

验证:bootstrap 三重重编 SUCCESS(无 vast_c 解析回退);全量 77/77 顺序回归通过(WebTest 首轮网络超时,单独重跑通过)。
2026-10-05 15:33:58 +08:00
TermiNexus 0f4aeeceff docs(tpv): R11 注释与 docstring 英文化并压缩到 2 行内
按 wiki/新规范:注释只讲作用、不掺杂历史原因、避免超长、尽可能英文(含三引号 __doc__)。全文 79 个 .vp:# 注释 16269 行(中文 13808)与 docstring(中文 1381 处)全部改英文;超 2 行的注释块(2093)与 docstring(397)全部压到 2 行。运行时可见文本未动(fatal_error/VLogger 的中文报错文案、stdio.printf 的横幅格式串)。校验:屏蔽注释与三引号后逐行比对各文件代码骨架,0 漂移。验证:自举重建 153.48s 通过(跨模块内联完成);TPV 顺序回归 77/77 全通过。
2026-10-05 13:38:22 +08:00
TermiNexus f2ef1bb240 refactor(tpv): 新语法 R10 括号内隐式续行并一行(1140 处 / 67 文件) 2026-10-05 07:14:14 +08:00
TermiNexus 4a685d478f refactor(tpv): 新语法 R10 单语句块并一行(7722 处 if/for/while 单语句体) 2026-10-05 03:10:55 +08:00
TermiNexus 30a3da4cd4 refactor(tpv): 新语法 R10(反斜杠续行合并)+ 回退 R5(语义不等价)
- 反斜杠续行合并为单行(304 处),呼应"尽可能不换行"。
- 回退 c.DerefAs(p, v) → *p = v 两处(StructGenMixin):原写法是 bitcast 后
  store 指针位,新写法按 pointee 值类型赋值,类型不符(i8* → List 值),
  语义不等价,保留 c.DerefAs。

种子自举通过。
2026-10-04 22:56:27 +08:00
TermiNexus 185a2196ca refactor(tpv): 迁移新语法 R1/R2/R7/R8/R3/R4/R5(指针 T*、短别名、define、&/*、__asm__)
按 wiki/新规范:
- T | t.CPtr → T*(无 | 标准)
- t.CInt/t.CUInt64T/t.CChar/t.CDouble 等 → int/uint64/char/double 等短别名
- X: t.CDefine = Y → define X = Y
- c.Asm → __asm__,c.AsmInp/c.AsmOut → asminp/asmout,t.ASM_DESCR. → ASM_DESCR.
- c.Addr(x) → &x,c.Deref(p) → *p,c.DerefAs(p, v) → *p = v

种子自举通过(96s)。
2026-10-04 22:49:01 +08:00
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 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 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 98ee53340f feat(call): 跨模块实参加 str/bytes 形参的 fail-fast(str 不作通用指针通配),并加 vpi 注解段等值判定 2026-10-03 14:23:12 +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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 b289e93650 fix(泛型实参 T* 形): 补齐 T* 消费侧(VpiReader 段解析 / AnnotClassName 提取 / 构造推断)
唯一规范形 T*(rawptr[T] ≡ t.CPtr[T] ≡ T|t.CPtr,见 83baf5a/2b2b70e)落地后,
所有"读**已归一实参文本**或**结构体名**并做文本解析/嗅探"的点都必须接受 T* 形,
否则会**静默**走错分支(不是报错,更难发现):

- VpiReader._resolve_single_vpi_type_str:尾随 * 段按层重写回 rawptr[...] 文本形
  再走原分支(与既有 rawptr[ 分支逐字等价)。必须补的原因:_subst_template_arg
  用特化类名 [...] 内的**已归一实参**替换模板注解里的 T,喂进来的就是 {sha}.AST*;
  旧代码只认 rawptr[ / t.CPtr[ / 裸 t.CPtr ⇒ 落到 strrchr('.') 取末段 "AST*"、
  查 struct miss ⇒ 返回 None/i8*,跨模块参数/返回宽度被悄悄收窄。
- ExprGenCls / AssignGenMixin 的 _extract_annot_class_name(各 2 处):段尾剔除集
  补 '*',剥后再 find_struct(旧形态 {sha}.AST|t.CPtr 按 '|' 分段能命中,新形态
  不剥则 miss ⇒ 返回 None ⇒ 链式 .get(0).kind() 分派退化)。
- CallGenCtor._infer_generic_ctor_type_arg:新增 _ptr_layer_type_arg —— 先经
  _norm_type_arg_name 归一(保证类名带定义模块 SHA1,否则裸 "AST*" 无法再归一、
  反而新分裂),再按 VarEntry/FieldEntry 的 Spec.PtrLevel 补 '*',使构造侧推出的
  类型实参与注解侧同票名(否则 Box(x) 这类隐式特化分裂成两个结构体)。
- CallGenDispatch.__sizeof__:'*' 判据由 strrchr 改为**尾随**判定 —— 原写法会把
  list[{sha}.AST*](元素为指针的容器)误判成指针并返回 8。

现场:Test/AstTest/App/main.vp:21 的 list[ast.AST | t.CPtr] | t.CPtr 特化链
(→ includes/_list.vp:36 T.__sizeof__())。
2026-09-30 21:16:48 +08:00
TermiNexus 31f341cf6f fix(transpyv): vpi 注入期登记「本模块声明的类」,裸类名实参不再冻结成裸名
类别 a(编译器缺陷):_norm_type_arg_name 规则 5.5 的入口闸门是
`_def_sha1 is None and _def_mod is not None`(_def_mod = CurDefModuleSha1),而
VpiReader._inject_class_struct_declare 是在**注入期**解析字段注解的——此刻既没有
"类定义窗口"(CurDefModuleSha1=None ⇒ 闸门整条关闭、四条证据一条都不查),被注入的类
也尚未 register_struct ⇒ 注解里的裸类名实参取不到定义模块 SHA1 ⇒ 特化名被冻结成
**裸名版本**,与该类在自身翻译期(类已注册)解析出的带前缀形式分裂成两个特化名,
最终以"非基本类型指针不可隐式转换"报出。现场:includes/ast/base.vp:359
  _append_child(self.children, node, self.pool)
  源 %"…list[rawptr[AST]]"* vs 目标 %"…list[rawptr<定义模块SHA1>.AST]"*
这也解释了前两轮修复为何无效:③(自引用判据)依赖 CurDefClassName、④(entry.Ty 取
前缀)依赖 struct 条目,而注入期两者都不可用,且此时连日志条件(entry 命中)都不成立。

- GenApi:新增 mark_module_class 委托(与 add_visible_struct / find_struct_by_module 同款)。
- VpiReader._inject_class_struct_declare:在解析字段注解之前调用
  GenApi.mark_module_class(pool, src_sha1, cls_name) —— 一次调用同时完成
  ① 登记"{src_sha1}.{cls_name}"(名字归属,与 struct 注册状态无关);
  ② 把 CurDefModuleSha1/CurDefClassName 指向本次注入的模块与类(打开规则 5.5 的闸门)。
2026-09-29 22:29:48 +08:00
TermiNexus c2d7480f3d fix(transpyv): 槽视图统一 cast 到声明类型(47 处,含 codegen 自身)
同 includes 的同类修正:把一级指针 i8* 隐式塞进二级注解(i8** / str*)。
TransPyV 侧 coerce(ExprGenBase._ptr_cast_reason)没有 TPC 那样的「值侧是显式
指针 cast ⇒ 按 bitcast 重解释」豁免,故一旦用新构建的 TransPyV 编到这些文件
必然 fail-fast(回归测试首爆点在 includes/llvmlite 侧,本仓同形态站点随后必然
同样触发)。

涉及的两种形态:
  · `slot: str | t.CPtr = (t.CPtr)(addr)`   —— cast 目标是一级 t.CPtr
  · `nm_slot: str* = (t.CPtr)(addr)`        —— 注解 str* 即 i8**
统一改为 cast 到声明类型:`(str | t.CPtr)(addr)` / `str*(addr)`(后者与
includes/sys.vp `argv_arr: str* = str*(...)` 同一写法)。

本仓 47 处 / 6 文件:
  App/lib/core/Gen/VarGenMixin.vp 3
  App/lib/core/Gen/TranslatorGenMixin.vp 8
  App/lib/core/Gen/Func/FuncGenMixin.vp 13
  App/lib/core/Gen/AsyncGenMixin.vp 15
  App/lib/core/Gen/DebugInfoGen.vp 6
  App/lib/Stub/StubGen/Converter.vp 2
Converter.vp:107 的 `_g_pyi_cdef_names: str | t.CPtr = None` 保持原样
(None 即空指针,任何指针类型均可,不属本类问题)。

只提交本 6 个文件;本仓其余未提交改动(CallGenBase/CallGenDispatch/CallGenFd/
CallGenProp/Generator/Translator/VLogger)与本迁移无关,保持不动。
2026-09-29 15:10:33 +08:00
TermiNexus fddadf9d56 fix(transpyv): 修正 _PyiClass 的 type_params 局部变量注解(list[ast.AST*]* → list[str]*)
c 源码违规(注解写错,被严格指针规则抓出):ClassDef.type_params 的真身是
rawptr[list[str]](**名字串**列表,见 includes/ast/stmts.vp:475-483 与
parser.vp:3619-3663 的 `list[str](ps.pool, 8)`);`list[rawptr[AST]]` 是
decorator_list 的类型。Converter._PyiClass 把局部变量注解从 decorator_list
抄错成 list[ast.AST*]* ⇒ `.get()` 被解析到 list[ast.AST*].get,而实参是
list[str]* ⇒ fail-fast:

  调用 650c1c3366d09e39.list[10f3f43ddd447eec.AST*].get 第 0 个实参
  %"650c1c3366d09e39.list[str]"* 不能隐式转为形参
  %"650c1c3366d09e39.list[10f3f43ddd447eec.AST*]"*

已核对全仓其余 8 处 type_params 用点(VpiReader.vp:2253/2274/4345、
Phase2/__init__.vp:317、Phase1Scan.vp:153/318、ClassGenDef.vp:69、
CallGenMixin.vp:415)均写作 list[str]*,本次与之对齐。
2026-09-28 22:54:50 +08:00