test(lifetime): 新增 P3 负向工程(堆对象字段收下本帧栈地址)
NewLifetimeFieldEscapeTest:h.p = &local(h 由 new heap hlife 得到,字段活得比本帧长)必须报 LifetimeError;同步注册到 SequentialRegressionTest / regression_test / run_lifetime_negative_tests 与 README。
This commit is contained in:
1 parent
399f396694
commit
cd102989a8
6 files changed
+65
-5
No files matched your search
@@ -0,0 +1,28 @@
|
||||
from stdint import *
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 负向工程(**应当编译失败**):P3 —— 禁止把生命期更短的右值赋给生命期更长的左值
|
||||
#
|
||||
# §12-P3 的一般规则:右值生命期比目标左值的存储更短 → 报 LifetimeError。
|
||||
# 本工程是规则点名的实例:`obj.f = &local`
|
||||
# · 左值 h.p 的存储属于**堆对象**(h 由 `new heap hlife` 得到)→ 活得比本帧长;
|
||||
# · 右值 &local 是**本帧栈地址**(LIFE_FRAME)→ 出帧即成悬垂指针。
|
||||
# 期望报错:LifetimeError。
|
||||
#
|
||||
# 对照(必须放行,由 --escape 集里的既有正向工程保证不误报):
|
||||
# · `local_struct.f = &local2`:左值也在本帧(LIFE_FRAME)→ 两者同命,合法;
|
||||
# · `p = &local`:p 是本帧局部指针槽(LIFE_FRAME)→ 合法。
|
||||
# 注意 h 必须在**同一个函数**里 `new`,否则它作为形参进来时 Life=UNKNOWN(保守放行)。
|
||||
# =============================================================================
|
||||
|
||||
|
||||
novirtual class Holder:
|
||||
p: int*
|
||||
|
||||
|
||||
export def main() -> int:
|
||||
h: Holder* = new heap hlife Holder()
|
||||
local: int = 7
|
||||
h.p = &local # ← 必须被拒:把本帧栈地址写进堆对象字段
|
||||
return 0
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/TermiNexus/TransPyC/main/schemas/project-schema.json",
|
||||
"name": "NewLifetimeFieldEscapeTest",
|
||||
"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": "NewLifetimeFieldEscapeTest.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
|
||||
}
|
||||
}
|
||||
@@ -138,6 +138,7 @@
|
||||
| NewLifetimeUseAfterFreeTest | [Test/NewLifetimeUseAfterFreeTest/](NewLifetimeUseAfterFreeTest/App/main.vp) | **编译失败**:P2 —— `free(p)` 之后读/传 `p` → `LifetimeError`(使用已释放变量) |
|
||||
| NewLifetimeWillFreeOkTest | [Test/NewLifetimeWillFreeOkTest/](NewLifetimeWillFreeOkTest/App/main.vp) | **编译成功 + 退出码 0**:P2 合法用法 —— 首次 `free` 本身合法;同名变量**重新赋值**会清除失效态,其后可再用、再 `free`(防误报) |
|
||||
| NewLifetimeWillFreeKwargTest | [Test/NewLifetimeWillFreeKwargTest/](NewLifetimeWillFreeKwargTest/App/main.vp) | **编译失败**:P2 **关键字实参**路径 —— `stdlib.free(p = p)` 之后再用 `p` → `LifetimeError` |
|
||||
| NewLifetimeFieldEscapeTest | [Test/NewLifetimeFieldEscapeTest/](NewLifetimeFieldEscapeTest/App/main.vp) | **编译失败**:P3 —— `h.p = &local`(堆对象字段收下本帧栈地址)→ `LifetimeError` |
|
||||
|
||||
单独运行(`regression_test.py` 与 `SequentialRegressionTest.py` 都支持):
|
||||
|
||||
@@ -162,7 +163,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`:断言**编译失败且输出含 `LifetimeError`**;编译成功 = 漏检(FAIL),报错但方向不符 = SKIP。
|
||||
> 默认跑 `EscapeTest` / `EscapeMethodTest` / `EscapeXmodTest` / `NewLifetimeFreeTest` / `NewLifetimeAsyncTest` / `NewLifetimeWillFreeTest` / `NewLifetimeUseAfterFreeTest` / `NewLifetimeWillFreeKwargTest` / `NewLifetimeFieldEscapeTest`:断言**编译失败且输出含 `LifetimeError`**;编译成功 = 漏检(FAIL),报错但方向不符 = SKIP。
|
||||
|
||||
负向项目在 `SequentialRegressionTest.py` 里是**反向判定**(`NEGATIVE_PROJECTS`):编译失败且输出含 `LifetimeError` 才算通过,且**不再进入运行阶段**;编译成功 = 漏检,直接终止。`regression_test.py --escape` 会**剔除**这些项目(该脚本要求"编译成功 + 能跑"),负向断言请用 `SequentialRegressionTest.py --escape` 或上面的 `run_lifetime_negative_tests.py`。
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ OVERVIEW = os.path.join(TEST_DIR, "README.md")
|
||||
# NewLifetimeFreeTest / NewLifetimeAsyncTest / NewLifetimeWillFreeTest /
|
||||
# NewLifetimeUseAfterFreeTest(负向);
|
||||
# 仓库根下:EscapeTest/EscapeOkTest/EscapeMethodTest/EscapeXmodTest
|
||||
ESCAPE_PROJECTS = ["EscapeTest", "EscapeOkTest", "EscapeMethodTest", "EscapeXmodTest", "NewTest", "NewLifetimeTest", "NewLifetimeWillFreeOkTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest", "NewLifetimeWillFreeTest", "NewLifetimeUseAfterFreeTest", "NewLifetimeWillFreeKwargTest"]
|
||||
ESCAPE_PROJECTS = ["EscapeTest", "EscapeOkTest", "EscapeMethodTest", "EscapeXmodTest", "NewTest", "NewLifetimeTest", "NewLifetimeWillFreeOkTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest", "NewLifetimeWillFreeTest", "NewLifetimeUseAfterFreeTest", "NewLifetimeWillFreeKwargTest", "NewLifetimeFieldEscapeTest"]
|
||||
|
||||
# 负向项目:**期望编译失败**且输出含指定关键字(反向判定,且不运行可执行文件)。
|
||||
# 这些项目里的用例故意交出本帧栈地址(或跨模块摘要路径),必须被 M1–M2.3 的
|
||||
@@ -61,6 +61,8 @@ NEGATIVE_PROJECTS = {
|
||||
"NewLifetimeUseAfterFreeTest": "LifetimeError",
|
||||
# P2:关键字实参路径(stdlib.free(p = p) 之后再用 p)
|
||||
"NewLifetimeWillFreeKwargTest": "LifetimeError",
|
||||
# P3:obj.f = &local(堆对象字段收下本帧栈地址)
|
||||
"NewLifetimeFieldEscapeTest": "LifetimeError",
|
||||
}
|
||||
|
||||
# IR 断言:项目名 → 生成 IR 中**必须出现**的子串。
|
||||
|
||||
+2
-2
@@ -90,12 +90,12 @@ def find_test_projects():
|
||||
# NewLifetimeFreeTest / NewLifetimeAsyncTest / NewLifetimeWillFreeTest /
|
||||
# NewLifetimeUseAfterFreeTest(负向);
|
||||
# 仓库根下:EscapeTest/EscapeOkTest/EscapeMethodTest/EscapeXmodTest
|
||||
ESCAPE_PROJECTS = ["EscapeTest", "EscapeOkTest", "EscapeMethodTest", "EscapeXmodTest", "NewTest", "NewLifetimeTest", "NewLifetimeWillFreeOkTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest", "NewLifetimeWillFreeTest", "NewLifetimeUseAfterFreeTest", "NewLifetimeWillFreeKwargTest"]
|
||||
ESCAPE_PROJECTS = ["EscapeTest", "EscapeOkTest", "EscapeMethodTest", "EscapeXmodTest", "NewTest", "NewLifetimeTest", "NewLifetimeWillFreeOkTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest", "NewLifetimeWillFreeTest", "NewLifetimeUseAfterFreeTest", "NewLifetimeWillFreeKwargTest", "NewLifetimeFieldEscapeTest"]
|
||||
|
||||
# 负向项目(**期望编译失败**,见 SequentialRegressionTest.NEGATIVE_PROJECTS):
|
||||
# 本脚本按"编译必须成功 + 能跑"判定,纳入会误报,故 --escape 时剔除;
|
||||
# 负向断言请用 SequentialRegressionTest(反向判定)或 run_lifetime_negative_tests.py。
|
||||
NEGATIVE_PROJECT_NAMES = {"EscapeTest", "EscapeMethodTest", "EscapeXmodTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest", "NewLifetimeWillFreeTest", "NewLifetimeUseAfterFreeTest", "NewLifetimeWillFreeKwargTest"}
|
||||
NEGATIVE_PROJECT_NAMES = {"EscapeTest", "EscapeMethodTest", "EscapeXmodTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest", "NewLifetimeWillFreeTest", "NewLifetimeUseAfterFreeTest", "NewLifetimeWillFreeKwargTest", "NewLifetimeFieldEscapeTest"}
|
||||
|
||||
|
||||
def find_project_file(proj_dir):
|
||||
|
||||
@@ -34,7 +34,7 @@ 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"]
|
||||
NEGATIVE_PROJECTS = ["EscapeTest", "EscapeMethodTest", "EscapeXmodTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest", "NewLifetimeWillFreeTest", "NewLifetimeUseAfterFreeTest", "NewLifetimeWillFreeKwargTest", "NewLifetimeFieldEscapeTest"]
|
||||
EXPECT_KEYWORD = "LifetimeError"
|
||||
COMPILE_TIMEOUT = 300
|
||||
|
||||
|
||||
Reference in new issue
Block a user