1
0
Fork 0
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:
tolk-vm 2025-02-24 20:13:36 +03:00
parent 1389ff6789
commit f3e620f48c
No known key found for this signature in database
GPG key ID: 7905DD7FE0324B12
62 changed files with 2031 additions and 702 deletions

View 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
*/