Skip to content

Commit 7bcb8b8

Browse files
committed
[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.
1 parent f3e620f commit 7bcb8b8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+3056
-832
lines changed

tolk-tester/tests/indexed-access.tolk

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ fun test114(f: int, s: int) {
178178
@method_id(115)
179179
fun test115() {
180180
var y = [[[[true]]]];
181-
return (y, y.0.0.0.0 = !y.0.0.0.0, y.0);
181+
return (y, ((((y).0).0).0).0 = !y.0.0.0.0, y.0);
182182
}
183183

184184
@method_id(116)
@@ -248,7 +248,7 @@ fun test122(x: (int, int)) {
248248
@method_id(123)
249249
fun test123() {
250250
var t = [[10, 20]] as [[int,int]]?;
251-
t!.0.0 = t!.0.1 = 100;
251+
((t!).0).0 = ((t!).0).1 = 100;
252252
return t;
253253
}
254254

tolk-tester/tests/invalid-generics-13.tolk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ fun cantApplyPlusOnNullable() {
66

77
/**
88
@compilation_should_fail
9-
@stderr while instantiating generic function `calcSum<int?>`
9+
@stderr in function `calcSum<int?>`
1010
@stderr can not apply operator `+` to `int?` and `int?`
1111
*/
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
fun eq<X>(v: X) {}
2+
3+
fun cantDeduceWhenNotInferred() {
4+
// at type inferring (before type checking) they are unknown
5+
var (x, y) = 2;
6+
7+
eq(x as int); // ok (since execution doesn't reach type checking)
8+
eq<int>(x); // ok (since execution doesn't reach type checking)
9+
eq(x);
10+
}
11+
12+
/**
13+
@compilation_should_fail
14+
@stderr in function `cantDeduceWhenNotInferred`
15+
@stderr can not deduce X for generic function `eq<X>`
16+
@stderr eq(x);
17+
*/

tolk-tester/tests/invalid-generics-7.tolk

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ fun foo<X>(value: X) : X {
1111

1212
/**
1313
@compilation_should_fail
14-
@stderr while instantiating generic function `foo<slice>`
15-
@stderr while instantiating generic function `bar<slice>`
14+
@stderr in function `bar<slice>`
1615
@stderr can not convert type `int` to return type `slice`
1716
@stderr return 1
1817
*/

tolk-tester/tests/invalid-typing-11.tolk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ fun failBitwiseNotOnBool() {
33
if (~eq) {
44
return 0;
55
}
6+
return -1;
67
}
78

89
/**
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
fun getNullableInt(): int? { return 5; }
2+
3+
fun testCantApplyNotNullForAlwaysNull() {
4+
var x: int? = getNullableInt();
5+
if (x != null) { return 0; }
6+
return x! + 1;
7+
}
8+
9+
/**
10+
@compilation_should_fail
11+
@stderr operator `!` used for always null expression
12+
*/
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
fun getNullableInt(): int? { return 5; }
2+
3+
fun testFlowContextAppliedInBinaryOperator() {
4+
var x: int? = getNullableInt();
5+
var y: int? = getNullableInt();
6+
if ((y = null) < y) {
7+
return -100;
8+
}
9+
return 0;
10+
}
11+
12+
/**
13+
@compilation_should_fail
14+
@stderr can not apply operator `<` to `null` and `null`
15+
*/
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
fun getNullableInt(): int? { return 5; }
2+
3+
fun testNeverTypeOccurs() {
4+
var x: int? = getNullableInt();
5+
if (x == null && x != null) {
6+
return x + 0;
7+
}
8+
return 0;
9+
}
10+
11+
/**
12+
@compilation_should_fail
13+
@stderr can not apply operator `+` to `never` and `int`
14+
*/
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
fun testLogicalAndNotConditionDoesntAffect(x: int?) {
2+
var gt1 = x != null && x > 1;
3+
return x + 0;
4+
}
5+
6+
/**
7+
@compilation_should_fail
8+
@stderr can not apply operator `+` to `int?` and `int`
9+
*/
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
fun getTensor(): (int?, int?) { return (5, null); }
2+
3+
fun testSmartCastsForFieldsDropAfterAssign() {
4+
var t = getTensor();
5+
if (t.0 != null && t.1 != null) {
6+
t = getTensor();
7+
return t.0 + t.1;
8+
}
9+
return -1;
10+
}
11+
12+
/**
13+
@compilation_should_fail
14+
@stderr can not apply operator `+` to `int?` and `int?`
15+
*/

0 commit comments

Comments
 (0)