test(lifetime): A2 补“形参位↔实参位映射”双向钉桩(受控用例)
背景:NewLifetimeRetXmodTest 曾漏检 —— ret(borrows=x) 的 pi→实参映射把模块限定函数 `m.f(...)` 误当方法(给 pi=0 加 self 偏移),取到模块名而非实参。 为防同类回归,在正向工程 NewLifetimeRetOkTest 里加两个“错就编译失败”的钉桩: - (4a) 模块限定函数:retdep.Identity2(fr, hp)(borrows=1)。 实参 0 故意是**本帧**指针、实参 1 是**堆**指针 ⇒ 正确映射取实参 1=堆(可存进堆字段); 若误加 self 偏移 → 取成实参 0(本帧)→ 结果 FRAME → 该行报错。 - (4b) 方法:box.SelfRef(fr)(borrows=self)。 接收者是**堆**对象、用户实参是**本帧**指针 ⇒ 正确映射取接收者=堆(可存进堆字段); 若忘了 self 偏移 → 取成实参 0(本帧)→ 结果 FRAME → 该行报错。 即两个方向任一偏移错都会让本正向工程编译失败(正向工程的“错就报错”比再堆一个负向 工程更能锁死偏移量)。README 的 NewLifetimeRetOkTest 行同步说明。
This commit is contained in:
1 parent
e8a61473b2
commit
b737bedceb
3 files changed
+40
-1
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
@@ -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=` 传来) |
|
||||
|
||||
|
||||
Reference in new issue
Block a user