test(lifetime): 补齐 A2 @life(ret(...)) 的正/负向回归工程

新增 3 个工程(均登记进 regression_test.py / SequentialRegressionTest.py /
run_lifetime_negative_tests.py 的 escape/负向清单):
- NewLifetimeRetOkTest(正向):ret(heap)/ret(static)/ret(borrows=none) 的结果
  可存进堆对象字段;ret(frame)/ret(borrows=x) 的结果只在本帧内使用;
  同模块契约表与跨模块 .vpi(escr=) 两条取用路径都覆盖 → 编译通过 + 退出码 0。
- NewLifetimeRetEscapeTest(负向):同模块 @life(ret(frame)) 的返回值写进堆对象
  字段 → LifetimeError(隔离性:FrameRet 摘要为 0,唯一报错源是 ret 契约)。
- NewLifetimeRetXmodTest(负向):跨模块 @life(noescape(x), ret(borrows=x)) 的结果
  写进堆对象字段 → LifetimeError(契约经对方 .vpi 的 escr= 传来;noescape 清掉了
  实参逃逸路径,判据收敛到 ret 契约)。
README.md 的 escape 测试表与负向清单同步。
This commit is contained in:
TermiNexus committed 2026-10-10 09:03:05 +08:00
1 parent 9307b34af6
commit 66ebd75dec
12 files changed
+344 -12

No files matched your search

+56
View File
@@ -0,0 +1,56 @@
from stdint import *
# =============================================================================
# 负向工程(**应当编译失败**):A2 `@life(ret(frame))` —— 去向由**调用点**判定
#
# A2 移除了定义端对 `return <本帧地址>` 的 fail-fast:定义端无法区分"交出本帧"
# 与"借出调用方的存储"(后者是 `return <借用形参>`,一律拦会误报)。取而代之:
# 被调者用 `@life(ret(...))` 声明返回值生命期,**调用点**按声明判去向。
#
# 本工程验:声明为 `frame` 的返回值被写进**长命存储**时必须被拒。
# 期望报错:LifetimeError(`a longer-lived field/element`)。
#
# 隔离性:FrameRet 的逃逸摘要为 0(`l` 不是形参,`return l` 不置任何逃逸位),
# 所以下面唯一的报错来源就是 `ret(frame)` → LIFE_FRAME。
#
# 本文件只保留**一条**会报错的语句:编译在第一个错误处终止,多条会掩盖顺序。
# 对照写法(`ret(borrows=x)` 的结果同样只在本帧)留作注释,见文末。
# =============================================================================
novirtual class Holder:
p: int*
@life(ret(frame))
def FrameRet() -> int*:
l: int* = new stack int
return l
# 唯一用例:ret(frame) 的结果存进**堆对象**字段 → 左值长命、右值本帧 → LifetimeError
def CaseFrame() -> int:
h: Holder* = new heap hlife Holder()
h.p = FrameRet() # ❌ 期望 LifetimeError
return 0
export def main() -> int:
CaseFrame()
return 0
# -----------------------------------------------------------------------------
# 对照用例(把 main 里的 CaseFrame() 换成本函数即可,一次只开一条):
#
# @life(noescape(x), ret(borrows=x))
# def IdentBorrow(x: int*) -> int*:
# return x
#
# def CaseBorrow() -> int:
# h: Holder* = new heap hlife Holder()
# l: int* = new stack int
# h.p = IdentBorrow(l) # ❌ 期望 LifetimeError(结果生命期 = l 的 = 本帧)
# return 0
# -----------------------------------------------------------------------------
+29
View File
@@ -0,0 +1,29 @@
{
"$schema": "https://raw.githubusercontent.com/TermiNexus/TransPyC/main/schemas/project-schema.json",
"name": "NewLifetimeRetEscapeTest",
"version": "1.0.0",
"source_dir": "./App",
"build_dir": "./.tpv_build",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-relocation-model=pic"]
},
"linker": {
"cmd": "clang++",
"flags": ["-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32"],
"output": "NewLifetimeRetEscapeTest.exe"
},
"includes": [
"../../includes"
],
"target": {
"triple": "x86_64-pc-windows-gnu",
"datalayout": "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}
}
+64
View File
@@ -0,0 +1,64 @@
from stdint import *
import stdio
import retdep
# =============================================================================
# 正向工程:A2 `@life(ret(...))` 契约**不得误报**(编译必须成功、运行退出码 0)
#
# 对照负向工程:
# · NewLifetimeRetEscapeTest —— 同模块 `ret(frame)` 的结果存进长命存储 → 必须报;
# · NewLifetimeRetXmodTest —— 跨模块 `ret(borrows=x)`(经 .vpi 的 escr=)→ 必须报。
#
# 本工程覆盖:
# (1) 非帧契约(heap / static / borrows=none)→ 结果可存进**堆对象字段**(长命左值);
# (2) ret(frame) → 结果只在本帧内使用 → 合法(不是"无害",而是调用点按声明**放行**);
# (3) ret(borrows=x) → 结果的生命期 = 实参 x 的生命期(本帧实参 → 帧内使用合法);
# (4) 同模块(本文件契约表)与跨模块(retdep.vp,经 .vpi 的 escr=)两条取用路径都走一遍。
#
# 注意:本文件**不得**把 `ret(frame)` / `ret(borrows=x)` 的结果写进堆对象字段
# (`h.p = …`)——那是负向工程负责证明会被拒的写法。
# =============================================================================
novirtual class Holder:
p: int*
c: char*
# ---- 同模块契约(走本文件 EscDirTable,不经 .vpi)----
@life(ret(heap))
def LocalHeap() -> int*:
return new heap hlife int
@life(ret(frame))
def LocalFrame() -> int*:
l: int* = new stack int
return l
export def main() -> int:
h: Holder* = new heap hlife Holder()
# (1) 非帧契约 → 存进堆对象字段(左值长命、右值非帧)→ 放行
h.p = retdep.MkHeap() # 跨模块 ret(heap)
h.c = retdep.MkStatic() # 跨模块 ret(static)
h.p = retdep.Fresh() # 跨模块 ret(borrows=none)
h.p = LocalHeap() # 同模块 ret(heap)
# (2) ret(frame):结果只在本帧内使用 → 放行
pf: int* = retdep.FrameRet() # 跨模块 ret(frame)
pl: int* = LocalFrame() # 同模块 ret(frame)
# (3) ret(borrows=x):结果生命期 = 实参生命期;实参在本帧 → 结果只在本帧用 → 放行
base: int* = new stack int
pb: int* = retdep.IdentBorrow(base)
if pf is None: return 1
if pl is None: return 2
if pb is None: return 3
stdio.printf("RetOkTest OK: @life(ret(...)) contracts accepted\n")
return 0
+45
View File
@@ -0,0 +1,45 @@
from stdint import *
# =============================================================================
# A2(§14)`@life(ret(...))` 的被调方:五种返回值生命期契约各一条。
#
# 调用点(main.vp)**跨模块**取用 → 本文件的契约只能经 .vpi 行尾
# `# esc=… escn=… escr=…` 传递(本地契约表按文件重置,调用点必然查不到)。
# 设计与编码见 Debugger/ownership-and-new.md §14.3 / §14.3.1(GenApi.RET_KIND_*)。
# =============================================================================
# ret(heap):自由生命期 → 结果可自由存进长命存储
@life(ret(heap))
def MkHeap() -> int*:
return new heap hlife int
# ret(static):指向静态存储(字符串常量)
@life(ret(static))
def MkStatic() -> char*:
return "static-lit"
# ret(borrows=none):返回值**不借用任何形参** → 非本帧
@life(ret(borrows=none))
def Fresh() -> int*:
return new heap hlife int
# ret(frame):返回值可能指向**本帧**存储 → 去向由调用点负责
# (A2 已移除定义端对 `return <本帧地址>` 的 fail-fast:定义端无法区分
# "交出本帧"与"借出调用方的存储",一律拦会误报 `return <借用形参>`。)
@life(ret(frame))
def FrameRet() -> int*:
l: int* = new stack int
return l
# ret(borrows=x):返回值借用形参 x —— 调用点把结果当 x 的生命期。
# 配 `noescape(x)`:本函数只**借出** x、不把它存进长命存储,故形参 x 不算逃逸;
# 否则调用点会先在"实参逃逸"那条路径上拦下来,掩盖 ret 契约本身。
@life(noescape(x), ret(borrows=x))
def IdentBorrow(x: int*) -> int*:
return x
+29
View File
@@ -0,0 +1,29 @@
{
"$schema": "https://raw.githubusercontent.com/TermiNexus/TransPyC/main/schemas/project-schema.json",
"name": "NewLifetimeRetOkTest",
"version": "1.0.0",
"source_dir": "./App",
"build_dir": "./.tpv_build",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-relocation-model=pic"]
},
"linker": {
"cmd": "clang++",
"flags": ["-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32"],
"output": "NewLifetimeRetOkTest.exe"
},
"includes": [
"../../includes"
],
"target": {
"triple": "x86_64-pc-windows-gnu",
"datalayout": "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}
}
+43
View File
@@ -0,0 +1,43 @@
from stdint import *
import retdep2
# =============================================================================
# 负向工程(**应当编译失败**):A2 **跨模块** `@life(ret(...))` —— 契约经 .vpi 的 `escr=`
#
# 被调者定义在 retdep2.vp,契约(含 ret_kind)只能经对方 .vpi 行尾的 `escr=` 传来;
# 本文件里它的本地契约表必然为空 → 命中跨模块回退路径。
#
# 本工程验:跨模块声明为 `borrows=x` 的返回值(生命期 = 实参 = 本帧)被写进长命存储
# 时必须被拒。期望报错:LifetimeError(`a longer-lived field/element`)。
#
# 隔离性:IdentBorrow 带 `@life(noescape(x))`,"实参逃逸"那条路径被清掉,
# 故唯一的报错来源是 .vpi 的 `escr=` → ret(borrows=x) → 结果 LIFE_FRAME。
# =============================================================================
novirtual class Holder:
p: int*
# 唯一用例:跨模块 ret(borrows=x) 的结果存进**堆对象**字段 → 左值长命、右值本帧
def CaseBorrow() -> int:
h: Holder* = new heap hlife Holder()
l: int* = new stack int
h.p = retdep2.IdentBorrow(l) # ❌ 期望 LifetimeError
return 0
export def main() -> int:
CaseBorrow()
return 0
# -----------------------------------------------------------------------------
# 对照用例(把 main 里的 CaseBorrow() 换成本函数即可,一次只开一条):
#
# def CaseFrame() -> int:
# h: Holder* = new heap hlife Holder()
# h.p = retdep2.FrameRet() # ❌ 期望 LifetimeError
# return 0
# -----------------------------------------------------------------------------
+24
View File
@@ -0,0 +1,24 @@
from stdint import *
# =============================================================================
# A2 跨模块负例的**被调方**:契约(逃逸掩码 + 返回值生命期)只能经本模块 .vpi
# 行尾 `# esc=… escn=… escr=…` 传到调用点 —— 本文件与 main.vp 不同模块,
# 调用点的本地契约表按文件重置、必然查不到,命中
# CallGenBase.lookup_ret_kind_via_vpi → VpiReader 的 escr= 缓存这条回退路径。
# =============================================================================
# ret(borrows=x):返回值借用形参 x —— 调用点把结果当 x 的生命期。
# 配 `noescape(x)`:本函数只**借出** x、不把它存进长命存储 → 形参 x 不算逃逸,
# 于是调用点上"实参逃逸"那条路径不会先报错,判据收敛到 ret 契约本身。
@life(noescape(x), ret(borrows=x))
def IdentBorrow(x: int*) -> int*:
return x
# 对照:`ret(frame)`(把 main 的用例换成 CaseFrame 时用)。返回值指向本帧存储。
@life(ret(frame))
def FrameRet() -> int*:
l: int* = new stack int
return l
+29
View File
@@ -0,0 +1,29 @@
{
"$schema": "https://raw.githubusercontent.com/TermiNexus/TransPyC/main/schemas/project-schema.json",
"name": "NewLifetimeRetXmodTest",
"version": "1.0.0",
"source_dir": "./App",
"build_dir": "./.tpv_build",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-relocation-model=pic"]
},
"linker": {
"cmd": "clang++",
"flags": ["-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32"],
"output": "NewLifetimeRetXmodTest.exe"
},
"includes": [
"../../includes"
],
"target": {
"triple": "x86_64-pc-windows-gnu",
"datalayout": "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}
}
+7 -2
View File
@@ -122,7 +122,9 @@
## 一·附、生命期 / 逃逸(escape)相关测试
验证 M1–M3 的生命期检查(栈地址逃逸、借用传播、函数逃逸摘要)、`new` 描述符分配,
以及 `new heap slife` 的 RAII 自动释放(M3)。因历史原因前 4 个位于**仓库根**(不在 `Test/` 下):
`new heap slife` 的 RAII 自动释放(M3),以及 `@life(...)` 标注面(P2 `willFree`、
§14 `escape/stores`、`noescape/borrows`、A2 `ret(...)`:返回值生命期契约,声明优先于推断)。
因历史原因前 4 个位于**仓库根**(不在 `Test/` 下):
| 项目 | 位置 | 期望结果 |
|---|---|---|
@@ -142,6 +144,9 @@
| 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=`)两条取用路径都走一遍 |
| NewLifetimeRetEscapeTest | [Test/NewLifetimeRetEscapeTest/](NewLifetimeRetEscapeTest/App/main.vp) | **编译失败**:A2 —— 同模块 `@life(ret(frame))` 的返回值被写进**堆对象字段**(长命左值)→ `LifetimeError`(A2 已移除定义端 `return <本帧地址>` 的 fail-fast,去向改由调用点按契约判定) |
| NewLifetimeRetXmodTest | [Test/NewLifetimeRetXmodTest/](NewLifetimeRetXmodTest/App/main.vp) | **编译失败**:A2 **跨模块** —— 被调者(`retdep2.vp`)声明 `@life(noescape(x), ret(borrows=x))`,结果(生命期 = 实参 = 本帧)写进堆对象字段 → `LifetimeError`(契约经对方 `.vpi` 的 `escr=` 传来) |
单独运行(`regression_test.py` 与 `SequentialRegressionTest.py` 都支持):
@@ -166,7 +171,7 @@ python Test/run_lifetime_negative_tests.py --exe .\TransPyV\output\TransPyV.exe
python Test/run_lifetime_negative_tests.py --exe .\TransPyV\output\TransPyV.exe --only EscapeMethodTest
```
> 默认跑 `EscapeTest` / `EscapeMethodTest` / `EscapeXmodTest` / `NewLifetimeFreeTest` / `NewLifetimeAsyncTest` / `NewLifetimeWillFreeTest` / `NewLifetimeUseAfterFreeTest` / `NewLifetimeWillFreeKwargTest` / `NewLifetimeWillFreeMethodTest` / `NewLifetimeWillFreeAddrTest` / `NewLifetimeFieldEscapeTest` / `NewLifetimeAnnFieldEscapeTest`:断言**编译失败且输出含 `LifetimeError`**;编译成功 = 漏检(FAIL),报错但方向不符 = SKIP。
> 默认跑 `EscapeTest` / `EscapeMethodTest` / `EscapeXmodTest` / `NewLifetimeFreeTest` / `NewLifetimeAsyncTest` / `NewLifetimeWillFreeTest` / `NewLifetimeUseAfterFreeTest` / `NewLifetimeWillFreeKwargTest` / `NewLifetimeWillFreeMethodTest` / `NewLifetimeWillFreeAddrTest` / `NewLifetimeFieldEscapeTest` / `NewLifetimeAnnFieldEscapeTest` / `NewLifetimeRetEscapeTest` / `NewLifetimeRetXmodTest`:断言**编译失败且输出含 `LifetimeError`**;编译成功 = 漏检(FAIL),报错但方向不符 = SKIP。
负向项目在 `SequentialRegressionTest.py` 里是**反向判定**(`NEGATIVE_PROJECTS`):编译失败且输出含 `LifetimeError` 才算通过,且**不再进入运行阶段**;编译成功 = 漏检,直接终止。`regression_test.py --escape` 会**剔除**这些项目(该脚本要求"编译成功 + 能跑"),负向断言请用 `SequentialRegressionTest.py --escape` 或上面的 `run_lifetime_negative_tests.py`。
+9 -4
View File
@@ -36,12 +36,13 @@ OVERVIEW = os.path.join(TEST_DIR, "README.md")
# ============================================================
# 单独运行支持:--escape / --only
# ============================================================
# escape 相关项目(生命期/逃逸检查 + M2.4 new 描述符 + M3 owned slife RAII + §3.6 析构 + P2 willFree):
# Test/ 下:NewTest、NewLifetimeTest、NewLifetimeWillFreeOkTest(正向)、
# escape 相关项目(生命期/逃逸检查 + M2.4 new 描述符 + M3 owned slife RAII + §3.6 析构 +
# P2 willFree + A2 @life(ret(...))):
# Test/ 下:NewTest、NewLifetimeTest、NewLifetimeWillFreeOkTest、NewLifetimeRetOkTest(正向)、
# NewLifetimeFreeTest / NewLifetimeAsyncTest / NewLifetimeWillFreeTest /
# NewLifetimeUseAfterFreeTest(负向);
# NewLifetimeUseAfterFreeTest / NewLifetimeRetEscapeTest / NewLifetimeRetXmodTest(负向);
# 仓库根下:EscapeTest/EscapeOkTest/EscapeMethodTest/EscapeXmodTest
ESCAPE_PROJECTS = ["EscapeTest", "EscapeOkTest", "EscapeMethodTest", "EscapeXmodTest", "NewTest", "NewLifetimeTest", "NewLifetimeWillFreeOkTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest", "NewLifetimeWillFreeTest", "NewLifetimeUseAfterFreeTest", "NewLifetimeWillFreeKwargTest", "NewLifetimeWillFreeMethodTest", "NewLifetimeWillFreeAddrTest", "NewLifetimeFieldEscapeTest", "NewLifetimeAnnFieldEscapeTest"]
ESCAPE_PROJECTS = ["EscapeTest", "EscapeOkTest", "EscapeMethodTest", "EscapeXmodTest", "NewTest", "NewLifetimeTest", "NewLifetimeWillFreeOkTest", "NewLifetimeRetOkTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest", "NewLifetimeWillFreeTest", "NewLifetimeUseAfterFreeTest", "NewLifetimeWillFreeKwargTest", "NewLifetimeWillFreeMethodTest", "NewLifetimeWillFreeAddrTest", "NewLifetimeFieldEscapeTest", "NewLifetimeAnnFieldEscapeTest", "NewLifetimeRetEscapeTest", "NewLifetimeRetXmodTest"]
# 负向项目:**期望编译失败**且输出含指定关键字(反向判定,且不运行可执行文件)。
# 这些项目里的用例故意交出本帧栈地址(或跨模块摘要路径),必须被 M1–M2.3 的
@@ -69,6 +70,10 @@ NEGATIVE_PROJECTS = {
"NewLifetimeFieldEscapeTest": "LifetimeError",
# P3:obj.f: T = &local(注解形态的属性目标,同一条规则;防"非 Name 目标被静默丢弃")
"NewLifetimeAnnFieldEscapeTest": "LifetimeError",
# A2(§14):@life(ret(frame)) 的结果存进堆对象字段(同模块契约表 EscDirTable)
"NewLifetimeRetEscapeTest": "LifetimeError",
# A2(§14):@life(ret(borrows=x)) 的结果存进堆对象字段(跨模块,经 .vpi 的 escr=)
"NewLifetimeRetXmodTest": "LifetimeError",
}
# IR 断言:项目名 → 生成 IR 中**必须出现**的子串。
+6 -5
View File
@@ -85,17 +85,18 @@ def find_test_projects():
# ============================================================
# 单独运行支持:--escape / --only
# ============================================================
# escape 相关项目(生命期/逃逸检查 + M2.4 new 描述符 + M3 owned slife RAII + §3.6 析构 + P2 willFree):
# Test/ 下:NewTest、NewLifetimeTest、NewLifetimeWillFreeOkTest(正向)、
# escape 相关项目(生命期/逃逸检查 + M2.4 new 描述符 + M3 owned slife RAII + §3.6 析构 +
# P2 willFree + A2 @life(ret(...))):
# Test/ 下:NewTest、NewLifetimeTest、NewLifetimeWillFreeOkTest、NewLifetimeRetOkTest(正向)、
# NewLifetimeFreeTest / NewLifetimeAsyncTest / NewLifetimeWillFreeTest /
# NewLifetimeUseAfterFreeTest(负向);
# NewLifetimeUseAfterFreeTest / NewLifetimeRetEscapeTest / NewLifetimeRetXmodTest(负向);
# 仓库根下:EscapeTest/EscapeOkTest/EscapeMethodTest/EscapeXmodTest
ESCAPE_PROJECTS = ["EscapeTest", "EscapeOkTest", "EscapeMethodTest", "EscapeXmodTest", "NewTest", "NewLifetimeTest", "NewLifetimeWillFreeOkTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest", "NewLifetimeWillFreeTest", "NewLifetimeUseAfterFreeTest", "NewLifetimeWillFreeKwargTest", "NewLifetimeWillFreeMethodTest", "NewLifetimeWillFreeAddrTest", "NewLifetimeFieldEscapeTest", "NewLifetimeAnnFieldEscapeTest"]
ESCAPE_PROJECTS = ["EscapeTest", "EscapeOkTest", "EscapeMethodTest", "EscapeXmodTest", "NewTest", "NewLifetimeTest", "NewLifetimeWillFreeOkTest", "NewLifetimeRetOkTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest", "NewLifetimeWillFreeTest", "NewLifetimeUseAfterFreeTest", "NewLifetimeWillFreeKwargTest", "NewLifetimeWillFreeMethodTest", "NewLifetimeWillFreeAddrTest", "NewLifetimeFieldEscapeTest", "NewLifetimeAnnFieldEscapeTest", "NewLifetimeRetEscapeTest", "NewLifetimeRetXmodTest"]
# 负向项目(**期望编译失败**,见 SequentialRegressionTest.NEGATIVE_PROJECTS):
# 本脚本按"编译必须成功 + 能跑"判定,纳入会误报,故 --escape 时剔除;
# 负向断言请用 SequentialRegressionTest(反向判定)或 run_lifetime_negative_tests.py。
NEGATIVE_PROJECT_NAMES = {"EscapeTest", "EscapeMethodTest", "EscapeXmodTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest", "NewLifetimeWillFreeTest", "NewLifetimeUseAfterFreeTest", "NewLifetimeWillFreeKwargTest", "NewLifetimeWillFreeMethodTest", "NewLifetimeWillFreeAddrTest", "NewLifetimeFieldEscapeTest", "NewLifetimeAnnFieldEscapeTest"}
NEGATIVE_PROJECT_NAMES = {"EscapeTest", "EscapeMethodTest", "EscapeXmodTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest", "NewLifetimeWillFreeTest", "NewLifetimeUseAfterFreeTest", "NewLifetimeWillFreeKwargTest", "NewLifetimeWillFreeMethodTest", "NewLifetimeWillFreeAddrTest", "NewLifetimeFieldEscapeTest", "NewLifetimeAnnFieldEscapeTest", "NewLifetimeRetEscapeTest", "NewLifetimeRetXmodTest"}
def find_project_file(proj_dir):
+3 -1
View File
@@ -34,7 +34,9 @@ PROJECT_ROOT = os.path.dirname(TEST_DIR)
# NewLifetimeAsyncTest:M3 §4.2(协程体里的 `new heap slife` → 释放点无法确定,fail-fast)。
# NewLifetimeWillFreeTest / NewLifetimeUseAfterFreeTest:P2(@life(willFree(p)) 标注驱动——
# 二次释放 / 释放后使用,对应正向工程 NewLifetimeWillFreeOkTest)。
NEGATIVE_PROJECTS = ["EscapeTest", "EscapeMethodTest", "EscapeXmodTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest", "NewLifetimeWillFreeTest", "NewLifetimeUseAfterFreeTest", "NewLifetimeWillFreeKwargTest", "NewLifetimeWillFreeMethodTest", "NewLifetimeWillFreeAddrTest", "NewLifetimeFieldEscapeTest", "NewLifetimeAnnFieldEscapeTest"]
# NewLifetimeRetEscapeTest / NewLifetimeRetXmodTest:A2(§14)`@life(ret(...))`——
# 声明为 frame / borrows=x 的返回值被写进长命存储(同模块契约表 / 跨模块 .vpi 的 escr=)。
NEGATIVE_PROJECTS = ["EscapeTest", "EscapeMethodTest", "EscapeXmodTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest", "NewLifetimeWillFreeTest", "NewLifetimeUseAfterFreeTest", "NewLifetimeWillFreeKwargTest", "NewLifetimeWillFreeMethodTest", "NewLifetimeWillFreeAddrTest", "NewLifetimeFieldEscapeTest", "NewLifetimeAnnFieldEscapeTest", "NewLifetimeRetEscapeTest", "NewLifetimeRetXmodTest"]
EXPECT_KEYWORD = "LifetimeError"
COMPILE_TIMEOUT = 300