diff --git a/NewLifetimeRetOkTest/App/main.vp b/NewLifetimeRetOkTest/App/main.vp index 9d6b7f0..621828f 100644 --- a/NewLifetimeRetOkTest/App/main.vp +++ b/NewLifetimeRetOkTest/App/main.vp @@ -26,6 +26,18 @@ novirtual class Holder: c: char* +# 方法版 borrows 的钉桩:`ret(borrows=self)` ⇒ 结果的生命期 = **接收者**(不是实参)。 +# 调用点故意让接收者是**堆**对象、用户实参是**本帧**指针: +# 映射正确 → 结果=堆(可存进长命存储); +# 若把方法误当"无 self"而不偏移 → 取成实参 0(本帧)→ 结果 FRAME → 报错 ⇒ 用例失败。 +novirtual class BorrowBox: + v: int* + + @life(noescape(self, x), ret(borrows=self)) + def SelfRef(self, x: int*) -> int*: + return self.v + + # ---- 同模块契约(走本文件 EscDirTable,不经 .vpi)---- @life(ret(heap)) @@ -56,9 +68,27 @@ export def main() -> int: base: int* = new stack int pb: int* = retdep.IdentBorrow(base) + # (4) 形参位↔实参位映射的**两个方向**都用"错就报错"的方式钉住(对照 §14.3.1): + # (4a) 模块限定函数 `retdep.Identity2(fr, hp)`:**不**含 self ⇒ 形参位 = 实参位。 + # 实参 0 故意用**本帧**指针、实参 1 用**堆**指针;borrows=1 → 结果应为堆 → 可存进 h.p。 + # 若误按方法偏移一格 → 取成实参 0(本帧)→ 结果 FRAME → 本行报错。 + # (4b) 方法 `box.SelfRef(fr)`:**含** self ⇒ 形参位要偏移。接收者用**堆**对象、实参用**本帧**指针; + # borrows=self → 结果应为堆(接收者)→ 可存进 out.v。 + # 若忘了偏移 → 取成实参 0(本帧)→ 结果 FRAME → 本行报错。 + fr: int* = new stack int + hp: int* = new heap hlife int + h.p = retdep.Identity2(fr, hp) + + box: BorrowBox* = new heap hlife BorrowBox() + box.v = new heap hlife int + out: BorrowBox* = new heap hlife BorrowBox() + out.v = box.SelfRef(fr) + if pf is None: return 1 if pl is None: return 2 if pb is None: return 3 + if h.p is None: return 4 + if out.v is None: return 5 stdio.printf("RetOkTest OK: @life(ret(...)) contracts accepted\n") return 0 diff --git a/NewLifetimeRetOkTest/App/retdep.vp b/NewLifetimeRetOkTest/App/retdep.vp index b0cafc6..d179712 100644 --- a/NewLifetimeRetOkTest/App/retdep.vp +++ b/NewLifetimeRetOkTest/App/retdep.vp @@ -44,3 +44,12 @@ def FrameRet() -> int*: @life(noescape(x), ret(borrows=x)) def IdentBorrow(x: int*) -> int*: return x + + +# ret(borrows=<形参 1>):借用**第 2 个**形参(不是第 1 个)。 +# 用途:钉住"模块限定函数调用 `retdep.Identity2(...)` **不**含 self ⇒ 形参位=实参位"这一映射。 +# 调用点故意让实参 0 是**本帧**指针、实参 1 是**堆**指针:映射正确 → 结果=堆(可存长命存储); +# 若把模块限定函数误当方法而偏移一格 → 取成实参 0(本帧)→ 结果 FRAME → 调用点报错 ⇒ 用例失败。 +@life(noescape(a, b), ret(borrows=1)) +def Identity2(a: int*, b: int*) -> int*: + return b diff --git a/README.md b/README.md index 6ec9f4b..cde14c3 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ | NewLifetimeWillFreeAddrTest | [Test/NewLifetimeWillFreeAddrTest/](NewLifetimeWillFreeAddrTest/App/main.vp) | **编译失败**:P2 **取地址**路径 —— `stdlib.free(p)` 之后取 `&p` → `LifetimeError`(与读取同一口径) | | NewLifetimeFieldEscapeTest | [Test/NewLifetimeFieldEscapeTest/](NewLifetimeFieldEscapeTest/App/main.vp) | **编译失败**:P3 —— `h.p = &local`(堆对象字段收下本帧栈地址)→ `LifetimeError` | | NewLifetimeAnnFieldEscapeTest | [Test/NewLifetimeAnnFieldEscapeTest/](NewLifetimeAnnFieldEscapeTest/App/main.vp) | **编译失败**:P3 **注解形态** —— `h.p: int* = &local`(AnnAssign 属性目标,同一条规则;也守住"非 Name 目标不得被静默丢弃")→ `LifetimeError` | -| NewLifetimeRetOkTest | [Test/NewLifetimeRetOkTest/](NewLifetimeRetOkTest/App/main.vp) | **编译成功 + 退出码 0**:A2(§14)`@life(ret(...))` 契约**不得误报** —— `ret(heap)`/`ret(static)`/`ret(borrows=none)` 的结果存进堆对象字段;`ret(frame)`/`ret(borrows=x)` 的结果只在本帧内使用;同模块(契约表)与跨模块(`retdep.vp`,经 `.vpi` 的 `escr=`)两条取用路径都走一遍 | +| NewLifetimeRetOkTest | [Test/NewLifetimeRetOkTest/](NewLifetimeRetOkTest/App/main.vp) | **编译成功 + 退出码 0**:A2(§14)`@life(ret(...))` 契约**不得误报** —— `ret(heap)`/`ret(static)`/`ret(borrows=none)` 的结果存进堆对象字段;`ret(frame)`/`ret(borrows=x)` 的结果只在本帧内使用;同模块(契约表)与跨模块(`retdep.vp`,经 `.vpi` 的 `escr=`)两条取用路径都走一遍。**另含两个"形参位↔实参位映射"的钉桩**(映射偏移错任一方向都会让它编译失败):`retdep.Identity2(fr, hp)`(模块限定函数**不**含 self ⇒ 形参位=实参位)与 `box.SelfRef(fr)`(方法**含** self ⇒ 需偏移) | | NewLifetimeRetEscapeTest | [Test/NewLifetimeRetEscapeTest/](NewLifetimeRetEscapeTest/App/main.vp) | **编译失败**:A2 —— 同模块 `@life(ret(frame))` 的返回值被写进**堆对象字段**(长命左值)→ `LifetimeError`(定义端按声明放行 `ret(frame)`,去向由**调用点**判定) | | NewLifetimeRetXmodTest | [Test/NewLifetimeRetXmodTest/](NewLifetimeRetXmodTest/App/main.vp) | **编译失败**:A2 **跨模块** —— 被调者(`retdep2.vp`)声明 `@life(noescape(x), ret(borrows=x))`,结果(生命期 = 实参 = 本帧)写进堆对象字段 → `LifetimeError`(契约经对方 `.vpi` 的 `escr=` 传来) |