mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
[Tolk] Smart casts and control flow graph
With the introduction of nullable types, we want the compiler to be smart in cases like > if (x == null) return; > // x is int now or > if (x == null) x = 0; > // x is int now These are called smart casts: when the type of variable at particular usage might differ from its declaration. Implementing smart casts is very challenging. They are based on building control-flow graph and handling every AST vertex with care. Actually, I represent cfg not a as a "graph with edges". Instead, it's a "structured DFS" for the AST: 1) at every point of inferring, we have "current flow facts" 2) when we see an `if (...)`, we create two derived contexts 3) after `if`, finalize them at the end and unify 4) if we detect unreachable code, we mark that context In other words, we get the effect of a CFG but in a more direct approach. That's enough for AST-level data-flow. Smart casts work for local variables and tensor/tuple indices. Compilation errors have been reworked and now are more friendly. There are also compilation warnings for always true/false conditions inside if, assert, etc.
This commit is contained in:
parent
f3e620f48c
commit
7bcb8b895f
47 changed files with 3057 additions and 833 deletions
|
@ -178,7 +178,7 @@ fun test114(f: int, s: int) {
|
|||
@method_id(115)
|
||||
fun test115() {
|
||||
var y = [[[[true]]]];
|
||||
return (y, y.0.0.0.0 = !y.0.0.0.0, y.0);
|
||||
return (y, ((((y).0).0).0).0 = !y.0.0.0.0, y.0);
|
||||
}
|
||||
|
||||
@method_id(116)
|
||||
|
@ -248,7 +248,7 @@ fun test122(x: (int, int)) {
|
|||
@method_id(123)
|
||||
fun test123() {
|
||||
var t = [[10, 20]] as [[int,int]]?;
|
||||
t!.0.0 = t!.0.1 = 100;
|
||||
((t!).0).0 = ((t!).0).1 = 100;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,6 @@ fun cantApplyPlusOnNullable() {
|
|||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr while instantiating generic function `calcSum<int?>`
|
||||
@stderr in function `calcSum<int?>`
|
||||
@stderr can not apply operator `+` to `int?` and `int?`
|
||||
*/
|
||||
|
|
17
tolk-tester/tests/invalid-generics-14.tolk
Normal file
17
tolk-tester/tests/invalid-generics-14.tolk
Normal file
|
@ -0,0 +1,17 @@
|
|||
fun eq<X>(v: X) {}
|
||||
|
||||
fun cantDeduceWhenNotInferred() {
|
||||
// at type inferring (before type checking) they are unknown
|
||||
var (x, y) = 2;
|
||||
|
||||
eq(x as int); // ok (since execution doesn't reach type checking)
|
||||
eq<int>(x); // ok (since execution doesn't reach type checking)
|
||||
eq(x);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr in function `cantDeduceWhenNotInferred`
|
||||
@stderr can not deduce X for generic function `eq<X>`
|
||||
@stderr eq(x);
|
||||
*/
|
|
@ -11,8 +11,7 @@ fun foo<X>(value: X) : X {
|
|||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr while instantiating generic function `foo<slice>`
|
||||
@stderr while instantiating generic function `bar<slice>`
|
||||
@stderr in function `bar<slice>`
|
||||
@stderr can not convert type `int` to return type `slice`
|
||||
@stderr return 1
|
||||
*/
|
||||
|
|
|
@ -3,6 +3,7 @@ fun failBitwiseNotOnBool() {
|
|||
if (~eq) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
12
tolk-tester/tests/invalid-typing-19.tolk
Normal file
12
tolk-tester/tests/invalid-typing-19.tolk
Normal file
|
@ -0,0 +1,12 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun testCantApplyNotNullForAlwaysNull() {
|
||||
var x: int? = getNullableInt();
|
||||
if (x != null) { return 0; }
|
||||
return x! + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr operator `!` used for always null expression
|
||||
*/
|
15
tolk-tester/tests/invalid-typing-20.tolk
Normal file
15
tolk-tester/tests/invalid-typing-20.tolk
Normal file
|
@ -0,0 +1,15 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun testFlowContextAppliedInBinaryOperator() {
|
||||
var x: int? = getNullableInt();
|
||||
var y: int? = getNullableInt();
|
||||
if ((y = null) < y) {
|
||||
return -100;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `<` to `null` and `null`
|
||||
*/
|
14
tolk-tester/tests/invalid-typing-21.tolk
Normal file
14
tolk-tester/tests/invalid-typing-21.tolk
Normal file
|
@ -0,0 +1,14 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun testNeverTypeOccurs() {
|
||||
var x: int? = getNullableInt();
|
||||
if (x == null && x != null) {
|
||||
return x + 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `+` to `never` and `int`
|
||||
*/
|
9
tolk-tester/tests/invalid-typing-22.tolk
Normal file
9
tolk-tester/tests/invalid-typing-22.tolk
Normal file
|
@ -0,0 +1,9 @@
|
|||
fun testLogicalAndNotConditionDoesntAffect(x: int?) {
|
||||
var gt1 = x != null && x > 1;
|
||||
return x + 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `+` to `int?` and `int`
|
||||
*/
|
15
tolk-tester/tests/invalid-typing-23.tolk
Normal file
15
tolk-tester/tests/invalid-typing-23.tolk
Normal file
|
@ -0,0 +1,15 @@
|
|||
fun getTensor(): (int?, int?) { return (5, null); }
|
||||
|
||||
fun testSmartCastsForFieldsDropAfterAssign() {
|
||||
var t = getTensor();
|
||||
if (t.0 != null && t.1 != null) {
|
||||
t = getTensor();
|
||||
return t.0 + t.1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `+` to `int?` and `int?`
|
||||
*/
|
16
tolk-tester/tests/invalid-typing-24.tolk
Normal file
16
tolk-tester/tests/invalid-typing-24.tolk
Normal file
|
@ -0,0 +1,16 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun getTensor(x: int?): (int?, int) { return (x, 0); }
|
||||
|
||||
fun testSmartCastsDropAfterAssign() {
|
||||
var x: int? = 0;
|
||||
var y: int? = 0;
|
||||
(getTensor(x = getNullableInt()).0, getTensor(y = getNullableInt()).0) = (x + y, x - y);
|
||||
return x+y;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `+` to `int?` and `int?`
|
||||
@stderr x + y, x - y
|
||||
*/
|
14
tolk-tester/tests/invalid-typing-25.tolk
Normal file
14
tolk-tester/tests/invalid-typing-25.tolk
Normal file
|
@ -0,0 +1,14 @@
|
|||
fun takeNullableTensor(mutate ij: (int, int)?) { }
|
||||
|
||||
fun testSmartCastsDropAfterMutate() {
|
||||
var x: (int, int)? = (1, 2);
|
||||
return x.0; // ok
|
||||
takeNullableTensor(mutate x);
|
||||
return x.1; // error
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr type `(int, int)?` is not indexable
|
||||
@stderr return x.1
|
||||
*/
|
12
tolk-tester/tests/invalid-typing-26.tolk
Normal file
12
tolk-tester/tests/invalid-typing-26.tolk
Normal file
|
@ -0,0 +1,12 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun testAssertThrowIsConditional() {
|
||||
var (x, y) = (getNullableInt(), getNullableInt());
|
||||
assert(x != null) throw(y = 10);
|
||||
return x + y;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `+` to `int` and `int?`
|
||||
*/
|
18
tolk-tester/tests/invalid-typing-27.tolk
Normal file
18
tolk-tester/tests/invalid-typing-27.tolk
Normal file
|
@ -0,0 +1,18 @@
|
|||
fun assignNull2<T1, T2>(mutate x: T1?, mutate y: T2?) {
|
||||
if (false) {
|
||||
x = null;
|
||||
y = null;
|
||||
}
|
||||
}
|
||||
|
||||
fun testSmartCastsDropAfterNullableGeneric() {
|
||||
var (x: int?, y: int?) = (1, 2);
|
||||
x * y; // ok
|
||||
assignNull2(x, y); // treated like assignments to nullable
|
||||
x << y; // error
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `<<` to `int?` and `int?`
|
||||
*/
|
15
tolk-tester/tests/invalid-typing-28.tolk
Normal file
15
tolk-tester/tests/invalid-typing-28.tolk
Normal file
|
@ -0,0 +1,15 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun testReassignInRedef() {
|
||||
var t1: int? = getNullableInt();
|
||||
if (t1 != null) {
|
||||
var (t1 redef, t2) = (getNullableInt(), 5);
|
||||
return t1 + t2;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `+` to `int?` and `int`
|
||||
*/
|
14
tolk-tester/tests/invalid-typing-29.tolk
Normal file
14
tolk-tester/tests/invalid-typing-29.tolk
Normal file
|
@ -0,0 +1,14 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun testTryBodyDontSmartCast() {
|
||||
var x = getNullableInt();
|
||||
try {
|
||||
x = 5;
|
||||
} catch {}
|
||||
return x * 10; // x is not int here; for now, we have no exception edges, assuming it can be anywhere inside try
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `*` to `int?` and `int`
|
||||
*/
|
15
tolk-tester/tests/invalid-typing-30.tolk
Normal file
15
tolk-tester/tests/invalid-typing-30.tolk
Normal file
|
@ -0,0 +1,15 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun testDoWhileCondition() {
|
||||
var (x: int?, y: int?) = (10, 20);
|
||||
do {
|
||||
x = getNullableInt();
|
||||
y = getNullableInt();
|
||||
} while(x == null);
|
||||
return x * y; // x is 100% int, but y is not
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `*` to `int` and `int?`
|
||||
*/
|
9
tolk-tester/tests/invalid-typing-44.tolk
Normal file
9
tolk-tester/tests/invalid-typing-44.tolk
Normal file
|
@ -0,0 +1,9 @@
|
|||
fun cantAssignIntToTensor() {
|
||||
var (x, y) = 2;
|
||||
x + y;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not assign `int` to a tensor
|
||||
*/
|
9
tolk-tester/tests/invalid-typing-45.tolk
Normal file
9
tolk-tester/tests/invalid-typing-45.tolk
Normal file
|
@ -0,0 +1,9 @@
|
|||
fun cantAssignSizesMismatch() {
|
||||
var [x, y] = [2, 3, 4];
|
||||
x + y;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not assign `[int, int, int]`, sizes mismatch
|
||||
*/
|
28
tolk-tester/tests/never-type-tests.tolk
Normal file
28
tolk-tester/tests/never-type-tests.tolk
Normal file
|
@ -0,0 +1,28 @@
|
|||
fun takeInt(a: int) {}
|
||||
|
||||
@method_id(101)
|
||||
fun test1(x: int?) {
|
||||
if (x == null && x != null) {
|
||||
var y = x;
|
||||
__expect_type(y, "never");
|
||||
__expect_type(y!, "never");
|
||||
// `never` type is assignable to anything, flow won't reach this point
|
||||
var t: (int, int) = x;
|
||||
t = y;
|
||||
takeInt(x);
|
||||
var cb: (int) -> int = x;
|
||||
x as int?;
|
||||
x as (int, int)?;
|
||||
x as never;
|
||||
return x;
|
||||
}
|
||||
return 123;
|
||||
}
|
||||
|
||||
fun main() {
|
||||
__expect_type(test1, "(int?) -> int");
|
||||
}
|
||||
|
||||
/**
|
||||
@testcase | 101 | null | 123
|
||||
*/
|
|
@ -26,7 +26,9 @@ fun test2(x: int?) {
|
|||
if (null != x) {
|
||||
var y: int? = null;
|
||||
if (y != null) { return 10; }
|
||||
return y;
|
||||
if (10 < 20) { // always true at runtime (not at compile-time)
|
||||
return y;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return x! + 10; // will throw, since not a number
|
||||
|
@ -45,14 +47,6 @@ fun test3(x: int) {
|
|||
return myIsNull(x > 10 ? null : x);
|
||||
}
|
||||
|
||||
fun getUntypedNull() {
|
||||
var untyped: null = null;
|
||||
if (true) {
|
||||
return untyped;
|
||||
}
|
||||
return untyped;
|
||||
}
|
||||
|
||||
@method_id(104)
|
||||
fun test4(): null {
|
||||
var (_, (_, untyped: null)) = (3, (createEmptyTuple, null));
|
||||
|
@ -62,12 +56,6 @@ fun test4(): null {
|
|||
return untyped;
|
||||
}
|
||||
|
||||
@method_id(105)
|
||||
fun test5() {
|
||||
var n: slice? = getUntypedNull();
|
||||
return !(null == n) ? n!.loadInt(32) : 100;
|
||||
}
|
||||
|
||||
@method_id(107)
|
||||
fun test7() {
|
||||
var b = beginCell().storeMaybeRef(null) as builder?;
|
||||
|
@ -85,6 +73,7 @@ fun test8() {
|
|||
}
|
||||
|
||||
fun main() {
|
||||
// the compiler optimizes this at compile-time
|
||||
var i: int? = null;
|
||||
if (i == null) {
|
||||
return 1;
|
||||
|
@ -99,7 +88,6 @@ fun main() {
|
|||
@testcase | 103 | 5 | 5
|
||||
@testcase | 103 | 15 | -1
|
||||
@testcase | 104 | | (null)
|
||||
@testcase | 105 | | 100
|
||||
@testcase | 107 | | -11
|
||||
@fif_codegen
|
||||
"""
|
||||
|
@ -127,12 +115,7 @@ fun main() {
|
|||
"""
|
||||
main PROC:<{
|
||||
//
|
||||
PUSHNULL // i
|
||||
ISNULL // '2
|
||||
IFJMP:<{ //
|
||||
1 PUSHINT // '3=1
|
||||
}> //
|
||||
10 PUSHINT // '4=10
|
||||
1 PUSHINT // '3=1
|
||||
}>
|
||||
"""
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ fun test104() {
|
|||
var t1_1: (int, int)? = (1, 2);
|
||||
var t1_2: (int, int)? = t1_1;
|
||||
var t1_3: (int, int)? = t1_1!;
|
||||
var t2_1: (int, int)? = null;
|
||||
var t2_1: (int, int)? = getNullableTensor(null);
|
||||
var t2_2 = t2_1;
|
||||
return (t1_3, t2_2);
|
||||
}
|
||||
|
@ -101,9 +101,12 @@ fun test108(x1: (int, int)) {
|
|||
incrementTensorComponents(mutate x1);
|
||||
x1.incrementTensorComponents();
|
||||
var x2: (int, int)? = x1;
|
||||
__expect_type(x2, "(int, int)");
|
||||
x2.incrementNullableTensorComponents().incrementNullableTensorComponents();
|
||||
incrementNullableTensorComponents(mutate x2);
|
||||
__expect_type(x2, "(int, int)?");
|
||||
var x3: (int, int)? = null;
|
||||
__expect_type(x3, "null");
|
||||
x3.incrementNullableTensorComponents().incrementNullableTensorComponents();
|
||||
incrementNullableTensorComponents(mutate x3);
|
||||
return (x1, x2, x3);
|
||||
|
@ -148,7 +151,7 @@ fun test111() {
|
|||
var x = (1, 2);
|
||||
assignFirstComponent(mutate x, 50);
|
||||
var x2: (int, int)? = null;
|
||||
var x3 = x2;
|
||||
var x3 = x2 as (int, int)?;
|
||||
assignFirstComponentNullable(mutate x2, 30);
|
||||
assignFirstComponentNullable(mutate x3, 70);
|
||||
g110_1 = (1, 2);
|
||||
|
@ -361,23 +364,36 @@ fun test132() {
|
|||
return (result, 777, aln1, aln2, doubleNulls.1 == null, doubleNulls);
|
||||
}
|
||||
|
||||
@method_id(133)
|
||||
fun test133() {
|
||||
var x: (int, int)? = (10, 20);
|
||||
return sumOfTensor(x) + x.0 + x.1; // smart casted
|
||||
}
|
||||
|
||||
@method_id(134)
|
||||
fun test134(): (int, int)? {
|
||||
var x: (int, int)? = (10, 20);
|
||||
incrementTensorComponents(mutate x); // smart casted
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
fun getNormalNullableTensorWidth1(vLess100: int?): ([int?], ())? {
|
||||
if (vLess100 != null && vLess100! >= 100) {
|
||||
if (vLess100 != null && vLess100 >= 100) {
|
||||
return null;
|
||||
}
|
||||
return ([vLess100], ()); // such a nullable tensor can store NULL in the same slot
|
||||
}
|
||||
|
||||
fun getTrickyNullableTensorWidth1(vLess100: int?): (int?, ())? {
|
||||
if (vLess100 != null && vLess100! >= 100) {
|
||||
if (vLess100 != null && vLess100 >= 100) {
|
||||
return null;
|
||||
}
|
||||
return (vLess100, ()); // such a nullable tensor requires an extra stack slot for null presence
|
||||
}
|
||||
|
||||
fun getEvenTrickierNullableWidth1(vLess100: int?): ((), (int?, ()), ())? {
|
||||
if (vLess100 != null && vLess100! >= 100) {
|
||||
if (vLess100 != null && vLess100 >= 100) {
|
||||
return null;
|
||||
}
|
||||
return ((), (vLess100, ()), ());
|
||||
|
@ -406,35 +422,35 @@ fun main(){}
|
|||
/**
|
||||
@testcase | 101 | | 1 2 -1
|
||||
@testcase | 102 | | 1 2 -1 (null) (null) 0
|
||||
@testcase | 103 | 1 2 | 3 3 0 1 2 -1
|
||||
@testcase | 104 | | 1 2 -1 (null) (null) 0
|
||||
@testcase | 103 | 1 2 | 3 3 0 1 2
|
||||
@testcase | 104 | | 1 2 (null) (null) 0
|
||||
@testcase | 105 | | (null) (null) (null) 0 1 2 3 -1
|
||||
@testcase | 106 | | 1 2 -1
|
||||
@testcase | 106 | | 1 2
|
||||
@testcase | 107 | | 0 0 -1 0 0 -1
|
||||
@testcase | 108 | 5 6 | 7 8 10 11 -1 (null) (null) 0
|
||||
@testcase | 109 | | 0 0 -1 0 -1 0 0 -1 -1
|
||||
@testcase | 110 | | 3 4 (null) (null) 0 6 7 -1
|
||||
@testcase | 111 | | 50 30 70 90 100
|
||||
@testcase | 112 | | 12 22 -1
|
||||
@testcase | 112 | | 12 22
|
||||
@testcase | 113 | | -1
|
||||
@testcase | 114 | | (null) (null) (null) 0 (null) (null) (null) 0
|
||||
@testcase | 115 | | 2 3 7 (null) (null) 0 5 0 -1 0
|
||||
@testcase | 116 | -1 | (null) (null) 0 (null) (null) 0
|
||||
@testcase | 116 | 0 | 1 2 -1 1 2 -1
|
||||
@testcase | 117 | | (null) (null) 0 1 3
|
||||
@testcase | 117 | | (null) 1 3
|
||||
@testcase | 118 | 5 | 5 10 -1
|
||||
@testcase | 118 | null | (null) (null) 0
|
||||
@testcase | 119 | | (null) (null) 0 (null) (null) 0 1 2 -1 100
|
||||
@testcase | 119 | | (null) (null) 1 2 -1 100
|
||||
@testcase | 120 | -1 | (null) (null) 0
|
||||
@testcase | 120 | 0 | 1 2 -1
|
||||
@testcase | 121 | | [ 1 [ 3 4 ] ]
|
||||
@testcase | 122 | 0 | [ 1 [ 3 4 ] 4 (null) ]
|
||||
@testcase | 122 | -1 | [ 1 (null) 4 (null) ]
|
||||
@testcase | 123 | | 1 3 4 -1
|
||||
@testcase | 124 | 0 | 1 3 4 -1 4 (null) (null) 0 -1
|
||||
@testcase | 124 | -1 | 1 (null) (null) 0 4 (null) (null) 0 -1
|
||||
@testcase | 124 | 0 | 1 3 4 -1 4 (null) (null) 0
|
||||
@testcase | 124 | -1 | 1 (null) (null) 0 4 (null) (null) 0
|
||||
@testcase | 125 | | 3
|
||||
@testcase | 126 | | 1 (null) (null) 0 2
|
||||
@testcase | 126 | | 1 (null) 2
|
||||
@testcase | 127 | 1 | 1 (null) (null) 0 2
|
||||
@testcase | 127 | 2 | 1 2 3 -1 4
|
||||
@testcase | 127 | 3 | 1 (null) (null) 0 5
|
||||
|
@ -447,6 +463,8 @@ fun main(){}
|
|||
@testcase | 130 | -1 | 1 (null) (null) 0
|
||||
@testcase | 131 | | -1 777 0 777 777 777 0 0 -1 -1 777 -1 -1 -1 777
|
||||
@testcase | 132 | | -1 0 -1 0 777 (null) (null) -1 0 0
|
||||
@testcase | 133 | | 60
|
||||
@testcase | 134 | | 11 21 -1
|
||||
@testcase | 135 | | [ 10 ] [ (null) ] (null) 777 10 -1 (null) -1 (null) 0 777 10 -1 (null) -1 (null) 0 777 0 0 -1 0 0 -1 0 0 -1 777 0 -1 0 0 -1 0
|
||||
|
||||
@fif_codegen
|
||||
|
|
|
@ -80,7 +80,7 @@ fun test107() {
|
|||
@method_id(108)
|
||||
fun test108() {
|
||||
var (a, b: cell?, c) = (1, beginCell().endCell(), 3);
|
||||
b = null;
|
||||
if (10>3) { b = null; }
|
||||
return a + (b == null ? 0 : b!.beginParse().loadInt(32)) + c;
|
||||
}
|
||||
|
||||
|
|
678
tolk-tester/tests/smart-cast-tests.tolk
Normal file
678
tolk-tester/tests/smart-cast-tests.tolk
Normal file
|
@ -0,0 +1,678 @@
|
|||
// the goal of this file is not only to @testcase results —
|
||||
// but to check that this file compiles
|
||||
|
||||
fun getNullableInt(): int? { return 5; }
|
||||
fun getNullableSlice(): slice? { return null; }
|
||||
fun takeNullableInt(a: int?) {}
|
||||
fun takeNullableSlice(a: slice?) {}
|
||||
fun increment(mutate self: int) { self += 1; }
|
||||
fun assignToInt(mutate self: int, value: int) { self = value; }
|
||||
fun assignToNullableInt(mutate self: int?, value: int) { self = value; }
|
||||
fun sameTensor(t: (int, int)) { return t; }
|
||||
fun sameTensor2(t: (int?, (slice, slice, slice, builder)?)) { return t; }
|
||||
fun eq<T>(v: T) { return v; }
|
||||
fun getTwo<X>(): X { return 2 as X; }
|
||||
|
||||
fun test1(): int {
|
||||
var x = getNullableInt();
|
||||
var y = getNullableInt();
|
||||
if (x != null && y != null) {
|
||||
__expect_type(x, "int");
|
||||
__expect_type(y, "int");
|
||||
return x + y;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
fun test2() {
|
||||
var (x, y) = (getNullableInt(), getNullableInt());
|
||||
if (x == null || y == null) {
|
||||
return null;
|
||||
}
|
||||
__expect_type(x, "int");
|
||||
__expect_type(y, "int");
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test3(): int {
|
||||
var ([x, y]) = [getNullableInt(), getNullableInt()];
|
||||
if (x != null) {
|
||||
if (((y)) != null) {
|
||||
__expect_type(x, "int");
|
||||
__expect_type(y, "int");
|
||||
return x + y;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
if (random() > -1) {
|
||||
if (y == null) { return -1; }
|
||||
else { return y; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fun test4() {
|
||||
var x = getNullableInt();
|
||||
if (x != null && x > 0) {
|
||||
var x = getNullableInt();
|
||||
if ((x) != null && x + 10 < 0) {
|
||||
var x = getNullableInt();
|
||||
return 10 > 3 && 10 < 10 && x != null && x + 8 > 10;
|
||||
}
|
||||
}
|
||||
if (x != null && x < 1) {
|
||||
return false;
|
||||
}
|
||||
if (x == null && x == null) {
|
||||
__expect_type(x, "null");
|
||||
return true;
|
||||
}
|
||||
return x < x + 3;
|
||||
}
|
||||
|
||||
fun test5() {
|
||||
var (a, (b, c)) = (getNullableInt(), (getNullableInt(), getNullableInt()));
|
||||
if (a == null) { return -1; }
|
||||
if (!(b != null)) { return -2; }
|
||||
if (random() ? c == null && c == null : c == null) { return -3; }
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
fun test6() {
|
||||
var a: int? = 5;
|
||||
__expect_type(a, "int");
|
||||
__expect_type(a != null ? a : null, "int");
|
||||
__expect_type(a == null ? "" : a, "int");
|
||||
takeNullableInt(a);
|
||||
__expect_type(a, "int");
|
||||
if (random()) {
|
||||
a = null;
|
||||
} else {
|
||||
if (random()) { a = null; }
|
||||
else { a = null; }
|
||||
}
|
||||
__expect_type(a, "null");
|
||||
takeNullableSlice(a); // ok, `slice?` is `slice | null`, here a definitely null
|
||||
var b: int? = true ? null : "sl";
|
||||
__expect_type(b, "null");
|
||||
takeNullableInt(b);
|
||||
takeNullableSlice(b); // same reason
|
||||
var c: int? = 10;
|
||||
__expect_type(c, "int");
|
||||
takeNullableSlice(c = null);
|
||||
}
|
||||
|
||||
fun test7() {
|
||||
var (a, b, c, d) = (getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt());
|
||||
if (a == null && true) { return -1; }
|
||||
if (true && true && 1 && !0 && b == null) { return -2; }
|
||||
if (true ? c == null && (((c))) == null && true : false) { return -3; }
|
||||
if (!true ? random() > 0 : a != null && (d == null && b != null)) { return -4; }
|
||||
return a + b + c + d;
|
||||
}
|
||||
|
||||
fun test8(x: int?, y: int?) {
|
||||
var allGt1 = x != null && x > 1 && y != null && y > 1;
|
||||
var xGtY = x != null && y != null && x > y;
|
||||
var xLtEq0 = x == null || x < 0;
|
||||
(x = 0) < random() || x > 10;
|
||||
return x + 0;
|
||||
}
|
||||
|
||||
fun test9() {
|
||||
var x = getNullableInt();
|
||||
var y = getNullableInt();
|
||||
if (x == null || y == null) {
|
||||
return -1;
|
||||
}
|
||||
__expect_type(x, "int");
|
||||
__expect_type(y, "int");
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test10(): int {
|
||||
var (x, y) = (getNullableInt(), getNullableInt());
|
||||
if (x == null) {
|
||||
if (y == null) { return -1; }
|
||||
__expect_type(x, "null");
|
||||
__expect_type(y, "int");
|
||||
return y;
|
||||
}
|
||||
if (y == null) {
|
||||
return x;
|
||||
}
|
||||
__expect_type(x, "int");
|
||||
__expect_type(y, "int");
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test11() {
|
||||
var [x, y] = [getNullableInt(), getNullableInt()];
|
||||
if (random()) { return x == null || y == null ? -1 : x + y; }
|
||||
if (true && (x == null || y == null) && !!true) { return 0; }
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test12() {
|
||||
var (x, y) = (getNullableInt(), getNullableInt());
|
||||
if (random() ? x == null || y == null : x == null || y == null) { return -1; }
|
||||
__expect_type(x, "int");
|
||||
__expect_type(y, "int");
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test13() {
|
||||
var x: int? = getNullableInt();
|
||||
var y: int? = 10;
|
||||
var z = getNullableInt();
|
||||
var w = getNullableInt();
|
||||
beginCell().storeInt(x!, 32).storeInt(x = getNullableInt()!, 32).storeInt(x, 32)
|
||||
.storeInt(y, 32).storeInt(z = 10, 32).storeInt(x + y + z, 32)
|
||||
.storeInt(w == null ? -1 : w, 32).storeInt(!(null == w) ? w : -1, 32);
|
||||
}
|
||||
|
||||
fun test14() {
|
||||
var (x, y) = (getNullableInt(), getNullableInt());
|
||||
if (x == null) {
|
||||
x = 0;
|
||||
}
|
||||
if (y == null) {
|
||||
if (random()) { return 0; }
|
||||
else { y = 0; }
|
||||
}
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test20() {
|
||||
var t = (getNullableInt(), getNullableInt());
|
||||
if (t.0 != null && t.1 != null) {
|
||||
__expect_type(t.0, "int");
|
||||
__expect_type(t.1, "int");
|
||||
return t.0 + t.1;
|
||||
}
|
||||
t.0 = 10;
|
||||
if (t.1 == null) {
|
||||
t.1 = 20;
|
||||
}
|
||||
__expect_type(t.0, "int");
|
||||
__expect_type(t.1, "int");
|
||||
return t.0 + t.1;
|
||||
}
|
||||
|
||||
fun test21() {
|
||||
var t = (getNullableInt(), (getNullableInt(), getNullableInt()));
|
||||
if (t.0 != null && t.1.0 != null) {
|
||||
if (t.1.1 != null) { return t.0 + t.1.0 + t.1.1; }
|
||||
return t.0 + t.1.0;
|
||||
}
|
||||
if (t.0 != null) {
|
||||
return t.0 + 0;
|
||||
}
|
||||
__expect_type(t.0, "null");
|
||||
__expect_type(t.1.0, "int?");
|
||||
return t.1.0 == null ? -1 : t.1.0 + 0;
|
||||
}
|
||||
|
||||
fun test22() {
|
||||
var t = (getNullableInt(), (getNullableInt(), getNullableInt()));
|
||||
if (t.0 == null || t.1.0 == null || t.1.1 == null) {
|
||||
return -1;
|
||||
}
|
||||
return t.0 + t.1.0 + t.1.1;
|
||||
}
|
||||
|
||||
@method_id(123)
|
||||
fun test23() {
|
||||
var (x: int?, y: int?, z: int?) = (getNullableInt(), getNullableInt(), getNullableInt());
|
||||
((x = 1, 0).0, (y = 2, 1).0) = (3, z = 4);
|
||||
return x + y + z;
|
||||
}
|
||||
|
||||
@method_id(124)
|
||||
fun test24(x: int?) {
|
||||
if (x == null) {
|
||||
__expect_type(x, "null");
|
||||
assignToNullableInt(mutate x, 10);
|
||||
__expect_type(x, "int?");
|
||||
x.assignToNullableInt(x! + 5);
|
||||
} else {
|
||||
__expect_type(x, "int");
|
||||
increment(mutate x);
|
||||
x.increment();
|
||||
__expect_type(x, "int");
|
||||
}
|
||||
__expect_type(x, "int?");
|
||||
return x;
|
||||
}
|
||||
|
||||
fun test25() {
|
||||
var x = (getNullableInt(), getNullableInt(), getNullableInt());
|
||||
x.0 = x.2 = random();
|
||||
return (x.0) + ((x.2));
|
||||
}
|
||||
|
||||
fun test26() {
|
||||
var x = [getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt(),
|
||||
getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt()];
|
||||
if (~(x.0 = random())) { return; }
|
||||
if ((x.1 = random()) < (x.2 = random())) { return; }
|
||||
else if (!(x.2 <=> (x.3 = random()))) { return; }
|
||||
x.5 = (x.4 = random()) ? (x.6 = random()) : (x.6 = random());
|
||||
if ((x.7 = random()) as int) { return; }
|
||||
if (((((x.8 = random()) != null)))) { return; }
|
||||
if ([x.1, (x.9 = random())!].1) { return; }
|
||||
val result = x.0+x.1+x.2+x.3+x.4+x.5+x.6+x.7+x.8+x.9;
|
||||
}
|
||||
|
||||
fun test27() {
|
||||
var (x, _) = ([getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt(),
|
||||
getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt()], []);
|
||||
+(x.0 = random());
|
||||
x.0 += [((x.1 = random()) < (x.2 = random() + x.1)) as int].0;
|
||||
!(x.2 <=> (x.3 = random() + x.2));
|
||||
x.5 = (x.4 = random()) ? (x.6 = random()) : (x.6 = random());
|
||||
(x.7 = random()) as int;
|
||||
(((((x.8 = random()) != null))));
|
||||
[x.1, (x.9 = random())!].1;
|
||||
return x.0+x.1+x.2+x.3+x.4+x.5+x.6+x.7+x.8+x.9;
|
||||
}
|
||||
|
||||
fun test28() {
|
||||
var x = (getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt());
|
||||
__expect_type((x.0 = random(), x.0 += (x.1 = random()) as int, !(x.1 <=> (x.2 = random() + x.0)) == null, (x.3 = random()) ? x.3 : (!x.3) as int),
|
||||
"(int, int, bool, int)");
|
||||
}
|
||||
|
||||
fun test29() {
|
||||
var x = (getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt());
|
||||
__expect_type([x.0 = random(), ((x.0 += (x.1 = random()) as int)), !(x.1 <=> (x.2 = random() + x.0)) == null, (x.3 = random()) ? x.3 : (!x.3) as int],
|
||||
"[int, int, bool, int]");
|
||||
}
|
||||
|
||||
@method_id(130)
|
||||
fun test30(initial5: bool) {
|
||||
var t: (int?, (int?, (int?, int?))) = initial5
|
||||
? (getNullableInt(), (getNullableInt(), (getNullableInt(), getNullableInt())))
|
||||
: (null, (null, (null, null)));
|
||||
if (t.0 == null || t.1.0 == null || t.1.1.0 == null || t.1.1.1 == null) {
|
||||
if (t.1.0 == null || t.1.1.0 == null) {
|
||||
if (t.1.1.0 == null) {
|
||||
t.1.1.0 = 4;
|
||||
}
|
||||
__expect_type(t.1.1.0, "int");
|
||||
__expect_type(t.1.1.1, "int?");
|
||||
__expect_type(t.1.0, "int?");
|
||||
t.1.1.1 = 3;
|
||||
t.1.0 = 2;
|
||||
__expect_type(t.1.1.1, "int");
|
||||
__expect_type(t.1.0, "int");
|
||||
}
|
||||
if (((((t.1.1.1)))) != null) {}
|
||||
else { t.1.1.1 = 3; }
|
||||
t.0 = 1;
|
||||
}
|
||||
return t.0 + t.1.0 + t.1.1.0 + t.1.1.1;
|
||||
}
|
||||
|
||||
fun test31() {
|
||||
var t = (getNullableInt(), getNullableInt());
|
||||
t.0 == null ? (t.0, t.1) = (1, 2) : (t.1, t.0) = (4, 3);
|
||||
return t.0 + t.1;
|
||||
}
|
||||
|
||||
@method_id(132)
|
||||
fun test32() {
|
||||
var t: (int?, (int?, int?)?, (int?, int?)) = (getNullableInt(), (getNullableInt(), getNullableInt()), (getNullableInt(), getNullableInt()));
|
||||
if (t.0 == null) { return -1; }
|
||||
t.1 != null && t.1.0 == null ? t.1 = (1, 2) : t.1 = (3, 4);
|
||||
if (t.2.1 != null) { t.2.0 = 1; t.2.1 = 2; }
|
||||
else { [t.2.0, t.2.1] = [3, 4]; }
|
||||
return t.0 + t.1.0! + t.1.1! + t.2.0 + t.2.1;
|
||||
}
|
||||
|
||||
@method_id(133)
|
||||
fun test33(): int {
|
||||
var x = getNullableInt();
|
||||
repeat (eq(x = 5)) {
|
||||
__expect_type(x, "int");
|
||||
increment(mutate x);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
fun test34() {
|
||||
var (x, y) = (getNullableInt(), getNullableInt());
|
||||
if (random()) { throw (x = 1, y = 2); }
|
||||
else { throw (x = 3, y = (1, getNullableInt()!).1); }
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test35() {
|
||||
var (x, y, z, t) = (getNullableInt(), getNullableInt(), getNullableInt(), (getNullableInt(), getNullableInt()));
|
||||
assert (x != null, 404);
|
||||
assert (t.0 != null && true && !(t.1 == null) && !(z = 4)) throw (y = 404);
|
||||
__expect_type(y, "int?");
|
||||
return x + t.0 + t.1 + z;
|
||||
}
|
||||
|
||||
fun test36() {
|
||||
var x = getNullableInt();
|
||||
assert (x == null, x + 0); // check that x is int there
|
||||
__expect_type(x, "null");
|
||||
}
|
||||
|
||||
fun test37() {
|
||||
var (x, code) = (getNullableInt()!, getNullableInt());
|
||||
try {
|
||||
} catch(code) {
|
||||
x = 20;
|
||||
return x + code; // code is scoped
|
||||
}
|
||||
return code == null ? x : x + code;
|
||||
}
|
||||
|
||||
fun assignNull2<T1, T2>(mutate x: T1?, mutate y: T2?) {
|
||||
x = null;
|
||||
y = null;
|
||||
}
|
||||
|
||||
fun test38() {
|
||||
var (x: int?, y: int?) = (1, 2);
|
||||
__expect_type(x, "int");
|
||||
__expect_type(y, "int");
|
||||
assignNull2<int, int>(mutate x, mutate y);
|
||||
__expect_type(x, "int?");
|
||||
__expect_type(y, "int?");
|
||||
if (x != null) {
|
||||
if (y == null) { return -1; }
|
||||
return x + y;
|
||||
}
|
||||
var t: (int?, slice?) = (null, null);
|
||||
if (!false) { t.0 = 1; }
|
||||
if (true) { t.1 = beginCell().endCell().beginParse(); }
|
||||
__expect_type(t.0, "int");
|
||||
__expect_type(t.1, "slice");
|
||||
t.0 + t.1.loadInt(32);
|
||||
assignNull2(mutate t.0, mutate t.1);
|
||||
__expect_type(t.0, "int?");
|
||||
__expect_type(t.1, "slice?");
|
||||
t.0 != null && t.1 != null ? t.0 + loadInt(mutate t.1, 32) : -1;
|
||||
return t.0 != null && t.1 != null ? t.0 + loadInt(mutate t.1, 32) : -1;
|
||||
}
|
||||
|
||||
@method_id(139)
|
||||
fun test39() {
|
||||
var x: (int?, int?)? = (4, null);
|
||||
x.1 = 10;
|
||||
x.1 += 1;
|
||||
x!.1 += 1;
|
||||
return (x!.0! + x.1);
|
||||
}
|
||||
|
||||
@method_id(140)
|
||||
fun test40(second: int?) {
|
||||
var x: (int?, int?)? = (4, second);
|
||||
if (x.1 != null) {
|
||||
val result = x.1 + x!.1 + x!!.1 + x.1! + x!!.1!!;
|
||||
}
|
||||
if (x!.1 != null) {
|
||||
val result = x.1 + x!.1 + x!!.1 + x.1! + x!!.1!!;
|
||||
}
|
||||
if (!(x!!.1 != null)) {
|
||||
return -1;
|
||||
}
|
||||
return x.1 + x!.1 + x!!.1 + x.1! + x!!.1!!;
|
||||
}
|
||||
|
||||
@method_id(141)
|
||||
fun test41() {
|
||||
var t: (int, int)? = null;
|
||||
return sameTensor(t = (1, 2));
|
||||
}
|
||||
|
||||
@method_id(142)
|
||||
fun test42() {
|
||||
var t: (int?, (int?, (int, int)?)?) = (getNullableInt(), (1, (2, 3)));
|
||||
t.1 = (3,null);
|
||||
__expect_type(t.1, "(int?, (int, int)?)");
|
||||
__expect_type(t, "(int?, (int?, (int, int)?)?)");
|
||||
return (t, t.1);
|
||||
}
|
||||
|
||||
@method_id(143)
|
||||
fun test43() {
|
||||
var t1: ((int, int), int?) = ((1, 2), 3);
|
||||
var t2: ((int?, int?), (int?,int?)?) = ((null, null), (null, 5));
|
||||
t2.0 = t1.0 = (10, 11);
|
||||
t2.1 = t1.1 = null;
|
||||
return (t1, t2);
|
||||
}
|
||||
|
||||
@method_id(144)
|
||||
fun test44() {
|
||||
var t1: ((int, int), int?) = ((1, 2), 3);
|
||||
var t2: ((int?, int?), (int?,int?)?) = ((null, null), (null, 5));
|
||||
t1.0 = t2.0 = (10, 11);
|
||||
t1.1 = t2.1 = null;
|
||||
__expect_type(t1, "((int, int), int?)");
|
||||
__expect_type(t2, "((int?, int?), (int?, int?)?)");
|
||||
return (t1, t2);
|
||||
}
|
||||
|
||||
@method_id(145)
|
||||
fun test45() {
|
||||
var t: (int?, (int?, (int, int)?)?) = (getNullableInt(), (1, (2, 3)));
|
||||
var t2 = sameTensor2(t.1 = (3,null));
|
||||
return (t, t2, t.1);
|
||||
}
|
||||
|
||||
fun autoInfer46() {
|
||||
var t1: int? = 3;
|
||||
var t2: (int, int)? = (4, 5);
|
||||
__expect_type(t1, "int");
|
||||
__expect_type(t2, "(int, int)");
|
||||
return (t1, t2); // proven to be not null, inferred (int, (int,int))
|
||||
}
|
||||
|
||||
@method_id(146)
|
||||
fun test46() {
|
||||
var r46_1: (int, (int,int)) = autoInfer46();
|
||||
var r46_2: (int, (int,int)?) = autoInfer46();
|
||||
return (r46_1, r46_2);
|
||||
}
|
||||
|
||||
@method_id(147)
|
||||
fun test47() {
|
||||
var t1: int? = 3;
|
||||
var t2: (int, int)? = (4, 5);
|
||||
t1 = t2 = null;
|
||||
__expect_type(t1, "null");
|
||||
__expect_type(t2, "null");
|
||||
var result = (t1, t2); // proven to be always null, inferred (null, null), 2 slots on a stack
|
||||
return (result, 100, result.1, 100, t2 as (int, int)?);
|
||||
}
|
||||
|
||||
fun test48() {
|
||||
var t1: int? = getNullableInt();
|
||||
if (t1 != null) {
|
||||
var (t1 redef, t2) = (10, 5);
|
||||
return t1 + t2;
|
||||
var t2 redef = getNullableInt()!;
|
||||
return t1 + t2;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
fun test49(x: int?) {
|
||||
while (x == null) {
|
||||
x = getNullableInt();
|
||||
}
|
||||
__expect_type(x, "int");
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
fun test50() {
|
||||
var (x: int?, y: int?) = (1, 2);
|
||||
do {
|
||||
x = getNullableInt();
|
||||
y = getNullableInt();
|
||||
} while (x == null || y == null);
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test51() {
|
||||
while (true) { return; }
|
||||
// test that no error "control reaches end of function"
|
||||
}
|
||||
|
||||
fun test52() {
|
||||
do { } while (true);
|
||||
}
|
||||
|
||||
fun test53() {
|
||||
var x1: int? = getNullableInt();
|
||||
var x2: int? = 5;
|
||||
var x3: int? = 5;
|
||||
var x10: int? = null;
|
||||
var x11: int? = 5;
|
||||
var x12: int? = 5;
|
||||
while (x1 != null) {
|
||||
__expect_type(x1, "int"); // because condition
|
||||
__expect_type(x2, "int?"); // because re-assigned
|
||||
__expect_type(x3, "int?"); // because re-assigned
|
||||
__expect_type(x10, "null");
|
||||
__expect_type(x11, "int");
|
||||
x1 = getNullableInt();
|
||||
__expect_type(x1, "int?");
|
||||
assignToNullableInt(mutate x2, 5);
|
||||
x3.assignToNullableInt(5);
|
||||
x11 = 10;
|
||||
assignToInt(mutate x12, 5);
|
||||
}
|
||||
__expect_type(x1, "null");
|
||||
__expect_type(x2, "int?");
|
||||
__expect_type(x3, "int?");
|
||||
}
|
||||
|
||||
fun test54() {
|
||||
var x1: int? = null;
|
||||
var x2: int? = 5;
|
||||
var x3: int? = 5;
|
||||
var x10: int? = null;
|
||||
var x11: int? = 5;
|
||||
var x12: int? = 5;
|
||||
do {
|
||||
__expect_type(x1, "int?"); // because re-assigned
|
||||
__expect_type(x2, "int?"); // because re-assigned
|
||||
__expect_type(x3, "int?"); // because re-assigned
|
||||
__expect_type(x10, "null");
|
||||
__expect_type(x11, "int");
|
||||
x1 = getNullableInt();
|
||||
__expect_type(x1, "int?");
|
||||
assignToNullableInt(mutate x2, 5);
|
||||
if (random()) { x3.assignToNullableInt(5); }
|
||||
x11 = 10;
|
||||
assignToInt(mutate x12, 5);
|
||||
} while (x1 != null);
|
||||
__expect_type(x1, "null");
|
||||
__expect_type(x2, "int?");
|
||||
__expect_type(x3, "int?");
|
||||
}
|
||||
|
||||
fun eq55<T>(v: T) { return v; }
|
||||
|
||||
fun test55() {
|
||||
var x: int? = 4;
|
||||
while (true) {
|
||||
// currently, generic functions are instantiated at the type inferring step
|
||||
// in case of loops, type inferring is re-enterable
|
||||
// first iteration: x is int, eq<int> instantiated
|
||||
// second (final) iteration: x is int?, eq<int?> instantiated
|
||||
// (checked via codegen)
|
||||
eq55(x);
|
||||
__expect_type(x, "int?"); // types are checked (unlike generics instantiated) after inferring
|
||||
x = random() ? 1 : null;
|
||||
}
|
||||
__expect_type(x, "int?");
|
||||
}
|
||||
|
||||
fun test56() {
|
||||
var i: int? = null;
|
||||
var (j: int?, k: int?) = (null, null);
|
||||
__expect_type(i, "null");
|
||||
__expect_type(k, "null");
|
||||
i = getTwo();
|
||||
[j, ((k))] = [getTwo(), ((getTwo()))];
|
||||
__expect_type(i, "int?");
|
||||
__expect_type(j, "int?");
|
||||
__expect_type(k, "int?");
|
||||
}
|
||||
|
||||
fun test57(mutate x: int?): int {
|
||||
if (x == null) { x = 5; }
|
||||
else {
|
||||
if (x < 10) { x = 10; }
|
||||
else { x = 20; }
|
||||
}
|
||||
if (x != null) {
|
||||
return 123;
|
||||
}
|
||||
__expect_type(x, "int");
|
||||
// no "return" needed, because end of function is unreachable
|
||||
}
|
||||
|
||||
@method_id(158)
|
||||
fun test58() {
|
||||
var (x1, x2: int?) = (getNullableInt(), null);
|
||||
return (test57(mutate x1), x1, test57(mutate x2), x2);
|
||||
}
|
||||
|
||||
fun test59() {
|
||||
var (x1: int?, x2, x3) = (getNullableInt()!, getNullableInt(), 5);
|
||||
if ((x2 = x3) != null) {
|
||||
__expect_type(x2, "int");
|
||||
}
|
||||
__expect_type(x2, "int");
|
||||
if ((x2 = getNullableInt()) != null) {
|
||||
__expect_type(x2, "int");
|
||||
}
|
||||
__expect_type(x2, "int?");
|
||||
if (((x1) = x2) == null) {
|
||||
return;
|
||||
}
|
||||
__expect_type(x1, "int");
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun main(x: int?): int {
|
||||
return x == null ? -1 : x;
|
||||
}
|
||||
|
||||
/**
|
||||
@testcase | 0 | 1 | 1
|
||||
@testcase | 123 | | 7
|
||||
@testcase | 124 | 4 | 6
|
||||
@testcase | 124 | null | 15
|
||||
@testcase | 130 | -1 | 20
|
||||
@testcase | 130 | 0 | 10
|
||||
@testcase | 132 | | 15
|
||||
@testcase | 133 | | 10
|
||||
@testcase | 139 | | 16
|
||||
@testcase | 140 | 5 | 25
|
||||
@testcase | 141 | | 1 2
|
||||
@testcase | 142 | | 5 3 (null) (null) 0 -1 3 (null) (null) 0
|
||||
@testcase | 143 | | 10 11 (null) 10 11 (null) (null) 0
|
||||
@testcase | 144 | | 10 11 (null) 10 11 (null) (null) 0
|
||||
@testcase | 145 | | 5 3 (null) (null) 0 -1 3 (null) (null) (null) (null) 0 3 (null) (null) 0
|
||||
@testcase | 146 | | 3 4 5 3 4 5 -1
|
||||
@testcase | 147 | | (null) (null) 100 (null) 100 (null) (null) 0
|
||||
@testcase | 158 | | 123 10 123 5
|
||||
|
||||
@stderr warning: expression of type `int` is always not null, this condition is always true
|
||||
@stderr warning: unreachable code
|
||||
@stderr var t2 redef = getNullableInt()!;
|
||||
|
||||
@fif_codegen eq55<int?> PROC:<{
|
||||
@fif_codegen eq55<int> PROC:<{
|
||||
*/
|
22
tolk-tester/tests/unreachable-3.tolk
Normal file
22
tolk-tester/tests/unreachable-3.tolk
Normal file
|
@ -0,0 +1,22 @@
|
|||
fun main(x: int?) {
|
||||
if (x != null && x == null) {
|
||||
return 1 + 2;
|
||||
}
|
||||
if (x == null) {
|
||||
return -1;
|
||||
}
|
||||
if (x != null) {
|
||||
return -2;
|
||||
}
|
||||
return 3 + 4;
|
||||
}
|
||||
|
||||
/**
|
||||
@testcase | 0 | 5 | -2
|
||||
@testcase | 0 | null | -1
|
||||
|
||||
@stderr warning: variable `x` of type `int` is always not null
|
||||
@stderr if (x != null)
|
||||
@stderr warning: unreachable code
|
||||
@stderr return 3 + 4
|
||||
*/
|
28
tolk-tester/tests/warnings-1.tolk
Normal file
28
tolk-tester/tests/warnings-1.tolk
Normal file
|
@ -0,0 +1,28 @@
|
|||
fun getNullableInt(): int? { return null; }
|
||||
|
||||
fun main() {
|
||||
var c: int? = 6;
|
||||
__expect_type(c, "int");
|
||||
if (c == null) {}
|
||||
|
||||
var d: int? = c;
|
||||
if (((d)) != null && tupleSize(createEmptyTuple())) {}
|
||||
|
||||
var e: int? = getNullableInt();
|
||||
if (e != null) {
|
||||
return true;
|
||||
}
|
||||
__expect_type(e, "null");
|
||||
null == e;
|
||||
|
||||
return null != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@testcase | 0 | | 0
|
||||
|
||||
@stderr warning: variable `c` of type `int` is always not null, this condition is always false
|
||||
@stderr warning: variable `d` of type `int` is always not null, this condition is always true
|
||||
@stderr warning: variable `e` is always null, this condition is always true
|
||||
@stderr warning: expression is always null, this condition is always false
|
||||
*/
|
26
tolk-tester/tests/warnings-2.tolk
Normal file
26
tolk-tester/tests/warnings-2.tolk
Normal file
|
@ -0,0 +1,26 @@
|
|||
fun main() {
|
||||
var (a, b, c, d, e) = (1, beginCell(), beginCell().endCell().beginParse(), [1], true as bool?);
|
||||
|
||||
var alwaysInt = a != null ? 1 : null;
|
||||
__expect_type(alwaysInt, "int");
|
||||
|
||||
if (!(c == null)) {
|
||||
if (10 < 3) { assert(b == null, 100); }
|
||||
}
|
||||
while (d == null || false) {}
|
||||
|
||||
return e! != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@testcase | 0 | | -1
|
||||
|
||||
@stderr warning: variable `a` of type `int` is always not null, this condition is always true
|
||||
@stderr warning: condition of ternary operator is always true
|
||||
@stderr warning: variable `c` of type `slice` is always not null, this condition is always false
|
||||
@stderr warning: condition of `if` is always true
|
||||
@stderr warning: variable `b` of type `builder` is always not null, this condition is always false
|
||||
@stderr warning: condition of `assert` is always false
|
||||
@stderr warning: condition of `while` is always false
|
||||
@stderr warning: expression of type `bool` is always not null, this condition is always true
|
||||
*/
|
Loading…
Add table
Add a link
Reference in a new issue