Skip to content

Commit 705a419

Browse files
committed
[Tolk] More correctly generate a serialization prefix tree for nullable unions
Now, treat `T?` as `Maybe<T>` even if T is a union. Example: A|B|C|D|null => 0 | 100+A | 101+B | 110+C | 111+D. If no `null`, just distribute sequentially: A|B|C => 00+A | 01+B | 10+C
1 parent 38a95cb commit 705a419

File tree

4 files changed

+48
-17
lines changed

4 files changed

+48
-17
lines changed

tolk-tester/tests/lazy-load-tests.tolk

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,18 @@ struct WithGlobalModifier {
241241
n: int8;
242242
}
243243

244+
struct WithN5 { n: uint5 }
245+
type U111 = int8 | int16 | WithN5 | bits100
246+
struct WithU111 {
247+
a: U111
248+
b: U111?
249+
bit: bool
250+
}
251+
fun WithU111.getSlice_a8_bnull(): slice asm "b{000000000101} PUSHSLICE"
252+
fun WithU111.getSlice_a16_b8(): slice asm "b{010000000000000001100000000011} PUSHSLICE"
253+
fun WithU111.getSlice_a8_b5(): slice asm "b{0000000001110111111} PUSHSLICE"
254+
fun WithU111.getSlice_a8_b5_nobit(): slice asm "b{000000000111011111} PUSHSLICE"
255+
244256

245257
@noinline
246258
fun contract.getFakeData(seqno: int): Cell<WalletStorage> {
@@ -981,6 +993,21 @@ fun demo158() {
981993
return (m2.n, gModByCustom, after1); // also modifies by skipping (unpack called)
982994
}
983995

996+
@method_id(159)
997+
fun demo159() {
998+
val p1 = lazy WithU111.fromSlice(WithU111.getSlice_a8_bnull());
999+
val p2 = lazy WithU111.fromSlice(WithU111.getSlice_a16_b8());
1000+
val p3 = lazy WithU111.fromSlice(WithU111.getSlice_a8_b5());
1001+
val p4 = lazy WithU111.fromSlice(WithU111.getSlice_a8_b5_nobit());
1002+
1003+
val p1_bit = p1.bit;
1004+
val p2_bit = p2.bit;
1005+
val p3_bit = p3.bit;
1006+
val p4_b = p4.b;
1007+
1008+
return (p1_bit, p2_bit, (p3.b is WithN5) ? p3.b.n as int : 777, p3_bit, (p4_b is WithN5) ? p4_b.n as int: 777);
1009+
}
1010+
9841011

9851012
@method_id(200)
9861013
fun demo200() {
@@ -1601,10 +1628,10 @@ fun main() {
16011628
@testcase | 113 | x{03ffff} | -1
16021629
@testcase | 113 | x{04000000FF} | 255
16031630
@testcase | 114 | x{0110} | 16 1
1604-
@testcase | 114 | x{020f} | [ -15 ] typeid-25
1631+
@testcase | 114 | x{020f} | [ -15 ] typeid-27
16051632
@testcase | 114 | x{03} | -1 1
16061633
@testcase | 114 | x{03ffff} | -1 1
1607-
@testcase | 114 | x{04000000FF} | [ 255 ] typeid-25
1634+
@testcase | 114 | x{04000000FF} | [ 255 ] typeid-27
16081635
@testcase | 115 | x{08} | 8 42
16091636
@testcase | 115 | x{88} | 0 2
16101637
@testcase | 115 | x{83} | -1 2
@@ -1669,6 +1696,7 @@ fun main() {
16691696
@testcase | 156 | x{FF10} | -100
16701697
@testcase | 157 | | -1
16711698
@testcase | 158 | | 2 10 255
1699+
@testcase | 159 | | -1 -1 31 -1 31
16721700

16731701
@testcase | 200 | | 1
16741702
@testcase | 201 | | 1 2 -1

tolk-tester/tests/pack-unpack-4.tolk

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ fun test1() {
3838

3939
type Union_8_16_32_n = int8 | null | int16 | int32;
4040

41-
fun ans102n_8(): slice asm "b{0100001111} PUSHSLICE";
42-
fun ans102n_16(): slice asm "b{100000000000001111} PUSHSLICE";
43-
fun ans102n_32(): slice asm "b{1100000000000000000000000000001111} PUSHSLICE";
44-
fun ans102n_null(): slice asm "b{00} PUSHSLICE";
41+
fun ans102n_8(): slice asm "b{10000001111} PUSHSLICE";
42+
fun ans102n_16(): slice asm "b{1010000000000001111} PUSHSLICE";
43+
fun ans102n_32(): slice asm "b{11000000000000000000000000000001111} PUSHSLICE";
44+
fun ans102n_null(): slice asm "b{0} PUSHSLICE";
4545

4646
@method_id(102)
4747
fun test2() {
@@ -65,10 +65,10 @@ fun ans104_8(): slice asm "b{0000001111} PUSHSLICE";
6565
fun ans104_16(): slice asm "b{010000000000001111} PUSHSLICE";
6666
fun ans104_32(): slice asm "b{1000000000000000000000000000001111} PUSHSLICE";
6767

68-
fun ans104n_8(): slice asm "b{0100001111} PUSHSLICE";
69-
fun ans104n_16(): slice asm "b{100000000000001111} PUSHSLICE";
70-
fun ans104n_32(): slice asm "b{1100000000000000000000000000001111} PUSHSLICE";
71-
fun ans104n_null(): slice asm "b{00} PUSHSLICE";
68+
fun ans104n_8(): slice asm "b{10000001111} PUSHSLICE";
69+
fun ans104n_16(): slice asm "b{1010000000000001111} PUSHSLICE";
70+
fun ans104n_32(): slice asm "b{11000000000000000000000000000001111} PUSHSLICE";
71+
fun ans104n_null(): slice asm "b{0} PUSHSLICE";
7272

7373
@method_id(104)
7474
fun test4() {

tolk-tester/tests/pack-unpack-5.tolk

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ struct Test9_bits4 { f: bits4; }
160160
type Test9_f1 = int32 | int64 | int128; // auto-generated 2-bit prefix
161161
type Test9_f2 = int32 | Inner8_2; // auto-generated 1-bit prefix (Either)
162162
type Test9_f3 = bits1 | Test9_bits2 | bits3 | bits4 | bits5; // auto-generated 3-bit prefix
163-
type Test9_f4 = bits1 | Test9_bits2 | bits3 | Test9_bits4?; // auto-generated 3-bit prefix
163+
type Test9_f4 = bits1 | Test9_bits2 | bits3 | Test9_bits4?; // auto-generated 0 / 100/101/110/111
164164

165165
@method_id(109)
166166
fun test9() {
@@ -213,7 +213,7 @@ fun main() {
213213
@testcase | 106 | | [ 64 64 0 0 ] [ 5 37 1 1 ] [ 5 65 0 1 ]
214214
@testcase | 107 | | [ 16 16 0 0 ] [ 16 16 0 0 ]
215215
@testcase | 108 | | [ 10 275 0 0 ] [ 34 158 0 0 ] [ 47 1143 0 1 ]
216-
@testcase | 109 | | [ 34 130 0 0 ] [ 33 159 0 0 ] [ 4 8 0 0 ] [ 3 7 0 0 ]
216+
@testcase | 109 | | [ 34 130 0 0 ] [ 33 159 0 0 ] [ 4 8 0 0 ] [ 1 7 0 0 ]
217217
@testcase | 110 | | [ 32 9999 0 4 ] [ 33 9999 0 8 ]
218218
@testcase | 111 | | [ 1023 1023 0 1 ] [ 0 0 2 2 ]
219219
@testcase | 120 | | 16 4128 1 1

tolk/pack-unpack-serializers.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,13 +1146,16 @@ std::vector<PackOpcode> auto_generate_opcodes_for_union(TypePtr union_type, std:
11461146

11471147
// okay, none of the opcodes are specified, generate a prefix tree;
11481148
// examples: `int32 | int64 | int128` / `int32 | A | null` / `A | B` / `A | B | C`;
1149-
// create prefixes `0b00 0b01 0b10` / `0b01 0b10 0b00` (use 0b00 for null if exists):
1150-
// for 3/4 variants — two bits, for 5 — three
1151-
int prefix_len = static_cast<int>(std::ceil(std::log2(t_union->size())));
1152-
int cur_prefix = has_null ? 1 : 0; // will use 0b00 for null, so start with 0b01
1149+
// if `null` exists, it's 0, all others are 1+tree: A|B|C|D|null => 0 | 100+A | 101+B | 110+C | 111+D;
1150+
// if no `null`, just distribute sequentially: A|B|C => 00+A | 01+B | 10+C
1151+
int n_without_null = t_union->size() - has_null;
1152+
int prefix_len = static_cast<int>(std::ceil(std::log2(n_without_null)));
1153+
int cur_prefix = 0;
11531154
for (TypePtr variant : t_union->variants) {
11541155
if (variant == TypeDataNullLiteral::create()) {
1155-
result.emplace_back(0, prefix_len);
1156+
result.emplace_back(0, 1);
1157+
} else if (has_null) {
1158+
result.emplace_back((1<<prefix_len) + (cur_prefix++), prefix_len + 1);
11561159
} else {
11571160
result.emplace_back(cur_prefix++, prefix_len);
11581161
}

0 commit comments

Comments
 (0)