diff --git a/NewLifetimeAsyncTest/App/main.vp b/NewLifetimeAsyncTest/App/main.vp new file mode 100644 index 0000000..e99b28d --- /dev/null +++ b/NewLifetimeAsyncTest/App/main.vp @@ -0,0 +1,27 @@ +from stdint import * + + +# ============================================================================= +# 负向工程(**应当编译失败**,对照工程:NewLifetimeTest) +# +# 协程体(async)里的 `new heap slife` 必须被拒:AsyncGenMixin 的状态机会把 body 拆成多个 +# state 块并多次转译,编译器无法确定释放点 → CallGenCtor._translate_new_expr 在此 fail-fast +# (LifetimeError,提示改用 `new heap hlife` 或把分配挪进普通函数)。 +# +# 说明见 Debugger/ownership-and-new.md §4.2 的「async 的收口」。 +# `new stack`(帧内 alloca,本来就无需释放)与 `new … hlife`(自行管理)不受这条约束影响。 +# ============================================================================= + + +novirtual class Point: + x: int + y: int + + +async def Bad() -> int: + p: Point* = new heap slife Point(1, 2) # ← 必须被拒 + return p.x + + +export def main() -> int: + return 0 diff --git a/NewLifetimeAsyncTest/project.json b/NewLifetimeAsyncTest/project.json new file mode 100644 index 0000000..6ef4137 --- /dev/null +++ b/NewLifetimeAsyncTest/project.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://raw.githubusercontent.com/TermiNexus/TransPyC/main/schemas/project-schema.json", + "name": "NewLifetimeAsyncTest", + "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": "NewLifetimeAsyncTest.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 + } +} diff --git a/NewLifetimeFreeTest/App/main.vp b/NewLifetimeFreeTest/App/main.vp index 644d125..d1cb77b 100644 --- a/NewLifetimeFreeTest/App/main.vp +++ b/NewLifetimeFreeTest/App/main.vp @@ -1,7 +1,5 @@ from stdint import * import w32.win32console -import t, c -from t import CInt, CExport import stdlib @@ -19,7 +17,7 @@ novirtual class Point: y: int -def main() -> CInt | CExport: +export def main() -> int: w32.win32console.SetConsoleCP(65001) w32.win32console.SetConsoleOutputCP(65001) diff --git a/NewLifetimeTest/App/main.vp b/NewLifetimeTest/App/main.vp index 2768f8a..3c6757e 100644 --- a/NewLifetimeTest/App/main.vp +++ b/NewLifetimeTest/App/main.vp @@ -1,7 +1,5 @@ from stdint import * import w32.win32console -import t, c -from t import CInt, CExport import stdio @@ -136,7 +134,7 @@ def CaseDtorOverwrite() -> int: return 0 -def main() -> CInt | CExport: +export def main() -> int: w32.win32console.SetConsoleCP(65001) w32.win32console.SetConsoleOutputCP(65001) diff --git a/README.md b/README.md index 9fa859e..b2876d0 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,7 @@ | 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` | | 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` 不受限) | 单独运行(`regression_test.py` 与 `SequentialRegressionTest.py` 都支持): @@ -157,7 +158,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`:断言**编译失败且输出含 `LifetimeError`**;编译成功 = 漏检(FAIL),报错但方向不符 = SKIP。 +> 默认跑 `EscapeTest` / `EscapeMethodTest` / `EscapeXmodTest` / `NewLifetimeFreeTest` / `NewLifetimeAsyncTest`:断言**编译失败且输出含 `LifetimeError`**;编译成功 = 漏检(FAIL),报错但方向不符 = SKIP。 负向项目在 `SequentialRegressionTest.py` 里是**反向判定**(`NEGATIVE_PROJECTS`):编译失败且输出含 `LifetimeError` 才算通过,且**不再进入运行阶段**;编译成功 = 漏检,直接终止。`regression_test.py --escape` 会**剔除**这些项目(该脚本要求"编译成功 + 能跑"),负向断言请用 `SequentialRegressionTest.py --escape` 或上面的 `run_lifetime_negative_tests.py`。 diff --git a/SequentialRegressionTest.py b/SequentialRegressionTest.py index f3dcfcf..201c660 100644 --- a/SequentialRegressionTest.py +++ b/SequentialRegressionTest.py @@ -36,10 +36,10 @@ OVERVIEW = os.path.join(TEST_DIR, "README.md") # ============================================================ # 单独运行支持:--escape / --only # ============================================================ -# escape 相关项目(生命期/逃逸检查 + M2.4 new 描述符 + M3 owned slife RAII): -# Test/ 下:NewTest、NewLifetimeTest(正向)、NewLifetimeFreeTest(负向); +# escape 相关项目(生命期/逃逸检查 + M2.4 new 描述符 + M3 owned slife RAII + §3.6 析构): +# Test/ 下:NewTest、NewLifetimeTest(正向)、NewLifetimeFreeTest / NewLifetimeAsyncTest(负向); # 仓库根下:EscapeTest/EscapeOkTest/EscapeMethodTest/EscapeXmodTest -ESCAPE_PROJECTS = ["EscapeTest", "EscapeOkTest", "EscapeMethodTest", "EscapeXmodTest", "NewTest", "NewLifetimeTest", "NewLifetimeFreeTest"] +ESCAPE_PROJECTS = ["EscapeTest", "EscapeOkTest", "EscapeMethodTest", "EscapeXmodTest", "NewTest", "NewLifetimeTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest"] # 负向项目:**期望编译失败**且输出含指定关键字(反向判定,且不运行可执行文件)。 # 这些项目里的用例故意交出本帧栈地址(或跨模块摘要路径),必须被 M1–M2.3 的 @@ -52,6 +52,8 @@ NEGATIVE_PROJECTS = { "EscapeXmodTest": "LifetimeError", # M3 §4.3 判据5:对 slife 对象手动 free → LifetimeError "NewLifetimeFreeTest": "LifetimeError", + # M3 §4.2:协程体(async)里的 `new heap slife` → 释放点无法确定,fail-fast + "NewLifetimeAsyncTest": "LifetimeError", } # IR 断言:项目名 → 生成 IR 中**必须出现**的子串。 diff --git a/regression_test.py b/regression_test.py index 5dff832..08522d6 100644 --- a/regression_test.py +++ b/regression_test.py @@ -85,15 +85,15 @@ def find_test_projects(): # ============================================================ # 单独运行支持:--escape / --only # ============================================================ -# escape 相关项目(生命期/逃逸检查 + M2.4 new 描述符 + M3 owned slife RAII): -# Test/ 下:NewTest、NewLifetimeTest(正向)、NewLifetimeFreeTest(负向); +# escape 相关项目(生命期/逃逸检查 + M2.4 new 描述符 + M3 owned slife RAII + §3.6 析构): +# Test/ 下:NewTest、NewLifetimeTest(正向)、NewLifetimeFreeTest / NewLifetimeAsyncTest(负向); # 仓库根下:EscapeTest/EscapeOkTest/EscapeMethodTest/EscapeXmodTest -ESCAPE_PROJECTS = ["EscapeTest", "EscapeOkTest", "EscapeMethodTest", "EscapeXmodTest", "NewTest", "NewLifetimeTest", "NewLifetimeFreeTest"] +ESCAPE_PROJECTS = ["EscapeTest", "EscapeOkTest", "EscapeMethodTest", "EscapeXmodTest", "NewTest", "NewLifetimeTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest"] # 负向项目(**期望编译失败**,见 SequentialRegressionTest.NEGATIVE_PROJECTS): # 本脚本按"编译必须成功 + 能跑"判定,纳入会误报,故 --escape 时剔除; # 负向断言请用 SequentialRegressionTest(反向判定)或 run_lifetime_negative_tests.py。 -NEGATIVE_PROJECT_NAMES = {"EscapeTest", "EscapeMethodTest", "EscapeXmodTest", "NewLifetimeFreeTest"} +NEGATIVE_PROJECT_NAMES = {"EscapeTest", "EscapeMethodTest", "EscapeXmodTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest"} def find_project_file(proj_dir): diff --git a/run_lifetime_negative_tests.py b/run_lifetime_negative_tests.py index b05c11e..2e4a81e 100644 --- a/run_lifetime_negative_tests.py +++ b/run_lifetime_negative_tests.py @@ -31,7 +31,8 @@ PROJECT_ROOT = os.path.dirname(TEST_DIR) # 注意:EscapeMethodTest / EscapeXmodTest 内部有多条互斥用例(编译在第一个错误处终止), # 每个项目只保证"至少触发一条 LifetimeError"。 # NewLifetimeFreeTest:M3 §4.3 判据5(对 slife 对象手动 free)。 -NEGATIVE_PROJECTS = ["EscapeTest", "EscapeMethodTest", "EscapeXmodTest", "NewLifetimeFreeTest"] +# NewLifetimeAsyncTest:M3 §4.2(协程体里的 `new heap slife` → 释放点无法确定,fail-fast)。 +NEGATIVE_PROJECTS = ["EscapeTest", "EscapeMethodTest", "EscapeXmodTest", "NewLifetimeFreeTest", "NewLifetimeAsyncTest"] EXPECT_KEYWORD = "LifetimeError" COMPILE_TIMEOUT = 300