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/null-keyword.tolk
tolk-vm 7bcb8b895f
[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.
2025-02-28 16:44:15 +03:00

136 lines
2.8 KiB
Text

import "@stdlib/lisp-lists"
@method_id(101)
fun test1() {
var numbers: tuple? = createEmptyList();
numbers = listPrepend(1, numbers);
numbers = listPrepend(2, numbers);
numbers = listPrepend(3, numbers);
numbers = listPrepend(4, numbers);
var (h: int, numbers redef) = listSplit(numbers!);
h += listGetHead(numbers!);
_ = null;
(_, _) = (null, null);
var t = createEmptyTuple();
do {
var num: int = numbers.listNext();
t.tuplePush(num);
} while (numbers != null);
return (h, numbers == null, t);
}
@method_id(102)
fun test2(x: int?) {
if (null != x) {
var y: int? = null;
if (y != null) { return 10; }
if (10 < 20) { // always true at runtime (not at compile-time)
return y;
}
}
try {
return x! + 10; // will throw, since not a number
} catch {
return -1;
}
return 100;
}
fun myIsNull(x: int?): int {
return x == null ? -1 : x!;
}
@method_id(103)
fun test3(x: int) {
return myIsNull(x > 10 ? null : x);
}
@method_id(104)
fun test4(): null {
var (_, (_, untyped: null)) = (3, (createEmptyTuple, null));
if (true) {
return untyped;
}
return untyped;
}
@method_id(107)
fun test7() {
var b = beginCell().storeMaybeRef(null) as builder?;
var s = b!.endCell().beginParse();
var c = s.loadMaybeRef();
return (null == c) as int * 10 + (b != null) as int;
}
fun test8() {
__expect_type(null, "null");
__expect_type([[null]], "[[null]]");
__expect_type(null as tuple?, "tuple?");
__expect_type(null as [int]?, "[int]?");
__expect_type(((null)) as (int, int)?, "(int, int)?");
}
fun main() {
// the compiler optimizes this at compile-time
var i: int? = null;
if (i == null) {
return 1;
}
return 10;
}
/**
@testcase | 101 | | 7 -1 [ 3 2 1 ]
@testcase | 102 | 5 | (null)
@testcase | 102 | null | -1
@testcase | 103 | 5 | 5
@testcase | 103 | 15 | -1
@testcase | 104 | | (null)
@testcase | 107 | | -11
@fif_codegen
"""
test1 PROC:<{
//
PUSHNULL // numbers
1 PUSHINT // numbers '2=1
SWAP // '2=1 numbers
CONS // numbers
2 PUSHINT // numbers '4=2
SWAP // '4=2 numbers
CONS // numbers
3 PUSHINT // numbers '6=3
SWAP // '6=3 numbers
CONS // numbers
4 PUSHINT // numbers '8=4
SWAP // '8=4 numbers
CONS // numbers
UNCONS // h numbers
DUP // h numbers numbers
CAR // h numbers '13
"""
@fif_codegen
"""
main PROC:<{
//
1 PUSHINT // '3=1
}>
"""
@fif_codegen
"""
test7 PROC:<{
...
LDOPTREF // b '9 '8
DROP // b c
ISNULL // b '11
10 MULCONST // b '13
SWAP // '13 b
ISNULL // '13 '14
NOT // '13 '14
ADD // '15
}>
"""
*/