mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
[Tolk] Nullable types T?
and null safety
This commit introduces nullable types `T?` that are distinct from non-nullable `T`. Example: `int?` (int or null) and `int` are different now. Previously, `null` could be assigned to any primitive type. Now, it can be assigned only to `T?`. A non-null assertion operator `!` was also introduced, similar to `!` in TypeScript and `!!` in Kotlin. If `int?` still occupies 1 stack slot, `(int,int)?` and other nullable tensors occupy N+1 slots, the last for "null precedence". `v == null` actually compares that slot. Assigning `(int,int)` to `(int,int)?` implicitly creates a null presence slot. Assigning `null` to `(int,int)?` widens this null value to 3 slots. This is called "type transitioning". All stdlib functions prototypes have been updated to reflect whether they return/accept a nullable or a strict value. This commit also contains refactoring from `const FunctionData*` to `FunctionPtr` and similar.
This commit is contained in:
parent
1389ff6789
commit
f3e620f48c
62 changed files with 2031 additions and 702 deletions
|
@ -2,7 +2,7 @@ import "@stdlib/tvm-lowlevel"
|
|||
|
||||
fun pair_first<X, Y>(p: [X, Y]): X asm "FIRST";
|
||||
|
||||
fun one(dummy: tuple) {
|
||||
fun one(dummy: tuple?) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -206,9 +206,9 @@ fun test116() {
|
|||
|
||||
|
||||
fun main(value: int) {
|
||||
var (x: int, y) = (autoInferIntNull(value), autoInferIntNull(value * 2));
|
||||
var (x: int?, y) = (autoInferIntNull(value), autoInferIntNull(value * 2));
|
||||
if (x == null && y == null) { return null; }
|
||||
return x == null || y == null ? -1 : x + y;
|
||||
return x == null || y == null ? -1 : x! + y!;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,7 +8,7 @@ fun unnamed_args(_: int, _: slice, _: int) {
|
|||
return true;
|
||||
}
|
||||
|
||||
fun main(x: int, y: int, z: int): bool {
|
||||
fun main(x: int, y: int, z: int): bool? {
|
||||
op = `_+_`;
|
||||
if (0) { return null; }
|
||||
return check_assoc(x, y, z);
|
||||
|
|
|
@ -32,7 +32,8 @@ fun test1(): [int,int,int,int,int] {
|
|||
fun test2(): [int,int,int] {
|
||||
var b: builder = beginCell().myStoreInt(1, 32);
|
||||
b = b.myStoreInt(2, 32);
|
||||
b.myStoreInt(3, 32);
|
||||
// operator ! here and below is used just for testing purposes, it doesn't affect the result
|
||||
b!.myStoreInt(3, 32);
|
||||
|
||||
var cs: slice = b.endCell().beginParse();
|
||||
var one: int = cs.myLoadInt(32);
|
||||
|
@ -43,14 +44,14 @@ fun test2(): [int,int,int] {
|
|||
|
||||
@method_id(103)
|
||||
fun test3(ret: int): int {
|
||||
val same: int = beginCell().storeUint(ret,32).endCell().beginParse().loadUint(32);
|
||||
val same: int = beginCell()!.storeUint(ret,32).endCell().beginParse().loadUint(32);
|
||||
return same;
|
||||
}
|
||||
|
||||
@method_id(104)
|
||||
fun test4(): [int,int] {
|
||||
var b: builder = beginCell().myStoreInt(1, 32);
|
||||
b = b.storeInt(2, 32).storeInt(3, 32);
|
||||
var b: builder = (beginCell() as builder).myStoreInt(1, 32);
|
||||
b = b!.storeInt(2, 32)!.storeInt(3, 32);
|
||||
|
||||
var cs: slice = b.endCell().beginParse();
|
||||
var (one, _, three) = (cs.getFirstBits(32).loadUint(32), cs.skipBits(64), cs.load_u32());
|
||||
|
@ -116,7 +117,7 @@ fun test10() {
|
|||
fun test11() {
|
||||
var s: slice = beginCell().storeInt(1, 32).storeInt(2, 32).storeInt(3, 32).storeInt(4, 32).storeInt(5, 32).storeInt(6, 32).storeInt(7, 32).endCell().beginParse();
|
||||
var size1 = getRemainingBitsCount(s);
|
||||
s.skipBits(32);
|
||||
s!.skipBits(32);
|
||||
var s1: slice = s.getFirstBits(64);
|
||||
var n1 = s1.loadInt(32);
|
||||
var size2 = getRemainingBitsCount(s);
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import "@stdlib/tvm-dicts"
|
||||
|
||||
fun addIntToIDict(mutate self: cell, key: int, number: int): void {
|
||||
fun addIntToIDict(mutate self: cell?, key: int, number: int): void {
|
||||
return self.iDictSetBuilder(32, key, beginCell().storeInt(number, 32));
|
||||
}
|
||||
|
||||
fun calculateDictLen(d: cell) {
|
||||
fun calculateDictLen(d: cell?) {
|
||||
var len = 0;
|
||||
var (k, v, f) = d.uDictGetFirst(32);
|
||||
while (f) {
|
||||
len += 1;
|
||||
(k, v, f) = d.uDictGetNext(32, k);
|
||||
(k, v, f) = d.uDictGetNext(32, k!);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
@ -25,13 +25,13 @@ fun loadTwoDigitNumberFromSlice(mutate self: slice): int {
|
|||
fun test101(getK1: int, getK2: int, getK3: int) {
|
||||
var dict = createEmptyDict();
|
||||
dict.uDictSetBuilder(32, 1, beginCell().storeUint(1, 32));
|
||||
var (old1: slice, found1) = dict.uDictSetAndGet(32, getK1, beginCell().storeUint(2, 32).endCell().beginParse());
|
||||
var (old2: slice, found2) = dict.uDictSetAndGet(32, getK2, beginCell().storeUint(3, 32).endCell().beginParse());
|
||||
var (cur3: slice, found3) = dict.uDictGet(32, getK3);
|
||||
var (old1: slice?, found1) = dict.uDictSetAndGet(32, getK1, beginCell().storeUint(2, 32).endCell().beginParse());
|
||||
var (old2: slice?, found2) = dict.uDictSetAndGet(32, getK2, beginCell().storeUint(3, 32).endCell().beginParse());
|
||||
var (cur3: slice?, found3) = dict.uDictGet(32, getK3);
|
||||
return (
|
||||
found1 ? old1.loadUint(32) : -1,
|
||||
found2 ? old2.loadUint(32) : -1,
|
||||
found3 ? cur3.loadUint(32) : -1
|
||||
found1 ? old1!.loadUint(32) : -1,
|
||||
found2 ? old2!.loadUint(32) : -1,
|
||||
found3 ? cur3!.loadUint(32) : -1
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ fun test102() {
|
|||
while (!shouldBreak) {
|
||||
var (kDel, kVal, wasDel) = dict.iDictDeleteLastAndGet(32);
|
||||
if (wasDel) {
|
||||
deleted.tuplePush([kDel, kVal.loadInt(32)]);
|
||||
deleted.tuplePush([kDel, kVal!.loadInt(32)]);
|
||||
} else {
|
||||
shouldBreak = true;
|
||||
}
|
||||
|
@ -82,14 +82,14 @@ fun test104() {
|
|||
var (old2, _) = dict.sDictDeleteAndGet(32, "key1");
|
||||
var (restK, restV, _) = dict.sDictGetFirst(32);
|
||||
var (restK1, restV1, _) = dict.sDictDeleteLastAndGet(32);
|
||||
assert (restK.isSliceBitsEqual(restK1)) throw 123;
|
||||
assert (restV.isSliceBitsEqual(restV1)) throw 123;
|
||||
assert (restK!.isSliceBitsEqual(restK1!)) throw 123;
|
||||
assert (restV!.isSliceBitsEqual(restV1!)) throw 123;
|
||||
return (
|
||||
old1.loadTwoDigitNumberFromSlice(),
|
||||
old2.loadTwoDigitNumberFromSlice(),
|
||||
restV.loadTwoDigitNumberFromSlice(),
|
||||
restK.loadTwoDigitNumberFromSlice(),
|
||||
restK.loadTwoDigitNumberFromSlice()
|
||||
old1!.loadTwoDigitNumberFromSlice(),
|
||||
old2!.loadTwoDigitNumberFromSlice(),
|
||||
restV!.loadTwoDigitNumberFromSlice(),
|
||||
restK!.loadTwoDigitNumberFromSlice(),
|
||||
restK!.loadTwoDigitNumberFromSlice()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -49,17 +49,17 @@ fun manyEq<T1, T2, T3>(a: T1, b: T2, c: T3): [T1, T2, T3] {
|
|||
fun test104(f: int) {
|
||||
var result = (
|
||||
manyEq(1 ? 1 : 1, f ? 0 : null, !f ? getTwo() as int : null),
|
||||
manyEq(f ? null as int : eq2(2), beginCell().storeBool(true).endCell().beginParse().loadBool(), eq4(f))
|
||||
manyEq(f ? null as int? : eq2(2), beginCell().storeBool(true).endCell().beginParse().loadBool(), eq4(f))
|
||||
);
|
||||
__expect_type(result, "([int, int, int], [int, bool, int])");
|
||||
__expect_type(result, "([int, int?, int?], [int?, bool, int])");
|
||||
return result;
|
||||
}
|
||||
|
||||
fun calcSum<X>(x: X, y: X) { return x + y; }
|
||||
fun calcSum<X>(x: X, y: X) { return x! + y!; }
|
||||
|
||||
@method_id(105)
|
||||
fun test105() {
|
||||
if (0) { calcSum(((0)), null); }
|
||||
if (0) { calcSum(((0 as int?)), null); }
|
||||
return (calcSum(1, 2));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import "@stdlib/tvm-dicts"
|
||||
|
||||
fun prepareDict_3_30_4_40_5_x(valueAt5: int): cell {
|
||||
var dict: cell = createEmptyDict();
|
||||
fun prepareDict_3_30_4_40_5_x(valueAt5: int): cell? {
|
||||
var dict: cell? = createEmptyDict();
|
||||
dict.iDictSetBuilder(32, 3, beginCell().storeInt(30, 32));
|
||||
dict.iDictSetBuilder(32, 4, beginCell().storeInt(40, 32));
|
||||
dict.iDictSetBuilder(32, 5, beginCell().storeInt(valueAt5, 32));
|
||||
return dict;
|
||||
}
|
||||
|
||||
fun lookupIdxByValue(idict32: cell, value: int): int {
|
||||
var cur_key = -1;
|
||||
fun lookupIdxByValue(idict32: cell?, value: int): int {
|
||||
var cur_key: int? = -1;
|
||||
do {
|
||||
var (cur_key redef, cs: slice, found: bool) = idict32.iDictGetNext(32, cur_key);
|
||||
var (cur_key redef, cs: slice?, found: bool) = idict32.iDictGetNext(32, cur_key!);
|
||||
// one-line condition (via &) doesn't work, since right side is calculated immediately
|
||||
if (found) {
|
||||
if (cs.loadInt(32) == value) {
|
||||
return cur_key;
|
||||
if (cs!.loadInt(32) == value) {
|
||||
return cur_key!;
|
||||
}
|
||||
}
|
||||
} while (found);
|
||||
|
|
|
@ -86,8 +86,8 @@ fun test104() {
|
|||
}
|
||||
|
||||
@method_id(105)
|
||||
fun test105(x: int, y: int): (tuple, int, (int, int), int, int) {
|
||||
var ab = (createEmptyTuple(), (x, y), tupleSize);
|
||||
fun test105(x: int, y: int): (tuple, int, (int?, int), int, int) {
|
||||
var ab = (createEmptyTuple(), (x as int?, y), tupleSize);
|
||||
ab.0.tuplePush(1);
|
||||
tuplePush(mutate ab.0, 2);
|
||||
ab.1.0 = null;
|
||||
|
@ -98,7 +98,7 @@ fun test105(x: int, y: int): (tuple, int, (int, int), int, int) {
|
|||
|
||||
@method_id(106)
|
||||
fun test106(x: int, y: int) {
|
||||
var ab = [createEmptyTuple(), [x, y], tupleSize];
|
||||
var ab = [createEmptyTuple(), [x as int?, y], tupleSize];
|
||||
ab.0.tuplePush(1);
|
||||
tuplePush(mutate ab.0, 2);
|
||||
ab.1.0 = null;
|
||||
|
@ -233,6 +233,25 @@ fun test121(zero: int) {
|
|||
return t;
|
||||
}
|
||||
|
||||
fun isFirstComponentGt0<T1,T2>(t: (T1, T2)): bool {
|
||||
return t.0 > 0;
|
||||
}
|
||||
|
||||
@method_id(122)
|
||||
fun test122(x: (int, int)) {
|
||||
return (
|
||||
isFirstComponentGt0(x), isFirstComponentGt0((2, beginCell())), isFirstComponentGt0<int,slice?>((0, null)),
|
||||
x.isFirstComponentGt0(), (2, beginCell()).isFirstComponentGt0(), (0, null).isFirstComponentGt0<int,slice?>()
|
||||
);
|
||||
}
|
||||
|
||||
@method_id(123)
|
||||
fun test123() {
|
||||
var t = [[10, 20]] as [[int,int]]?;
|
||||
t!.0.0 = t!.0.1 = 100;
|
||||
return t;
|
||||
}
|
||||
|
||||
fun main(){}
|
||||
|
||||
|
||||
|
@ -258,6 +277,8 @@ fun main(){}
|
|||
@testcase | 119 | 1 2 3 4 | 4 1 3
|
||||
@testcase | 120 | | 3 4 [ 5 6 ]
|
||||
@testcase | 121 | 0 | [ 3 ]
|
||||
@testcase | 122 | 1 2 | -1 -1 0 -1 -1 0
|
||||
@testcase | 123 | | [ [ 100 100 ] ]
|
||||
|
||||
@fif_codegen
|
||||
"""
|
||||
|
|
|
@ -18,10 +18,12 @@ fun test1(x: int, y: int) {
|
|||
__expect_type(random() ? x : y, "int");
|
||||
__expect_type(eq(x), "int");
|
||||
__expect_type(eq<int>(x), "int");
|
||||
__expect_type(eq<int>(null), "int");
|
||||
__expect_type(eq<int?>(null), "int?");
|
||||
__expect_type(x as int, "int");
|
||||
__expect_type(+x, "int");
|
||||
__expect_type(~x, "int");
|
||||
__expect_type(x!, "int");
|
||||
__expect_type(x!!!, "int");
|
||||
{
|
||||
var x: slice = beginCell().endCell().beginParse();
|
||||
__expect_type(x, "slice");
|
||||
|
@ -62,9 +64,9 @@ fun test5(x: int) {
|
|||
__expect_type([], "[]");
|
||||
__expect_type([x], "[int]");
|
||||
__expect_type([x, x >= 1], "[int, bool]");
|
||||
__expect_type([x, x >= 1, null as slice], "[int, bool, slice]");
|
||||
__expect_type([x, x >= 1, null as slice?], "[int, bool, slice?]");
|
||||
__expect_type((x, [x], [[x], x]), "(int, [int], [[int], int])");
|
||||
__expect_type(getMyOriginalBalanceWithExtraCurrencies(), "[int, cell]");
|
||||
__expect_type(getMyOriginalBalanceWithExtraCurrencies(), "[int, cell?]");
|
||||
}
|
||||
|
||||
fun test6() {
|
||||
|
|
|
@ -6,5 +6,5 @@ fun failCantDeduceWithoutArgument() {
|
|||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not deduce X for generic function `f<X>`
|
||||
@stderr too few arguments in call to `f`, expected 2, have 1
|
||||
*/
|
||||
|
|
11
tolk-tester/tests/invalid-generics-13.tolk
Normal file
11
tolk-tester/tests/invalid-generics-13.tolk
Normal file
|
@ -0,0 +1,11 @@
|
|||
fun calcSum<X>(x: X, y: X) { return x + y; }
|
||||
|
||||
fun cantApplyPlusOnNullable() {
|
||||
return calcSum(((0 as int?)), null);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr while instantiating generic function `calcSum<int?>`
|
||||
@stderr can not apply operator `+` to `int?` and `int?`
|
||||
*/
|
10
tolk-tester/tests/invalid-mutate-18.tolk
Normal file
10
tolk-tester/tests/invalid-mutate-18.tolk
Normal file
|
@ -0,0 +1,10 @@
|
|||
fun getNullableTuple(): tuple? { return createEmptyTuple(); }
|
||||
|
||||
fun cantUseLValueUnwrappedNotNull() {
|
||||
tuplePush(mutate getNullableTuple()!, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr function call can not be used as lvalue
|
||||
*/
|
10
tolk-tester/tests/invalid-mutate-19.tolk
Normal file
10
tolk-tester/tests/invalid-mutate-19.tolk
Normal file
|
@ -0,0 +1,10 @@
|
|||
fun getNullableTuple(): tuple? { return createEmptyTuple(); }
|
||||
|
||||
fun cantUseLValueUnwrappedNotNull() {
|
||||
tuplePush(mutate getNullableTuple()!, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr function call can not be used as lvalue
|
||||
*/
|
13
tolk-tester/tests/invalid-mutate-20.tolk
Normal file
13
tolk-tester/tests/invalid-mutate-20.tolk
Normal file
|
@ -0,0 +1,13 @@
|
|||
fun acceptMutateNullableTensor(mutate self: (int, int)?) {
|
||||
}
|
||||
|
||||
fun cantModifyTupleIndexWithTypeTransition() {
|
||||
var t = [1, null];
|
||||
t.1.acceptMutateNullableTensor();
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not call method for mutate `(int, int)?` with object of type `null`
|
||||
@stderr because mutation is not type compatible
|
||||
*/
|
14
tolk-tester/tests/invalid-typing-14.tolk
Normal file
14
tolk-tester/tests/invalid-typing-14.tolk
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
fun autoGetIntOrNull() {
|
||||
if (random()) { return 1; }
|
||||
return null;
|
||||
}
|
||||
|
||||
fun testAutoInferredIntOrNull() {
|
||||
var b: builder = autoGetIntOrNull() as builder;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr type `int?` can not be cast to `builder`
|
||||
*/
|
13
tolk-tester/tests/invalid-typing-15.tolk
Normal file
13
tolk-tester/tests/invalid-typing-15.tolk
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
fun getNullable4(): int? {
|
||||
return 4;
|
||||
}
|
||||
|
||||
fun testCantSumNullable() {
|
||||
return 1 + getNullable4();
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `+` to `int` and `int?`
|
||||
*/
|
13
tolk-tester/tests/invalid-typing-16.tolk
Normal file
13
tolk-tester/tests/invalid-typing-16.tolk
Normal file
|
@ -0,0 +1,13 @@
|
|||
@pure
|
||||
fun myDictDeleteStrict(mutate self: cell, keyLen: int, key: int): bool
|
||||
asm(key self keyLen) "DICTIDEL";
|
||||
|
||||
|
||||
fun testCantCallDictMethodsOnNullable(c: cell) {
|
||||
c.beginParse().loadDict().myDictDeleteStrict(16, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not call method for `cell` with object of type `cell?`
|
||||
*/
|
10
tolk-tester/tests/invalid-typing-17.tolk
Normal file
10
tolk-tester/tests/invalid-typing-17.tolk
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
fun testCantUseNullableAsCondition(x: int?) {
|
||||
if (x) { return 1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not use `int?` as a boolean condition
|
||||
*/
|
16
tolk-tester/tests/invalid-typing-18.tolk
Normal file
16
tolk-tester/tests/invalid-typing-18.tolk
Normal file
|
@ -0,0 +1,16 @@
|
|||
fun incrementOrSetNull(mutate x: int?) {
|
||||
if (random()) { x! += 1; }
|
||||
else { x = null; }
|
||||
}
|
||||
|
||||
fun cantCallMutateMethodNotNullable() {
|
||||
var x = 1;
|
||||
incrementOrSetNull(mutate x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not pass `int` to mutate `int?`
|
||||
@stderr because mutation is not type compatible
|
||||
*/
|
|
@ -53,9 +53,8 @@ fun testDict(last: int) {
|
|||
}
|
||||
|
||||
@method_id(105)
|
||||
fun testNotNull(x: int) {
|
||||
// return [x == null, null == x, !(x == null), null == null, +(null != null)];
|
||||
return [x == null, null == x, !(x == null)];
|
||||
fun testNotNull(x: int?) {
|
||||
return [x == null, null == x, !(x == null), null == null, (null != null) as int];
|
||||
}
|
||||
|
||||
@method_id(106)
|
||||
|
@ -170,8 +169,8 @@ fun main() {
|
|||
@testcase | 104 | 50 | 3 5 -1
|
||||
@testcase | 104 | 100 | 3 5 5
|
||||
@testcase | 104 | 0 | 3 -1 5
|
||||
@testcase | 105 | 0 | [ 0 0 -1 ]
|
||||
@testcase | 105 | null | [ -1 -1 0 ]
|
||||
@testcase | 105 | 0 | [ 0 0 -1 -1 0 ]
|
||||
@testcase | 105 | null | [ -1 -1 0 -1 0 ]
|
||||
@testcase | 106 | | [ 0 0 0 -1 ] [ 0 0 0 ] [ -1 -1 -1 ] [ 0 -1 ]
|
||||
@testcase | 107 | | [ -1 -1 0 -1 ] [ 0 0 0 ] [ -1 -1 -1 ] [ -1 0 ]
|
||||
@testcase | 108 | 1 2 | -1
|
||||
|
|
|
@ -2,13 +2,13 @@ import "@stdlib/lisp-lists"
|
|||
|
||||
@method_id(101)
|
||||
fun test1() {
|
||||
var numbers: tuple = createEmptyList();
|
||||
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);
|
||||
var (h: int, numbers redef) = listSplit(numbers!);
|
||||
h += listGetHead(numbers!);
|
||||
|
||||
_ = null;
|
||||
(_, _) = (null, null);
|
||||
|
@ -22,22 +22,22 @@ fun test1() {
|
|||
}
|
||||
|
||||
@method_id(102)
|
||||
fun test2(x: int) {
|
||||
fun test2(x: int?) {
|
||||
if (null != x) {
|
||||
var y: int = null;
|
||||
var y: int? = null;
|
||||
if (y != null) { return 10; }
|
||||
return y;
|
||||
}
|
||||
try {
|
||||
return x + 10; // will throw, since not a number
|
||||
return x! + 10; // will throw, since not a number
|
||||
} catch {
|
||||
return -1;
|
||||
}
|
||||
return 100;
|
||||
}
|
||||
|
||||
fun myIsNull(x: int): int {
|
||||
return x == null ? -1 : x;
|
||||
fun myIsNull(x: int?): int {
|
||||
return x == null ? -1 : x!;
|
||||
}
|
||||
|
||||
@method_id(103)
|
||||
|
@ -64,21 +64,28 @@ fun test4(): null {
|
|||
|
||||
@method_id(105)
|
||||
fun test5() {
|
||||
var n: slice = getUntypedNull();
|
||||
return !(null == n) ? n.loadInt(32) : 100;
|
||||
var n: slice? = getUntypedNull();
|
||||
return !(null == n) ? n!.loadInt(32) : 100;
|
||||
}
|
||||
|
||||
@method_id(107)
|
||||
fun test7() {
|
||||
var b = beginCell().storeMaybeRef(null);
|
||||
var s = b.endCell().beginParse();
|
||||
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() {
|
||||
// now, the compiler doesn't optimize this at compile-time, fif codegen contains ifs
|
||||
var i: int = null;
|
||||
var i: int? = null;
|
||||
if (i == null) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -120,12 +127,12 @@ fun main() {
|
|||
"""
|
||||
main PROC:<{
|
||||
//
|
||||
PUSHNULL // i
|
||||
ISNULL // '2
|
||||
IFJMP:<{ //
|
||||
1 PUSHINT // '3=1
|
||||
}> //
|
||||
10 PUSHINT // '4=10
|
||||
PUSHNULL // i
|
||||
ISNULL // '2
|
||||
IFJMP:<{ //
|
||||
1 PUSHINT // '3=1
|
||||
}> //
|
||||
10 PUSHINT // '4=10
|
||||
}>
|
||||
"""
|
||||
|
||||
|
@ -139,8 +146,8 @@ fun main() {
|
|||
10 MULCONST // b '13
|
||||
SWAP // '13 b
|
||||
ISNULL // '13 '14
|
||||
NOT // '13 '15
|
||||
ADD // '16
|
||||
NOT // '13 '14
|
||||
ADD // '15
|
||||
}>
|
||||
"""
|
||||
*/
|
||||
|
|
474
tolk-tester/tests/nullable-tensors.tolk
Normal file
474
tolk-tester/tests/nullable-tensors.tolk
Normal file
|
@ -0,0 +1,474 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun sumOfNullableTensorComponents(t: (int, int)?): int {
|
||||
if (t == null) { return 0; }
|
||||
return t!.0 + t!.1;
|
||||
}
|
||||
|
||||
fun isTensorNull(t: (int, int)?) {
|
||||
return t == null;
|
||||
}
|
||||
|
||||
fun incrementNullableTensorComponents(mutate self: (int, int)?): self {
|
||||
if (self != null) {
|
||||
self!.0 += 1;
|
||||
self!.1 += 1;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
fun incrementTensorComponents(mutate self: (int, int)): self {
|
||||
self.0 += 1;
|
||||
self.1 += 1;
|
||||
return self;
|
||||
}
|
||||
|
||||
fun assignFirstComponent(mutate t: (int, int), first: int) {
|
||||
t!.0 = first;
|
||||
}
|
||||
|
||||
fun assignFirstComponentNullable(mutate t: (int, int)?, first: int) {
|
||||
if (t == null) {
|
||||
t = (first, 0);
|
||||
} else {
|
||||
t!.0 = first;
|
||||
}
|
||||
}
|
||||
|
||||
fun getNullableTensor(firstComponent: int?): (int, int)? {
|
||||
return firstComponent == null ? null : (firstComponent!, 2);
|
||||
}
|
||||
|
||||
fun sumOfTensor(x: (int, int)) {
|
||||
return x.0 + x.1;
|
||||
}
|
||||
|
||||
fun assignNullTo<T>(mutate x: T?) {
|
||||
x = null;
|
||||
}
|
||||
|
||||
fun getTensor12() {
|
||||
return (1,2);
|
||||
}
|
||||
|
||||
@method_id(101)
|
||||
fun test101(): (int, int)? {
|
||||
return (1, 2);
|
||||
}
|
||||
|
||||
@method_id(102)
|
||||
fun test102(): ((int, int)?, (int, int)?) {
|
||||
var t = (1, 2);
|
||||
return (t, null);
|
||||
}
|
||||
|
||||
@method_id(103)
|
||||
fun test103(t: (int, int)) {
|
||||
var t2: (int, int)? = t;
|
||||
return (sumOfNullableTensorComponents(t), sumOfNullableTensorComponents(t2), sumOfNullableTensorComponents(null), t2);
|
||||
}
|
||||
|
||||
@method_id(104)
|
||||
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_2 = t2_1;
|
||||
return (t1_3, t2_2);
|
||||
}
|
||||
|
||||
@method_id(105)
|
||||
fun test105() {
|
||||
return (null as (int, slice, cell)?, (1, 2, 3) as (int, int, int)?);
|
||||
}
|
||||
|
||||
@method_id(106)
|
||||
fun test106() {
|
||||
var t: (int?, int?)? = (((((1, 2))) as (int, int)));
|
||||
return t;
|
||||
}
|
||||
|
||||
@method_id(107)
|
||||
fun test107() {
|
||||
var ab = (1, 2);
|
||||
var ab2: (int, int)? = ab;
|
||||
return (isTensorNull(ab), isTensorNull(ab2), isTensorNull(null), ab.isTensorNull(), ab2.isTensorNull(), null.isTensorNull());
|
||||
}
|
||||
|
||||
@method_id(108)
|
||||
fun test108(x1: (int, int)) {
|
||||
incrementTensorComponents(mutate x1);
|
||||
x1.incrementTensorComponents();
|
||||
var x2: (int, int)? = x1;
|
||||
x2.incrementNullableTensorComponents().incrementNullableTensorComponents();
|
||||
incrementNullableTensorComponents(mutate x2);
|
||||
var x3: (int, int)? = null;
|
||||
x3.incrementNullableTensorComponents().incrementNullableTensorComponents();
|
||||
incrementNullableTensorComponents(mutate x3);
|
||||
return (x1, x2, x3);
|
||||
}
|
||||
|
||||
fun isTensorNullGen<T1, T2>(t: (T1, T2)?) {
|
||||
return t == null;
|
||||
}
|
||||
|
||||
@method_id(109)
|
||||
fun test109() {
|
||||
var x1 = (1, 2);
|
||||
var x2: (int, int)? = x1;
|
||||
var x3: (int, int)? = x1.1 > 10 ? (1, 2) : null;
|
||||
return (
|
||||
isTensorNullGen(x1), isTensorNullGen(x2), isTensorNullGen<int,int>(null),
|
||||
isTensorNullGen<int,int>(x1), isTensorNullGen<int,int>(x3),
|
||||
x1.isTensorNullGen(), x2.isTensorNullGen(), x3.isTensorNullGen(), null.isTensorNullGen<int,int>()
|
||||
);
|
||||
}
|
||||
|
||||
global g110_1: (int, int);
|
||||
global g110_2: (int, int)?;
|
||||
|
||||
@method_id(110)
|
||||
fun test110() {
|
||||
g110_1 = getNullableTensor(1)!;
|
||||
incrementTensorComponents(mutate g110_1);
|
||||
g110_1.incrementTensorComponents();
|
||||
g110_2 = g110_1;
|
||||
g110_2.incrementNullableTensorComponents().incrementNullableTensorComponents();
|
||||
incrementNullableTensorComponents(mutate g110_2);
|
||||
var tmp = g110_2;
|
||||
g110_2 = null;
|
||||
g110_2.incrementNullableTensorComponents();
|
||||
incrementNullableTensorComponents(mutate g110_2);
|
||||
return (g110_1, g110_2, tmp);
|
||||
}
|
||||
|
||||
@method_id(111)
|
||||
fun test111() {
|
||||
var x = (1, 2);
|
||||
assignFirstComponent(mutate x, 50);
|
||||
var x2: (int, int)? = null;
|
||||
var x3 = x2;
|
||||
assignFirstComponentNullable(mutate x2, 30);
|
||||
assignFirstComponentNullable(mutate x3, 70);
|
||||
g110_1 = (1, 2);
|
||||
g110_2 = null;
|
||||
assignFirstComponent(mutate g110_1, 90);
|
||||
assignFirstComponentNullable(mutate g110_2, 100);
|
||||
return (x.0, x2!.0, x3!.0, g110_1.0, g110_2!.0);
|
||||
}
|
||||
|
||||
@method_id(112)
|
||||
fun test112() {
|
||||
var x: (int, int)? = (10, 20);
|
||||
incrementTensorComponents(mutate x!);
|
||||
x!.incrementTensorComponents();
|
||||
return x;
|
||||
}
|
||||
|
||||
@method_id(113)
|
||||
fun test113() {
|
||||
var t = [1, null]; // t.1 is always null
|
||||
return isTensorNull(t.1);
|
||||
}
|
||||
|
||||
@method_id(114)
|
||||
fun test114(): ((slice, (cell, [int, slice, tuple]))?, slice?, (int?, bool?)?) {
|
||||
var t = [[null]];
|
||||
return (t.0.0, t.0.0, t.0.0);
|
||||
}
|
||||
|
||||
@method_id(115)
|
||||
fun test115() {
|
||||
var tt = getNullableTensor(null);
|
||||
assignFirstComponentNullable(mutate tt, 5);
|
||||
return (
|
||||
getNullableTensor(1)!.incrementTensorComponents(),
|
||||
sumOfNullableTensorComponents(getNullableTensor(1).incrementNullableTensorComponents().incrementNullableTensorComponents()),
|
||||
getNullableTensor(null).incrementNullableTensorComponents(),
|
||||
tt,
|
||||
sumOfNullableTensorComponents(getNullableTensor(null))
|
||||
);
|
||||
}
|
||||
|
||||
@method_id(116)
|
||||
fun test116(returnNull: bool) {
|
||||
var t1: (int, int)? = returnNull ? null : getTensor12();
|
||||
var t2 = returnNull ? null as (int, int)? : getTensor12() as (int, int)?;
|
||||
returnNull ? null : (1, 2);
|
||||
return (t1, t2);
|
||||
}
|
||||
|
||||
@method_id(117)
|
||||
fun test117() {
|
||||
var (a, b: (int, int)?, c) = (1, null, 3);
|
||||
return (b, a, c);
|
||||
}
|
||||
|
||||
fun autoInferNullableTensor(a: int?, b: int) {
|
||||
if (a != null) {
|
||||
return (a!, b);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@method_id(118)
|
||||
fun test118(a: int?) {
|
||||
return autoInferNullableTensor(a, 10);
|
||||
}
|
||||
|
||||
@method_id(119)
|
||||
fun test119() {
|
||||
var x: (int, int)? = (1, 2);
|
||||
x = null;
|
||||
var tt: (int, (int, int)?) = (0, (1, 2));
|
||||
tt.1 = null;
|
||||
var third: (int, (int, int)?, int) = (0, (1, 2), 3);
|
||||
third.2 = 100;
|
||||
return (x, tt.1, third.1, third.2);
|
||||
}
|
||||
|
||||
@method_id(120)
|
||||
fun test120(setNull: bool) {
|
||||
var x: (int, int)? = (1, 2);
|
||||
if (setNull) {
|
||||
assignNullTo(mutate x);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
@method_id(121)
|
||||
fun test121() {
|
||||
var t: [int?, [int?, int?]?] = [1, [2, 3]];
|
||||
t.1 = [3, 4];
|
||||
return t;
|
||||
}
|
||||
|
||||
@method_id(122)
|
||||
fun test122(setNull: bool) {
|
||||
var t: [int?, [int?, int?]?, int?, [int?, int?]?]? = [1, [2, 3], 4, null];
|
||||
if (setNull) {
|
||||
assignNullTo(mutate t!.1);
|
||||
} else {
|
||||
var rhs = [3, 4];
|
||||
t!!.1 = rhs;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
@method_id(123)
|
||||
fun test123() {
|
||||
var t: (int?, (int?, int?)?) = (1, (2, 3));
|
||||
t.1 = (3, 4);
|
||||
return t;
|
||||
}
|
||||
|
||||
@method_id(124)
|
||||
fun test124(setNull: bool) {
|
||||
var t: (int?, (int?, int?)?, int?, (int?, int?)?)? = (1, (2, 3), 4, null);
|
||||
if (setNull) {
|
||||
assignNullTo(mutate t!.1);
|
||||
} else {
|
||||
var rhs = (3, 4);
|
||||
t!!.1 = rhs;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
global g125: int;
|
||||
fun getT125(): (int, (int, int)?, (int?, int)?) { return (g125 += 1, null, null); }
|
||||
|
||||
@method_id(125)
|
||||
fun test125() {
|
||||
g125 = 0;
|
||||
getT125().1 = null;
|
||||
getT125().2 = (1, 2);
|
||||
(getT125()!! as (int, (int, int)?, (int?, int)?)).2 = null;
|
||||
// test that nothing left on a stack
|
||||
return g125;
|
||||
}
|
||||
|
||||
@method_id(126)
|
||||
fun test126() {
|
||||
var tt1: (int, null, int) = (1, null, 2);
|
||||
var (a: int, b: (int, int)?, c: int) = tt1;
|
||||
return (a, b, c);
|
||||
}
|
||||
|
||||
@method_id(127)
|
||||
fun test127(choice: int) {
|
||||
var tt1: (int, null, int) = (1, null, 2);
|
||||
var tt2: (int, (int, int), int) = (1, (2, 3), 4);
|
||||
var tt3: (int, (int, int)?, int) = (1, null, 5);
|
||||
var abc: (int, (int, int)?, int) = choice == 1 ? tt1 : choice == 2 ? tt2 : tt3;
|
||||
return abc;
|
||||
}
|
||||
|
||||
fun get128_1() { return (1, null, 2); }
|
||||
fun get128_2() { return null; }
|
||||
fun get128_3() { return (1, (2, 3), 4); }
|
||||
fun takeT128(abc: (int, (int, int)?, int)?) { return abc; }
|
||||
|
||||
@method_id(128)
|
||||
fun test128(choice: int) {
|
||||
if (choice == 1) {
|
||||
return takeT128(get128_1())!;
|
||||
}
|
||||
if (choice == 2) {
|
||||
return takeT128(get128_2());
|
||||
}
|
||||
return takeT128(get128_3());
|
||||
}
|
||||
|
||||
@method_id(129)
|
||||
fun test129(setNull: bool) {
|
||||
var t: (int?, int?) = (getNullableInt(), getNullableInt());
|
||||
var r1 = (t, t == null, t != null);
|
||||
t = (setNull ? null : 1, setNull ? null : 2);
|
||||
var r2 = (t, t == null, t != null);
|
||||
return (r1, r2);
|
||||
}
|
||||
|
||||
@method_id(130)
|
||||
fun test130(setNull: bool) {
|
||||
var os: (int, (int, int)?) = (1, setNull ? null : (2, 3));
|
||||
return os;
|
||||
}
|
||||
|
||||
fun getEmptyNullableTensor(getNull: bool): ()? {
|
||||
return getNull ? null : ();
|
||||
}
|
||||
|
||||
@method_id(131)
|
||||
fun test131() {
|
||||
var nonNullEmptyT = getEmptyNullableTensor(false);
|
||||
var nullEmptyT = getEmptyNullableTensor(true);
|
||||
var emptyT = nonNullEmptyT!;
|
||||
__expect_type(emptyT, "()");
|
||||
var doubleNulls1 = (null, null) as (()?, ()?);
|
||||
var doubleNulls2 = ((), ()) as (()?, ()?);
|
||||
var doubleNulls3 = ((), ()) as (()?, ()?)?;
|
||||
var stillEmpty = ((), ());
|
||||
return (nonNullEmptyT, 777, nullEmptyT, 777, emptyT, 777, nullEmptyT!, 777, doubleNulls1, doubleNulls2, 777, doubleNulls3, 777, stillEmpty);
|
||||
}
|
||||
|
||||
@method_id(132)
|
||||
fun test132() {
|
||||
var doubleNulls: (()?, ()?) = (getEmptyNullableTensor(true), getEmptyNullableTensor(false));
|
||||
var result = ((null as ()?) == null, (() as ()?) == null, doubleNulls.0 == null, doubleNulls.1 == null);
|
||||
var aln1: int? = (doubleNulls.1 = null);
|
||||
var aln2: null = (doubleNulls.1 = null);
|
||||
return (result, 777, aln1, aln2, doubleNulls.1 == null, doubleNulls);
|
||||
}
|
||||
|
||||
|
||||
fun getNormalNullableTensorWidth1(vLess100: int?): ([int?], ())? {
|
||||
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) {
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
return ((), (vLess100, ()), ());
|
||||
}
|
||||
|
||||
@method_id(135)
|
||||
fun test135() {
|
||||
var n1 = getNormalNullableTensorWidth1(10); // ([10], ())
|
||||
var n2 = getNormalNullableTensorWidth1(null); // ([null], ())
|
||||
var n3 = getNormalNullableTensorWidth1(100); // null
|
||||
var t1 = getTrickyNullableTensorWidth1(10); // (10, ())
|
||||
var t2 = getTrickyNullableTensorWidth1(null); // (null, ())
|
||||
var t3 = getTrickyNullableTensorWidth1(100); // null
|
||||
var e1 = getEvenTrickierNullableWidth1(10); // ((), (10, ()), ())
|
||||
var e2 = getEvenTrickierNullableWidth1(null); // ((), (null, (), ())
|
||||
var e3 = getEvenTrickierNullableWidth1(100); // null
|
||||
return (n1, n2, n3, 777, t1, t2, t3, 777, e1, e2, e3, 777,
|
||||
n1 == null, n2 == null, n3 == null, t1 == null, t2 == null, t3 == null, e1 == null, e2 == null, e3 == null, 777,
|
||||
t1!.0 == null, t2!.0 == null, e1!.1.0 == null, e1!.1.1 == null, e2!.1.0 == null, e2!.1.1 == null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 | 105 | | (null) (null) (null) 0 1 2 3 -1
|
||||
@testcase | 106 | | 1 2 -1
|
||||
@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 | 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 | 118 | 5 | 5 10 -1
|
||||
@testcase | 118 | null | (null) (null) 0
|
||||
@testcase | 119 | | (null) (null) 0 (null) (null) 0 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 | 125 | | 3
|
||||
@testcase | 126 | | 1 (null) (null) 0 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
|
||||
@testcase | 128 | 1 | 1 (null) (null) 0 2 -1
|
||||
@testcase | 128 | 2 | (null) (null) (null) (null) (null) 0
|
||||
@testcase | 128 | 3 | 1 2 3 -1 4 -1
|
||||
@testcase | 129 | 0 | 5 5 0 -1 1 2 0 -1
|
||||
@testcase | 129 | -1 | 5 5 0 -1 (null) (null) 0 -1
|
||||
@testcase | 130 | 0 | 1 2 3 -1
|
||||
@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 | 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
|
||||
"""
|
||||
isTensorNull PROC:<{
|
||||
// t.0 t.1 t.NNFlag
|
||||
2 1 BLKDROP2 // t.NNFlag
|
||||
0 EQINT // '3
|
||||
}>
|
||||
"""
|
||||
|
||||
@fif_codegen
|
||||
"""
|
||||
test113 PROC:<{
|
||||
//
|
||||
1 PUSHINT // '2=1
|
||||
PUSHNULL // '2=1 '3
|
||||
PAIR // t
|
||||
1 INDEX // '5
|
||||
PUSHNULL // '5 '6
|
||||
0 PUSHINT // '5 '6 '7=0
|
||||
isTensorNull CALLDICT // '8
|
||||
}>
|
||||
"""
|
||||
*/
|
109
tolk-tester/tests/nullable-types.tolk
Normal file
109
tolk-tester/tests/nullable-types.tolk
Normal file
|
@ -0,0 +1,109 @@
|
|||
|
||||
fun getNullable4(): int? { return 4; }
|
||||
fun getNullableIntNull(): int? asm "PUSHNULL";
|
||||
|
||||
fun eqInt(x: int) { return x; }
|
||||
fun eq<T>(x: T) { return x; }
|
||||
|
||||
fun unwrap<T>(x: T?): T { return x!; }
|
||||
fun intOr0(x: int?): int { return null == x ? 0 : x!; }
|
||||
|
||||
@method_id(101)
|
||||
fun test101(x: int) {
|
||||
var re = x == 0 ? null : 100;
|
||||
return re == null ? re : 200 + getNullable4()!;
|
||||
}
|
||||
|
||||
@method_id(102)
|
||||
fun test102(a: int) {
|
||||
try {
|
||||
throw (123, a > 10 ? null : a);
|
||||
return 0;
|
||||
} catch (excno, arg) {
|
||||
var i = arg as int?;
|
||||
return excno + (i != null ? i!!!!! : -100);
|
||||
}
|
||||
}
|
||||
|
||||
@method_id(103)
|
||||
fun test103(x: int?): (bool, bool, int) {
|
||||
var x_gt_0 = x != null && eqInt(x!) > 0;
|
||||
var x_lt_0 = x != null && eq(x)! < 0;
|
||||
if (x == null) {
|
||||
return (x_gt_0, x_lt_0, 0);
|
||||
}
|
||||
return (x_gt_0, x_lt_0, x!);
|
||||
}
|
||||
|
||||
@method_id(104)
|
||||
fun test104(x: int?) {
|
||||
var x2 = eq(x = 10);
|
||||
var ab = (x2, getNullableIntNull());
|
||||
return (unwrap(ab.0) + (ab.1 == null ? -100 : ab.1!), ab.1);
|
||||
}
|
||||
|
||||
@method_id(105)
|
||||
fun test105() {
|
||||
var xy: (int?, int?) = (5, null);
|
||||
var ab = [1 ? [xy.0, xy.1] : null];
|
||||
ab.0!.0 = intOr0(ab.0!.0);
|
||||
ab.0!.1 = intOr0(ab.0!.1);
|
||||
return ab.0!.0! + ab.0!.1!;
|
||||
}
|
||||
|
||||
global gTup106: tuple?;
|
||||
global gInt106: int?;
|
||||
|
||||
@method_id(106)
|
||||
fun test106() {
|
||||
gInt106 = 0;
|
||||
gInt106! += 5;
|
||||
var int106: int? = 0;
|
||||
var gTup106 = createEmptyTuple();
|
||||
gTup106!.tuplePush(createEmptyTuple());
|
||||
(gTup106!.0 as tuple?)!.tuplePush(0 as int?);
|
||||
tuplePush(mutate gTup106!, gInt106);
|
||||
tuplePush(mutate gTup106!.0, int106! += 1);
|
||||
return (gTup106 == null, null != gTup106, gTup106, gTup106!.0 as tuple?);
|
||||
}
|
||||
|
||||
@method_id(107)
|
||||
fun test107() {
|
||||
var b: builder? = beginCell();
|
||||
b!.storeInt(1, 32).storeInt(2, 32);
|
||||
b = b!.storeInt(3, 32);
|
||||
storeInt(mutate b!, 4, 32);
|
||||
(b! as builder).storeInt(5, 32);
|
||||
return b!.getBuilderBitsCount();
|
||||
}
|
||||
|
||||
@method_id(108)
|
||||
fun test108() {
|
||||
var (a, b: cell?, c) = (1, beginCell().endCell(), 3);
|
||||
b = null;
|
||||
return a + (b == null ? 0 : b!.beginParse().loadInt(32)) + c;
|
||||
}
|
||||
|
||||
@method_id(109)
|
||||
fun test109() {
|
||||
var a = getNullable4();
|
||||
var b = getNullable4();
|
||||
return ([a, b] = [3, 4], a, b);
|
||||
}
|
||||
|
||||
fun main(x: int?, y: int?) {
|
||||
}
|
||||
|
||||
/**
|
||||
@testcase | 101 | 0 | (null)
|
||||
@testcase | 101 | -1 | 204
|
||||
@testcase | 102 | 5 | 128
|
||||
@testcase | 102 | 15 | 23
|
||||
@testcase | 103 | 10 | -1 0 10
|
||||
@testcase | 104 | 8 | -90 (null)
|
||||
@testcase | 105 | | 5
|
||||
@testcase | 106 | | 0 -1 [ [ 0 1 ] 5 ] [ 0 1 ]
|
||||
@testcase | 107 | | 160
|
||||
@testcase | 108 | | 4
|
||||
@testcase | 109 | | [ 3 4 ] 3 4
|
||||
*/
|
|
@ -27,8 +27,8 @@ fun test1(): int {
|
|||
var demo_var: int = demo_10;
|
||||
var demo_slice: int = demo_20;
|
||||
if (demo_var > 0) {
|
||||
var demo_var: tuple = null;
|
||||
var demo_slice: tuple = null;
|
||||
var demo_var: tuple? = null;
|
||||
var demo_slice: tuple? = null;
|
||||
}
|
||||
return demo_var + demo_slice;
|
||||
}
|
||||
|
|
|
@ -138,6 +138,43 @@ fun testIndexedAccessApply() {
|
|||
return functions2.0(functions1.1(b)).loadInt(32);
|
||||
}
|
||||
|
||||
fun getNullable4(): int? { return 4; }
|
||||
fun myBeginCell(): builder? asm "NEWC";
|
||||
|
||||
@method_id(108)
|
||||
fun testCallingNotNull() {
|
||||
var n4: () -> int? = getNullable4;
|
||||
var creator: (() -> builder?)? = myBeginCell;
|
||||
var end2: [int, (builder -> cell)?] = [0, endCell];
|
||||
var c: cell = end2.1!((creator!()!)!.storeInt(getNullable4()!, 32));
|
||||
return c.beginParse().loadInt(32);
|
||||
}
|
||||
|
||||
fun sumOfTensorIfNotNull(t: (int, int)?) {
|
||||
if (t == null) { return 0; }
|
||||
return t!.0 + t!.1;
|
||||
}
|
||||
|
||||
@method_id(109)
|
||||
fun testTypeTransitionOfVarCall() {
|
||||
var summer = sumOfTensorIfNotNull;
|
||||
var hh1 = [1, null];
|
||||
var tt1 = (3, 4);
|
||||
return (summer(null), summer((1,2)), summer(hh1.1), summer(tt1));
|
||||
}
|
||||
|
||||
fun makeTensor(x1: int, x2: int, x3: int, x4: int, x5: int) {
|
||||
return (x1, x2, x3, x4, x5);
|
||||
}
|
||||
|
||||
fun eq<T>(x: T): T { return x; }
|
||||
|
||||
@method_id(110)
|
||||
fun testVarsModificationInsideVarCall(x: int) {
|
||||
var cb = makeTensor;
|
||||
return x > 3 ? cb(x, x += 5, eq(x *= x), x, eq(x)) : null;
|
||||
}
|
||||
|
||||
fun main() {}
|
||||
|
||||
/**
|
||||
|
@ -148,4 +185,8 @@ fun main() {}
|
|||
@testcase | 105 | | 1
|
||||
@testcase | 106 | | 1 1 [ 2 ] [ 2 ]
|
||||
@testcase | 107 | | 65537
|
||||
@testcase | 108 | | 4
|
||||
@testcase | 109 | | 0 3 0 7
|
||||
@testcase | 110 | 5 | 5 10 100 100 100 -1
|
||||
@testcase | 110 | 0 | (null) (null) (null) (null) (null) 0
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue