test(lifetime): NewLifetimeTest 增 P4 所有权转移用例

CaseOwnershipTransfer:q = p 后对 p 赋值不得析构(函数内断言 dtor_hits 未变),出口释放 q 恰好一次(调用方断言);同时把 CaseAlias 的注释更新为所有权转移语义。
This commit is contained in:
TermiNexus committed 2026-10-09 08:43:58 +08:00
1 parent cd102989a8
commit e8f47e3197
2 files changed
+22 -2

No files matched your search

+21 -1
View File
@@ -45,7 +45,8 @@ def CaseLoop(n: int) -> int:
return 0
# (3) 别名:q 只是借用 p 的对象,出口必须只释放一次(释放两次会 abort)
# (3) 别名:P4 后 `q = p` 是**所有权转移**(q 接管、p 交出)——出口只释放 q 恰好一次
# (p 的影子已置 0,出口对它 free(NULL))。释放两次会 abort。
def CaseAlias() -> int:
p: Point* = new heap slife Point(3, 4)
q: Point* = p
@@ -180,6 +181,18 @@ def CaseRaiseCaught() -> int:
return 0
# P4 别名所有权转移:`q = p` 后 **q 接管生命周期**(影子标志从 p 移到 q)。
# 于是对 p 重新赋值不得释放对象(否则 q 立刻悬垂);出口只释放 q 恰好一次。
def CaseOwnershipTransfer() -> int:
before: int = dtor_hits
p: DtorPoint* = new heap slife DtorPoint(4)
q: DtorPoint* = p
p = None # P4:所有权已在 q,这一步**不得**触发析构
if dtor_hits != before: return 1 # 未转移时这里会析构一次(对象被提前释放)
if q.x != 4: return 2 # 对象必须仍然有效(值仍在)
return 0
# 失败可诊断:每个 case 失败时打印名字与返回码(否则只有一个总数,无法定位是哪条挂了)
def Check(name: str, rc: int, fails: int) -> int:
if rc != 0:
@@ -219,6 +232,13 @@ export def main() -> int:
fails = Check("CaseRaiseExit", CaseRaiseExit(), fails)
fails = Check("CaseRaiseCaught", CaseRaiseCaught(), fails)
# P4 别名所有权转移:函数内对 p 改写不得析构;出口释放 q 恰好一次
before_ot: int = dtor_hits
fails = Check("CaseOwnershipTransfer", CaseOwnershipTransfer(), fails)
if dtor_hits != before_ot + 1:
stdio.printf(" FAIL dtor_hits after CaseOwnershipTransfer: %d (want %d)\n", dtor_hits, before_ot + 1)
fails += 1
if fails != 0:
stdio.printf("NewLifetimeTest FAILED, %d case(s) wrong\n", fails)
return 1
+1 -1
View File
@@ -131,7 +131,7 @@
| EscapeMethodTest | 仓库根 `EscapeMethodTest/` | **编译失败**:方法 / `__init__` / 嵌套函数交出栈地址 → `LifetimeError`(三用例互斥,一次开一条) |
| EscapeXmodTest | 仓库根 `EscapeXmodTest/` | **编译失败**:跨模块摘要(`.vpi` 落盘 `# esc=`)路径 → `LifetimeError` |
| NewTest | [Test/NewTest/](NewTest/App/main.vp) | 编译通过并输出 4 行:`stack`(显式 slife) / `heap` / `stack默认slife`(验证**缺省规则**:省略生命期时 stack→slife、heap/memgr→hlife)/ `memgr` |
| NewLifetimeTest | [Test/NewLifetimeTest/](NewLifetimeTest/App/main.vp) | 编译通过、退出码 0(**M3 owned slife RAII**:出口释放 / 循环内覆盖释放 / 别名只释放一次 / 嵌套函数登记隔离 / early return / void 裸 return / 仅分支里 new 时 `free(NULL)` / stack slife 不释放;**§3.6 析构调度**:出口释放恰好 1 次 `__del__`、循环 3 轮共 3 次)。**另含 IR 断言**:IR 中必须出现 `llvm.call @free` |
| NewLifetimeTest | [Test/NewLifetimeTest/](NewLifetimeTest/App/main.vp) | 编译通过、退出码 0(**M3 owned slife RAII**:出口释放 / 循环内覆盖释放 / 别名只释放一次 / 嵌套函数登记隔离 / early return / void 裸 return / 仅分支里 new 时 `free(NULL)` / stack slife 不释放;**§3.6 析构调度**:出口释放恰好 1 次 `__del__`、循环 3 轮共 3 次;**P1 异常出口**:传播出去恰好 1 次、被本函数捕获时不得提前释放;**P4 别名所有权转移**:`q = p` 后改写 p 不析构、出口只释放 q 一次)。**另含 IR 断言**:IR 中必须出现 `llvm.call @free` |
| NewLifetimeFreeTest | [Test/NewLifetimeFreeTest/](NewLifetimeFreeTest/App/main.vp) | **编译失败**:对 `slife` 对象手动 `stdlib.free(p)` → `LifetimeError`(§4.3 判据5,编译器独占释放;手动 `del p` 同样被拒) |
| NewLifetimeAsyncTest | [Test/NewLifetimeAsyncTest/](NewLifetimeAsyncTest/App/main.vp) | **编译失败**:协程体(`async`)里的 `new heap slife` → `LifetimeError`(§4.2:状态机拆分 body,释放点无法确定;`new stack` / `new … hlife` 不受限) |
| NewLifetimeWillFreeTest | [Test/NewLifetimeWillFreeTest/](NewLifetimeWillFreeTest/App/main.vp) | **编译失败**:P2 `@life(willFree(p))` 标注驱动 —— `free(p)` 之后再 `free(p)` → `LifetimeError`(二次释放) |