TermiNexus
|
40ea5d6513
|
fix(viperc/diag): 节点行号等于函数定义行时也用实参位置兜底(定位到具体表达式)
上一版只在 lineno==0 时用 CurArg 兜底;实测该报错的节点自带行号且恰好等于当前函数定义行
(节点其实是 FunctionDef 本身,构造器内联路径把它传了下来),于是兜底没触发,错误位置与
栈帧都在定义行、被去重合并,看起来只剩一帧。
现把“节点位置不可信”判据放宽为:lineno==0,**或** lineno 等于最内层栈帧的行号(即函数
定义行)→ 两条都优先用 CurArg,报错精确指向调用处的实参表达式(如 `&_on_submit`)。
|
2026-10-14 17:02:16 +08:00 |
|
TermiNexus
|
4f45b391ea
|
fix(viperc/diag): 保留函数定义帧,不覆盖栈帧位置(补齐“定义帧 + 出错语句帧”两帧)
上一版 B 把最内层栈帧的位置改写成“当前语句”,结果它与错误帧落在同一 file:line,
被去重合并 —— 输出只剩一帧,`_start` 那帧(函数定义)反而没了。
改为:栈帧只记录**被调函数的定义位置**(不随语句推进覆盖);“当前语句/具体表达式”
统一由错误帧给出(CurArg 兜底,见 TypeGenErr)。这样能得到完整两帧:
File "./Apps/HelloWorld/main.vp", line 11 export def _start() -> int: ← 定义帧
File "./Apps/HelloWorld/main.vp", line 36 ... &_on_submit) ← 出错语句帧
同时删除已无调用的 TypeGenBase.update_top_trace_position。
|
2026-10-14 16:51:51 +08:00 |
|
TermiNexus
|
d6b87da063
|
fix(viperc/attr): 不再要求 import c 才认 @__attribute__(否则函数属性被静默丢弃)
ast/parser.vp 的 _DesugarMagicAttribute 会把用户写的 `@__attribute__(...)` 脱糖成
`@c.Attribute(...)`,此处看到的 `c.` 前缀是编译器自己产生的(用户并未写 `@c.Attribute`)。
原先这里要求 is_module_imported(...,"c"),用户没写 `import c` 时整条装饰器被静默丢弃,
stackrealign / always_inline 等函数属性全部失效。现去掉该守卫,与 extract_func_section
(本就没有此守卫)保持一致。
|
2026-10-14 16:42:16 +08:00 |
|
TermiNexus
|
0f2675e794
|
fix(viperc/codegen): Win64 变参提升按实参符号性选择 zext/sext,避免无符号值高位被填脏
变参调用把 <64 位整数提升到 i64 时原先一律 sext:uint8 0x8E 会被填成
0xFFFFFFFFFFFFFF8E,va_arg(i64) 读到脏高位(即 Debugger/VKERNELERR.md 记录的 sext 家族)。
现按实参符号性选择:无符号走 zext、有符号仍走 sext,与
CallGenVa._check_and_promote_variadic_call 同口径(复用 ExprGenOps.should_zext_widen)。
|
2026-10-14 16:41:58 +08:00 |
|
TermiNexus
|
0678cc8c48
|
feat(viperc/diag): 报错定位到具体表达式 + 栈帧跟踪当前语句(方案 A+B)
A:报错节点是合成节点(lineno==0,如构造器内联出的参数赋值)时,优先用“正在翻译的实参
位置”兜底 → 报错精确指向调用处的表达式(如 `&_on_submit`),不再退回函数定义行。
- TypeGenBase 新增 CurArgFile/CurArgLine/CurArgCol 与 set_cur_arg()
- CallGenLib 翻译实参前调用 set_cur_arg(当前文件, arg_node 位置)
- TypeGenErr 兜底顺序改为:节点自身行号 → CurArg → 栈最内层帧
- 语句边界清空 CurArg,避免兜底串味到上一条语句的实参
B:栈帧跟踪“当前语句”位置(Python traceback 口径)
- TypeGenBase.update_top_trace_position():更新最内层帧的 lineno/col
- translate_stmt 每条语句推进时调用 → 报错时栈帧给出的是调用点所在语句,而非函数定义行
配合 A 即可得到“调用点 + 具体表达式”两帧。
|
2026-10-14 16:40:35 +08:00 |
|
TermiNexus
|
70c271d2a4
|
fix(viperc): &func 对函数指针变量应取其值(Ptr(Func) 一级),不再多一层地址
现场:Apps/HelloWorld 里 vpui.Button(..., &_on_submit) 报
CastError: pointer level differs (source level 2 -> target level 1);
source: level 2, pointee !llvm.ptr -> target: level 1, pointee i8
根因:嵌套函数名在符号表里是 Ptr(Func) 变量。ExprGenOps 的取址分支有一条
“变量本身就是那个对象”的规则(结构体指针变量要 load 一次、而非取变量地址),
但该 match 只覆盖了 Ptr(Ptr(Struct)),未覆盖 Ptr(Ptr(Func)) —— 于是 `&f` 落到末尾的
`return v_ao`,给出的是变量地址 Ptr(Ptr(Func))(二级),而不是函数地址 Ptr(Func)(一级)。
修改:在该 match 中补上 Ptr(Ptr(Func)) 分支,load 一次后返回 Ptr(Func),
与结构体指针变量同口径。
|
2026-10-14 16:37:37 +08:00 |
|
TermiNexus
|
9d02a3ac0b
|
improve(viperc/diag): 指针级数不符报错补印“级数 + pointee 类型”
LLVM 不透明指针下 source/target 两边的类型文本都是 !llvm.ptr,看不出差异,现场只剩
一个 “2 -> 1”。现改为同时打印:
source: level N, pointee <T> -> target: level M, pointee <U>
这样一眼能定位是哪个表达式/哪个形参触发的级数不符。
|
2026-10-14 16:01:09 +08:00 |
|
TermiNexus
|
6b180c8f59
|
fix(viperc/life): 进程入口豁免补上 _start(ViperOS 应用入口),消除栈逃逸误报
现象:Apps/HelloWorld/main.vp 报 LifetimeError(`master` frame-local,传给构造器参数会
悬垂)。而 `master = &root` 中 root 是 _start 的帧内 Tk,_start 是进程入口
(@__attribute__(section(".text.startup")),结尾 mainloop/process.exit),本帧返回即进程
结束 —— 与 main 同口径,确无逃逸。
_CurFuncIsEntryMain 此前只认 "main",现把 `_start` 一并视为进程入口。
|
2026-10-14 15:53:28 +08:00 |
|
TermiNexus
|
a23c1bda3e
|
fix(viperc/diag): 调用栈帧与错误点同位置时不再重复打印
现象:`_start` 定义行在 Traceback 里被打印两遍 —— 栈内最内层帧与随后的“错误点帧”
落在同一 file:line(错误的 AST 节点 lineno 为 0 时,错误点位置会回退到栈最内层帧,
于是与栈帧完全重合)。
Diag.emit 增加两条去重规则(DIAG_DIR_UP / DIAG_DIR_DOWN 两个方向都生效):
① 连续两个位置完全相同的栈帧只印一次;
② 与紧随其后的“错误点帧”(emit_current 打印的那一帧)位置相同的栈帧不再重复印。
|
2026-10-14 15:53:05 +08:00 |
|
TermiNexus
|
dc24970af2
|
fix(gen): 修 __before_init__ 字段默认值写入的参数次序(build_store 缺 builder/file 顺序颠倒),修编译期参数缺失报错
|
2026-10-13 21:46:20 +08:00 |
|
TermiNexus
|
ea54d5685d
|
feat(gen): virtual(HasVTable)类豁免「无 def => struct」规则,必须合成 __before_init__/__init__ 且必发 vtable;非泛型构造路径按符号存在性判定调用
|
2026-10-13 21:29:24 +08:00 |
|
TermiNexus
|
99a021ad67
|
fix(gen): 类合成默认构造——有方法但无显式 __init__ 的类合成空 __init__(补 undefined 符号);完全无 def 的类按 struct 处理,不合成 __init__/__before_init__
|
2026-10-13 20:59:41 +08:00 |
|
TermiNexus
|
12da1cbfc1
|
fix(gen): define 复用前置声明时按源码形参名对齐签名参数名,修签名与函数体两套参数名导致 MLIR「region entry argument already in use」
|
2026-10-13 20:33:06 +08:00 |
|
TermiNexus
|
bc1eeb6e38
|
feat(gen): REPL 会话变量的复合赋值下传槽类型作为 target_ty(复用 repl_slot_ty,缺失即 fail-fast),与二元/普通变量路径对齐
|
2026-10-13 20:04:34 +08:00 |
|
TermiNexus
|
c1e5a11962
|
feat(gen): 复合赋值统一二元运算口径(常量无损适配另一侧、不做整数提升、RHS 按左值类型翻译),修 u16 -= 1 误报窄化
|
2026-10-13 20:01:16 +08:00 |
|
TermiNexus
|
d2138678ef
|
feat(gen): 同型聚合值 -> 同型聚合指针 允许隐式值拷贝(调用者帧溢出临时槽后取址),修裸类形参接收聚合值实参报错
|
2026-10-13 18:33:49 +08:00 |
|
TermiNexus
|
1c3cdf3493
|
fix(stub): 消费模块补齐按值返回聚合的具名结构体完整体(_inject_method_declare 补 _ensure_full_value_types、_inject_class_struct_declare 补登完整定义),修 MLIR opaque 报错
|
2026-10-13 17:48:57 +08:00 |
|
TermiNexus
|
377c0db5d3
|
fix(gen): life_of_expr 的构造分支按目标类型判定——目标为同型聚合值时判 LIFE_VALUE(值拷贝),不再误判帧逃逸
|
2026-10-13 17:48:35 +08:00 |
|
TermiNexus
|
6cb1f525d9
|
feat(gen): 常量操作数隐式适配另一侧位宽(仅另一侧非常量时),修 u16 变量 + 字面量被抬到 i32 导致误报窄化
|
2026-10-13 17:01:53 +08:00 |
|
TermiNexus
|
a01e9280cc
|
fix(stub,phase1): 因取消整数提升,模块属性 CDefine 的窄乘法显式加宽为 sizet(修缓冲区尺寸回绕成 0/负值)
|
2026-10-13 16:34:39 +08:00 |
|
TermiNexus
|
14d8c05a2a
|
feat(gen): 二元运算取消 C 式整数提升(任何位宽不再默认提升到 i32),改为按 max(两侧)/目标位宽运算
|
2026-10-13 16:20:02 +08:00 |
|
TermiNexus
|
97c0a1c6fd
|
fix(gen): 取消「宽整型 -> bool(i1)」的隐式真值归一化,改为 fail-fast(仅保留编译期常量豁免)
|
2026-10-13 16:07:43 +08:00 |
|
TermiNexus
|
d7c5af9b80
|
fix(gen): callable[[]] 省略返回类型按 void 注册(作为值使用时报错,不再静默按 int)
|
2026-10-13 13:32:47 +08:00 |
|
TermiNexus
|
cb2dc4cb3a
|
feat(gen): 字节序 bswap 改为按目标端序判断(BigEndian/LittleEndian 都相对目标),原语改名 apply_byte_order_bswap
|
2026-10-13 13:29:03 +08:00 |
|
TermiNexus
|
444e87dd7d
|
fix(gen): callable[[]](省略返回类型)按默认 int(i32) 注册函数指针签名,修间接调用签名缺失 fail-fast
|
2026-10-13 13:26:51 +08:00 |
|
TermiNexus
|
d3997e0746
|
fix(gen): 函数指针变量别名(f2: rawptr = f)继承已注册的返回类型签名,修 rawptr 间接调用的签名缺失 fail-fast
|
2026-10-13 13:26:11 +08:00 |
|
TermiNexus
|
32599a08c1
|
fix(stub): .vpi 读取侧复刻字段标记判定(volatile/pointee-volatile/const/字节序),跨模块不再丢字段标记
|
2026-10-13 12:30:23 +08:00 |
|
TermiNexus
|
37c4b0da3b
|
feat(gen): 字节序 bswap 代码生成——字段/局部变量/数组元素读写处成对 llvm.bswap(仅 BigEndian 且 16/32/64 位整数)
|
2026-10-13 12:30:05 +08:00 |
|
TermiNexus
|
5d15d01e21
|
feat(gen): 支持 t.BigEndian / t.LittleEndian 字节序描述符(注解识别 X|marker 并把字节序登记到 FieldEntry.ByteOrder,暂不生成 bswap)
|
2026-10-13 12:17:02 +08:00 |
|
TermiNexus
|
3ca2165fd9
|
feat(gen): 指向同型聚合的指针→同型聚合值允许隐式值拷贝(裸类形参 T* ABI 的直接赋值仍按值拷贝)
|
2026-10-13 10:30:15 +08:00 |
|
TermiNexus
|
b859f8a136
|
fix(gen): CDefine 常量(含 mod.N 属性形态)拓宽方向单独判定——负值 sext、非负 zext,修 zlib 窗口尺寸被 sext 成负数与 Z_DEFAULT_COMPRESSION->255
|
2026-10-12 21:48:05 +08:00 |
|
TermiNexus
|
22475e0fda
|
fix(gen): 逃逸检查把模块级(根 scope)变量判为 LIFE_STATIC,修模块级数组 return 误判帧逃逸(pyzlib_error_msg)
|
2026-10-12 21:23:23 +08:00 |
|
TermiNexus
|
c0ff5c9d96
|
fix(gen): 复合赋值取消「右值有符号 vs 左值无符号」拦截,与二元 x = x op y 口径一致(修 sizet += int 误报)
|
2026-10-12 21:16:50 +08:00 |
|
TermiNexus
|
c9562a6e04
|
feat(gen): 一元 +/-/~ 按符号性提升——u 类不提升(保持原位宽),i 类提升到 i32 防负数陷阱
|
2026-10-12 21:10:06 +08:00 |
|
TermiNexus
|
d206abf3e1
|
chore: 全仓同步 w32→win32 模块改名与 range(int(...)) 显式收窄适配
|
2026-10-12 20:59:11 +08:00 |
|
TermiNexus
|
ea2af739dd
|
feat(gen): range 边界与循环变量统一提升到至少 i32(取消 u8/u16 循环变量档),u64 边界保持 i64
|
2026-10-12 20:57:12 +08:00 |
|
TermiNexus
|
00bf7ebf38
|
fix(gen): 非负 define 常量按最小位宽的无符号位模式参与宽化/比较(修 range(50000) 迭代 0 次)
|
2026-10-12 20:52:52 +08:00 |
|
TermiNexus
|
e3743b185f
|
fix(gen): zip/enumerate 的 range 源工作位宽随参数(仿 for-range),修 range(3) 被 i64 索引误判窄化
|
2026-10-12 20:24:50 +08:00 |
|
TermiNexus
|
54b239747e
|
chore(gen): 清理「按值物化」残留注释(裸类形参即 T*,非值语义槽)
|
2026-10-12 20:03:10 +08:00 |
|
TermiNexus
|
ae12ae8581
|
Revert "feat(gen): 裸类形参按值物化(值语义),调用方 ABI 不变"
This reverts commit 87914b0a51.
|
2026-10-12 19:56:02 +08:00 |
|
TermiNexus
|
001f8afc37
|
fix(gen): 常量适配限定为位运算,算术一律按 C 语义拓宽(修 i8 乘法回绕致 memhub 不变式误报)
|
2026-10-12 19:40:13 +08:00 |
|
TermiNexus
|
e7b05c6a3d
|
fix(gen): 三元表达式两支统一按常量无损适配,修 i8 表元素 vs 字符字面量被抬成 i32 的窄化误报
|
2026-10-12 19:27:58 +08:00 |
|
TermiNexus
|
3d638927d5
|
fix(gen): 移位的工作位宽不计入计数宽度,修 ushort 表达式被抬成 32 位后赋回 ushort 误报窄化
|
2026-10-12 19:22:36 +08:00 |
|
TermiNexus
|
4a66aa7fd2
|
feat(gen): 二元运算采用目标感知工作位宽(仿 C:算到目标位宽;移位只提升左操作数),隐式窄化仍禁止
|
2026-10-12 18:53:50 +08:00 |
|
TermiNexus
|
9d02cc4684
|
fix(gen): 移位按 C 语义把左操作数提升到 int,且移位数不参与常量适配(修 uchar<<8 误报)
|
2026-10-12 18:42:54 +08:00 |
|
TermiNexus
|
65c72fd55c
|
fix(gen): 二元运算按常量无损适配另一侧位宽,修 crc(uint)^0xFFFFFFFF 被抬成 64 位
|
2026-10-12 18:32:55 +08:00 |
|
TermiNexus
|
8453698f66
|
chore(gen): ExprGenBase 残留清理(range(int()) 与 TPV->VPC 注释)
|
2026-10-12 18:25:56 +08:00 |
|
TermiNexus
|
74fafb3b56
|
fix(gen): 整型字面量宽度按值推导(新增 u32 档)+ 显式宽度严格校验
|
2026-10-12 18:25:38 +08:00 |
|
TermiNexus
|
f044d94e90
|
fix(gen): -x/~x 对编译期常量折叠,修负字面量被隐式窄化误拦
|
2026-10-12 18:25:24 +08:00 |
|
TermiNexus
|
87914b0a51
|
feat(gen): 裸类形参按值物化(值语义),调用方 ABI 不变
|
2026-10-12 18:25:04 +08:00 |
|
TermiNexus
|
d7a5f7e68b
|
fix(gen): 值语义聚合槽豁免改为类型级判定,去掉易漏判的槽身份比较
|
2026-10-12 16:56:03 +08:00 |
|
TermiNexus
|
7c11c589f7
|
fix(gen): 值语义聚合槽允许隐式值拷贝,修 self.field = param 的 Foo* -> Foo 误报
|
2026-10-12 16:42:52 +08:00 |
|
TermiNexus
|
3384941cd1
|
chore: 改名同步 TransPyV -> ViperC(源码/配置内的纯替换)
|
2026-10-12 15:06:28 +08:00 |
|
TermiNexus
|
d0c7b9c77c
|
chore: 改名同步 TransPyV -> ViperC(.gitignore/文档/脚本)
|
2026-10-12 15:02:13 +08:00 |
|
TermiNexus
|
9b93586eec
|
docs: README 随改名同步(ViperC/viperc.exe/__vpcbuild__)并纠正过时内容
- w32.* -> win32.*(中文前提条件/对比表、英文 Prerequisites/对比表 共 4 处)
- 规模数字按实测刷新:App 82 文件 63,783 行;Gen 54 文件 47,297 行;
Phase 6/5,339;Stub 9/6,207;Projectrans 3/712
- 自测套件位置纠正:不在本仓库,位于工作区根 Test\(独立仓库,97 工程 129 个 .vp),
由 Test\SequentialRegressionTest.py 驱动;删除已不存在的 Test/{App,NegativeTest,Sha1Test} 描述
- 工作区布局补 Test\ 与 exe\,修正 ViperC\ 对齐
|
2026-10-12 14:48:47 +08:00 |
|
TermiNexus
|
023a22af08
|
chore: 工程改名 TransPyV -> ViperC(输出 viperc.exe,构建目录 __vpcbuild__)
|
2026-10-12 14:28:52 +08:00 |
|
TermiNexus
|
4e46a25ab1
|
fix(gen): define 位宽/整数提升/移位/AugAssign 规则收敛,修 va_list 与 traceback 卡死
- define 位宽:cdefine_decl_bits 单一裁决(注解 → 右侧强转 → 值最小可存宽度),定义方与 VpiReader 两处跨模块注册共用;lookup_cdefine_bits 取最后一条非零记录;存储即截断;使用侧改按声明位宽;通用求值器加编译期除零/移位越界 fail-fast
- 消除对无符号强转加宽方向的依赖:define_min_width 改 (value>>32)==0,截断改左移+算术右移,修 TPC/TPV 判定不一致导致 0x80000000 误判 64 位
- 整数提升:算术/移位、比较、一元 +/-/~ 提升到 i32;位运算保持同宽不提升
- AugAssign:y 有效宽度须 <= x、禁 y 显式有符号而 x 无符号;移位计数静态范围检查
- 函数入口复位 va_list 状态与 willFree 失效槽表(FuncGenDef/FuncGenMixin/VarGenMixin 等)
- BuildPipeline:traceback 逐行扫描改 span 判定,去掉对全文 strlen 的 O(n^2)
|
2026-10-12 12:37:48 +08:00 |
|
TermiNexus
|
be4db7b5d1
|
fix(narrow): StmtGenMixin exc_code 显式 int(cst_r.int_val)
ast.Constant.int_val 为 int64,exc_code 为 int(并传入 const_int32),改用显式 int(...)
|
2026-10-11 10:07:37 +08:00 |
|
TermiNexus
|
985983391b
|
fix(narrow): ClassGenBase 方法元数据指针声明 int* 而非 t.CPtr
flags_ptr/flags_ptr2 被显式 (int*)(...) 赋值但声明为 t.CPtr,下标元素被判为 i8,与 i*4 的 32 位步长及位标志语义不符(且 >127 的值会读出符号错误);声明改为 int*
|
2026-10-11 10:04:19 +08:00 |
|
TermiNexus
|
3b9deb08d8
|
fix(narrow): ClassGenEnum RenumClassVariantCounts 元素读取改用 int(...)
n_first_v(1029) 与 vcount(1010/1055) 承接 sizet* 元素,原先 64->32 隐式窄化;改用显式 int(...) 保持原截断语义
|
2026-10-11 10:00:05 +08:00 |
|
TermiNexus
|
7468b007f7
|
fix(narrow): ClassGenEnum pos_max 加宽为 sizet
pos_max 承接 variant_field_sizes 的 sizet 元素,仅与 sizet 比较并流入 sizet();原 int 触发 64->32 隐式窄化
|
2026-10-11 09:54:18 +08:00 |
|
TermiNexus
|
3c0d3d13b5
|
fix(narrow): 清理注解式整数窄化(Converter/TypeGenCType/CallGenLeafKit/ClassGenEnum)
CDefine 查询返回 int64 处按下游用法定型:回传给 int 的用显式 int(...),流入 int64 目标的加宽为 int64;sizet 数组元素赋给 int 的用 int(...)
|
2026-10-11 09:51:08 +08:00 |
|
TermiNexus
|
966fc3b8f5
|
fix(narrow): BuildPipeline.ensure_dir 消除隐式窄化(int(path_len) / char(saved))
buf[i] 元素为 i8,saved 由 int 改为 char 并用 char(ch) 显式收窄;plen 由 sizet 显式 int(path_len) 收窄
|
2026-10-11 09:42:39 +08:00 |
|
TermiNexus
|
77f4a25055
|
fix(narrow): CallGenFd 的 fd_can 改用 int(len(...)) 消除 64->32 隐式窄化
|
2026-10-11 09:38:24 +08:00 |
|
TermiNexus
|
82ad5da7c7
|
fix(narrow): len() 结果赋给 int 的形参/默认值计数改用显式 int(...)
FuncGenDef 的 fd_param_count/fd_default_count/tfd_param_count/tfd_default_count 与 ClassGenVTable 的 md_param_count/md_default_count 原本直接接收 len()(sizet),触发隐式窄化;改 int(len(...)) 保持原有截断语义
|
2026-10-11 09:34:43 +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
|
41772b4a0c
|
refactor(narrow): 撤销 File.write 实参多余的 ULONG(...) 显式窄化
File.write 形参已改为 sizet,调用点不再需要显式 ULONG 包裹(覆盖 WriteCallSidecar/_phase2_write_cmd/write_ir_to_file/WriteStackMap/TranslateFileGetTrans)
|
2026-10-10 22:15:49 +08:00 |
|
TermiNexus
|
58951a29a8
|
fix(narrow): File.write 长度实参加显式 ULONG(...)(避免 64→32 隐式窄化)
fileio.File.write(buf, count: ULONG) 的 count 是 32 位,而实参是 sizet/int64(strlen、pos、ir_len、total 等);规则收紧后为隐式窄化,改为显式 ULONG(...)。本文件其它位置无隐式窄化。
|
2026-10-10 22:10:01 +08:00 |
|
TermiNexus
|
5bb6538703
|
feat(types): 禁止隐式窄化(整数 高→低);_tb_read_whole 容量改 sizet
规则收紧(2026-10-10):整数位宽 高→低 隐式窄化一律 fail-fast(CastError),仅两类豁免:① 目标 i1 的真值归一化;② 源是编译期常量且值落进目标位宽。_scalar_convert_reason 的 int↔int 同步改为'目标位宽≥源位宽才 PC_OK'。BuildPipeline._tb_read_whole 去掉 ULONG(sz)+ULONG(2) 的窄化,改 sizet(sz)+2。
|
2026-10-10 21:57:18 +08:00 |
|
TermiNexus
|
9db3748f68
|
feat(gen): main 内栈逃逸检查整体豁免(收窄为结构体指针不再采用)
按用户决定:入口 main 的帧生命期=进程生命期,main 内任何栈地址写进长命存储都不报,不再区分结构体/标量。移除上一版为'仅豁免结构体指针'新增的 _TypeIsPtrToStruct/_ExprIsStructPtr,fail_if_life_escapes 只保留 _CurFuncIsEntryMain 短路。嵌套函数不豁免。
|
2026-10-10 14:46:45 +08:00 |
|
TermiNexus
|
87e0bce5ee
|
feat(gen): 入口 main 内豁免栈上结构体指针的逃逸检查
memhub.MemBuddy(...) 是裸构造(无 __new__),life_of_expr 判定为 LIFE_FRAME;main 里 argparse._mbuddy = mb 把帧内结构体指针写进模块级字段即触发 P3 拦截。入口 main 的帧生命期=进程生命期,其栈上结构体不会被本帧返回后使用,故在 fail_if_life_escapes 放行。仅豁免'结构体指针'(新增 _CurFuncIsEntryMain/_TypeIsPtrToStruct/_ExprIsStructPtr):标量指针(&local、栈数组退让、ret(frame) 返回 int*)仍受检,保持 NewLifetimeFieldEscapeTest / NewLifetimeAnnFieldEscapeTest 等负向工程不回归。
|
2026-10-10 14:45:07 +08:00 |
|
TermiNexus
|
034293df59
|
fix(gen): freed_restore/freed_join/freed_capture 补 global 声明,修复野写崩溃
上一提交(eea8111)在 freed_restore 里重新赋值全局指针 _g_freed_slots 却未声明 global,被解析成未初始化的局部变量:is None 判假 → 跳过分配 → 对垃圾地址(0x11)野写,ASan access-violation。为三个新函数统一声明 global _g_freed_slots, _g_freed_count(与 _FreedMark 同口径)。
|
2026-10-10 14:27:00 +08:00 |
|
TermiNexus
|
eea8111e61
|
fix(gen): willFree 失效槽表补控制流合流,修复分支内 free 泄漏导致的误报
失效表原为平坦单调全局表,无分支合流语义:if 分支体内 free(x) 会把失效态泄漏到合流之后,于是 fileio.vp 的 if ...: free(buf); return None 之后 return buf 误报 LifetimeError(1784eaf 修好 free 的 wf 位后才暴露)。新增 freed_capture/freed_restore/freed_join(pool 快照 + 求交),并在 if(结合 then/else 是否到达合流)与 while/for 的既有 life_* 同点位接入:只有所有到达合流的路径都失效才算失效。
|
2026-10-10 14:10:53 +08:00 |
|
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
|
c1d3f62bf0
|
fix(gen): ret(borrows=...) 支持 0 起序号写法(原来只认形参名)
现场:@life(noescape(a, b), ret(borrows=1)) 报
LifetimeError: @life(ret(borrows=...)) accepts only a parameter name or `none`
—— _RetKindFromTag 的关键字分支只处理 Name(形参名)与 CONST_NONE,遇到整数常量
(形参位置序号)就落到 fail-fast。
修法:Name(形参名 / none)与 Constant(CONST_NONE = 非本帧)先分流,
其余一律交给 _WillFreeArgIndex —— 它本来就同时认"形参名"与"0 起序号"
(与 willFree(...) 同口径,见 includes/stdlib.vp 注释),非法实参在那边 fail-fast。
于是 ret(borrows=x) 与 ret(borrows=0) 等价可用。
|
2026-10-10 13:27:24 +08:00 |
|
TermiNexus
|
5ec75b2d38
|
fix(gen): 模块限定函数调用的 ret(borrows=x) 形参位映射错位(A2 跨模块静默失效)
现场:NewLifetimeRetXmodTest 漏检(应拒绝却通过)。诊断(printf 断点,已删)显示
_CallRetKind 完全正确:cls=(none) → 模块分支 → mod_p=retdep2 sha=1 → vpi lookup rk=8。
即契约取到了,错在**下一跳**:life_of_expr 的 borrows 分支用
_CallArgNodeForParam(cl, pi) 把 pi=0 当"接收者"——那是**方法**的语义(形参表含 self)。
`retdep2.IdentBorrow(l)` 是**模块限定函数**(callee 也是 Attribute,但不是方法),
形参 0 就是第一个实参,于是取到了 `retdep2` 这个模块名节点:
life_of_expr(Name retdep2) 查不到变量 → 返回 LIFE_STATIC(≠FRAME)→ 放行。
修法:
- _CallArgNodeForParam 增加 is_method 参数:为 1 时才把 pi=0 映射到接收者、其余偏移一格;
为 0(模块限定/裸函数)时 pi 直接就是实参下标。
- 新增 _CallIsMethod(trans, cl):接收者是对象变量(_RecvClassName 命中)才算方法调用。
- life_of_expr 的 borrows 分支据此传参。
同模块用例(NewLifetimeRetEscapeTest)与正向用例(NewLifetimeRetOkTest)在此之前"碰巧"
通过/失效——前者走 Name callee 不涉偏移,后者的正例把结果用在本帧内、STATIC 也放行。
|
2026-10-10 13:00:51 +08:00 |
|
TermiNexus
|
1784eaf906
|
fix(gen): _WillFreeMergeTag 置位判据写反(is_wf != 0 → == 0),P2 willFree 全面漏检
string.strcmp 相等返回 0,所以 `is_wf != 0` 的含义是"这个标签**不是** willFree"。原来的
`if is_wf != 0: mask |= 1 << idx` 于是:真正标了 willFree 的形参位**永不置位**,而任何别的
标签(noescape/escape/stores/borrows)反而把 willFree 位置 1。
实测证据(本轮编译产物):
- EscapeXmodTest/.tpv_build/.../73b2b3ff01d73ce2.vpi(stdlib)第 9 行
`def free(p: rawptr) -> t.State: pass` —— 连尾注都没有(wf_mask 为 0 ⇒ 不写 wf=);
- EscapeOkTest/.tpv_build/.../4ccdbfc0e006b3d5.vpi 第 44 行 StoreNS(只标了 noescape)
却写着 `# wf=1 escn=1`(反向误标)。
后果:跨模块 wf= 通道形同虚设 → 调用点从不"标记实参失效" → P2 全面漏检
(NewLifetimeWillFreeTest / WillFreeKwargTest / WillFreeMethodTest / WillFreeAddrTest 静默通过),
同时 NewLifetimeWillFreeOkTest 是**空过**(根本没有标记发生)。反向误标还可能造成假报。
回归来源:fc797da(A1 把三元 guard 扩成五元时把这行一起改反)。本行判据改为 is_wf == 0,
与同文件 @willFree 兼容分支(strcmp(...) == 0 即相等)的口径一致。
|
2026-10-10 11:55:57 +08:00 |
|
TermiNexus
|
948e1855a6
|
fix(gen): 跨模块 vpi 查询只在加载成功时记"已加载"备忘(修漏检回归)
A2 引入的 lookup_ret_kind_via_vpi 会在 life_of_expr(判右值生命期,可能在极早的翻译阶段)
里触发对方模块 vpi 的按需加载,但它沿用 _vpi_escape_packed 的旧写法:**无论加载成功与否**
都把该模块记入 _vpi_escape_module_loaded 备忘。于是当那个时刻 vpi 还没生成/打不开时,
该模块被毒化成"已加载",之后所有共用这条备忘的跨模块查询(逃逸摘要 esc=、willFree wf=)
都跳过真正的加载 → 缓存恒空 → 判据静默失效。
现场:EscapeXmodTest(跨模块 esc)与 NewLifetimeWillFreeTest(跨模块 wf)双双漏检,
即"应拒绝却通过"。同模块用例不受影响,故更隐蔽。
修法:两处加载点都改为 _ensure_vpi_ret_annot_loaded 返回非 0(= 真加载/已缓存)才
_vpi_escape_mark_module;失败直接返回 0,留给后续查询重试。
- lookup_ret_kind_via_vpi(新代码,回归根源)
- _vpi_escape_packed(既有同款隐患,一并修)。
|
2026-10-10 11:25:43 +08:00 |
|
TermiNexus
|
a3a627f513
|
fix(gen): lambda 返回类型完全跟随 body(整数也跟随,不再一律 i32)
承接上一提交:原来只修正指针/浮点两类,整数仍保留 i32 语义(i64 实参会被 trunc 成
i32,静默有损)。现按用户要求改为**完全跟随 body 的实际类型**:
- func.RetTy = body_val.Ty,然后原样 build_ret,不做任何强转;
- void body(如 lambda: <void 调用>)→ RetTy = Void + build_ret_void
(原来会因 "a void value cannot take part in any operation" 直接报错)。
副作用(预期内):body 为 bool → i1;body 为 i64/u64/f64 → 同宽返回;指针 → 指针。
调用点从函数签名取返回类型(CallGenMixin 读 local_fn.RetTy),自动适配。
注意:这是 ABI 行为变更,重编后必须回归 ClosureTest / ClosureTest2 / ClosureCallTest。
|
2026-10-10 10:46:51 +08:00 |
|
TermiNexus
|
ea57986312
|
fix(gen): lambda 提升函数按 body 类型修正返回类型(原先恒 i32)
translate_lambda 第 4 步创建提升函数时只能先写 i32(body 尚未翻译);第 11 步原来把
body 值一律 coerce_to_type 到 i32,于是:
- body 返回指针 → 撞 CastError(implicit pointer-to-integer conversion is forbidden);
- body 返回浮点 → fptosi 成 i32(静默有损)。
现按 body 的实际类型修正 func.RetTy(mlirlite 公开字段;本步之后才发 ret,期间无人读
RetTy;调用点读到的已是修正后的签名):
- 指针 → RetTy = body 类型,直接 ret(不再强转);
- 浮点 → 同上(不再 fptosi);
- 整数/其它 → 保持既有 i32 语义(与"无注解嵌套 def 保持 i32"同口径,避免改动既有行为)。
与嵌套 def 的返回类型缺口同类(见上一提交)。
|
2026-10-10 10:44:07 +08:00 |
|
TermiNexus
|
5d1e604aac
|
fix(gen): 嵌套 def 的返回类型跟随 -> T 注解(原先写死 i32)
现场:def Inner(p: char*) -> char*: return p 报
CastError: implicit pointer-to-integer conversion is forbidden ...
—— 因为 translate_nested_function_def 把提升后的闭包函数写死成
define i32 @__closure_Inner(i8* %env, i8* %p)(grep 过整个文件,从未读 fd.returns),
return p 于是要把 char* 隐式转 i32。即"嵌套 def 只支持 int 返回",此前被同文件
更早的用例报错掩盖(EscapeMethodTest 的 (A)/(B) 因同命裁剪静音后才露出来)。
改动:
- 提升函数按 fd.returns 解析返回类型(resolve_annotation_type);解析不出 -> fail-fast。
边界:fd.returns 为 None 或注解是 None / t.CVoid 时**保持 i32**(不改既有 ABI,
避免打断现有无返回值/无注解的嵌套 def)。
- 隐式收尾的零值改为 ConstZero(ret_ty)(原写死 const_int32(0),签名一变 llc 会拒绝),
取不到零值则 fail-fast。
已知未覆盖:lambda 的提升路径同样写死 i32(见 FuncGenMixin 中 lambda 分支),
本轮不动(lambda 无返回注解可跟随)。
|
2026-10-10 10:40:36 +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
|
4907e64714
|
feat(lifetime): _g_new_life 透传——new stack/slife 的构造也算 FRAME(参与构造器同命裁剪)
新增模块级 _g_new_life 与 _g_new_storage 同步维护(set/restore 支持嵌套 ctor),口径与 life_of_expr 的
New 分支一致:stack(含 stack slife)/slife → LIFE_FRAME;heap·memgr + 缺省/hlife → LIFE_HEAP;-1 = 未知。
_translate_struct_ctor 两份副本的 recv_life_ct 改为:裸 T() → FRAME;有 new → 取 _g_new_life。
于是 new stack S(&l) / new heap slife S(&l) 不再误报,new heap hlife S(&l) 仍报。
|
2026-10-09 22:43:43 +08:00 |
|
TermiNexus
|
953c829e9f
|
feat(lifetime): 构造器路径填 recv_life——裸 T() 构造(入口块 alloca)算 FRAME
_translate_struct_ctor 两份副本都算 recv_life_ct:_g_new_storage == None(无 new 描述符提供存储)→
裸 T() 的存储是入口块 alloca → LIFE_FRAME(参与构造器同命裁剪);非空(存储来自 new …)→ -1 不裁剪。
两处 __init__ 调用点(:171 / :675)均传入。覆盖 CtorHolder(&l) / BoxCtor(&l) 这类裸构造误报。
|
2026-10-09 22:40:30 +08:00 |
|
TermiNexus
|
357a8bba05
|
feat(lifetime): 构造器路径同命裁剪的透传管道(recv_life,默认 -1 = 现状不变)
_call_method_on_ptr 新增 recv_life: int = -1(被构造对象的生命期,由调用方 CallGenCtor 算出);
构造器分支在指令最终裁决之后,若 recv_life ∈ {FRAME, VALUE} 则复用 _FrameArgsEscapeClearsMethod
(recv_node=None,接收者已由 recv_life 判定)清"写进形参对象字段"那类位。
下一轮只需在 3 个 __init__ 调用点(CallGenCtor:171/665、ExprGenConst:257)填 recv_life;未填 = 完全现状。
|
2026-10-09 22:36:09 +08:00 |
|
TermiNexus
|
bf5ecd7668
|
fix(lifetime): 方法路径补上"同命裁剪"(此前缺失)—— h.set_q(l) 不该报
现场:h: S* = new heap slife S()(生命期本就是 FRAME:作用域出口释放)+ l 是栈数组;
h.set_q(l) 却报"escaping method parameter"。根因:同命裁剪只做在模块级函数路径,方法路径一直没接
(此前已明确告知"还差一半")。
新增 CallGenMixin._FrameArgsEscapeClearsMethod:位 0 = 接收者(self)、位 (i+1) = 用户第 i 个实参;
某位可清 ⇔ 该位对应实参是 FRAME/VALUE 且其余全部实参(含接收者)也在本帧。
CallGenCall 方法路径在 refine 之后、指令最终裁决之前套用;硬位仍用 get_param_escapes_hard_method 排除。
构造器路径暂未接(__init__ 的 self 是"正在构造的对象",此处拿不到接收者 AST 变量)。
|
2026-10-09 22:30:35 +08:00 |
|
TermiNexus
|
6000734704
|
feat(life): A1——@life 新增 stores(x)/borrows(x)(复用 escape/noescape 契约位);ret/owns 显式报未实现
§14 路线 A 第一步(零语义变化):
- stores(x) 归到"强制逃逸"位、borrows(x) 归到"强制不逃逸"位(同一对契约位,调用点已在最后套用、.vpi 已有 esce=/escn= 通道)
- _WillFreeMergeTag 白名单同步扩 stores/borrows;ret/owns 显式 fail-fast "not implemented yet (planned A2/A3)",不做静默忽略
- 矛盾契约报文改为 escape/stores 与 noescape/borrows 两两对立
|
2026-10-09 22:14:26 +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
|
e82e2ac768
|
chore(dbg): 加 NLL 判定链临时诊断([NLL-DBG]),定位后删除
|
2026-10-09 21:05:31 +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
|
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
|
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 |
|