TermiNexus
5b68732a94
refactor: remove dead ViperC compiler code
...
Drop unreachable struct/type helpers, the legacy wrapper-main and build pipeline paths, debug-only scaffolding, unused logging helpers/constants, and the no-op LSP signature map. Add the audit report for the remaining duplication and logging work.
2026-11-02 12:46:53 +08:00
TermiNexus
49683fc881
fix(gen): OOP __len__/__getitem__/methods beat pointer arithmetic
...
A pointer chain that reaches a class is a reference to the object, not an
array of objects. `arr: &list[int]*` (inside `arr` is `list[int]**`) used
to be treated as raw pointer arithmetic: `len(arr)` read the pointer slot,
`arr[i]` indexed it, and `arr.append(x)` passed the slot as self -- all
producing garbage or a runtime crash.
- add `_oop_self_ptr`: peel extra pointer levels down to Ptr(Struct);
- `len()` normalizes the receiver after resolving the class, so list
inline `__count__` and class `__len__` receive the object pointer;
- subscript reads dispatch list / class `__getitem__` with the peeled
receiver; raw pointer traversal remains the fallback when the chain
does not reach a class (or the class has no `__getitem__`);
- the list builtin method branch and `_translate_method_call` peel the
receiver, so `arr.append(x)` works through a reference;
- `is_list_subscript` unwraps every level for the assignment path, so
`arr[i] = v` and tuple swaps on elements write the real list; value-type
list variables keep their old path.
Probe: `&list[int]*` len/read/write/swap/append and `&Box*` custom
`__len__`/`__getitem__` all produce the expected values; self-host
110/110; ListTest, GenericTest3, PtrOpProbe and HashLibTest pass.
2026-11-02 10:50:35 +08:00
TermiNexus
4bf04c2452
feat(vpc): binary .vpb stub companion and str->char* migration in call gen
...
- add VpiStub: fixed-layout blob written next to every .vpi (dual write at all
writers, Ensure() backfills a missing or stale companion on cache reuse)
- serve arity/signature queries for top-level functions from the blob, with a
silent fallback to the text scanner (methods, varargs, duplicates, corrupt file)
- prefer the injected declare over the text arity in the cross-module check
- CDefine/type text indexes and in-place SSA renumbering and bulk appends
- WIP: char* conversion in CallGenBase and CallGenLeafKit
2026-10-31 16:25:18 +08:00
TermiNexus
e63fc34057
fix(codegen): reject out-of-range field defaults at declaration
2026-10-27 21:23:01 +08:00
TermiNexus
782b4d63ff
feat(build): 增加 --build-self 与 copyself
...
- 新增 copyself:延迟后删除目标并复制文件/目录
- ViperC 新增 --build-self:自举到 __vpcbuild_2__ 后由 copyself 替换 __vpcbuild__
- 同步当前 ViperC 工作区已有的 Gen/Diag/Stub 改动
- .gitignore 忽略 __vpcbuild_* 与 _sha1_map.txt
2026-10-26 02:09:45 +08:00
TermiNexus
42b094a575
refactor(viperc): 退役 t/c 模块标记,内建类型改由编译器直接识别
2026-10-18 12:56:57 +08:00
TermiNexus
d46fdf152a
refactor(struct): AST vtable 拷贝改为按 uint64 槽显式读写
...
原写法 *dst_c = vt_ptr_c 把 i8* 赋给结构体类型的左值,TPC 会以「非基本类型禁止指针隐式转值类型」报错。新增 _copy_ast_vtable(src, dst):把两侧 AST 节点都视作一个 uint64 槽(src_vt[0] -> dst_vt[0]),语义不变(只复制首字段 vptr),Constant 与 List 两条深拷贝路径统一调用它。
2026-10-17 19:13:08 +08:00
TermiNexus
5d15d01e21
feat(gen): 支持 t.BigEndian / t.LittleEndian 字节序描述符(注解识别 X|marker 并把字节序登记到 FieldEntry.ByteOrder,暂不生成 bswap)
2026-10-13 12:17:02 +08:00
TermiNexus
d206abf3e1
chore: 全仓同步 w32→win32 模块改名与 range(int(...)) 显式收窄适配
2026-10-12 20:59:11 +08:00
TermiNexus
d023e9932d
fix(narrow): StructGenMixin 守卫候选与归纳区间常量改用 int64,消除 64->32 隐式窄化
...
kv/lo/hi/k 原本声明为 int 承接 int64(Scratch[...]),触发新禁止的隐式窄化;_AddGuardCand/_PackEscapeClear 的 kv 形参同步拓宽为 int64(打包范围检查需看到完整 64 位值),打包结果处显式 int() 收窄
2026-10-10 22:20:47 +08:00
TermiNexus
41674d125c
fix(gen): 修 vpi 生成阶段 is_file_func 读到已回收池的悬垂表(ASan 野读崩溃)
...
现场:TransPyV.exe(ASan)编译 EscapeOkTest 崩在 Phase A-pre-vpi →
_GeneratePyiFromAst → _PyiFunction → compute_escape_summary → _ScanEscapes →
_PushInductionEntry → is_file_func → HashTable._slot_hash
(access-violation on unknown address 0xffffffffffffffff)。
根因(潜伏已久,非本阶段语义改动引起):
- FileClassNames / FileFuncNames / FileVarNames / FileModuleSha1 是按文件重建的表,
表与键共用该文件的瞬时池;契约要求跨文件/跨阶段先清空再重建
(原写在 reset_visible_structs 里那四行)。
- Phase A-pre-vpi 为生成 vpi 会重新 lex/parse 每个文件,但不走 register_file_*、
也不清表 → _PushInductionEntry 的 is_file_func 只认 range 的判据,落在 Phase1
最后一个文件的表上,而那个池已被 reset_to(mark1) 回收。非 ASan 下常表现为读到
0/旧值 的静默错答,ASan 下 0xfd 填充 → 垃圾容量/垃圾 __slots__ → 野读崩溃。
修法:
- 抽出 reset_file_name_window() 作为文件级名字窗口的唯一开启入口(原四行清空搬进去,
reset_visible_structs 改为调它),并在注释里写死契约。
- 新增轻量 register_file_func_names(pool, tree):只重建本文件顶层函数名(核对过 vpi
路径只需这一张;FileClassNames/FileModuleSha1/FileVarNames 的读取点全在翻译期)。
- Phase A-pre-vpi 循环里,在 _GeneratePyiFromAst 之前调用
reset_file_name_window() + register_file_func_names(mb, tree_vp2)。
- register_file_classes / register_file_funcs 改为无条件重建(原来 没有才建 的写法在
表已被别的阶段建过时会静默复用悬垂表)。
2026-10-10 09:41:15 +08:00
TermiNexus
79d1c86982
fix(gen): return 本帧存储改为声明感知 fail-fast(A2 方案 A)
...
A2 原本整体移除定义端 return <本帧地址> 的 fail-fast,导致无 @life(ret(...))
声明的函数返回本帧存储时静默通过(调用点只拿到 LIFE_UNKNOWN,悬垂一路无声)。
现改为按被调者自己的 ret 声明判定:
- ret(frame) / ret(borrows=<该形参>) → 放行;
- ret(heap|static|borrows=none) → 报错(声明与返回值矛盾);
- 无声明 → 报错(提示补 @life(ret(frame)) 或 ret(borrows=<param>))。
落点:AssignGenMixin.check_return_frame_decl(判据复用 life_of_expr,target_ty 传
返回类型,与移除前的口径一致);ret_kind 由 StructGenMixin.get_ret_kind_by_func
反查 EscFuncTable → EscDirTable(顶层函数/方法/嵌套 def 一次覆盖,无需猜类名),
当前 FunctionDef 取 StructGenMixin.NllCurFunc。
新检查是旧检查的严格子集 ⇒ 自举无回归。
2026-10-10 09:16:26 +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
71730d920c
chore(dbg): 删除 NLL 临时诊断(§13 已在 EscapeTest 上验证通过)
...
实测:var=h confined=1,g(l,h) 不再报错;报错前移到真正的泄漏点 `if 1==0 or a == 1: return l`(where=a return value)。
2026-10-09 21:59:48 +08:00
TermiNexus
0f504a9010
chore(dbg): 给 NLL 谓词插桩([NLL-ST] 逐条语句 + [NLL-UNSAFE]/[NLL-MENTION] 拒绝原因),定位后删除
2026-10-09 21:53:44 +08:00
TermiNexus
960ecdfd74
fix(nll): 补 BoolOp/Compare 的字段递归(if 1==0 or a == 1 曾把整条判据否掉)
...
_init_ast 里 node.children = None 是**默认值**(只有语句体这类显式赋值的才有子树),所以"认不出的表达式
一律保守"这条策略会被任何未列举的表达式种类触发。新用例的 if 条件 BoolOp(values) 与 Compare(left/comparators)
就是这种。补上后 h 应能降级,报错位置回到真正的泄漏点 return l(第 19 行)。
2026-10-09 21:34:17 +08:00
TermiNexus
5a10a2ea10
fix(nll): 谓词改为按字段显式递归(AST 表达式节点不填 children,导致 new stack [2]char 被误判"提及")
...
根因([NLL-DBG] var=l 已证实):_set_parent_list 只设父指针、不填 children,故 New/Attribute/Call 等表达式节点
的 children 基本都是 None。原实现把"children==None"当作"提及/拿不到子树",于是 k 里第一条
`l = new stack [2]char` 就把整条判据否掉(var=h confined=0)。
现在 _NllMentions / _NllExprUnsafe 均按字段显式递归:Name/Constant/Attribute/Subscript/Call/New/UnaryOp/
BinOp/IfExp/Tuple|List|Set;认不出的种类保守(提及=1 / 不安全=1)。_NllStmts 的"认不出的语句"
分支改为按子节点判(无子节点的 pass 之类放行)。
2026-10-09 21:23:51 +08:00
TermiNexus
337be883d3
fix(nll): Attribute 节点显式只递归 .value,避免"拿不到子树=算提及"把 stdio.printf(...) 误判成不安全
...
现场:EscapeTest 的 h = new heap S() 永不降级(g(l,h) 照旧报逃逸)。根因是 _NllMentions 对 Attribute
落进保守分支(children 不含 value 时返回"提及")→ _NllExprUnsafe 把 stdio.printf("%s", h.q) 判为不安全
→ 整个 NLL 判据返回 0。属性名是静态字符串,变量只可能在基对象里,显式递归 .value 即可。
2026-10-09 20:57:31 +08:00
TermiNexus
d0ffb094c2
feat(lifetime): §13 NLL——默认生命期的 new heap 证明帧内封闭时降级为帧对象
...
StructGenMixin: 新增 NllFrameConfined 及遍历器(_NllStmts/_NllExprUnsafe/_NllMentions/_NllInSet/_NllAdd/_NllCalleeEscMask)+
has_param_escapes(区分"有记录=权威不逃逸"与"无记录=判不定");别名集只增不减故单遍即可,if/while/for 全走、
不进嵌套 def/class;认不出的语句"不提及别名才放行";取址/实参位逃逸/return/长命目标一律按外泄拒绝。
CallGenCtor._new_is_nll_demotable:只降级"堆且未显式指定生命期"的 new;AssignGenMixin.propagate_life
判据成立则 life=FRAME(于是调用点同命裁剪放过 g(l,h)),_slife_note_assign 接 target_name、判据成立按托管堆
对象登记 → 复用 slife 的作用域出口释放(手动 free/del 也会被既有检查挡住)。
三条函数体翻译入口记录 NllCurFunc(谓词要遍历当前函数体),与 traceback 推帧同一处。
注:谓词按 §13.2 保守优先,判不定一律维持 HEAP;本提交未编译验证。
2026-10-09 20:49:21 +08:00
TermiNexus
2bd37e80e3
feat(lifetime): 方法/构造器调用点也接 @life(escape/noescape) 最终裁决
...
StructGenMixin: 新增 _DirForceOf / _DirForceMethod(查 "Class.method" 并沿 MRO 回退)与
get_param_escape_force_method / get_param_noescape_force_method;GenApi 加同名委托。
CallGenCall: 构造器分支(esc_mask_ci)与方法分支(esc_mask_mc)在跨模块回退、常量特化之后
最后套用一次 esc_mask = (esc_mask | escape_force) & ~noescape_force,与模块级函数那条口径一致。
2026-10-09 20:34:34 +08:00
TermiNexus
fc7c5d5560
feat(lifetime): @life(escape/noescape) 升为调用点最终裁决(契约不可被推断推翻)
...
StructGenMixin: 新增 EscDirTable(3 槽/项:[名哈希, 强制逃逸位, 强制不逃逸位]),
_RegisterFuncEscapeSummary 把 compute_escape_summary 落在 box[3]/box[4] 的指令掩码单独落表
(与 EscTable 的"推断结果"分开);新增 _DirLookup / get_param_escape_force / get_param_noescape_force。
compute_escape_summary 的指令输出缓冲由 g+68 改为 box[3](同一内存,语义显式化)。
CallGenMixin: 在常量特化、同命裁剪、跨模块回退**全部做完之后**,最后套用一次指令:
esc_mask = (esc_mask | escape_force) & ~noescape_force。这样 escape(...) 不会被"死分支/同命"折掉,
noescape(...) 也不会被任何上游路径重新解释成"逃逸"。
2026-10-09 20:30:03 +08:00
TermiNexus
8881dea2e2
fix(diag): 泄漏点采集改为"确认报错后才跑",并保存/恢复扫描期状态
...
原先在调用点的位循环里就调 escape_leak_line:该函数会重扫被调者函数体并改 EscConstEnv/EscIndCount,
而循环里可能紧接着发现"该实参其实不是帧生命期"从而不报错 —— 等于白白跑一遍扫描、还留下脏状态。
现在改成把 (被调者, 形参位) 传给检查方,过了 LIFE_FRAME 判定才采集;escape_leak_line 内部
把 EscConstEnv/EscIndCount/EscLeakWant 先存后还。
2026-10-09 19:50:31 +08:00
TermiNexus
2353dca8ec
feat(diag): 逃逸类 LifetimeError 追加 File lifetime leak 帧,标出泄漏点
...
StructGenMixin: 新增 escape_leak_line——错误路径重扫被调者函数体,取指定形参的**首个逃逸点**行号;
_NoteLeak 在三处置位来源采集(字段/全局写、return 形参、传递闭包调用),新增 EscLeakWant/EscLeakLine。
Diag: DiagEntry 增加 LeakFileName/LeakLine + set_leak + emit_leak_frame(标签 File lifetime leak),
打印顺序 = 调用链 → 错误点 → 泄漏点 → 类别: 消息。
TypeGenErr.fatal_error / GenApi.fail_if_stack_addr_escape 增加 leak_file/leak_line 透传;
CallGenMixin 调用点按失败实参位取行号。**仅同模块**:跨模块 EscFuncTable 无 AST → 返回 0,不打该帧。
2026-10-09 19:32:54 +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
14a8dcb98b
feat(gen): new 支持基本类型 + 跨过程常量特化重算逃逸摘要
...
- CallGenCtor._translate_new_primitive:
ew [stack|heap|memgr(x)] T(T 为标量)取存储 + 零初始化 → T*;stack 走入口块 alloca、heap 走 malloc、memgr 复用既有通道;标量带实参 fail-fast,不引入第二套标量构造语义
- StructGenMixin:新增 EscFuncTable / EscRefineScratch / EscConstEnv 与常量求值器 _EvalConstInt / _EvalConstCond;_ScanEscapes 的 if 分支按形参常量折叠只扫被走到的分支(死分支里的写不记为逃逸)
- 调用点(普通函数 + 方法,方法沿 MRO 回退)在逃逸检查前用常量实参重算摘要;跨模块拿不到函数体仍保守返回原掩码
- 折叠只会减少逃逸位,不引入新的误报
2026-10-09 14:55:28 +08:00
TermiNexus
b60ce06335
feat(gen): willFree 覆盖关键字实参、方法调用与取地址三条路径
...
关键字/混合实参:CallGenMixin._MarkWillFreeKwargs 按形参序(位置实参占前 can 个形参,其余按关键字名匹配)标记。方法调用:CallGenCall._translate_method_call 的 kwargs 与位置两条循环,实参链含 self 故用户第 ai 个实参对应 bit (ai+1);新增 GenApi/StructGenMixin.get_param_willfree_method(查 Class.method 并沿 MRO 回退)。取地址:ExprGenOps 的 AddrOf 分支对已失效变量 fail-fast。
2026-10-08 21:39:53 +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
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
9f98e683a9
feat(gen): M2.3 覆盖面扩展——类方法与嵌套函数的逃逸摘要
...
摘要键约定:模块级/嵌套函数用裸名(与调用点 func_name 一致),类方法用 "Class.method",查询沿 MRO 回退;刻意不回退裸名,避免方法名与同名模块函数(error/init 等)串味产生误报。
register_file_funcs 登记类方法与嵌套函数摘要,v2 传递闭包相应覆盖(_RescanEscapes 拆为 _RescanOne/_RescanNested/_RescanClassMethods)。
调用点新增:_translate_method_call 的方法路径检查(形参下标含 self,用户第 ai 个实参对应 bit ai+1)、_call_method_on_ptr 的 __init__ 构造器检查(self.f = p 的逃逸;__new__ 形参可能与 __init__ 共享并裁剪,不查)。
已知边界:摘要表按文件重置,跨模块查询待 v3;_PropagateCallEscapes 只处理裸名被调者,方法调用链的传递待补。
2026-10-08 16:55:34 +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
1fb0f4a4ea
feat(gen): 逃逸摘要传递闭包(M2.3 v2)
...
- walker 认值位置上的调用(Return/Assign/AnnAssign/Expr 的 value 为 Call)。 - 传递规则:g(p,...) 中 g 会逃逸的实参位若绑到本函数形参 p,则 p 也逃逸。 - 注册完再重扫 2 遍(单调 OR-in)。已知:超过 2 层的链仍可能漏,真正收敛需改为扫描到不变为止。
2026-10-08 13:47:54 +08:00
TermiNexus
4ce577d8fb
feat(gen): 函数生命期摘要表 + 逃逸 walker(M2.3 v1b 步骤1-3)
...
- StructGenMixin:EscTable/EscCount(追加在类末尾,避免偏移漂移);register_file_funcs 每文件重置并逐函数算摘要。 - 逃逸 walker:return p / global 赋值 / obj.f 赋值,递归 if/while/for;不进嵌套 def/class;形参访问逐级显式重转(AST 退化)。 - 表内只存 u64 名哈希(djb2-64),不解引用字符串指针,消除槽内坏指针导致的 strcmp 崩溃。 - 表从 PermPool 分配并 memset。 - GenApi:get_param_escapes/get_ret_borrow 查询入口。调用点检查(步骤4)待接。
2026-10-08 13:29:28 +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
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
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
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
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
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
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
c1a22411ca
fix(TypeGenResolve): 规则 5.5 增「本文件顶层类名」证据,模块级早于类定义的窗口不再留裸名
...
类别:a 编译器缺陷(TPV 侧;补齐"第二个裸类名窗口")
现象(与上一轮 e1bb42f 同一现场、不同窗口):编译 includes/ast/base.vp 时 :359
`_append_child(self.children, node, self.pool)` 报"指针隐式转指针已禁止(被指类型是
非基本类型…)",源 list[rawptr[AST]]* 与目标 list[rawptr<定义模块SHA1>.AST]* 差在实参的
SHA1 前缀。
判据(第二个窗口 = 模块级语句早于类定义):
· Translator.translate 的 prologue 先跑模块级 AnnAssign/Assign(Translator.vp:592-599)
与 forward_declare_functions(:601),**之后**才 translate_children(:602) 逐个翻译类;
而"正在定义类"窗口(CurDefModuleSha1/CurDefClassName)只在类定义入口
(ClassGenDef.vp:50)或 .vpi 注入(VpiReader.vp:2659)才建立 ⇒ 前者解析注解时窗口为
None ⇒ `_norm_type_arg_name` 规则 5.5 的 ①②③④ 全落空 ⇒ 注解里的裸类名(base.vp:148
`_append_child(..., lst: list[rawptr[AST]])` 的形参即此窗口)以**裸名**形式被首次注册,
并被后续复用(首写者胜:StructGenMixin 按 Ty 去重、ClassGenDef existing 早退不重算)
⇒ 与同一特化在类定义期解析出的带 SHA1 形式分裂。
修法(正向事实,不猜;与窗口/类定义顺序无关):
1. `StructGenMixin` 增文件级登记:字段 `FileClassNames`(本文件顶层 ClassDef 类名表,
表与键同池、每文件随 `reset_visible_structs` 重建)+ `FileModuleSha1`(本文件模块
SHA1),以及 `register_file_classes` / `is_file_class` / `get_file_module_sha1`。
键从 AST 的 name **复制到 pool** 后再登记(AST name 零拷贝指向 src_buf,跨阶段可能
失效,直接当表的键会 strcmp 到悬空内存)。
2. `Translator.translate` prologue:在模块级变量/前向声明**之前**调用
`GenApi.register_file_classes(pool, self.ModuleSha1, tree)`(GenApi 增同名委托)。
3. `TypeGenResolve._norm_type_arg_name` 规则 5.5 增**第五类证据**:①②③④ 全落空时,
若 `is_file_class(name)`(该裸名确由本文件声明)且本文件模块 SHA1 已知 ⇒ 归属本文件。
验证:TPV 侧改动需重新自举 TransPyV.exe 后生效;本轮以代码级证据链
(Translator.vp:592-602 / ClassGenDef.vp:50 / TypeGenResolve.vp 规则 5.5 闸门)自证。
TransPyV 仓另有他人未提交文件,本次只提交本改动的 4 个文件。
2026-09-30 00:23:43 +08:00
TermiNexus
bbd0cdbf62
fix(transpyv): 自引用类名归属加「表无关」判据,字段与形参不再分裂成两个特化名
...
类别 a(编译器缺陷):_norm_type_arg_name 规则 5.5 判定"正在定义的类"的裸名归属
时只认两条证据——struct 注册表(find_struct_by_module)与 ModuleClasses 登记表
(is_module_class)。自引用字段(includes/ast/base.vp `AST.children:
list[rawptr[AST]]`)在类定义体内解析时本类尚未 register_struct,而 ModuleClasses
的键是 pool 字符串,登记/查询受"表未就绪、键随 pool 回收"影响 ⇒ 两条都可能落空
⇒ 实参留在裸名 `list[rawptr[AST]]`,与同一注解在形参处(类已注册)解析出的
`list[rawptr<定义模块SHA1>.AST]` 分裂成两个特化名 ⇒ 被"非基本类型指针不可隐式
转换"拦下(现场:includes/ast/base.vp:359
`_append_child(self.children, node, self.pool)`,源 %"list[rawptr[AST]]"* →
目标 %"list[rawptr<sha1>.AST]"*)。
- StructGenMixin:新增 CurDefClassName —— 与 CurDefModuleSha1 同源同生命周期,
由 mark_module_class 在类定义入口(ClassGenDef 第 50 行,早于字段注解解析)
一并记录;InitStructGen / reset_visible_structs 同步清空。
- TypeGenResolve._norm_type_arg_name 规则 5.5:新增证据 ③(表无关)——查询名与
正在定义的类名逐字相同即判为本类,用 CurDefModuleSha1 加前缀。类名直接来自类
定义入口、不经任何表 ⇒ 自引用窗口内必然可判;证据 ② 失败时不再退回裸名。
2026-09-29 20:16:05 +08:00
TermiNexus
1b2324edab
fix(transpyv): 修正 mark_module_class 的表生命周期(ASan 悬空键崩溃)
...
类别:a(编译器缺陷 —— 上一提交自引入的跨文件悬空 HashTable)
现场:ASan access-violation(READ @0x1),rcx=0x1
string.strcmp (includes/string.vp:86)
→ HashTable._find_slot (_resize) → __setitem__ → set_int
→ StructGenMixin.mark_module_class → ClassGenDef.translate_class_def
→ TranslatorGenMixin.translate_children → Translator.translate → RunPhase1 → main
根因:`ModuleClasses` 原先在 `mark_module_class` 里**懒创建**,并长期挂在 Gen 上;
表体与键都取自当次文件的 pool,而 pool 在文件/遍切换时会被 reset ⇒ 下一个文件里
`_resize` 重新散列旧键时 strcmp 读到已回收内存(0x1)⇒ 崩溃。
改动(与既有 `VisibleStructs` 同口径:表与键同池、同文件生命周期):
1. `ModuleClasses` 改由 `reset_visible_structs`(每文件翻译前调用)**无条件新建**
(不再区分 strict 分支),与键同池 ⇒ 无悬空。
2. `CurDefModuleSha1` 指向 `trans.ModuleSha1`(同一 pool 的串),同样在该函数里
清空,避免下一次 strcat/strcmp 读已回收内存。
3. `mark_module_class` 不再懒建表:池或表缺失时只记 `CurDefModuleSha1`、跳过登记
(宁可退化为改前行为,也不冒悬空风险)。
语义不变:规则 5.5 的证据链仍是 ① 结构体表按名(跨文件/跨遍)→ ② 正在定义类所属
模块 + find_struct_by_module → ③ 本文件已声明类归属表(自引用字段窗口 —— 登记与
查询在同一文件同一遍内,正是该表的作用域)。
2026-09-29 19:14:10 +08:00