1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

[Tolk] throw interrupts control flow; never type

In FunC (and in Tolk before) throwing an exception is just
calling a built-in function:
> throw 123; // actually, __throw(123)
Since it's a regular function, the compiler was not aware
that execution will stop, and all following code is unreachable.
For instance, `throw` in the end on function needed to be
followed by `return` statement.

Now, `throw` interrupts control flow, all statements after
it are considered unreachable. At IR level, code Ops are
also not produced.

This works because a built-in __throw() now has `never` type.
It can also be applied to custom functions:
> fun alwaysThrow(): never { throw 123; }
The code after alwaysThrow() call will also be unreachable.
This commit is contained in:
tolk-vm 2025-02-24 20:15:24 +03:00
parent 7bcb8b895f
commit ef0328837f
No known key found for this signature in database
GPG key ID: 7905DD7FE0324B12
10 changed files with 227 additions and 25 deletions

View file

@ -86,6 +86,17 @@ fun test7() {
// __expect_type(eq<(int, slice)>, "(int, slice) -> (int, slice)");
}
fun alwaysThrows(): never { throw 123; }
fun alwaysThrowsNotAnnotated() { throw 123; }
fun alwaysThrowsNotAnnotated2() { alwaysThrows(); }
fun test9() {
__expect_type(alwaysThrows(), "never");
__expect_type(alwaysThrows, "() -> never");
__expect_type(alwaysThrowsNotAnnotated(), "void");
__expect_type(alwaysThrowsNotAnnotated2(), "void");
}
fun main() {
return 0;

View file

@ -0,0 +1,8 @@
fun invalidNever(): never {
if (random()) { throw 123; }
}
/**
@compilation_should_fail
@stderr a function returning `never` can not have a reachable endpoint
*/

View file

@ -164,6 +164,78 @@ fun test109(): (int, int) {
return (g_reg, l_reg);
}
fun alwaysThrow123(): never {
throw 123;
}
fun alwaysThrowX(x: int): never {
if (x > 10) { throw (x, beginCell()); }
else { throw (x, null); }
}
fun anotherNever(throw123: bool): never {
if (throw123) { alwaysThrow123(); }
alwaysThrowX(456);
}
fun testCodegen1(x: int) {
if (x > 10) {
throw 123;
anotherNever(true); // unreachable, will be dropped
}
else if (x < 10) {
throw x;
return -123; // unreachable, will be dropped
}
return 0;
}
fun testCodegen2(x: int) {
if (x > 10) {
alwaysThrow123();
anotherNever(true); // unreachable, will be dropped
}
else if (x < 10) {
anotherNever(false);
return -123; // unreachable, will be dropped
}
return 0;
}
@method_id(110)
fun test110(b: bool) {
try {
if (b == true) { testCodegen1(100); }
testCodegen1(5);
return -1;
} catch (ex) {
return ex;
}
}
@method_id(111)
fun test111(b: bool) {
try {
if (b == true) { testCodegen2(100); }
testCodegen2(5);
return -1;
} catch (ex) {
return ex;
}
}
fun mySetCode(newCode: slice): void
asm "SETCODE";
fun testCodegen3(numberId: int, paramVal: cell) {
if (numberId == -1000) {
var cs = paramVal.beginParse();
mySetCode(cs);
throw 0;
}
paramVal.beginParse();
}
fun main() {
}
@ -187,6 +259,65 @@ fun main() {
@testcase | 107 | 5 | 5
@testcase | 107 | 20 | 20
@testcase | 108 | | 0
@testcase | 109 | | 10 10
@testcase | 110 | -1 | 123
@testcase | 110 | 0 | 5
@testcase | 111 | -1 | 123
@testcase | 111 | 0 | 456
@code_hash 39307974281105539319288356721945232226028429128341177951717392648324358675585
@code_hash 57361460846265694653029920796509802052573595128418810728101968091567195330515
@fif_codegen
"""
testCodegen1 PROC:<{
// x
DUP // x x
10 GTINT // x '2
IFJMP:<{ // x
123 THROW
}> // x
DUP // x x
10 LESSINT // x '6
IFJMP:<{ // x
THROWANY
}> // x
DROP //
0 PUSHINT // '8=0
}>
"""
@fif_codegen
"""
testCodegen2 PROC:<{
// x
DUP // x x
10 GTINT // x '2
IFJMP:<{ // x
DROP //
alwaysThrow123 CALLDICT
}> // x
10 LESSINT // '5
IFJMP:<{ //
FALSE // '6
anotherNever CALLDICT
}> //
0 PUSHINT // '8=0
}>
"""
@fif_codegen
"""
testCodegen3 PROC:<{
// numberId paramVal
SWAP
-1000 PUSHINT // paramVal numberId '2=-1000
EQUAL // paramVal '3
IFJMP:<{ // paramVal
CTOS // cs
SETCODE
0 THROW
}> // paramVal
DROP //
}>
"""
*/

View file

@ -0,0 +1,24 @@
fun alwaysThrows(): never {
throw 456;
}
fun testUnreachable(x: int) {
if (x) { throw 123; }
else { alwaysThrows(); }
return 1;
}
fun main() {
try {
testUnreachable(100);
throw 80;
} catch (excNo) {
return excNo;
}
}
/**
@testcase | 0 | | 123
@stderr warning: unreachable code
@stderr return 1;
*/