diff --git a/NewLifetimeTest/App/main.vp b/NewLifetimeTest/App/main.vp index 8f6d7ea..5955688 100644 --- a/NewLifetimeTest/App/main.vp +++ b/NewLifetimeTest/App/main.vp @@ -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 diff --git a/README.md b/README.md index f933cdf..9dc975b 100644 --- a/README.md +++ b/README.md @@ -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`(二次释放) |