1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00
ton/tolk-tester/tests/try-func.tolk
tolk-vm ef0328837f
[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.
2025-02-28 16:44:18 +03:00

323 lines
6.3 KiB
Text

fun foo(x: int): int {
try {
if (x == 7) {
throw 44;
}
return x;
} catch {
return 2;
}
}
@inline
fun foo_inline(x: int): int {
try {
assert(!(x == 7)) throw 44;
return x;
} catch {
return 2;
}
}
@inline_ref
fun foo_inlineref(x: int): int {
try {
if (x == 7) { throw (44, 2); }
return x;
} catch (_, arg) {
return arg as int;
}
}
@method_id(101)
fun test(x: int, y: int, z: int): int {
y = foo(y);
return x * 100 + y * 10 + z;
}
@method_id(102)
fun test_inline(x: int, y: int, z: int): int {
y = foo_inline(y);
return x * 100 + y * 10 + z;
}
@method_id(103)
fun test_inlineref(x: int, y: int, z: int): int {
y = foo_inlineref(y);
return x * 100 + y * 10 + z;
}
@inline
fun foo_inline_big(
x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int, x8: int, x9: int, x10: int,
x11: int, x12: int, x13: int, x14: int, x15: int, x16: int, x17: int, x18: int, x19: int, x20: int
): int {
try {
if (x1 == 7) {
throw 44;
}
return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20;
} catch {
return 1;
}
}
@method_id(104)
fun test_inline_big(x: int, y: int, z: int): int {
y = foo_inline_big(
y, y + 1, y + 2, y + 3, y + 4, y + 5, y + 6, y + 7, y + 8, y + 9,
y + 10, y + 11, y + 12, y + 13, y + 14, y + 15, y + 16, y + 17, y + 18, y + 19);
return x * 1000000 + y * 1000 + z;
}
fun foo_big(
x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int, x8: int, x9: int, x10: int,
x11: int, x12: int, x13: int, x14: int, x15: int, x16: int, x17: int, x18: int, x19: int, x20: int
): int {
try {
if (x1 == 7) {
throw (44, 1);
}
return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20;
} catch (code, arg) {
return arg as int;
}
}
@method_id(105)
fun test_big(x: int, y: int, z: int): int {
y = foo_big(
y, y + 1, y + 2, y + 3, y + 4, y + 5, y + 6, y + 7, y + 8, y + 9,
y + 10, y + 11, y + 12, y + 13, y + 14, y + 15, y + 16, y + 17, y + 18, y + 19);
return x * 1000000 + y * 1000 + z;
}
@method_id(106)
fun test_catch_into_same(x: int): int {
var code = x;
try {
assert(x <= 10, 44);
} catch(code) {
return code;
}
return code;
}
@method_id(107)
fun test_catch_into_same_2(x: int): int {
var code = x;
try {
if (x > 10) {
throw 44;
}
} catch(code) {
}
return code;
}
global after046: int;
// this bug existed in FunC and is fixed in v0.4.6
fun bug_046_internal(op: int) {
if (op == 1) {
return;
} else if (op == 2) {
return;
} else {
throw 1;
}
}
fun bug_046_called() {
after046 = 0;
try {
bug_046_internal(1337);
after046 = 1; // shouldn't be called
} catch(n) {
return;
}
return;
}
@method_id(108)
fun bug_046_entrypoint() {
bug_046_called();
return after046;
}
global g_reg: int;
@method_id(109)
fun test109(): (int, int) {
var l_reg = 10;
g_reg = 10;
try {
// note, that regardless of assignment, an exception RESTORES them to previous (to 10)
// it's very unexpected, but is considered to be a TVM feature, not a bug
g_reg = 999;
l_reg = 999;
bug_046_internal(999); // throws
} catch {
}
// returns (10,10) because of an exception, see a comment above
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() {
}
/**
method_id | in | out
@testcase | 101 | 1 2 3 | 123
@testcase | 101 | 3 8 9 | 389
@testcase | 101 | 3 7 9 | 329
@testcase | 102 | 1 2 3 | 123
@testcase | 102 | 3 8 9 | 389
@testcase | 102 | 3 7 9 | 329
@testcase | 103 | 1 2 3 | 123
@testcase | 103 | 3 8 9 | 389
@testcase | 103 | 3 7 9 | 329
@testcase | 104 | 4 8 9 | 4350009
@testcase | 104 | 4 7 9 | 4001009
@testcase | 105 | 4 8 9 | 4350009
@testcase | 105 | 4 7 9 | 4001009
@testcase | 106 | 5 | 5
@testcase | 106 | 20 | 44
@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 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 //
}>
"""
*/