diff --git a/server/src/e2e/tolk/testcases/completion/keywords.test b/server/src/e2e/tolk/testcases/completion/keywords.test index 0ecb9593..6c0900e0 100644 --- a/server/src/e2e/tolk/testcases/completion/keywords.test +++ b/server/src/e2e/tolk/testcases/completion/keywords.test @@ -57,9 +57,11 @@ fun foo() { 2 createEmptyCell(): cell 2 createEmptyDict(): dict 2 createEmptyList(): tuple +2 createEmptyMap(): map 2 createEmptySlice(): slice 2 createEmptyTuple(): tuple 2 createExternalLogMessage(options: CreateExternalLogMessageOptions): OutMessage +2 createMapFromLowLevelDict(d: dict): map 2 createMessage(options: CreateMessageOptions): OutMessage 2 divMod(x: int, y: int): (int, int) 2 foo() @@ -121,6 +123,8 @@ fun foo() { 21 ExtOutLogBucket {} 21 InMessage {} 21 InMessageBounced {} +21 MapEntry {} +21 MapLookupResult {} 21 OutMessage {} 21 PackOptions {} 21 StateInit {} @@ -129,8 +133,9 @@ fun foo() { 21 blockchain 21 contract 21 debug +21 map 21 random -24 ExtraCurrenciesDict +24 ExtraCurrenciesMap 24 RemainingBitsAndRefs 24 address 24 bits256 @@ -243,9 +248,11 @@ fun foo() { 2 createEmptyCell(): cell 2 createEmptyDict(): dict 2 createEmptyList(): tuple +2 createEmptyMap(): map 2 createEmptySlice(): slice 2 createEmptyTuple(): tuple 2 createExternalLogMessage(options: CreateExternalLogMessageOptions): OutMessage +2 createMapFromLowLevelDict(d: dict): map 2 createMessage(options: CreateMessageOptions): OutMessage 2 divMod(x: int, y: int): (int, int) 2 foo() @@ -307,6 +314,8 @@ fun foo() { 21 ExtOutLogBucket {} 21 InMessage {} 21 InMessageBounced {} +21 MapEntry {} +21 MapLookupResult {} 21 OutMessage {} 21 PackOptions {} 21 StateInit {} @@ -315,8 +324,9 @@ fun foo() { 21 blockchain 21 contract 21 debug +21 map 21 random -24 ExtraCurrenciesDict +24 ExtraCurrenciesMap 24 RemainingBitsAndRefs 24 address 24 bits256 @@ -418,9 +428,11 @@ fun foo() { 2 createEmptyCell(): cell 2 createEmptyDict(): dict 2 createEmptyList(): tuple +2 createEmptyMap(): map 2 createEmptySlice(): slice 2 createEmptyTuple(): tuple 2 createExternalLogMessage(options: CreateExternalLogMessageOptions): OutMessage +2 createMapFromLowLevelDict(d: dict): map 2 createMessage(options: CreateMessageOptions): OutMessage 2 divMod(x: int, y: int): (int, int) 2 foo() @@ -482,6 +494,8 @@ fun foo() { 21 ExtOutLogBucket {} 21 InMessage {} 21 InMessageBounced {} +21 MapEntry {} +21 MapLookupResult {} 21 OutMessage {} 21 PackOptions {} 21 StateInit {} @@ -490,8 +504,9 @@ fun foo() { 21 blockchain 21 contract 21 debug +21 map 21 random -24 ExtraCurrenciesDict +24 ExtraCurrenciesMap 24 RemainingBitsAndRefs 24 address 24 bits256 diff --git a/server/src/e2e/tolk/testcases/completion/struct.test b/server/src/e2e/tolk/testcases/completion/struct.test index 6e25fdc2..c0130dea 100644 --- a/server/src/e2e/tolk/testcases/completion/struct.test +++ b/server/src/e2e/tolk/testcases/completion/struct.test @@ -63,6 +63,8 @@ struct Foo { 21 Foo 21 InMessage 21 InMessageBounced +21 MapEntry +21 MapLookupResult 21 OutMessage 21 PackOptions 21 StateInit @@ -71,8 +73,9 @@ struct Foo { 21 blockchain 21 contract 21 debug +21 map 21 random -24 ExtraCurrenciesDict +24 ExtraCurrenciesMap 24 RemainingBitsAndRefs 24 address 24 bits256 diff --git a/server/src/e2e/tolk/testcases/completion/top-level.test b/server/src/e2e/tolk/testcases/completion/top-level.test index 1e6610be..1cb91787 100644 --- a/server/src/e2e/tolk/testcases/completion/top-level.test +++ b/server/src/e2e/tolk/testcases/completion/top-level.test @@ -135,9 +135,11 @@ fun foo() { 2 createEmptyCell(): cell 2 createEmptyDict(): dict 2 createEmptyList(): tuple +2 createEmptyMap(): map 2 createEmptySlice(): slice 2 createEmptyTuple(): tuple 2 createExternalLogMessage(options: CreateExternalLogMessageOptions): OutMessage +2 createMapFromLowLevelDict(d: dict): map 2 createMessage(options: CreateMessageOptions): OutMessage 2 divMod(x: int, y: int): (int, int) 2 foo() @@ -199,6 +201,8 @@ fun foo() { 21 ExtOutLogBucket {} 21 InMessage {} 21 InMessageBounced {} +21 MapEntry {} +21 MapLookupResult {} 21 OutMessage {} 21 PackOptions {} 21 StateInit {} @@ -207,8 +211,9 @@ fun foo() { 21 blockchain 21 contract 21 debug +21 map 21 random -24 ExtraCurrenciesDict +24 ExtraCurrenciesMap 24 RemainingBitsAndRefs 24 address 24 bits256 diff --git a/server/src/e2e/tolk/testcases/documentation/basic.test b/server/src/e2e/tolk/testcases/documentation/basic.test index 478f2c00..ac373f3b 100644 --- a/server/src/e2e/tolk/testcases/documentation/basic.test +++ b/server/src/e2e/tolk/testcases/documentation/basic.test @@ -183,7 +183,7 @@ struct CreateMessageOptions { /// whether a message will bounce back on error bounce: bool; /// message value: attached tons (or tons + extra currencies) - value: coins | (coins, ExtraCurrenciesDict); + value: coins | (coins, ExtraCurrenciesMap); /// destination is either a provided address, or is auto-calculated by stateInit dest: | address // either just send a message to some address | builder // ... or a manually constructed builder with a valid address @@ -196,7 +196,7 @@ struct CreateMessageOptions { ```tolk struct CreateMessageOptions { bounce: bool - value: coins | (coins, ExtraCurrenciesDict) + value: coins | (coins, ExtraCurrenciesMap) dest: | address // either just send a message to some address | builder // ... or a manually constructed builder with a valid address | (int8, uint256) // ... or to workchain + hash (also known as accountID) diff --git a/server/src/e2e/tolk/testcases/documentation/map.test b/server/src/e2e/tolk/testcases/documentation/map.test index 53ca0969..00347eb0 100644 --- a/server/src/e2e/tolk/testcases/documentation/map.test +++ b/server/src/e2e/tolk/testcases/documentation/map.test @@ -53,10 +53,9 @@ type dict = --- -In Tolk v1.x there would be a type `map`. -Currently, working with dictionaries is still low-level, with raw cells. -But just for clarity, we use "dict" instead of a "cell?" where a cell-dictionary is assumed. -Every dictionary object can be null. TVM NULL is essentially "empty dictionary". +`dict` is a low-level TVM dictionary. +Think of it as "a map with unknown keys and unknown values". +Prefer using `map`, not `dict`. ```tolk fun emptyMap V diff --git a/server/src/e2e/tolk/testcases/resolving/instance-methods.test b/server/src/e2e/tolk/testcases/resolving/instance-methods.test index d75692dd..7239249f 100644 --- a/server/src/e2e/tolk/testcases/resolving/instance-methods.test +++ b/server/src/e2e/tolk/testcases/resolving/instance-methods.test @@ -206,7 +206,7 @@ fun test() { foo.toCell(); } ------------------------------------------------------------------------ -4:8 -> 258:6 resolved +4:8 -> 354:6 resolved ======================================================================== Generic struct alias method resolving diff --git a/server/src/e2e/tolk/testcases/resolving/type-parameters.test b/server/src/e2e/tolk/testcases/resolving/type-parameters.test index 6d0b1168..4664c684 100644 --- a/server/src/e2e/tolk/testcases/resolving/type-parameters.test +++ b/server/src/e2e/tolk/testcases/resolving/type-parameters.test @@ -12,21 +12,21 @@ Default type for function type parameter ======================================================================== fun fooint>() {} ------------------------------------------------------------------------ -0:10 -> 1:5 resolved +0:10 -> 10:5 resolved ======================================================================== Default type for struct type parameter ======================================================================== struct Fooint> {} ------------------------------------------------------------------------ -0:13 -> 1:5 resolved +0:13 -> 10:5 resolved ======================================================================== Default type for type alias type parameter ======================================================================== type Fooint> = T | null; ------------------------------------------------------------------------ -0:11 -> 1:5 resolved +0:11 -> 10:5 resolved ======================================================================== T receiver diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/assignment-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/assignment-tests.tolk index 5b8c2094..0343bdf5 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/assignment-tests.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/assignment-tests.tolk @@ -249,6 +249,14 @@ fun test118(x: int) { return (i1, i2, st.owner.remainingBitsCount(), i3.0); } +@method_id(119) +fun test119(a: int, b: int) { + var t: tuple = [a + 1, b * 2] as tuple; + val l = t.size(); + val [c,d:int] = [t.0 as int, t.1]; + return (l, c, d); +} + fun main(value: int, ) { @@ -279,6 +287,7 @@ fun main(value: int, ) { @testcase | 116 | | 1 2 3 4 [ 1 2 3 4 ] @testcase | 117 | | [ 20 ] @testcase | 118 | 3 | 10 3 0 1 +@testcase | 119 | 1 2 | 2 2 4 @fif_codegen diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/calls-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/calls-tests.tolk index 9405c9ea..2177136f 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/calls-tests.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/calls-tests.tolk @@ -179,7 +179,11 @@ fun test11(a: int, b: int) { return ((b < a), (b <= a), (b > a), (b >= a)); } -fun main() {} +fun main() { + // mark used to codegen them + cmp1; cmp2; cmp3; cmp4; + leq1; leq2; leq3; leq4; +} /** @testcase | 101 | 10 | 10 15 100 12 3 110 300 diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/cells-slices.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/cells-slices.tolk index 57a92f03..e1c2ac9c 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/cells-slices.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/cells-slices.tolk @@ -16,6 +16,16 @@ fun endCell(b: builder): cell fun beginParse(c: cell): slice asm "CTOS"; +@noinline +fun triggerOverflowIntConst() { + return beginCell().storeInt(10, 4) +} + +@noinline +fun triggerOverflowUintConst() { + return beginCell().storeUint(123, 6) +} + @method_id(101) fun test1(): [int,int,int,int,int] { var b: builder = beginCell().storeUint(1, 32); @@ -249,6 +259,7 @@ fun test18() { return (s.loadUint(14), s.loadInt(8), s.loadUint(4)); } +@method_id(119) fun test19() { // numbers with potential overflow for STU are not joined, check via codegen var b = beginCell(); @@ -267,6 +278,7 @@ fun test20() { return (s.loadBool(), s.loadBool(), s.loadBool(), s.loadUint(8), s.loadBool(), s.loadCoins()); } +@method_id(121) fun test21(s: slice) { // successive skipBits are also joined var x = 8; @@ -433,6 +445,30 @@ fun test34(p: int, n: int) { return b; } +@method_id(135) +fun test35(overflowMode: int) { + try { + val b: builder = match (overflowMode) { + 1 => triggerOverflowIntConst(), + 2 => triggerOverflowUintConst(), + 3 => beginCell().storeUint(10, -4), + 4 => beginCell().storeInt(100, 1), + 5 => beginCell().storeInt(1, 0), + 6 => beginCell().storeInt(115792089237316195423570985008687907853269984665640564039457584007913129639935, 256), + 7 => beginCell().storeInt(15, 4), + 8 => beginCell().storeUint(1<<170, 169), + else => beginCell(), + }; + return b.endCell().beginParse().loadUint(1) * 1000 + } + catch (ex) { return ex } +} + +@method_id(136) +fun test36() { + return beginCell().storeInt(0, 0) +} + fun main(): int { return 0; } @@ -467,6 +503,15 @@ fun main(): int { @testcase | 132 | 0 | BC{00080000000a} @testcase | 133 | 0 | BC{00020a} @testcase | 134 | 0 8 | BC{00020a} +@testcase | 135 | 1 | 5 +@testcase | 135 | 2 | 5 +@testcase | 135 | 3 | 5 +@testcase | 135 | 4 | 5 +@testcase | 135 | 5 | 5 +@testcase | 135 | 6 | 5 +@testcase | 135 | 7 | 5 +@testcase | 135 | 8 | 5 +@testcase | 136 | | BC{0000} We test that consequtive storeInt/storeUint with constants are joined into a single number @@ -628,4 +673,32 @@ We test that consequtive storeInt/storeUint with constants are joined into a sin }> """ +@fif_codegen +""" + triggerOverflowIntConst() PROC:<{ + 10 PUSHINT + NEWC + 4 STI + }> +""" + +@fif_codegen +""" + triggerOverflowUintConst() PROC:<{ + 123 PUSHINT + NEWC + 6 STU + }> +""" + +@fif_codegen +""" + test36() PROC:<{ + 0 PUSHINT + NEWC + OVER + STIX + }> +""" + */ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/comments-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/comments-tests.tolk index 96caefe9..82878080 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/comments-tests.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/comments-tests.tolk @@ -18,6 +18,7 @@ fun demo_fields_def(x: int) { } fun main() { + var cb = demo_fields_def; return 1; } diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/dicts-demo.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/dicts-demo.tolk index f53debb7..cec83280 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/dicts-demo.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/dicts-demo.tolk @@ -104,6 +104,26 @@ fun test105(takeNext: bool) { return data!.loadUint(8); } +@method_id(106) +fun test106() { + var dict = createEmptyDict(); + dict.addIntToIDict(2, 102); + dict.addIntToIDict(-1, 101); + dict.addIntToIDict(-4, 104); + dict.addIntToIDict(3, 103); + + var m = createMapFromLowLevelDict(dict); + val fk = m.findFirst().key; + val fv = m.findFirst().loadValue(); + var t = createEmptyTuple(); + var r = m.findKeyLess(3); + while (r.isFound) { + t.push(r.loadValue()); + r = m.iteratePrev(r); + } + return (fk, fv, t); +} + fun main() {} /** @@ -115,4 +135,5 @@ fun main() {} @testcase | 104 | | 12 34 56 78 0 @testcase | 105 | -1 | 1 @testcase | 105 | 0 | 2 +@testcase | 106 | | -4 104 [ 102 101 104 ] */ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/enums-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/enums-tests.tolk new file mode 100644 index 00000000..4ba856c7 --- /dev/null +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/enums-tests.tolk @@ -0,0 +1,285 @@ +const SIX = 6 + +enum EEmpty {} + +enum Color { + Red, + Green, + Blue +} + +type ColorAlias = Color + +struct WithColor { + c1: Color + c2: Color = Color.Green +} + +enum EPartialAssigned { + M100 = -100, + M99, + P1 = (1 + 0) << 0, + P2 = ((+1 + 1) << 1) / 2, + P3, + P4, + P5 = 0b101, + P6 = SIX, + Max = 0x7FFFFFFF, +} + +struct Box { + value: T +} + +fun checkIsBlue(c: Color) { + return c == Color.Blue +} + +fun Color.isBlue(self) { + return self == Color.Blue +} + +fun Color.createBlue() { + return Color.Blue +} + +const C_BLUE = Color.Blue +const C_TEN = 5 * (Color.Green as int) * 2 + +// these methods do not bother Color.Red and other +fun T.Red() { return 123 } +fun T.Max() { return -C_TEN } +fun int.Max(self) { return self } +fun Color.Max(): EPartialAssigned { return ((EPartialAssigned.Max as int) + 1) as EPartialAssigned } + +@noinline +fun checkIsColor(obj: Color | T) { return obj is Color } + +@inline_ref +fun Color.mapToNumber(self) { + // match over enum is exhaustive, it's like "if Red {} else if Green {} else {}" + return match (self) { + Color.Red => 100, + Color.Green => 200, + Color.Blue => 300, // this works like `else` and handles invalid values at runtime also + } +} + + +@method_id(101) +fun test1() { + var c = Color.Red; + + return ( + checkIsBlue(Color.Blue), checkIsBlue(c), checkIsBlue(Color.createBlue()), + Color.Blue.isBlue(), Color.Red.isBlue(), Color.createBlue()!!.isBlue(), C_BLUE.isBlue(), + Color.createBlue() + ); +} + +@method_id(102) +fun test2(c: Color) { + return (c is Color, c !is Color, c is EPartialAssigned, c is EEmpty, c is int, c is int8); +} + +@method_id(103) +fun test3() { + assert (EPartialAssigned.P4 as int == 4) throw 123; + assert (EPartialAssigned.M99 as int <= 0) throw 123; + assert (EPartialAssigned.P3 == (3 as EPartialAssigned)) throw 123; + assert (EPartialAssigned.P3 != (4 as EPartialAssigned)) throw 123; + return ( + EPartialAssigned.M100, + EPartialAssigned.M99, + EPartialAssigned.P1, + EPartialAssigned.P2, + EPartialAssigned.P3, + EPartialAssigned.P4, + EPartialAssigned.P5, + EPartialAssigned.P6, + EPartialAssigned.Max, + ) +} + +@method_id(104) +fun test4(chooseGreen: bool) { + var c: ColorAlias = chooseGreen ? Color.Green : ColorAlias.Blue; + return (c is Color, (c!) is ColorAlias, ColorAlias.Blue, ColorAlias.createBlue().isBlue(), c); +} + +@method_id(105) +fun test5() { + return (int.Red(), int8.Max(), 456.Max(), EPartialAssigned.Max, Color.Max()); +} + +@method_id(106) +fun test6(c: Color) { + val p = c as EPartialAssigned; + val e = c as EEmpty; + match (e) { EEmpty => {} } + return (p, e, c as int, c as int?, 100 as Color, -100 as EEmpty); +} + +@method_id(107) +fun test7() { + return ( + WithColor{c1: Color.Red}, + WithColor{c1: -100 as Color, c2: Color.createBlue()} + ) +} + +@method_id(108) +fun test8() { + var (c0: Color | int, c1: Color | int) = (Color.Red, 123); + var (u1, u2) = (Color.Red as Color | int, 0 as Color | int); + var (b1, b2, b3) = (Color.Red as Color | EPartialAssigned | builder, EPartialAssigned.Max as ColorAlias | builder | EPartialAssigned, beginCell() as builder | Color | EPartialAssigned); + return ( + checkIsColor(u1), checkIsColor(u2), + checkIsColor(b1), checkIsColor(b2), checkIsColor(b3), + b2 is EPartialAssigned, + u1, + match (u1) { int => -100, Color => -200 } + ) +} + +@method_id(109) +fun test9() { + return ( + Color.Red.mapToNumber(), Color.Green.mapToNumber(), Color.Blue.mapToNumber(), + // this is UB, the compiler may do any optimizations + (100 as Color).mapToNumber(), (-99 as Color).mapToNumber(), + ); +} + +@method_id(110) +fun test10(c: Color) { + var t = createEmptyTuple(); + match (c) { + Color.Red => { t.push(1) } + else => { t.push(-100) } + } + match (c) { + Color.Green => { t.push(2) } + Color.Blue => { t.push(3) } + Color.Red => { t.push(1) } + } + match (c) { + else => { t.push(-100) } + } + match (c) { + Color => { t.push(88) } + } + match (c) { + Color.Blue => { t.push(3) } + Color.Green => { t.push(2) } + else => { t.push(-100) } + } + match (c) { + Color.Red => { t.push(1) } + Color.Blue => { t.push(3) } + Color.Green => { t.push(2) } + } + return t; +} + +@method_id(111) +fun test11(b1Value: Color) { + var b1: Box = { value: b1Value }; + var b2 = Box { value: Color.Green }; + var b3 = Box { value: Color.Blue }; + return (b1.value == b2.value, b1.value != b2.value, b1.value == b3.value, b1.value != b3.value) +} + +@method_id(112) +fun test12(c1: Color, c2: Color) { + var t = createEmptyTuple(); + + match (c1) { + Color.Red => { t.push(10 + c2.mapToNumber()) } + Color.Green => { t.push(20 + c2.mapToNumber()) } + Color.Blue => { t.push(30 + c2.mapToNumber()) } + } + + match (c2) { + Color.Red => { + match (c1) { + Color.Red => t.push(100), + Color.Blue => t.push(300), + Color.Green => t.push(200), + } + } + Color.Blue => match (c1) { + Color.Green => t.push(210), + Color.Red => t.push(110), + Color.Blue => t.push(310), + }, + Color.Green => match (c1) { + Color.Green => t.push(220), + Color.Red => t.push(120), + Color.Blue => t.push(320), + } + } + + t.push((999 as Color).mapToNumber()); + return t; +} + + +fun main(c: Color) { + __expect_type(c, "Color"); + __expect_type(C_BLUE, "Color"); + __expect_type(Color.createBlue(), "Color"); + __expect_type(ColorAlias.Red, "Color"); + __expect_type(EPartialAssigned.Max, "EPartialAssigned"); + __expect_type(Box{value:Color.Red}, "Box"); + return c; +} + + +/** +@testcase | 0 | 123 | 123 +@testcase | 101 | | -1 0 -1 -1 0 -1 -1 2 +@testcase | 102 | 1 | -1 0 0 0 0 0 +@testcase | 103 | | -100 -99 1 2 3 4 5 6 2147483647 +@testcase | 104 | -1 | -1 -1 2 -1 1 +@testcase | 105 | | 123 -10 456 2147483647 2147483648 +@testcase | 106 | 1 | 1 1 1 1 100 -100 +@testcase | 107 | | 0 1 -100 2 +@testcase | 108 | | -1 0 -1 0 0 -1 0 typeid-1 -200 +@testcase | 109 | | 100 200 300 300 300 +@testcase | 110 | 0 | [ 1 1 -100 88 -100 1 ] +@testcase | 110 | 1 | [ -100 2 -100 88 2 2 ] +@testcase | 110 | 2 | [ -100 3 -100 88 3 3 ] +@testcase | 110 | 44 | [ -100 1 -100 88 -100 2 ] +@testcase | 111 | 2 | 0 -1 -1 0 +@testcase | 112 | 0 1 | [ 210 120 300 ] +@testcase | 112 | 2 4 | [ 330 320 300 ] + +@fif_codegen +""" + test1() PROC:<{ + TRUE + FALSE + TRUE + TRUE + FALSE + TRUE + TRUE + 2 PUSHINT + }> +""" + +@fif_codegen +""" + test2() PROC:<{ // c + DROP // + -1 PUSHINT // '1=-1 + 0 PUSHINT // '1=-1 '2 + 0 PUSHINT // '1=-1 '2 '3=0 + s0 s0 s0 PUSH3 // '1=-1 '2 '3=0 '4=0 '5=0 '6=0 + }> +""" + +@fif_codegen DECLPROC checkIsColor() + +*/ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/generics-1.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/generics-1.tolk index b92f27a6..d44f083f 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/generics-1.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/generics-1.tolk @@ -15,7 +15,7 @@ fun tuplePush(mutate t: tuple, value: T): void @method_id(101) fun test101(x: int) { var (a, b, c) = (x, (x,x), [x,x]); - return (eq1(a), eq1(b), eq1(c), eq2(a), eq2(b), eq2(c), eq3(a), eq4(b), eq3(createEmptyTuple())); + return (eq1(a), eq1(b), eq1(c), eq2(a), eq2(b), eq2(c), eq3(a), eq4(b), eq3(createEmptyTuple()), eq1(Color.Red)); } fun getTwo(): X { return 2 as X; } @@ -28,6 +28,7 @@ struct Wrapper { struct Err { errPayload: T } +enum Color { Red, Green, Blue } @method_id(102) fun test102(): (int, int, int, [int, int], Wrapper< Wrapper >) { @@ -211,6 +212,7 @@ fun test113(a: int | slice) { fun main(x: int): (int, [Tup2Int]) { __expect_type(test110, "() -> (Tensor2Int, Tensor2Int)"); + __expect_type(eq2(10 as Color), "Color"); try { if(x) { throw (1, x); } } catch (excNo, arg) { return (arg as int, [[eq2(arg as int), getTwo()]]); } @@ -219,7 +221,7 @@ fun main(x: int): (int, [Tup2Int]) { /** @testcase | 0 | 1 | 1 [ [ 1 2 ] ] -@testcase | 101 | 0 | 0 0 0 [ 0 0 ] 0 0 0 [ 0 0 ] 0 0 0 [] +@testcase | 101 | 0 | 0 0 0 [ 0 0 ] 0 0 0 [ 0 0 ] 0 0 0 [] 0 @testcase | 102 | | 2 2 2 [ 2 2 ] 2 @testcase | 103 | 0 | 0 100 100 @testcase | 104 | 0 | [ 1 (null) 2 ] [ 2 -1 0 ] @@ -241,6 +243,7 @@ fun main(x: int): (int, [Tup2Int]) { @fif_codegen DECLPROC eq1() @fif_codegen DECLPROC eq1() @fif_codegen DECLPROC eq1>>() +@fif_codegen DECLPROC eq1() // was inlined @fif_codegen_avoid DECLPROC getTwo() diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/generics-2.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/generics-2.tolk index 2a6684f0..94ae2ea0 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/generics-2.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/generics-2.tolk @@ -330,8 +330,8 @@ type StrangeAlsoInt = StrangeInt<()>; fun test19() { var i1: StrangeInt = 20; var i2: StrangeInt = 30; - var i3: StrangeInt> = 40 as StrangeAlsoInt; - i1 = i2; i2 = i1; i1 = i3; i3 = i1; + var i3: StrangeInt> = 40; + i1 = i2 as StrangeInt; i1 = i3 as StrangeInt; return (i1 as StrangeInt<()>, i3 as StrangeAlsoInt, i1 is StrangeInt, i1 is StrangeInt, i3 is StrangeInt, i3 is StrangeAlsoInt); } @@ -376,7 +376,7 @@ fun test22(w: int | WrapperAlias | slice) { WrapperAlias => {} slice => {} } - __expect_type(w, "int | Wrapper | slice"); + __expect_type(w, "int | WrapperAlias | slice"); match (w) { MyInt => {} slice => {} @@ -453,15 +453,15 @@ fun main(c: Wrapper, d: WrappedInt) { @testcase | 104 | | 11 11 13 13 13 2 @testcase | 105 | 0 | 80 @testcase | 105 | -1 | (null) -@testcase | 106 | | (null) (null) 0 777 1 2 typeid-14 +@testcase | 106 | | (null) (null) 0 777 1 2 typeid-12 @testcase | 107 | 5 | 20 20 5 5 20 777 (null) (null) (null) 0 -@testcase | 108 | 0 | 777 typeid-15 777 typeid-15 +@testcase | 108 | 0 | 777 typeid-1 777 typeid-1 @testcase | 108 | -1 | 777 0 777 0 @testcase | 109 | | 40 40 70 @testcase | 110 | | 20 1 20 42 -@testcase | 111 | | 5 1 typeid-4 5 1 typeid-4 5 1 typeid-4 5 1 typeid-4 -@testcase | 112 | -1 | 10 1 777 10 1 typeid-5 -@testcase | 112 | 0 | 20 1 777 (null) 0 typeid-6 +@testcase | 111 | | 5 1 typeid-2 5 1 typeid-2 5 1 typeid-2 5 1 typeid-2 +@testcase | 112 | -1 | 10 1 777 10 1 typeid-3 +@testcase | 112 | 0 | 20 1 777 (null) 0 typeid-4 @testcase | 113 | | 30 -1 @testcase | 114 | | 999 (null) 2 @testcase | 115 | | 10 0 200 1 -1 200 1 -1 @@ -469,7 +469,7 @@ fun main(c: Wrapper, d: WrappedInt) { @testcase | 117 | | 100 123 @testcase | 118 | | 123 1 777 123 1 -1 2 @testcase | 119 | | 40 40 -1 -1 -1 -1 -@testcase | 120 | | (null) typeid-9 777 (null) (null) 0 typeid-11 -1 -1 +@testcase | 120 | | (null) typeid-9 777 (null) (null) 0 typeid-10 -1 -1 @testcase | 123 | | 10 10 10 10 10 diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/generics-4.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/generics-4.tolk index c5f3e038..6833d386 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/generics-4.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/generics-4.tolk @@ -63,6 +63,7 @@ fun mySend(p: Parameters): int { return total; } +@method_id(101) fun test1() { eqUnusedT(100); eqUnusedU(100); @@ -175,7 +176,7 @@ fun main() { /** @testcase | 103 | | 10 20 30 777 40 40 @testcase | 104 | | (null) (null) (null) -@testcase | 105 | | -1 (null) 0 (null) typeid-6 777 0 123 0 456 typeid-5 +@testcase | 105 | | -1 (null) 0 (null) typeid-2 777 0 123 0 456 typeid-1 @testcase | 106 | | -1 0 -1 -1 @testcase | 107 | | 1 10 110 117 @testcase | 108 | | 10 (null) 20 diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/handle-msg-6.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/handle-msg-6.tolk index 0d349f9c..57668f2b 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/handle-msg-6.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/handle-msg-6.tolk @@ -5,7 +5,7 @@ fun test1() { } fun onBouncedMessage(in: InMessageBounced) { - if (in.valueExtra != null) { + if (!in.valueExtra.isEmpty()) { return; } throw in.originalForwardFee; @@ -23,7 +23,7 @@ fun onInternalMessage(in: InMessage) { onBouncedMessage() PROC:<{ DROP INMSG_VALUEEXTRA - ISNULL + DICTEMPTY IFNOTJMP:<{ }> INMSG_FWDFEE diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/indexed-access.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/indexed-access.tolk index af6888df..c3091101 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/indexed-access.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/indexed-access.tolk @@ -179,6 +179,7 @@ fun getConstTuple(): Tup2Int { return [1,2]; } +@noinline fun testCodegenNoPureIndexedAccess() { (getConstTuple().1, getConstTuple().0) = (3, 4); return 0; @@ -277,7 +278,9 @@ fun test123() { return t; } -fun main(){} +fun main(){ + testCodegenNoPureIndexedAccess(); +} /** diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/inference-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/inference-tests.tolk index 9c836baf..7ec66f01 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/inference-tests.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/inference-tests.tolk @@ -75,7 +75,7 @@ fun test5(x: int) { __expect_type([x, x >= 1], "[int, bool]"); __expect_type([x, x >= 1, null as slice?], "[int, bool, slice?]"); __expect_type((x, [x], [[x], x]), "(int, [int], [[int], int])"); - __expect_type(contract.getOriginalBalanceWithExtraCurrencies(), "[coins, dict]"); + __expect_type(contract.getOriginalBalanceWithExtraCurrencies(), "[coins, ExtraCurrenciesMap]"); } fun test6() { @@ -111,7 +111,7 @@ fun alwaysThrowsNotAnnotated2() { alwaysThrows(); } fun test9() { __expect_type(alwaysThrows(), "never"); __expect_type(alwaysThrows, "() -> never"); - __expect_type(alwaysThrowsNotAnnotated(), "void"); + __expect_type(alwaysThrowsNotAnnotated(), "never"); __expect_type(alwaysThrowsNotAnnotated2(), "void"); } diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/inline-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/inline-tests.tolk index d85c5543..d27e79bb 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/inline-tests.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/inline-tests.tolk @@ -537,7 +537,7 @@ fun usedIn10ButDeclaredBelow(x: int) { """ wrap16() PROC:<{ IFJMP:<{ - 139 PUSHINT + 129 PUSHINT OVER EQUAL IFJMP:<{ @@ -545,7 +545,7 @@ fun usedIn10ButDeclaredBelow(x: int) { 0 GTINT 100 THROWIFNOT }> - 140 PUSHINT + 130 PUSHINT s2 POP EQUAL IFJMP:<{ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/intN-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/intN-tests.tolk index cd557092..2555d983 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/intN-tests.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/intN-tests.tolk @@ -201,6 +201,7 @@ fun test11() { ]; } +@method_id(112) fun test12() { var a = ton("1") + ton("2"); // check absence in fif codegen return ton("0.1"); @@ -247,7 +248,7 @@ fun main() { @testcase | 110 | | 50000000 50000100 1234000000 51000000 @testcase | 111 | | [ 1000000000 1000000000 1000000000 -321123456789 321123456789 1100000000 ] @testcase | 114 | 5 | (null) (null) 0 -@testcase | 114 | 15 | 15 2 typeid-2 +@testcase | 114 | 15 | 15 2 typeid-1 @fif_codegen DECLPROC assign0() @fif_codegen DECLPROC assign0() diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/lazy-algo-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/lazy-algo-tests.tolk index 26ef767d..ac8e9fcd 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/lazy-algo-tests.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/lazy-algo-tests.tolk @@ -894,7 +894,7 @@ fun algo116() { __expect_lazy("[o] load f1 f2 ab f4 f5"); o.f1 + o.f2; match (o.ab) { - A => return o.f4 + A => { return o.f4 } B => throw o.f5 } } diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/lazy-load-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/lazy-load-tests.tolk index 4d097a21..b850a728 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/lazy-load-tests.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/lazy-load-tests.tolk @@ -241,6 +241,18 @@ struct WithGlobalModifier { n: int8; } +struct WithN5 { n: uint5 } +type U111 = int8 | int16 | WithN5 | bits100 +struct WithU111 { + a: U111 + b: U111? + bit: bool +} +fun WithU111.getSlice_a8_bnull(): slice asm "b{000000000101} PUSHSLICE" +fun WithU111.getSlice_a16_b8(): slice asm "b{010000000000000001100000000011} PUSHSLICE" +fun WithU111.getSlice_a8_b5(): slice asm "b{0000000001110111111} PUSHSLICE" +fun WithU111.getSlice_a8_b5_nobit(): slice asm "b{000000000111011111} PUSHSLICE" + @noinline fun contract.getFakeData(seqno: int): Cell { @@ -981,6 +993,21 @@ fun demo158() { return (m2.n, gModByCustom, after1); // also modifies by skipping (unpack called) } +@method_id(159) +fun demo159() { + val p1 = lazy WithU111.fromSlice(WithU111.getSlice_a8_bnull()); + val p2 = lazy WithU111.fromSlice(WithU111.getSlice_a16_b8()); + val p3 = lazy WithU111.fromSlice(WithU111.getSlice_a8_b5()); + val p4 = lazy WithU111.fromSlice(WithU111.getSlice_a8_b5_nobit()); + + val p1_bit = p1.bit; + val p2_bit = p2.bit; + val p3_bit = p3.bit; + val p4_b = p4.b; + + 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); +} + @method_id(200) fun demo200() { @@ -1601,10 +1628,10 @@ fun main() { @testcase | 113 | x{03ffff} | -1 @testcase | 113 | x{04000000FF} | 255 @testcase | 114 | x{0110} | 16 1 -@testcase | 114 | x{020f} | [ -15 ] typeid-25 +@testcase | 114 | x{020f} | [ -15 ] typeid-2 @testcase | 114 | x{03} | -1 1 @testcase | 114 | x{03ffff} | -1 1 -@testcase | 114 | x{04000000FF} | [ 255 ] typeid-25 +@testcase | 114 | x{04000000FF} | [ 255 ] typeid-2 @testcase | 115 | x{08} | 8 42 @testcase | 115 | x{88} | 0 2 @testcase | 115 | x{83} | -1 2 @@ -1619,9 +1646,9 @@ fun main() { @testcase | 122 | x{010288} | -8 1 @testcase | 123 | x{010280} | 5 @testcase | 124 | x{010283} | [ -3 2 ] -@testcase | 125 | | 127 32 15 2 3 typeid-1 15 3 -1 -@testcase | 126 | x{7F0F20} | 127 (null) (null) 15 typeid-3 32 15 -1 -@testcase | 126 | x{7F8F020320} | 127 15 2 3 typeid-4 32 -1 2 +@testcase | 125 | | 127 32 15 2 3 typeid-6 15 3 -1 +@testcase | 126 | x{7F0F20} | 127 (null) (null) 15 typeid-8 32 15 -1 +@testcase | 126 | x{7F8F020320} | 127 15 2 3 typeid-7 32 -1 2 @testcase | 127 | | 1 2 -1 255 @testcase | 128 | x{5500} | -1 -1 @testcase | 128 | x{558F01024} | 0 2 @@ -1669,6 +1696,7 @@ fun main() { @testcase | 156 | x{FF10} | -100 @testcase | 157 | | -1 @testcase | 158 | | 2 10 255 +@testcase | 159 | | -1 -1 31 -1 31 @testcase | 200 | | 1 @testcase | 201 | | 1 2 -1 diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/logical-operators.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/logical-operators.tolk index 9b70ae40..0fcd3f25 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/logical-operators.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/logical-operators.tolk @@ -1,9 +1,11 @@ import "imports/use-dicts.ext.tolk" +@method_id(90) fun simpleAllConst() { return (!0, !!0 & !false, !!!0, !1, !!1, !-1, !!-1, (!5 as int == 0) == !0, !0 == true); } +@method_id(91) fun compileTimeEval1(x: int) { // todo now compiler doesn't understand that bool can't be equal to number other than 0/-1 // (but understands that it can't be positive) diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/maps-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/maps-tests.tolk new file mode 100644 index 00000000..0597c301 --- /dev/null +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/maps-tests.tolk @@ -0,0 +1,651 @@ + +struct JustInt32 { + v: MInt32 +} + +struct JustUint64 { + v: uint64 +} + +struct JustAddress { + v: address +} + +type MInt32 = int32 +type MSlice = slice + +struct Point { + x: int8 + y: int8 +} + +struct Point3d { + x: int8 + y: int8 + z: int8 +} + +struct DataWithCell { + a: address + v: coins + p: Cell? + payload: cell? = null +} + +fun map.calculateSize(self) { + var size = 0; + var r = self.findFirst(); + while (r.isFound) { + size += 1; + r = self.iterateNext(r); + } + return size; +} + + +@method_id(101) +fun test1() { + var m: map = createEmptyMap(); + m.set(1, 10); + return m.exists(1); +} + +@method_id(102) +fun test2(k1: int, v1: int, k2: int, v2: int) { + var m: map = createMapFromLowLevelDict(null); + m.set(k1, v1); + m.set(k2, v2); + return (m.exists(1), m.exists(k1), m.mustGet(k2)); +} + +@method_id(103) +fun test3(v1: int) { + var m = createEmptyMap(); + m.set(1, (1, 2)); + m.set(2, (3, v1)); + m.delete(1); + return (m.exists(1), m.mustGet(2)); +} + +@method_id(104) +fun test4() { + var m = createEmptyMap(); + m.set(1, 10); + m.set(2, 20); + m.set(3, 30); + val g1 = m.get(1); + val g4 = m.get(4); + return (g1.isFound, g4.isFound, g1.loadValue()); +} + +@method_id(105) +fun test5() { + var m = createEmptyMap(); + m.set(1, 10); + m.set(2, 20); + m.set(3, 30); + var t = createEmptyTuple(); + var r = m.findFirst(); + while (r.isFound) { + t.push(r.getKey()); + t.push(r.loadValue()); + r = m.iterateNext(r); + } + r = m.findLast(); + while (r.isFound) { + t.push(r.loadValue()); + r = m.iterateNext(r); + } + r = m.findLast(); + while (r.isFound) { + t.push(r.loadValue()); + r = m.iteratePrev(r); + } + return t; +} + +@method_id(106) +fun test6() { + var m = createEmptyMap(); + m.set(1, 10); + m.set(2, 20); + m.set(3, 30); + return ( + m.findKeyGreater(1).getKey(), m.findKeyGreaterOrEqual(1).getKey(), + m.findKeyLess(3).getKey(), m.findKeyLessOrEqual(3).getKey(), + m.findKeyLess(3).loadValue(), m.findKeyLessOrEqual(3).loadValue(), + m.findKeyLessOrEqual(3).isFound, m.findKeyGreater(4).isFound, + ) +} + +@method_id(107) +fun test7() { + var m1 = createEmptyMap(); + m1.addIfNotExists(1, true); + m1.addIfNotExists(1, false); + m1.replaceIfExists(2, true); + return (m1.mustGet(1), m1.replaceIfExists(2, false), m1.get(2).isFound); +} + +@method_id(108) +fun test8() { + var (a, m: map) = (1, createEmptyMap()); + val prev1 = m.setAndGetPrevious(a, createAddressNone()); + val prev2 = m.setAndGetPrevious(a, address("EQCRDM9h4k3UJdOePPuyX40mCgA4vxge5Dc5vjBR8djbEKC5")); + return (prev1.isFound, prev2.isFound, prev2.loadValue().isNone(), prev2.loadValue().isNone(), m.get(a).loadValue().getWorkchain()); +} + +@method_id(109) +fun test9() { + var m = createEmptyMap(); + m.set(1, (ton("1"), ())); + val replaced = m.replaceIfExists(1, (ton("0.05"), ())); + val prev = m.replaceAndGetPrevious(1, (ton("0.01"), ())); + return (replaced, prev.loadValue(), m.replaceAndGetPrevious(2, (0,())), m.mustGet(1).0); +} + +@method_id(110) +fun test10() { + var m = createEmptyMap(); + val added1 = m.addIfNotExists(1<<100, 1<<10); + val added2 = m.addIfNotExists(1<<100, 2<<10); + val b1 = m.addOrGetExisting(2<<100, 20); + val b2 = m.addOrGetExisting(2<<100, 30); + return (added1, added2, m.mustGet(1<<100), b1.isFound, b2.isFound, b2.loadValue()); +} + +@method_id(111) +fun test11() { + var m: map; + m = createEmptyMap(); + m.set(0, null); + m.set(1, 1); + m.set(2, null); + return (m.exists(0), m.exists(1), m.mustGet(0), m.mustGet(1), m.get(0).isFound, m.get(0).loadValue(), m.get(3)); +} + +@method_id(112) +fun test12() { + var m1: map = createEmptyMap(); + val s1 = m1.calculateSize(); + m1.set(1, 10); + m1.addIfNotExists(1, -100); + val s2 = m1.calculateSize(); + var m2 = m1; + m2.addIfNotExists(2, 20); + m2.addIfNotExists(3, 30); + var m3 = m2; + val wasDeleted = m2.delete(2); + var del1 = m1.deleteAndGetDeleted(-100); + var del2 = m2.deleteAndGetDeleted(2); + var del3 = m2.deleteAndGetDeleted(3); + return ( + s1, s2, m3.calculateSize(), + wasDeleted, del1.isFound, del2.isFound, del3.isFound, + del3.loadValue(), + ); +} + +@method_id(113) +fun test13(m: map) { + // key remains just int at the TVM level, no runtime packing, checked via codegen + return m.get({v: 123}) +} + +@method_id(114) +fun test14(fromV: int) { + var m = createEmptyMap(); + m.set({v: 1}, 10); + m.set({v: 2}, 20); + m.set({v: 3}, 30); + return m.findKeyGreater({v: fromV}).key +} + +@method_id(115) +fun test15() { + var m = createEmptyMap(); + m.set({v: address("Ef9rU-_AAnBkHB71TIC3QvUf5LcAsvj0B4IoYzAXLpEFd5CA")}, 10); + m.set({v: address("Ef9LynHHKgBxY6-l-W_dWN-CtGT2_ji5rN3EzOI-p9zWEfq6")}, 20); + m.set({v: address("Ef9hMd78gzSiVsK0zz0AHtEja8x1UoB_NDZMjn-l86NQK_2Y")}, 30); + return m.mustGet({v:address("Ef9LynHHKgBxY6-l-W_dWN-CtGT2_ji5rN3EzOI-p9zWEfq6")}); +} + +@method_id(116) +fun test16() { + var m = createEmptyMap(); + m.set(100, {x: 10, y: 20}); + return (m.deleteAndGetDeleted(100).loadValue(), m.calculateSize(), m.isEmpty()); +} + +@method_id(117) +fun test17() { + var m = createEmptyMap(); + m.set(true, 123); + return m.findFirst().key; +} + +@method_id(118) +fun test18() { + var m: map = createEmptyMap(); + m.set({x: 1, y: 1}, {x: 10, y: 10}); + m.addIfNotExists({x: 1, y: 1}, {x: -100, y: -100}); + val f1 = m.get({x: 1, y: 1}); + m.replaceIfExists({x: 1, y: 1}, {x: 20, y: 20}); + val f2 = m.get({x: 1, y: 1}); + val deleted1 = m.delete({x: 1, y: 1}); + val deleted2 = m.delete({x: 1, y: 1}); + val f3 = m.get({x: 1, y: 1}); + return (m, f1.isFound, f2.isFound, f3.isFound, f1.loadValue(), f2.loadValue(), deleted1, deleted2, m.isEmpty()); +} + +@method_id(119) +fun test19() { + var m: map> = createEmptyMap(); + m.addIfNotExists(address("Ef9rU-_AAnBkHB71TIC3QvUf5LcAsvj0B4IoYzAXLpEFd5CA"), Point{x:10,y:10}.toCell()); + return m.get(address("Ef9rU-_AAnBkHB71TIC3QvUf5LcAsvj0B4IoYzAXLpEFd5CA")).loadValue().load(); +} + +@method_id(120) +fun test20() { + var m: map> = createEmptyMap(); + m.addIfNotExists(address("Ef9rU-_AAnBkHB71TIC3QvUf5LcAsvj0B4IoYzAXLpEFd5CA"), Point{x:10,y:10}.toCell()); + return m.mustGet(address("Ef9rU-_AAnBkHB71TIC3QvUf5LcAsvj0B4IoYzAXLpEFd5CA")).load(); +} + +@method_id(121) +fun test21() { + var m: map> = createEmptyMap(); + val r1 = m.addOrGetExisting(address("Ef9rU-_AAnBkHB71TIC3QvUf5LcAsvj0B4IoYzAXLpEFd5CA"), Point{x:10,y:10}.toCell()); + val r2 = m.addOrGetExisting(address("Ef9rU-_AAnBkHB71TIC3QvUf5LcAsvj0B4IoYzAXLpEFd5CA"), Point{x:20,y:20}.toCell()); + val d1 = m.deleteAndGetDeleted(address("Ef9rU-_AAnBkHB71TIC3QvUf5LcAsvj0B4IoYzAXLpEFd5CA")); + val d2 = m.deleteAndGetDeleted(address("Ef9rU-_AAnBkHB71TIC3QvUf5LcAsvj0B4IoYzAXLpEFd5CA")); + return (r1.isFound, r2.isFound, r2.loadValue().load(), d1.isFound, d2.isFound); +} + +@method_id(122) +fun test22() { + var m: map> = createEmptyMap(); + m.set(3, Point{x:30,y:30}.toCell()); + m.set(2, Point{x:20,y:20}.toCell()); + m.set(1, Point{x:10,y:10}.toCell()); + return (m.findFirst().key, m.findLast().key, m.findKeyGreater(2).key, m.findKeyGreaterOrEqual(2).loadValue().load(), m.mustGet(2).load().x); +} + +@method_id(123) +fun test23() { + var m: map = createEmptyMap(); + val p1 = m.setAndGetPrevious({x:1,y:1}, {a: createAddressNone(), v: ton("0.05"), p: Point{x:10,y:10}.toCell(), payload: null}); + val p2 = m.setAndGetPrevious({x:1,y:1}, {a: createAddressNone(), v: ton("0.1"), p: null, payload: createEmptyCell()}); + return (p1.isFound, p2.isFound, p2.loadValue().p!.load(), m.mustGet({x:1,y:1}).a.isNone(), m.get({x:1,y:1}).loadValue().payload.depth()); +} + +@method_id(124) +fun test24() { + var m = createEmptyMap(); + val p1 = m.deleteAndGetDeleted({x: 1, y: 1}); + m.set({x: 3, y: 3}, {a: createAddressNone(), v: ton("0.05"), p: Point{x:30,y:30}.toCell(), payload: createEmptyCell()}); + m.set({x: 1, y: 1}, {a: createAddressNone(), v: ton("0.05"), p: Point{x:10,y:10}.toCell(), payload: null}); + m.set({x: 2, y: 2}, {a: createAddressNone(), v: ton("0.05"), p: null, payload: null}); + val k2 = m.findKeyGreaterOrEqual({x: 2, y: 2}).getKey(); + var r = m.findFirst(); + var t = createEmptyTuple(); + while (r.isFound) { + t.push(r.getKey().x); + t.push(r.getKey().y); + t.push(r.loadValue().payload == null); + r = m.iterateNext(r); + } + return (p1, k2, t); +} + +@method_id(125) +fun test25() { + var m = createEmptyMap(); + m.set(1, stringHexToSlice("0101")); + return m; +} + +@method_id(126) +fun test26() { + var m = createEmptyMap(); + m.set(1, stringHexToSlice("0101")); + val f1 = m.setAndGetPrevious(1, stringHexToSlice("010101")); + val f2 = m.deleteAndGetDeleted(1); + val f3 = m.setAndGetPrevious(1, stringHexToSlice("01010101")); + return ( + f1.isFound, f2.isFound, f3.isFound, + f1.loadValue().remainingBitsCount(), f2.loadValue().remainingBitsCount(), + m.mustGet(1).remainingBitsCount(), + ); +} + +@method_id(127) +fun test27() { + var m = createEmptyMap(); + m.set(1, ""); + return (m.findFirst().key, m.findFirst().loadValue().remainingBitsCount()); +} + +@method_id(128) +fun test28() { + var m = createEmptyMap(); + m.set({x: 1, y: 1}, stringHexToSlice("01")); + m.set({x: 3, y: 3}, stringHexToSlice("03")); + m.set({x: 2, y: 2}, stringHexToSlice("02")); + var r = m.findKeyLessOrEqual({x: 4, y: 0}); + var sum = 0; + while (r.isFound) { + sum += r.loadValue().loadUint(8); + r = m.iteratePrev(r); + } + return (sum, m.calculateSize()); +} + +@method_id(129) +fun test29() { + var m = createEmptyMap(); + val a1 = m.addIfNotExists(1, DataWithCell{a: createAddressNone(), v: 0, p: null, payload: null}.toCell().beginParse()); + val a2 = m.addIfNotExists(2, DataWithCell{a: createAddressNone(), v: 0, p: Point{x:20,y:20}.toCell(), payload: null}.toCell().beginParse()); + val a3 = m.addIfNotExists(3, DataWithCell{a: createAddressNone(), v: 0, p: Point{x:30,y:30}.toCell(), payload: createEmptyCell()}.toCell().beginParse()); + val r4 = m.replaceAndGetPrevious(4, ""); + return ( + a1, a2, a3, r4, + DataWithCell.fromSlice(m.mustGet(2)).payload, + m.get(3).loadValue().loadAny().p!.load(), + m.get(3).loadValue().remainingRefsCount(), + m.findLast().key, + m.findLast().loadValue().remainingRefsCount(), + ); +} + +@method_id(130) +fun test30() { + var m = createEmptyMap(); + m.set(1, createEmptyCell()); + m.set(2, Point{x:20,y:20}.toCell()); + m.set(3, Point{x:30,y:30}.toCell()); + val c3 = m.get(3); + var r = m.findKeyLess(3); + var t = createEmptyTuple(); + while (r.isFound) { + t.push(r.loadValue().beginParse().remainingBitsCount()); + r = m.iteratePrev(r); + } + val p2 = lazy Point.fromCell(m.mustGet(2)); + return (c3.isFound, c3.loadValue().beginParse().loadAny(), t, p2.y); +} + +@method_id(131) +fun test31() { + var m = createEmptyMap(); + m.set(1, createEmptyCell()); + m.set(2, Point{x:20,y:20}.toCell()); + m.set(3, Point{x:30,y:30}.toCell()); + m.set(4, null); + val c4 = m.get(4); + val c5 = m.get(5); + var r = m.findLast(); + var t = createEmptyTuple(); + while (r.isFound) { + t.push(r.loadValue() == null); + r = m.iteratePrev(r); + } + return (m.mustGet(4), c4.loadValue(), c5.isFound, t); +} + +@method_id(132) +fun test32() { + var m = createEmptyMap(); + m.set(stringHexToSlice("0102") as bits16, stringHexToSlice("aaaa") as bits16); + m.set(stringHexToSlice("0304") as bits16, stringHexToSlice("bbbb") as bits16); + val repl = m.replaceAndGetPrevious(stringHexToSlice("0304") as bits16, stringHexToSlice("0000") as bits16); + var r = m.iteratePrev(m.findLast()); + return ((r.key as slice).remainingBitsCount(), (r.key as slice).loadAny(), (r.loadValue() as slice).remainingBitsCount(), repl.isFound, (repl.loadValue() as slice).loadUint(8)); +} + +@method_id(133) +fun test33() { + var m = createEmptyMap(); + m.set(123, {x: 10, y: 10}); + return m.mustGet(123); +} + +@method_id(134) +fun test34(z: int) { + var m = createEmptyMap(); + m.set(123, {x: 10, y: 10, z}); + return [m.mustGet(123).z, m.addIfNotExists(456, {x: 10, y: z, z}), m.mustGet(456).y]; +} + +@method_id(135) +fun test35() { + var m: map> = createEmptyMap(); + m.set(1, createEmptyMap()); + val f1 = m.mustGet(1).findFirst(); + assert (!f1.isFound) throw 123; + m.get(1).loadValue().set(10, 100); + assert (m.mustGet(1).isEmpty()) throw 123; // the previous set() changed a copy + + var nested = createEmptyMap(); + nested.set(1,10).set(2, 20); + m.replaceIfExists(1, nested); + nested.set(3, 30); + assert (nested.calculateSize() == 3) throw 123; + assert (m.mustGet(1).calculateSize() == 2) throw 123; + + val (k1, v2) = (m.mustGet(1).findFirst().getKey(), m.get(1).loadValue().findLast().loadValue()); + val s1 = m.findFirst().loadValue().calculateSize(); + m.mustGet(1).delete(10); + val d2 = m.mustGet(1).deleteAndGetDeleted(2); + return (f1.isFound, k1, v2, s1, d2.loadValue(), m.mustGet(1).isEmpty(), m.get(1).loadValue().calculateSize()); +} + +@method_id(136) +fun test36() { + val k = address("Ef_vA6yRfmt2P4UHnxlrQUZFcBnKux8mL2eMqBgpeMFPorr4"); + var m = createEmptyMap(); + m.set(k, 10); + return m.mustGet(k); +} + +@method_id(137) +fun test37() { + try { + var m = createEmptyMap(); + m.set(1, 10); + return m.mustGet(555) as int; + } catch (ex) { + return ex; + } +} + +@method_id(138) +fun test38(k: int) { + try { + var m = createEmptyMap(); + m.set(1, 10); + return m.mustGet(k, 55); + } catch (ex) { + return ex as int64; + } +} + + +fun main() { + __expect_type(createEmptyMap, "() -> map"); +} + +/** +@testcase | 101 | | -1 +@testcase | 102 | 1 10 2 20 | -1 -1 20 +@testcase | 102 | 2 20 3 30 | 0 -1 30 +@testcase | 103 | 100 | 0 3 100 +@testcase | 104 | | -1 0 10 +@testcase | 105 | | [ 1 10 2 20 3 30 30 30 20 10 ] +@testcase | 106 | | 2 1 2 3 20 30 -1 0 +@testcase | 107 | | -1 0 0 +@testcase | 108 | | 0 -1 -1 -1 0 +@testcase | 109 | | -1 50000000 (null) 0 10000000 +@testcase | 110 | | -1 0 1024 0 -1 20 +@testcase | 111 | | -1 -1 (null) 1 -1 (null) (null) 0 +@testcase | 112 | | 0 1 3 -1 0 0 -1 30 +@testcase | 114 | 2 | 3 +@testcase | 114 | 3 | (null) +@testcase | 115 | | 20 +@testcase | 116 | | 10 20 0 -1 +@testcase | 117 | | -1 +@testcase | 118 | | (null) -1 -1 0 10 10 20 20 -1 0 -1 +@testcase | 119 | | 10 10 +@testcase | 120 | | 10 10 +@testcase | 121 | | 0 -1 10 10 -1 0 +@testcase | 122 | | 1 3 3 20 20 20 +@testcase | 123 | | 0 -1 10 10 -1 0 +@testcase | 124 | | (null) 0 2 2 [ 1 1 -1 2 2 -1 3 3 0 ] +@testcase | 126 | | -1 -1 0 16 24 32 +@testcase | 127 | | 1 0 +@testcase | 128 | | 6 3 +@testcase | 129 | | -1 -1 -1 (null) 0 (null) 30 30 2 3 2 +@testcase | 130 | | -1 30 30 [ 16 0 ] 20 +@testcase | 131 | | (null) (null) 0 [ -1 0 0 0 ] +@testcase | 132 | | 16 1 2 16 -1 187 +@testcase | 133 | | 10 10 +@testcase | 134 | 5 | [ 5 -1 5 ] +@testcase | 135 | | 0 1 20 2 20 0 2 +@testcase | 136 | | 10 +@testcase | 137 | | 9 +@testcase | 138 | 1 | 10 +@testcase | 138 | 4 | 55 + +@fif_codegen +""" + test1() PROC:<{ // + NEWDICT // m + 1 PUSHINT // m '2=1 + x{0000000a} PUSHSLICE + s0 s2 XCHG + 8 PUSHINT + DICTISET // m + 1 PUSHINT // m '11=1 + SWAP + 8 PUSHINT // '11=1 m '16=8 + DICTIGET NULLSWAPIFNOT // '12 '13 + NIP // '13 + }> +""" + +@fif_codegen +""" + test13() PROC:<{ // m + 123 PUSHINT // m '1=123 + SWAP + 32 PUSHINT // '1=123 m '6=32 + DICTIGET NULLSWAPIFNOT // '2 '3 + }> +""" + +@fif_codegen +""" + test14() PROC:<{ + ... + 64 PUSHINT + DICTUGETNEXT NULLSWAPIFNOT2 + DROP + NIP + }> +""" + +@fif_codegen +""" + test15() PROC:<{ + ... + x{9FE9794E38E5400E2C75F4BF2DFBAB1BF0568C9EDFC717359BB8999C47D4FB9AC23_} PUSHSLICE + SWAP + 267 PUSHINT + DICTGET + 9 THROWIFNOT + 8 LDI + ENDS + }> +""" + +@fif_codegen +""" + test16() PROC:<{ + NEWDICT + 100 PUSHINT + x{0a14} PUSHSLICE + s0 s2 XCHG + 32 PUSHINT + DICTISET +""" + +@fif_codegen +""" + test25() PROC:<{ // + NEWDICT // m + 1 PUSHINT // m '2=1 + x{0101} PUSHSLICE // m '2=1 '3 + s0 s2 XCHG + 8 PUSHINT // '3 '2=1 m '6=8 + DICTISET // m + }> +""" + +@fif_codegen +""" + test30() PROC:<{ + NEWDICT + 1 PUSHINT + PUSHREF + s0 s2 XCHG + 8 PUSHINT + DICTISETREF +""" + +@fif_codegen +""" + test33() PROC:<{ + NEWDICT + 123 PUSHINT + x{0a0a} PUSHSLICE + s0 s2 XCHG + 8 PUSHINT + DICTUSET + 123 PUSHINT + SWAP + 8 PUSHINT + DICTUGET + 9 THROWIFNOT + 8 LDI + 8 LDI + ENDS + }> +""" + +@fif_codegen +""" + test34() PROC:<{ + NEWDICT + 123 PUSHINT + NEWC + x{0a0a} STSLICECONST + s3 PUSH + 8 STIR + s0 s2 XCHG + 32 PUSHINT + DICTUSETB +""" + +@fif_codegen +""" + test36() PROC:<{ // + x{9FFDE075922FCD6EC7F0A0F3E32D6828C8AE03395763E4C5ECF19503052F1829F45_} PUSHSLICE // k + NEWDICT // k m + x{0000000a} PUSHSLICE + s2 s1 PUXC + 267 PUSHINT + DICTSET // k m +""" + +*/ + diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/methods-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/methods-tests.tolk index 4b09a53d..c4d5948b 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/methods-tests.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/methods-tests.tolk @@ -191,11 +191,11 @@ fun T?.arbitraryMethod(self): never { throw 123; } fun test12() { __expect_type(10.arbitraryMethod(), "int?"); - __expect_type((10 as int8?).arbitraryMethod(), "int?"); __expect_type((10 as coins?)!.arbitraryMethod(), "int?"); - __expect_type(null.arbitraryMethod(), "int?"); - __expect_type((10 as coins?).arbitraryMethod(), "int?"); + __expect_type((10 as int8?).arbitraryMethod(), "never"); + __expect_type(null.arbitraryMethod(), "never"); + __expect_type((10 as coins?).arbitraryMethod(), "never"); __expect_type(Wrapper { item: 10 }.arbitraryMethod(), "Wrapper"); __expect_type(Wrapper { item: 10 as int8 }.arbitraryMethod(), "Wrapper"); @@ -280,7 +280,7 @@ fun Wrapper.createFrom(item: U): Wrapper { return {item}; } fun test18() { __expect_type(10.copy, "(int) -> int"); __expect_type(Wrapper{item:null as int8?}.copy, "(Wrapper) -> Wrapper"); - __expect_type(Wrapper.createNull, "() -> Wrapper"); + __expect_type(Wrapper.createNull, "() -> Wrapper"); __expect_type(Wrapper?>.createNull, "() -> Wrapper?>"); __expect_type(Wrapper.createFrom, "(int8) -> Wrapper"); @@ -288,6 +288,25 @@ fun test18() { return cb(10); } +type BalanceList = dict +type AssetList = dict + +fun BalanceList.validate(self): int1 { return 0 } +fun AssetList.validate(self): int2 { return 0 } + +fun test19(b: BalanceList, a: AssetList) { + __expect_type(b.validate(), "int1"); + __expect_type(a.validate(), "int2"); + + if (b == null) {} + match (a) { + null => {} + cell => {} + } + __expect_type(b, "BalanceList"); + __expect_type(a, "AssetList"); +} + fun main() {} /** @@ -300,7 +319,7 @@ fun main() {} @testcase | 107 | | 2 1 -1 @testcase | 108 | 5 | 17 5 @testcase | 109 | | 5 100500 20 30 5 5 31 -@testcase | 110 | | [ 2 3 1 1 3 1 1 ] -1 +@testcase | 110 | | [ 2 3 1 1 1 1 1 ] -1 @testcase | 111 | | [ 1 2 3 3 2 ] @testcase | 113 | | 0 -1 -1 -1 0 -1 @testcase | 114 | | -1 -1 0 0 0 diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/mutate-methods.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/mutate-methods.tolk index f3e93b69..b31e91ac 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/mutate-methods.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/mutate-methods.tolk @@ -307,7 +307,12 @@ fun testExplicitReturn() { } -fun main(){} +fun main(){ + // mark used to codegen them + load_next; + testStoreUintPureUnusedResult; + testStoreUintImpureUnusedResult +} /** @testcase | 101 | | 70 60 diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/never-return-functions.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/never-return-functions.tolk new file mode 100644 index 00000000..9024ed48 --- /dev/null +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/never-return-functions.tolk @@ -0,0 +1,31 @@ +fun alwaysThrows() { + throw 10 +} + +fun alwaysThrowsWithConditions(a: bool) { + if (a) { + throw 10; + } + throw 1 ; +} + +fun lastThrowAndConditionalReturn(a: bool) { + if (a) { + return 10; + } + throw 1 ; +} + +fun conditionallyThrow(a: bool) { + if (a) { + throw 1 + } + return 10 +} + +fun main() { + __expect_type(alwaysThrows(), "never"); + __expect_type(alwaysThrowsWithConditions(true), "never"); + __expect_type(lastThrowAndConditionalReturn(true), "int"); + __expect_type(conditionallyThrow(true), "int"); +} diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/nullable-tensors.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/nullable-tensors.tolk index 5b039454..4aa804b4 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/nullable-tensors.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/nullable-tensors.tolk @@ -503,7 +503,7 @@ fun main(){} @testcase | 102 | | 1 2 typeid-1 (null) (null) 0 @testcase | 103 | 1 2 | 3 3 0 1 2 @testcase | 104 | | 1 2 (null) (null) 0 -@testcase | 105 | | (null) (null) (null) 0 1 2 3 typeid-3 +@testcase | 105 | | (null) (null) (null) 0 1 2 3 typeid-2 @testcase | 106 | | 1 2 @testcase | 107 | | 0 0 -1 0 0 -1 @testcase | 108 | 5 6 | 7 8 10 11 typeid-1 (null) (null) 0 @@ -525,33 +525,33 @@ fun main(){} @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 typeid-4 -@testcase | 124 | 0 | 1 3 4 typeid-4 4 (null) (null) 0 +@testcase | 123 | | 1 3 4 typeid-3 +@testcase | 124 | 0 | 1 3 4 typeid-3 4 (null) (null) 0 @testcase | 124 | -1 | 1 (null) (null) 0 4 (null) (null) 0 @testcase | 125 | | 3 @testcase | 126 | | 1 (null) 2 @testcase | 127 | 1 | 1 (null) (null) 0 2 @testcase | 127 | 2 | 1 2 3 typeid-1 4 @testcase | 127 | 3 | 1 (null) (null) 0 5 -@testcase | 128 | 1 | 1 (null) (null) 0 2 typeid-11 +@testcase | 128 | 1 | 1 (null) (null) 0 2 typeid-6 @testcase | 128 | 2 | (null) (null) (null) (null) (null) 0 -@testcase | 128 | 3 | 1 2 3 typeid-1 4 typeid-11 +@testcase | 128 | 3 | 1 2 3 typeid-1 4 typeid-6 @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 typeid-1 @testcase | 130 | -1 | 1 (null) (null) 0 -@testcase | 131 | | typeid-12 777 0 777 777 777 0 0 typeid-12 typeid-12 777 typeid-12 typeid-12 typeid-13 777 +@testcase | 131 | | typeid-7 777 0 777 777 777 0 0 typeid-7 typeid-7 777 typeid-7 typeid-7 typeid-8 777 @testcase | 132 | | -1 0 -1 0 777 (null) (null) -1 0 0 @testcase | 133 | | 60 @testcase | 134 | | 11 21 typeid-1 -@testcase | 135 | | [ 10 ] [ (null) ] (null) 777 10 typeid-15 (null) typeid-15 (null) 0 777 10 typeid-16 (null) typeid-16 (null) 0 777 0 0 -1 0 0 -1 0 0 -1 777 0 -1 0 0 -1 0 +@testcase | 135 | | [ 10 ] [ (null) ] (null) 777 10 typeid-9 (null) typeid-9 (null) 0 777 10 typeid-10 (null) typeid-10 (null) 0 777 0 0 -1 0 0 -1 0 0 -1 777 0 -1 0 0 -1 0 @testcase | 136 | 9 | 9 0 @testcase | 136 | null | (null) -1 -@testcase | 140 | 8 9 | 8 9 typeid-17 (null) (null) 0 +@testcase | 140 | 8 9 | 8 9 typeid-11 (null) (null) 0 @testcase | 141 | | (null) 10 @testcase | 142 | | 3 3 1 2 @testcase | 143 | -1 0 | 1 2 1 3 777 (null) (null) (null) (null) 0 -@testcase | 143 | 0 -1 | 1 (null) 0 3 777 1 2 1 3 typeid-18 +@testcase | 143 | 0 -1 | 1 (null) 0 3 777 1 2 1 3 typeid-12 @fif_codegen """ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/overloads-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/overloads-tests.tolk new file mode 100644 index 00000000..792c1eb2 --- /dev/null +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/overloads-tests.tolk @@ -0,0 +1,289 @@ +struct Wrapper { item: T } +struct Nothing {} +struct MyPair {} + +struct Point { x: int8; y: int8 } + +type MInt = int +type MSlice = slice +type PointAlias = Point + +fun T.realValue(self): int1 { return 0 } +fun TT?.realValue(self): int2 { return 0 } +fun int.realValue(self): int3 { return 0 } +fun int8.realValue(self): int4 { return 0 } +fun int?.realValue(self): int5 { return 0 } +fun Nothing.realValue(self): int6 { return 0 } +fun Nothing>.realValue(self): int7 { return 0 } +fun Nothing.realValue(self): int8 { return 0 } + +fun test1() { + __expect_type(beginCell().realValue(), "int1"); + __expect_type((5 as int|slice).realValue(), "int1"); + __expect_type((beginCell() as builder?).realValue(), "int2"); + __expect_type((null as null|Nothing).realValue(), "int2"); + __expect_type((null as Nothing|null).realValue(), "int2"); + __expect_type(5.realValue(), "int3"); + __expect_type((5 as int?).realValue(), "int5"); + __expect_type((5 as int8).realValue(), "int4"); + __expect_type((5 as int8?).realValue(), "int2"); + __expect_type(Nothing{}.realValue(), "int6"); + __expect_type(Nothing{}.realValue(), "int6"); + __expect_type(Nothing{}.realValue(), "int8"); + __expect_type(Nothing{}.realValue(), "int8"); + __expect_type(Nothing{}.realValue(), "int6"); + __expect_type(Nothing>{}.realValue(), "int7"); + __expect_type(Nothing>>{}.realValue(), "int7"); + __expect_type(Nothing>?>{}.realValue(), "int8"); +} + +fun T.doSome2(self): int { return 0 } +fun int8.doSome2(self): cell { return createEmptyCell() } +fun int16.doSome2(self): slice { return "" } + +fun test2() { + __expect_type(5.doSome2(), "int"); + __expect_type((5 as int8).doSome2(), "cell"); + __expect_type(((5 as int8) as int16).doSome2(), "slice"); + __expect_type((5 as int?).doSome2(), "int"); + __expect_type(5.doSome2().doSome2(), "int"); + __expect_type((1, 2).doSome2(), "int"); +} + +fun MyPair.mapFn(self): int1 { return 0 } +fun MyPair.mapFn(self): int2 { return 0 } +fun MyPair.mapFn(self): int3 { return 0 } +fun MyPair.mapFn(self): int4 { return 0 } + +fun test3() { + __expect_type(MyPair{}.mapFn(), "int1"); + __expect_type(MyPair{}.mapFn(), "int1"); + __expect_type(MyPair{}.mapFn(), "int2"); + __expect_type(MyPair{}.mapFn(), "int2"); + __expect_type(MyPair{}.mapFn(), "int3"); + __expect_type(MyPair{}.mapFn(), "int4"); + __expect_type(MyPair{}.mapFn(), "int4"); +} + +fun (int|slice).unionMix(self): slice { return "" } +fun int.unionMix(self): bool { return true } + +fun test4() { + __expect_type((5 as int).unionMix(), "bool"); + __expect_type(("".unionMix()), "slice"); + __expect_type(((5 as int|slice).unionMix()), "slice"); +} + +fun WrapA.dup(self): WrapA { return self } +fun WrapA>.dup(self): WrapA> { return self } +struct WrapA {} + +fun test5() { + __expect_type(WrapA{}.dup(), "WrapA"); + __expect_type(WrapA>{}.dup(), "WrapA>"); +} + +type AliasBuilder1 = builder +type AliasBuilder2 = builder + +fun AliasBuilder1.myBAlias(self): int1 { return 0 } +fun AliasBuilder2.myBAlias(self): int2 { return 0 } +fun builder?.myBAlias(self): int3 { return 0 } +fun T.myBAlias(self): int4 { return 0 } +fun U?.myBAlias(self): int5 { return 0 } + +fun test6(b: builder, b1: AliasBuilder1, b2: AliasBuilder2, b1n: AliasBuilder1?, b2n: AliasBuilder2?) { + __expect_type(b1.myBAlias(), "int1"); + __expect_type(b2.myBAlias(), "int2"); + __expect_type(b1n.myBAlias(), "int3"); + __expect_type((b2n as builder?).myBAlias(), "int3"); + + __expect_type((b as builder|slice).myBAlias(), "int4"); + __expect_type(0.myBAlias(), "int4"); + __expect_type((0 as int|int16).myBAlias(), "int4"); + __expect_type(b2n.myBAlias(), "int3"); + __expect_type((null as builder|slice?).myBAlias(), "int5"); + __expect_type((0 as int?).myBAlias(), "int5"); +} + +type AssetBuilder = builder +type BalanceBuilder = builder + +fun AssetBuilder.validate(self): int1 { return 0 } +fun BalanceBuilder.validate(self): int2 { return 0 } +fun Wrapper.validate(self): int3 { return 0 } +fun Wrapper.validate(self): int4 { return 0 } + +fun test7() { + var a: AssetBuilder = beginCell(); + var b: BalanceBuilder = beginCell(); + + __expect_type(a.validate(), "int1"); + __expect_type(b.validate(), "int2"); + __expect_type(Wrapper{item:a}.validate(), "int3"); + __expect_type(Wrapper{item:b}.validate(), "int4"); +} + +fun (slice|TT).supposeMe(self): TT { if (self is slice) { throw 123 } return self } +fun T.supposeMe(self): int2 { return 0 } +fun int|slice|null.supposeMe(self): int3 { return 0 } + +fun test8(a: cell|slice, b: cell|()|slice|builder, c:slice, d:int|slice, e:null|(slice|int), f: int?, g:cell) { + __expect_type(a.supposeMe(), "cell"); + __expect_type(b.supposeMe(), "cell | () | builder"); + __expect_type(c.supposeMe(), "slice"); + __expect_type(d.supposeMe(), "int"); + __expect_type(e.supposeMe(), "int3"); + __expect_type(f.supposeMe(), "int2"); + __expect_type(g.supposeMe(), "int2"); +} + +fun MyPair.mySize(self): int1 { return 0 } +fun MyPair.mySize(self): int2 { return 0 } +// fun MyPair>.mySize(self): int3 { return 0 } +// fun MyPair>.mySize(self): int4 { return 0 } +fun MyPair>.mySize(self): int5 { return 0 } + +fun test9( + m1: MyPair, m2: MyPair, + m3: MyPair>, m4: MyPair>, + m5: MyPair>, m6: MyPair>, + m7: MyPair>, m8: MyPair>, + m9: MyPair?>, m10: MyPair?>, + m11: MyPair>, m12: MyPair>, +) { + __expect_type(m1.mySize(), "int1"); + __expect_type(m2.mySize(), "int2"); + // __expect_type(m3.mySize(), "int3"); + // __expect_type(m4.mySize(), "int3"); + // __expect_type(m5.mySize(), "int4"); + // __expect_type(m6.mySize(), "int4"); + // __expect_type(m7.mySize(), "int5"); + // __expect_type(m8.mySize(), "int5"); + __expect_type(m9.mySize(), "int2"); + __expect_type(m10.mySize(), "int1"); + // __expect_type(m11.mySize(), "int4"); + // __expect_type(m12.mySize(), "int4"); +} + +// fun MyPair>.h1(self): int1 { return 0 } +// fun MyPair>.h1(self): int2 { return 0 } +// fun MyPair>.h1(self): int3 { return 0 } +// fun MyPair>.h1(self): int4 { return 0 } + +fun test10() { + // __expect_type(MyPair>{}.h1(), "int1"); + // __expect_type(MyPair>{}.h1(), "int2"); + // __expect_type(MyPair>{}.h1(), "int3"); + // __expect_type(MyPair>{}.h1(), "int4"); + // __expect_type(MyPair>{}.h1(), "int4"); +} + +fun (int, U).p(self): int1 { return 0 } +fun (T, U).p(self): T { return self.0 } +fun T.p(self): int3 { return 0 } + +fun test11() { + __expect_type((5, "").p(), "int1"); + __expect_type((5 as int8, "").p(), "int8"); + __expect_type(("", "").p(), "slice"); + __expect_type((1, 2, 3).p(), "int3"); +} + +fun Wrapper.w(self): int1 { return 0 } +fun Wrapper.w(self): int2 { return 0 } + +fun test12() { + __expect_type(Wrapper{item:0}.w(), "int1"); + __expect_type(Wrapper{item:""}.w(), "int1"); + __expect_type(Wrapper>{item:{item:0}}.w(), "int2"); + // here the compiler finds T=slice resulting in Wrapper (it's correct) + // and chooses (1) due to a deeper shape + __expect_type(Wrapper{item:""}.w(), "int1"); +} + +type MyCell = cell +type MInt8 = int8 +type MInt32 = int32 + +fun MyCell.makeIterator(self): MyIterator { + return MyIterator.new(self) +} + +struct MyIterator { + data: slice +} + +fun MyIterator.new(c: cell): MyIterator { + return { data: c.beginParse() } +} + +fun MyIterator.empty(self): bool { + return self.data.isEmpty() +} + +fun MyIterator.next(mutate self): T { + return self.data.loadAny() +} + +fun MyCell.iterateAndAppend(self, mutate t: tuple) { + var it = self.makeIterator(); + while (!it.empty()) { + t.push(it.next()) + } +} + +fun MyCell.iterateAndAppend(self, mutate t: tuple) { + t.push(-777); +} + +@method_id(113) +fun test13() { + var c = beginCell().storeSlice(stringHexToSlice("0102")).endCell(); + var t = createEmptyTuple(); + (c as MyCell).iterateAndAppend(mutate t); + (c as MyCell).iterateAndAppend(mutate t); + (c as MyCell).iterateAndAppend(mutate t); + (c as MyCell).iterateAndAppend(mutate t); + return t; // [ 1 2 258 1 2 -777 ] +} + + +fun map.mySize(self): int1 { return 0 } +fun map.mySize(self): int2 { return 0 } +fun map.mySize(self): int3 { return 0 } +fun map>.mySize(self): int4 { return 0 } +fun map>.mySize(self): int5 { return 0 } + +type MInt16 = int16 +type MInt16ov = MInt16 +type MMap32 = map> +type MMap8Of32 = map + +fun test14( + m1: map>, m2: map>, + m3: map>, m4: map, m5: MMap8Of32, + m6: map?>, + m7: map, m8: map, + m9: map, +) { + // __expect_type(m1.mySize(), "int5"); + // __expect_type(m2.mySize(), "int5"); + // __expect_type(m3.mySize(), "int4"); + // __expect_type(m4.mySize(), "int4"); + // __expect_type(m5.mySize(), "int4"); + // __expect_type(m6.mySize(), "int3"); + // __expect_type(m7.mySize(), "int2"); + // __expect_type(m8.mySize(), "int2"); + // __expect_type(m9.mySize(), "int1"); +} + +fun main() { + // besides these positive tests, there are many negative resulting in "ambiguous call" + return 0 +} + +/** +@testcase | 0 | | 0 +@testcase | 113 | | [ 1 2 258 1 2 -777 ] + */ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-1.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-1.tolk index 9c9acf82..3cc20603 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-1.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-1.tolk @@ -7,6 +7,11 @@ struct Point { y: int32; } +struct TwoU { + a: uint8 + b: uint8 +} + @method_id(101) fun test1(value: int) { var t: JustInt32 = { value }; @@ -84,6 +89,15 @@ fun test7(s: slice) { return (6, s.remainingBitsCount()); } +@method_id(108) +fun test8(): cell | int { + try { + return TwoU{a:1<<10, b:0}.toCell() + } catch (ex) { + return ex + } +} + fun main(c: cell) { c as Cell; (c as Cell) as cell; @@ -103,6 +117,7 @@ fun main(c: cell) { @testcase | 107 | x{09332} | 4 12 @testcase | 107 | x{2} | 5 1 @testcase | 107 | x{0234} | 6 16 +@testcase | 108 | | 5 1 @fif_codegen """ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-2.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-2.tolk index cb01fbfe..7ed19ee8 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-2.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-2.tolk @@ -406,6 +406,20 @@ struct WithTwoRestFields { rest2: RemainingBitsAndRefs; } +struct WithMaps { + m1: map + m2: map +} + +enum EStoredAsInt8 { M100 = -100, Z = 0, P100 = 100 } +enum EStoredAsUint1 { ZERO, ONE } + +struct WithEnums { + e1: EStoredAsInt8 + e2: EStoredAsUint1 + rem: uint7 = 0 +} + // --------------------------------------------- @@ -716,6 +730,37 @@ fun test_EdgeCaseIntegers() { return edge.toCell().hash() == manual.endCell().hash() } +@method_id(230) +fun test_serializeMaps() { + var ( + m1: map, + m2: map, + ) = (createEmptyMap(), createEmptyMap()); + m1.set(1, ""); + m1.set(2, stringHexToSlice("01").appendRef(stringHexToSlice("02"))); + m2.set({value: 3}, (3, 30)); + m2.set({value: 4}, (4, 40)); + + var w = (WithMaps { m1, m2 }).toCell().load(); + w.m2.delete({value: 4}); + val d3 = w.m2.deleteAndGetDeleted({value: 3}); + var r = w.m1.findFirst(); + var kl = (0, 0); + while (r.isFound) { + kl = r.loadValue().remainingBitsAndRefsCount(); + r = w.m1.iterateNext(r); + } + return (w.m1.isEmpty(), w.m2.isEmpty(), d3.isFound, d3.loadValue(), kl); +} + +@method_id(231) +fun test_SimpleEnums() { + run({e1: EStoredAsInt8.M100, e2: EStoredAsUint1.ZERO}, stringHexToSlice("9c00")); + run({e1: EStoredAsInt8.Z, e2: EStoredAsUint1.ZERO}, stringHexToSlice("0000")); + run({e1: EStoredAsInt8.P100, e2: EStoredAsUint1.ONE, rem: 1}, stringHexToSlice("6481")); + return -1; +} + fun main() { var t: JustInt32 = { value: 10 }; var c = t.toCell(); @@ -737,18 +782,18 @@ fun main() { @testcase | 208 | | -1 @testcase | 209 | | -1 @testcase | 210 | | 123 555 46 (null) -@testcase | 211 | | (null) typeid-4 123 -@testcase | 212 | | (null) typeid-5 123 +@testcase | 211 | | (null) typeid-3 123 +@testcase | 212 | | (null) typeid-4 123 @testcase | 213 | | -1 @testcase | 214 | | -1 @testcase | 215 | | -1 @testcase | 216 | | -1 @testcase | 217 | | -1 @testcase | 218 | | -1 -@testcase | 219 | | 44 (null) (null) 46 typeid-15 +@testcase | 219 | | 44 (null) (null) 46 typeid-11 @testcase | 220 | | 0 0 (null) 99 typeid-8 typeid-9 99 1234 (null) 889129 14 @testcase | 221 | | -1 -@testcase | 222 | | 510 567 9392843922 typeid-18 81923 81923 typeid-19 777 0 -1 (null) (null) 0 100000 100000 typeid-19 +@testcase | 222 | | 510 567 9392843922 typeid-13 81923 81923 typeid-14 777 0 -1 (null) (null) 0 100000 100000 typeid-14 @testcase | 223 | | 10 55 (null) @testcase | 224 | | 60 50000000 5 9 123 -1 @testcase | 225 | | 5 40 65535 8 50000000 @@ -756,4 +801,6 @@ fun main() { @testcase | 227 | | 16 @testcase | 228 | | -1 @testcase | 229 | | -1 +@testcase | 230 | | 0 -1 -1 3 30 8 1 +@testcase | 231 | | -1 */ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-4.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-4.tolk index 6d913f58..08a1b432 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-4.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-4.tolk @@ -38,10 +38,10 @@ fun test1() { type Union_8_16_32_n = int8 | null | int16 | int32; -fun ans102n_8(): slice asm "b{0100001111} PUSHSLICE"; -fun ans102n_16(): slice asm "b{100000000000001111} PUSHSLICE"; -fun ans102n_32(): slice asm "b{1100000000000000000000000000001111} PUSHSLICE"; -fun ans102n_null(): slice asm "b{00} PUSHSLICE"; +fun ans102n_8(): slice asm "b{10000001111} PUSHSLICE"; +fun ans102n_16(): slice asm "b{1010000000000001111} PUSHSLICE"; +fun ans102n_32(): slice asm "b{11000000000000000000000000000001111} PUSHSLICE"; +fun ans102n_null(): slice asm "b{0} PUSHSLICE"; @method_id(102) fun test2() { @@ -65,10 +65,10 @@ fun ans104_8(): slice asm "b{0000001111} PUSHSLICE"; fun ans104_16(): slice asm "b{010000000000001111} PUSHSLICE"; fun ans104_32(): slice asm "b{1000000000000000000000000000001111} PUSHSLICE"; -fun ans104n_8(): slice asm "b{0100001111} PUSHSLICE"; -fun ans104n_16(): slice asm "b{100000000000001111} PUSHSLICE"; -fun ans104n_32(): slice asm "b{1100000000000000000000000000001111} PUSHSLICE"; -fun ans104n_null(): slice asm "b{00} PUSHSLICE"; +fun ans104n_8(): slice asm "b{10000001111} PUSHSLICE"; +fun ans104n_16(): slice asm "b{1010000000000001111} PUSHSLICE"; +fun ans104n_32(): slice asm "b{11000000000000000000000000000001111} PUSHSLICE"; +fun ans104n_null(): slice asm "b{0} PUSHSLICE"; @method_id(104) fun test4() { diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-5.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-5.tolk index 1baa2406..05a9ee1e 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-5.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-5.tolk @@ -10,29 +10,34 @@ struct JustMaybeInt32 { value: MaybeInt32; } +fun T.estimate(): [int, int, int, int] { + // convert a tensor to a tuple for better output readability + val (minBits, maxBits, minRefs, maxRefs) = T.estimatePackSize(); + return [minBits, maxBits, minRefs, maxRefs]; +} @method_id(101) fun test1() { return ( - int32.estimatePackSize(), - uint64.estimatePackSize(), - int1.estimatePackSize(), - bool.estimatePackSize(), - RemainingBitsAndRefs.estimatePackSize(), - coins.estimatePackSize(), - bits6.estimatePackSize(), - bytes8.estimatePackSize(), - varint32.estimatePackSize(), + int32.estimate(), + uint64.estimate(), + int1.estimate(), + bool.estimate(), + RemainingBitsAndRefs.estimate(), + coins.estimate(), + bits6.estimate(), + bytes8.estimate(), + varint32.estimate(), ); } @method_id(102) fun test2() { return ( - JustInt32.estimatePackSize(), - JustMaybeInt32.estimatePackSize(), - MaybeInt32.estimatePackSize(), - Int16Or32.estimatePackSize(), + JustInt32.estimate(), + JustMaybeInt32.estimate(), + MaybeInt32.estimate(), + Int16Or32.estimate(), ); } @@ -51,7 +56,7 @@ struct Test3_2 { @method_id(103) fun test3() { - return (Test3_1.estimatePackSize(), Test3_2.estimatePackSize()); + return (Test3_1.estimate(), Test3_2.estimate()); } struct Test4_1 { @@ -71,7 +76,7 @@ struct Test4_3 { @method_id(104) fun test4() { - return (Test4_1.estimatePackSize(), Test4_2.estimatePackSize(), Test4_3.estimatePackSize()); + return (Test4_1.estimate(), Test4_2.estimate(), Test4_3.estimate()); } struct Test5_1 { @@ -99,7 +104,7 @@ struct Test5_3 { @method_id(105) fun test5() { - return (Test5_1.estimatePackSize(), Test5_2.estimatePackSize(), Test5_3.estimatePackSize()); + return (Test5_1.estimate(), Test5_2.estimate(), Test5_3.estimate()); } struct(0x00112233) Test6_1 { @@ -115,7 +120,7 @@ type Test6_or = Test6_1 | Test6_2; @method_id(106) fun test6() { - return (Test6_1.estimatePackSize(), Test6_2.estimatePackSize(), Test6_or.estimatePackSize()); + return (Test6_1.estimate(), Test6_2.estimate(), Test6_or.estimate()); } struct(0x1020) Test7_1; @@ -127,7 +132,7 @@ type Test7_or = | Test7_1 | Test7_2 | Test7_3 @method_id(107) fun test7() { assert((Test7_1{} as Test7_or).toCell().beginParse().remainingBitsCount() == Test7_or.estimatePackSize().0, 400); - return (Test7_1.estimatePackSize(), Test7_or.estimatePackSize()); + return (Test7_1.estimate(), Test7_or.estimate()); } struct(0x10) Inner8_1 { @@ -151,7 +156,7 @@ struct Test8 { @method_id(108) fun test8() { - return (Inner8_1.estimatePackSize(), Inner8_2.estimatePackSize(), Test8.estimatePackSize()); + return (Inner8_1.estimate(), Inner8_2.estimate(), Test8.estimate()); } struct Test9_bits2 { f: bits2; } @@ -160,11 +165,11 @@ struct Test9_bits4 { f: bits4; } type Test9_f1 = int32 | int64 | int128; // auto-generated 2-bit prefix type Test9_f2 = int32 | Inner8_2; // auto-generated 1-bit prefix (Either) type Test9_f3 = bits1 | Test9_bits2 | bits3 | bits4 | bits5; // auto-generated 3-bit prefix -type Test9_f4 = bits1 | Test9_bits2 | bits3 | Test9_bits4?; // auto-generated 3-bit prefix +type Test9_f4 = bits1 | Test9_bits2 | bits3 | Test9_bits4?; // auto-generated 0 / 100/101/110/111 @method_id(109) fun test9() { - return (Test9_f1.estimatePackSize(), Test9_f2.estimatePackSize(), Test9_f3.estimatePackSize(), Test9_f4.estimatePackSize()); + return (Test9_f1.estimate(), Test9_f2.estimate(), Test9_f3.estimate(), Test9_f4.estimate()); } struct Test10_1 { @@ -176,7 +181,7 @@ type Test10_2 = (Test10_1, bool?, RemainingBitsAndRefs); @method_id(110) fun test10() { - return (Test10_1.estimatePackSize(), Test10_2.estimatePackSize()); + return (Test10_1.estimate(), Test10_2.estimate()); } struct Test11_1 { @@ -191,7 +196,7 @@ struct Test11_2 { @method_id(111) fun test11() { - return (Test11_1.estimatePackSize(), Test11_2.estimatePackSize()); + return (Test11_1.estimate(), Test11_2.estimate()); } @method_id(120) @@ -200,8 +205,25 @@ fun test20() { CellInner8_1.getDeclaredPackPrefixLen(), CellInner8_1.getDeclaredPackPrefix()); } +enum Color { Red, Green, Blue } +enum EInt9 { P133 = 133, M1 = -1 } +enum EUint6 { P63 = 63, P30 = 30, P1 = 1 } +enum EUint1 { A, B } +enum EInt1 { A = -1, B } +enum EUint123 { Z, V5 = 5, E123 = 1<<122, E123_2 = (1<<122) + (1<<121) } +type EUint123Alias = EUint123 + +@method_id(121) +fun test21() { + return ( + Color.estimate(), EInt9.estimate(), EUint6.estimate(), + EUint1.estimate(), EInt1.estimate(), EInt1.estimate(), + EUint123.estimate(), EUint123Alias.estimate(), + ) +} + fun main() { - __expect_type(int8.estimatePackSize(), "[int, int, int, int]"); + __expect_type(int8.estimatePackSize(), "(int, int, int, int)"); } /** @@ -213,8 +235,9 @@ fun main() { @testcase | 106 | | [ 64 64 0 0 ] [ 5 37 1 1 ] [ 5 65 0 1 ] @testcase | 107 | | [ 16 16 0 0 ] [ 16 16 0 0 ] @testcase | 108 | | [ 10 275 0 0 ] [ 34 158 0 0 ] [ 47 1143 0 1 ] -@testcase | 109 | | [ 34 130 0 0 ] [ 33 159 0 0 ] [ 4 8 0 0 ] [ 3 7 0 0 ] +@testcase | 109 | | [ 34 130 0 0 ] [ 33 159 0 0 ] [ 4 8 0 0 ] [ 1 7 0 0 ] @testcase | 110 | | [ 32 9999 0 4 ] [ 33 9999 0 8 ] @testcase | 111 | | [ 1023 1023 0 1 ] [ 0 0 2 2 ] @testcase | 120 | | 16 4128 1 1 +@testcase | 121 | | [ 2 2 0 0 ] [ 9 9 0 0 ] [ 6 6 0 0 ] [ 1 1 0 0 ] [ 1 1 0 0 ] [ 1 1 0 0 ] [ 123 123 0 0 ] [ 123 123 0 0 ] */ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-6.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-6.tolk index 364d026a..3b5074f5 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-6.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-6.tolk @@ -146,14 +146,14 @@ RETALT """ IF:<{ DROP - 139 PUSHINT + 129 PUSHINT }>ELSE<{ x{03} SDBEGINSQ NIP IFNOTJMP:<{ 104 THROW }> - 140 PUSHINT + 130 PUSHINT }> """ @@ -201,7 +201,7 @@ IF:<{ x{01} SDBEGINSQ IF:<{ DROP - 139 PUSHINT + 129 PUSHINT }>ELSE<{ x{03} SDBEGINSQ NIP @@ -209,9 +209,9 @@ IF:<{ 16 PUSHPOW2DEC THROWANY }> - 140 PUSHINT + 130 PUSHINT }> - 139 PUSHINT + 129 PUSHINT EQUAL }> """ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-7.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-7.tolk index 60e9c2b5..0a11b378 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-7.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-7.tolk @@ -100,6 +100,22 @@ fun Tensor3Skipping1.packToBuilder(self, mutate b: builder) { b.storeUint(self.0, 8).storeUint(self.2, 8); } +enum Color { + Red, + Green, + Blue, +} + +type UnsafeColor = Color + +fun UnsafeColor.unpackFromSlice(mutate s: slice) { + return s.loadUint(2) as Color +} + +fun UnsafeColor.packToBuilder(self, mutate b: builder) { + b.storeUint(self as int, 2) +} + @method_id(101) fun test1() { @@ -155,6 +171,15 @@ fun test7() { return t.toCell().load(); } +@method_id(108) +fun test8(c: UnsafeColor) { + var way1 = beginCell().storeAny(c).endCell(); + var way2 = c.toCell(); + __expect_type(way2, "Cell"); + assert (way1.hash() == way2.hash()) throw 123; + return way2.load(); +} + fun main() { __expect_type("" as TelegramString, "TelegramString"); } @@ -169,6 +194,8 @@ fun main() { @testcase | 105 | | 123 1 @testcase | 106 | | 6 2 255 0 8 9 @testcase | 107 | | 1 0 3 +@testcase | 108 | 0 | 0 +@testcase | 108 | 3 | 3 @fif_codegen """ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-8.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-8.tolk new file mode 100644 index 00000000..a5c84074 --- /dev/null +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/pack-unpack-8.tolk @@ -0,0 +1,184 @@ +enum EFits2Bits { ZERO, ONE, TWO } + +fun EFits2Bits.sliceWithZero(): slice asm "b{00} PUSHSLICE" +fun EFits2Bits.sliceWithThree(): slice asm "b{11} PUSHSLICE" + +enum EStartFrom1 { + ONE = 1, + TWO, + THREE, +} + +enum EFillsAllUint3 { + p0, p1, p2, p3, p4, p5, p6, p7 +} + +enum EStartFromM2 { + M2 = -2, + M1, ZERO, P1, P2, P3 = 3, +} + +enum EFits8Bits { + E0 = 0, + E110 = 110, + E220 = 220, +} + +const MAX_UINT = 115792089237316195423570985008687907853269984665640564039457584007913129639935 + +enum EMinMax { + MIN_INT = -115792089237316195423570985008687907853269984665640564039457584007913129639935 - 1, + MAX_INT = +115792089237316195423570985008687907853269984665640564039457584007913129639935, +} + +enum E0Max { + ZERO, + MAX_INT = +115792089237316195423570985008687907853269984665640564039457584007913129639935, +} + +struct WithEnumsUnion { + u: EFits8Bits | EStartFromM2 +} + + +@method_id(101) +fun test1() { + var e = EFits2Bits.fromSlice(EFits2Bits.sliceWithZero()); + return (e == EFits2Bits.ZERO, e == EFits2Bits.TWO); +} + +@method_id(102) +fun test2(c: EFits2Bits) { + try { + return (c.toCell().beginParse().loadAny() == c) as int + } catch (ex) { + return ex * 10 + } +} + +@method_id(103) +fun test3() { + return EStartFrom1.fromCell(EStartFrom1.ONE.toCell()) +} + +@method_id(104) +fun test4(v: EFillsAllUint3) { + return EFillsAllUint3.fromCell(v.toCell()) +} + +@method_id(105) +fun test5(s: slice) { + return EStartFromM2.fromSlice(s, {assertEndAfterReading: false}) +} + +@method_id(106) +fun test6() { + try { + return (4 as EFits2Bits).toCell().beginParse().loadUint(1) + } catch (ex) { + return ex + } +} + +@method_id(107) +fun test7(v: EFits8Bits) { + try { return EFits8Bits.fromCell(v.toCell()) as int } + catch (ex) { return ex * 1000 } +} + +@method_id(108) +fun test8() { + var s_min = beginCell().storeInt(-MAX_UINT - 1, 257).endCell(); + var s_max = beginCell().storeInt(+MAX_UINT, 257).endCell(); + return ( + EMinMax.fromCell(s_min) == EMinMax.MIN_INT, EMinMax.fromCell(s_max) == EMinMax.MAX_INT, + EMinMax.fromCell(s_max) == EMinMax.MIN_INT, EMinMax.fromCell(s_min) == EMinMax.MAX_INT, + ) +} + +@method_id(109) +fun test9() { + var s_min = beginCell().storeUint(0, 256).endCell(); + var s_max = beginCell().storeUint(MAX_UINT, 256).endCell(); + return ( + E0Max.fromCell(s_min) == E0Max.ZERO, E0Max.fromCell(s_max) == E0Max.MAX_INT, + ) +} + +@method_id(110) +fun test10() { + var w1 = WithEnumsUnion{u: EFits8Bits.E110}.toCell().load(); + var w2 = WithEnumsUnion{u: (220 as EFits8Bits)}.toCell().load(); + var w3 = WithEnumsUnion{u: ([0] as tuple).get(0)}.toCell().load(); + var w4 = WithEnumsUnion{u: EStartFromM2.P3}.toCell().load(); + return ( + w1.u is EFits8Bits && w1.u == EFits8Bits.E110, + w2.u is EFits8Bits && w2.u == EFits8Bits.E220, + w3.u is EStartFromM2 && w3.u == EStartFromM2.ZERO, + w4.u is EStartFromM2 && w4.u == EStartFromM2.P3, + w4.u is EFits8Bits && w4.u == EFits8Bits.E220, + ) +} + +fun main() { + +} + +/** +@testcase | 101 | | -1 0 +@testcase | 102 | 1 | -1 +@testcase | 102 | 3 | 50 +@testcase | 103 | | 1 +@testcase | 104 | 3 | 3 +@testcase | 104 | 7 | 7 +@testcase | 105 | x{d} | -2 +@testcase | 105 | x{7} | 3 +@testcase | 106 | | 5 +@testcase | 107 | 0 | 0 +@testcase | 107 | 110 | 110 +@testcase | 107 | 220 | 220 +@testcase | 107 | -50 | 5000 +@testcase | 107 | 1 | 5000 +@testcase | 107 | 222 | 5000 +@testcase | 108 | | -1 -1 0 0 +@testcase | 109 | | -1 -1 +@testcase | 110 | | -1 -1 -1 -1 0 + +@fif_codegen +""" + test1() PROC:<{ + b{00} PUSHSLICE + 2 LDU + OVER + 2 GTINT + 5 THROWIF + ENDS +""" + +@fif_codegen +""" + test4() PROC:<{ // v + NEWC // v b + 3 STU // b + ENDC // '4 + CTOS // s + 3 LDU // '9 s + ENDS // '9 + }> +""" + +@fif_codegen +""" + test5() PROC:<{ + // s + 3 PLDI + DUP + -2 LESSINT + 5 THROWIF + DUP + 3 GTINT + 5 THROWIF + }> +""" + + */ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/parse-address.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/parse-address.tolk index 0079871a..05b95f46 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/parse-address.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/parse-address.tolk @@ -30,6 +30,7 @@ fun check0(addr: address) { assert (addr.getWorkchain() == 0) throw 111; } +@method_id(90) fun codegenAddrEq(a: address, b: address) { if (a == b) { return 1; } if (a != b) { return 2; } diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/remove-unused-functions.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/remove-unused-functions.tolk index a6b91000..71f71d76 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/remove-unused-functions.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/remove-unused-functions.tolk @@ -28,8 +28,6 @@ fun main(): (int, int, int) { } /** -@experimental_options remove-unused-functions - @testcase | 0 | | 3 10 20 @fif_codegen DECLPROC used_as_noncall1() diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/send-msg-1.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/send-msg-1.tolk index 3a889dcf..c15b2d35 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/send-msg-1.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/send-msg-1.tolk @@ -170,13 +170,13 @@ fun test4(value: coins) { @noinline fun test5_manual() { - var ec_dict = createEmptyDict(); - ec_dict.iDictSet(32, 1, "ec1"); + var ec_dict: ExtraCurrenciesMap = createEmptyMap(); + ec_dict.set(1, 100); return beginCell() .storeUint(0x18, 6) // bounce .storeAddress(address("UQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPuwA")) .storeCoins(123) // value.grams - .storeMaybeRef(ec_dict) // value.extra + .storeMaybeRef(ec_dict.toLowLevelDict()) // value.extra .storeUint(0, 0 + 4 + 4 + 64 + 32 + 1 + 1) // ... + 0 init + 0 body (not ref) // body: op + queryId .storeUint(0x12345678, 32) @@ -186,8 +186,8 @@ fun test5_manual() { @method_id(105) fun test5() { - var ec_dict = createEmptyDict(); - ec_dict.iDictSet(32, 1, "ec1"); + var ec_dict: ExtraCurrenciesMap = createEmptyMap(); + ec_dict.set(1, 100); val body: MyBody = { queryId: 800 }; var b = createMessage({ body, @@ -215,9 +215,9 @@ fun test6_manual(value: coins, ec_dict: dict) { @method_id(106) fun test6(value: coins, dictKey: int?) { - var ec_dict = createEmptyDict(); + var ec_dict: ExtraCurrenciesMap = createEmptyMap(); if (dictKey != null) { - ec_dict.iDictSet(32, dictKey, "ec1"); + ec_dict.set(dictKey, 100500); } val body: MyBody = { queryId: 800 }; var b = createMessage({ @@ -226,7 +226,7 @@ fun test6(value: coins, dictKey: int?) { value: (value, ec_dict), body, }); - assert(b.hash() == test6_manual(value, ec_dict).hash(), 106); + assert(b.hash() == test6_manual(value, ec_dict.toLowLevelDict()).hash(), 106); return b.hash(); } @@ -617,7 +617,7 @@ fun test21(queryId: uint64) { var b = createMessage({ bounce: false, dest: address("EQAUzE-Nef80O9dLZy91HfPiOb6EEQ8YqyWKyIU-KeaYLNUi"), - value: (ton("0.1"), createEmptyDict()), + value: (ton("0.1"), createEmptyMap()), body, }); assert(b.hash() == test21_manual(body).hash(), 121); @@ -732,9 +732,9 @@ fun main() { @testcase | 102 | | 20192939504955637835708665496574868659039935190188156593169026529135727309085 @testcase | 103 | | 97654287980681401436727082042373998183264988661375688119718809500301413968039 @testcase | 104 | 600000 | 755636693689039391990782995030008885663781699175339033402019272057515711062 -@testcase | 105 | | 24816341499673835567887890014278904436471582322802948121781418622643959482495 +@testcase | 105 | | 24503969966890554363249789782591743702412487637374098978386746563683471749050 @testcase | 106 | 123 null | 104134380371273907780196393444164225714229635235007677971195651971972203592811 -@testcase | 106 | 456 8888 | 39337586036945718311402746340438400160817844833530971545330721291986281100430 +@testcase | 106 | 456 8888 | 6425272102977874346517989655071023150283043721608819726435980878339463285784 @testcase | 107 | 1000 | 55093441331748625324828489600632232039914212774002148634088483962817636598198 @testcase | 108 | 50000000 | 95023796475113775225029817428715936488418545169963429399979521091689824066088 @testcase | 109 | 0 | 55999621586681214992294941423256376619779969729861696464321825639854258502733 diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/send-msg-4.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/send-msg-4.tolk new file mode 100644 index 00000000..84706fd7 --- /dev/null +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/send-msg-4.tolk @@ -0,0 +1,60 @@ +struct LiquidityDepositWithInitData { + code: cell + data: cell +} + +type LiquidityDepositAddress = address + +type LiquidityDepositDestination = LiquidityDepositWithInitData | LiquidityDepositAddress + +struct (0x1b434676) AddLiquidityPartTon { + destination: LiquidityDepositDestination +} + +type AllowedMessages = AddLiquidityPartTon + +fun main() { + val msgCell = beginCell(). + storeUint(0x1b434676, 32) + .storeBool(true) // either right + .storeAddress(address("Ef_vA6yRfmt2P4UHnxlrQUZFcBnKux8mL2eMqBgpeMFPorr4")) + .endCell(); + + val msg = lazy AllowedMessages.fromCell(msgCell); + + match (msg) { + AddLiquidityPartTon => { + // dynamic union: the compiler inserts lots of IFs at runtime to handle this + var destination: address | AutoDeployAddress; + + match (msg.destination) { + LiquidityDepositWithInitData => { + destination = { + stateInit: { code: msg.destination.code, data: msg.destination.data }, + }; + } + LiquidityDepositAddress => { + destination = msg.destination; + } + } + + val sendMsg = createMessage({ + bounce: false, + dest: destination, + value: 0, + }); + sendMsg.send(SEND_MODE_REGULAR); + } + } + + return 0; +} + +/** +@testcase | 0 | | 0 + +@fif_codegen ONE HASHEXT_SHA256 +@fif_codegen }>ELSE<{ +@fif_codegen HASHCU +@fif_codegen 256 STU + */ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/smart-cast-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/smart-cast-tests.tolk index 90f1ae64..589bf652 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/smart-cast-tests.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/smart-cast-tests.tolk @@ -610,7 +610,7 @@ fun test55() { // in case of loops, type inferring is re-enterable // first iteration: x is int, eq instantiated // second (final) iteration: x is int?, eq instantiated - // (checked via codegen) + // (but since eq finally becomes unused, it's not generated to fift) eq55(x); __expect_type(x, "int?"); // types are checked (unlike generics instantiated) after inferring x = rand() ? 1 : null; @@ -716,8 +716,70 @@ fun test63() { __expect_type(r!.sub, "Point"); } +type BalanceDict = dict + +fun cell.myDepth(self): int1 { return 0 } +fun BalanceDict.myDepth(self): int3 { return 0 } +fun null.myDepth(self): int4 { return 0 } + +fun test64(d: dict, b: BalanceDict) { + match (d) { + null => { __expect_type(d.myDepth(), "int4") } + cell => { __expect_type(d.myDepth(), "int1") } + } + __expect_type(d, "dict"); + __expect_type(d.myDepth(), "int3"); + + match (b) { + null => { __expect_type(b.myDepth(), "int4") } + cell => { __expect_type(b.myDepth(), "int1") } + } + __expect_type(b, "BalanceDict"); + __expect_type(b.myDepth(), "int3"); + + if (d == null) { return; } + if (b != null) { return; } + + __expect_type(d.myDepth(), "int1"); + __expect_type(b.myDepth(), "int4"); + + if ([0].0) { + (b, d) = (createEmptyCell(), null) + } + __expect_type(b, "BalanceDict"); + __expect_type(d, "dict"); +} + +struct WithDict { + d: dict + b: BalanceDict +} + +fun test65(w: WithDict, t: (dict, BalanceDict)) { + match (w.d) { + null => { __expect_type(w.d, "null") } + cell => { __expect_type(w.d, "cell") } + } + if (w.b == null) {} + else { __expect_type(w.b, "cell") } + + match (t.1) { + null => { __expect_type(t.1.myDepth(), "int4") } + cell => { __expect_type(t.1.myDepth(), "int1") } + } + if (t.0 != null) { __expect_type(t.0, "cell") } + + __expect_type(w.d, "dict"); + __expect_type(w.b, "BalanceDict"); + __expect_type(t.0, "dict"); + __expect_type(t.1, "BalanceDict"); +} + fun main(x: int?): int { + // mark used to codegen them + test55; + return x == null ? -1 : x; } @@ -734,23 +796,23 @@ fun main(x: int?): int { @testcase | 139 | | 16 @testcase | 140 | 5 | 25 @testcase | 141 | | 1 2 -@testcase | 142 | | 5 3 (null) (null) 0 typeid-5 3 (null) (null) 0 +@testcase | 142 | | 5 3 (null) (null) 0 typeid-3 3 (null) (null) 0 @testcase | 143 | | 10 11 (null) 10 11 (null) (null) 0 @testcase | 144 | | 10 11 (null) 10 11 (null) (null) 0 -@testcase | 145 | | 5 3 (null) (null) 0 typeid-5 3 (null) (null) (null) (null) 0 3 (null) (null) 0 -@testcase | 146 | | 3 4 5 3 4 5 typeid-4 +@testcase | 145 | | 5 3 (null) (null) 0 typeid-3 3 (null) (null) (null) (null) 0 3 (null) (null) 0 +@testcase | 146 | | 3 4 5 3 4 5 typeid-2 @testcase | 147 | | (null) (null) 100 (null) 100 (null) (null) 0 @testcase | 158 | | 123 10 123 5 @testcase | 160 | | 101 109 @testcase | 161 | 9 9 | (null) (null) 0 (null) (null) -@testcase | 161 | 19 0 | 19 0 typeid-1 19 0 +@testcase | 161 | 19 0 | 19 0 typeid-4 19 0 @stderr warning: expression of type `int` can never be `null`, this condition is always true @stderr warning: unreachable code @stderr var t2 redef = getNullableInt()!; -@fif_codegen eq55() PROC:<{ -@fif_codegen eq55() PROC:<{ +@fif_codegen eq55() PROC:<{ +@fif_codegen_avoid eq55() PROC:<{ @fif_codegen """ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/some-tests-4.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/some-tests-4.tolk new file mode 100644 index 00000000..74645e15 --- /dev/null +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/some-tests-4.tolk @@ -0,0 +1,110 @@ +const __precisionZeros = 1000000000000000000; // 18 zeros +const __base0 = 1000000043929018416; // 4x every year (3600 * (24 * 365 + 6) seconds) + +const __base1 = __base0 * __base0 / __precisionZeros; +const __base2 = __base1 * __base1 / __precisionZeros; +const __base3 = __base2 * __base2 / __precisionZeros; +const __base4 = __base3 * __base3 / __precisionZeros; +const __base5 = __base4 * __base4 / __precisionZeros; +const __base6 = __base5 * __base5 / __precisionZeros; +const __base7 = __base6 * __base6 / __precisionZeros; +const __base8 = __base7 * __base7 / __precisionZeros; +const __base9 = __base8 * __base8 / __precisionZeros; +const __base10 = __base9 * __base9 / __precisionZeros; +const __base11 = __base10 * __base10 / __precisionZeros; +const __base12 = __base11 * __base11 / __precisionZeros; +const __base13 = __base12 * __base12 / __precisionZeros; +const __base14 = __base13 * __base13 / __precisionZeros; +const __base15 = __base14 * __base14 / __precisionZeros; +const __base16 = __base15 * __base15 / __precisionZeros; +const __base17 = __base16 * __base16 / __precisionZeros; +const __base18 = __base17 * __base17 / __precisionZeros; +const __base19 = __base18 * __base18 / __precisionZeros; +const __base20 = __base19 * __base19 / __precisionZeros; +const __base21 = __base20 * __base20 / __precisionZeros; +const __base22 = __base21 * __base21 / __precisionZeros; +const __base23 = __base22 * __base22 / __precisionZeros; +const __base24 = __base23 * __base23 / __precisionZeros; +const __base25 = __base24 * __base24 / __precisionZeros; +const __base26 = __base25 * __base25 / __precisionZeros; +const __base27 = __base26 * __base26 / __precisionZeros; +const __base28 = __base27 * __base27 / __precisionZeros; +const __base29 = __base28 * __base28 / __precisionZeros; +const __base30 = __base29 * __base29 / __precisionZeros; + +const ERROR_EXP_TOO_LARGE = 10970; + +@method_id(101) +fun test1(value: int, exp: int): int { + assert (exp <= 0x7fffffff) throw ERROR_EXP_TOO_LARGE; + + if ((exp & 0x00000001) != 0) { value = mulDivFloor(value, __base0, __precisionZeros); } + if ((exp & 0x00000002) != 0) { value = mulDivFloor(value, __base1, __precisionZeros); } + if ((exp & 0x00000004) != 0) { value = mulDivFloor(value, __base2, __precisionZeros); } + if ((exp & 0x00000008) != 0) { value = mulDivFloor(value, __base3, __precisionZeros); } + + if ((exp & 0x00000010) != 0) { value = mulDivFloor(value, __base4, __precisionZeros); } + if ((exp & 0x00000020) != 0) { value = mulDivFloor(value, __base5, __precisionZeros); } + if ((exp & 0x00000040) != 0) { value = mulDivFloor(value, __base6, __precisionZeros); } + if ((exp & 0x00000080) != 0) { value = mulDivFloor(value, __base7, __precisionZeros); } + + if ((exp & 0x00000100) != 0) { value = mulDivFloor(value, __base8, __precisionZeros); } + if ((exp & 0x00000200) != 0) { value = mulDivFloor(value, __base9, __precisionZeros); } + if ((exp & 0x00000400) != 0) { value = mulDivFloor(value, __base10, __precisionZeros); } + if ((exp & 0x00000800) != 0) { value = mulDivFloor(value, __base11, __precisionZeros); } + + if ((exp & 0x00001000) != 0) { value = mulDivFloor(value, __base12, __precisionZeros); } + if ((exp & 0x00002000) != 0) { value = mulDivFloor(value, __base13, __precisionZeros); } + if ((exp & 0x00004000) != 0) { value = mulDivFloor(value, __base14, __precisionZeros); } + if ((exp & 0x00008000) != 0) { value = mulDivFloor(value, __base15, __precisionZeros); } + + if ((exp & 0x00010000) != 0) { value = mulDivFloor(value, __base16, __precisionZeros); } + if ((exp & 0x00020000) != 0) { value = mulDivFloor(value, __base17, __precisionZeros); } + if ((exp & 0x00040000) != 0) { value = mulDivFloor(value, __base18, __precisionZeros); } + if ((exp & 0x00080000) != 0) { value = mulDivFloor(value, __base19, __precisionZeros); } + + if ((exp & 0x00100000) != 0) { value = mulDivFloor(value, __base20, __precisionZeros); } + if ((exp & 0x00200000) != 0) { value = mulDivFloor(value, __base21, __precisionZeros); } + if ((exp & 0x00400000) != 0) { value = mulDivFloor(value, __base22, __precisionZeros); } + if ((exp & 0x00800000) != 0) { value = mulDivFloor(value, __base23, __precisionZeros); } + + if ((exp & 0x01000000) != 0) { value = mulDivFloor(value, __base24, __precisionZeros); } + if ((exp & 0x02000000) != 0) { value = mulDivFloor(value, __base25, __precisionZeros); } + if ((exp & 0x04000000) != 0) { value = mulDivFloor(value, __base26, __precisionZeros); } + if ((exp & 0x08000000) != 0) { value = mulDivFloor(value, __base27, __precisionZeros); } + + if ((exp & 0x10000000) != 0) { value = mulDivFloor(value, __base28, __precisionZeros); } + if ((exp & 0x20000000) != 0) { value = mulDivFloor(value, __base29, __precisionZeros); } + if ((exp & 0x40000000) != 0) { value = mulDivFloor(value, __base30, __precisionZeros); } + + return value; +} + +@method_id(102) +fun test2(g1: bool) { + if (g1) { + return __base30; + } + return __base30; +} + +fun main() { + return 0x7fffffff; +} + +/** +@testcase | 101 | 12345678901234567890 0 | 12345678901234567890 +@testcase | 101 | 12345678901234567890 2147483647 | 1152091301147391687695287037940761486473052236056853112522947 +@testcase | 102 | 0 | 305482242253527345283975278648594775678 +@testcase | 102 | -1 | 305482242253527345283975278648594775678 + +@fif_codegen +""" + test2() PROC:<{ + IFJMP:<{ + 305482242253527345283975278648594775678 PUSHINT // '62 + }> + 305482242253527345283975278648594775678 PUSHINT // '124 + }> +""" + */ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/special-fun-names.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/special-fun-names.tolk index 11c34f98..8771762a 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/special-fun-names.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/special-fun-names.tolk @@ -22,8 +22,6 @@ fun test101() { } /** -@experimental_options remove-unused-functions - @testcase | 0 | | 0 @testcase | -1 | | -1 @testcase | -2 | | -2 diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/struct-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/struct-tests.tolk index 8eba3b42..ae8d51ac 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/struct-tests.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/struct-tests.tolk @@ -586,37 +586,37 @@ type PointAlias = Point; @testcase | 105 | | 35 30 45 @testcase | 106 | | 10 20 @testcase | 107 | | 5 5 10 20 15 -@testcase | 108 | | 777 typeid-3 777 0 777 777 777 typeid-3 777 typeid-4 777 777 +@testcase | 108 | | 777 typeid-2 777 0 777 777 777 typeid-2 777 typeid-3 777 777 @testcase | 109 | | 70 30 20 20 -80 @testcase | 110 | 0 | (null) (null) 0 -@testcase | 110 | -1 | 10 20 typeid-5 +@testcase | 110 | -1 | 10 20 typeid-1 @testcase | 111 | 0 | 0 2 10 20 30 @testcase | 111 | -1 | 0 0 3 4 7 @testcase | 112 | 0 | (null) (null) 0 -@testcase | 112 | -1 | 1 2 typeid-5 +@testcase | 112 | -1 | 1 2 typeid-1 @testcase | 113 | | (null) (null) 0 @testcase | 114 | | 1 2 @testcase | 115 | | (null) (null) (null) (null) 0 -@testcase | 116 | | -1 -4 [ 8 4 ] 7 (null) 0 [ 10 11 ] typeid-7 +@testcase | 116 | | -1 -4 [ 8 4 ] 7 (null) 0 [ 10 11 ] typeid-5 @testcase | 117 | 5 | 5 5 5 5 5 @testcase | 117 | null | (null) (null) (null) -1 (null) @testcase | 118 | | 17 -@testcase | 119 | | 10 20 9 10 typeid-5 9 -@testcase | 120 | 0 | 80 9 8 80 typeid-5 +@testcase | 119 | | 10 20 9 10 typeid-1 9 +@testcase | 120 | 0 | 80 9 8 80 typeid-1 @testcase | 120 | -1 | 8 14 (null) (null) 0 @testcase | 121 | 0 | 17 17 8 136 @testcase | 121 | -1 | 8 (null) 8 8 @testcase | 122 | 100 | 101 50 106 212 100 101 101 @testcase | 124 | 66 | 66 -@testcase | 125 | | (null) (null) 40 60 typeid-5 +@testcase | 125 | | (null) (null) 40 60 typeid-1 @testcase | 126 | | 118 -@testcase | 127 | | 0 0 typeid-3 typeid-3 777 -1 -1 0 0 777 0 0 typeid-10 777 0 0 777 (null) (null) 0 -@testcase | 128 | -1 | (null) (null) typeid-3 -1 0 0 777 (null) (null) typeid-3 -1 0 0 -@testcase | 128 | 0 | 1 2 typeid-2 0 -1 0 777 0 typeid-3 typeid-10 0 -1 0 +@testcase | 127 | | 0 0 typeid-2 typeid-2 777 -1 -1 0 0 777 0 0 typeid-7 777 0 0 777 (null) (null) 0 +@testcase | 128 | -1 | (null) (null) typeid-2 -1 0 0 777 (null) (null) typeid-2 -1 0 0 +@testcase | 128 | 0 | 1 2 typeid-8 0 -1 0 777 0 typeid-2 typeid-7 0 -1 0 @testcase | 129 | | (null) -@testcase | 130 | -1 | typeid-12 777 (null) typeid-12 777 0 777 0 0 -@testcase | 130 | 0 | typeid-11 777 4 1 777 typeid-11 777 0 0 -@testcase | 131 | | 5 typeid-13 (null) typeid-13 (null) 0 777 0 0 -1 777 0 -1 +@testcase | 130 | -1 | typeid-9 777 (null) typeid-9 777 0 777 0 0 +@testcase | 130 | 0 | typeid-10 777 4 1 777 typeid-10 777 0 0 +@testcase | 131 | | 5 typeid-11 (null) typeid-11 (null) 0 777 0 0 -1 777 0 -1 @testcase | 132 | | 10 20 10 0 0 20 0 0 0 0 @testcase | 133 | | -1 0 5 (null) (null) (null) 0 0 46 @testcase | 134 | | 10 20 diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/try-catch-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/try-catch-tests.tolk index 136c606d..df2e7c6d 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/try-catch-tests.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/try-catch-tests.tolk @@ -264,6 +264,8 @@ fun testBigExcno() { } fun main() { + // mark used to codegen them + testCodegen3; } /** diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/type-aliases-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/type-aliases-tests.tolk index 3bc0e511..587e1643 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/type-aliases-tests.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/type-aliases-tests.tolk @@ -8,6 +8,8 @@ type Pair2_v1 = | (int, int) type Pair2_v2 = (MInt, MInt) type MBool = | bool type Tuple2Int = [int, int] +type UserId = int +type OwnerId = int struct Point { x: int; y: int } type PointAlias = Point; @@ -35,9 +37,15 @@ fun test1(x: MInt): MVoid { __expect_type(rand() ? (1, 2) : (1, 2) as Pair2_v1, "(int, int)"); __expect_type(rand() ? (1, 2) : (1, 2) as Pair2_v2, "(int, int)"); __expect_type(rand() ? (1, 2) as Pair2_v1 : (1, 2), "Pair2_v1"); - __expect_type(rand() ? (1, 2) as Pair2_v1 : (1, 2) as Pair2_v2, "(int, int)"); __expect_type(rand() ? (1, 2) as Pair2_v2 : (1, 2) as Pair2_v2, "Pair2_v2"); + // since `Pair2_v1` and `Pair2_v2` underlying type is equal, a union of them can't be created + var d1 = (1, 2) as Pair2_v1 | Pair2_v2; + var d2 = (1, 2) as Pair2_v2 | Pair2_v1; + __expect_type(d1, "Pair2_v1"); + __expect_type(d2, "Pair2_v2"); + __expect_type(rand() ? (1, 2) as Pair2_v1 : (1, 2) as Pair2_v2, "Pair2_v1"); + __expect_type(!x, "bool"); __expect_type(x as int?, "int?"); @@ -63,9 +71,9 @@ fun test3(x: MIntN, y: MInt?) { if (x != null) { __expect_type(x, "MInt"); } - __expect_type(x, "MInt?"); + __expect_type(x, "MIntN"); var (z1, z2) = (x, x!, ); - __expect_type(z1, "MInt?"); + __expect_type(z1, "MIntN"); __expect_type(z2, "MInt"); } @@ -76,9 +84,9 @@ fun test4(x: MIntN, y: MIntN) { __expect_type(x + y, "int"); return x + y; } - __expect_type(x, "MInt?"); + __expect_type(x, "MIntN"); __expect_type(x!, "MInt"); - __expect_type(rand() ? x : y, "MInt?"); + __expect_type(rand() ? x : y, "MIntN"); __expect_type(rand() ? x : 0, "MInt?"); __expect_type(rand() ? 0 : x, "int?"); __expect_type(rand() ? 0 : x!, "int"); @@ -97,8 +105,8 @@ fun test5() { __expect_type(x, "Pair2_v1"); __expect_type(y, "Pair2_v2"); - takeTensor_v1(x); takeTensor_v2(x); takeTensor_v3(x); - takeTensor_v1(y); takeTensor_v2(y); takeTensor_v3(y); + takeTensor_v1(x); takeTensor_v2(x as Pair2_v2); takeTensor_v3(x); + takeTensor_v1(y as Pair2_v1); takeTensor_v2(y); takeTensor_v3(y); takeTensor_v1(z); takeTensor_v2(z); takeTensor_v3(z); var t = (y, x); @@ -117,16 +125,16 @@ fun test6() { __expect_type(z.0, "MInt"); } -fun someFn1(v: MInt): MIntN { return v; } +fun someFn1(v: MInt): MIntN { return v as MIntN; } fun test7() { var f1: (int) -> int? = someFn1; var f2: (int) -> MInt? = someFn1; - var f3: (MInt_v2) -> MInt_v2? = someFn1; + var f3: (MInt_v2) -> MInt_v2? = someFn1 as (MInt_v2) -> MInt_v2?; f1 = f2; f1 = f3; - f2 = f1; f2 = f3; - f3 = f1; f3 = f2; + f2 = f1; f2 = f3 as (int) -> MInt?; + f3 = f1; f3 = f2 as (MInt_v2) -> MInt_v2?; } fun test8() { @@ -143,6 +151,7 @@ fun test8() { someFn1 as (MInt_v2) -> MInt_v2?; } +@method_id(109) fun test9(b: MBool): MBool { if (!b) { __expect_type(b, "MBool"); @@ -156,7 +165,7 @@ fun test10() { var x1: Pair2_v1 = (5, 6); var (a1, b1) = x1; __expect_type(a1, "int"); - var x2: Pair2_v2? = x1; + var x2: Pair2_v2? = x1 as (int, int); var (a2, b2) = x2; __expect_type(a2, "MInt"); var x3: Tuple2Int = [9, 10]; @@ -198,6 +207,188 @@ fun test12(makeNotNull: bool) { return t; } +struct Wrapper { + value: T; +} +type WrapperOfInt = Wrapper +type WrapperOfMInt = Wrapper +type WrapperAlias = Wrapper +type WrapperAliasOfInt = WrapperAlias +type WrapperAliasOfMInt = WrapperAlias + +fun WrapperOfInt.myFn(self): int1 { return 0 } +fun WrapperOfMInt.myFn(self): int2 { return 0 } +fun WrapperAliasOfInt.myFn(self): int3 { return 0 } +fun WrapperAliasOfMInt.myFn(self): int4 { return 0 } + +fun test13(a: WrapperAlias, b: WrapperAlias, c: Wrapper, d: Wrapper, + e: WrapperAliasOfInt, f: WrapperAliasOfMInt, g: WrapperOfInt, h: WrapperOfMInt) { + __expect_type(g.myFn(), "int1"); + __expect_type(h.myFn(), "int2"); + __expect_type(e.myFn(), "int3"); + __expect_type(f.myFn(), "int4"); + + a = a; a = b; a = c; a = d; a = e; + b = a; b = b; b = c; b = d; b = f; + c = a; c = b; c = c; c = d; c = e; c = f; c = g; c = h; + d = a; d = b; d = c; d = d; d = e; d = f; d = g; d = h; + e = a; e = c; e = d; e = e; + f = b; f = c; f = d; f = f; + g = c; g = d; g = g; + h = c; h = d; h = h; + + a as WrapperAlias; + a as WrapperAlias; + a as Wrapper; + a as Wrapper; + a as WrapperOfInt; + a as WrapperOfMInt; + a as WrapperAliasOfInt; + a as WrapperAliasOfMInt; + + match (a) { WrapperAlias => {} } + match (a) { WrapperAlias => {} } + match (a) { Wrapper => {} } + match (a) { Wrapper => {} } + // match (a) { WrapperOfInt => {} } + // match (a) { WrapperOfMInt => {} } + match (a) { WrapperAliasOfInt => {} } + // match (a) { WrapperAliasOfMInt => {} } + + b as WrapperAlias; + b as WrapperAlias; + b as Wrapper; + b as Wrapper; + b as WrapperOfInt; + b as WrapperOfMInt; + b as WrapperAliasOfInt; + b as WrapperAliasOfMInt; + + match (b) { WrapperAlias => {} } + match (b) { WrapperAlias => {} } + match (b) { Wrapper => {} } + match (b) { Wrapper => {} } + // match (b) { WrapperOfInt => {} } + // match (b) { WrapperOfMInt => {} } + // match (b) { WrapperAliasOfInt => {} } + match (b) { WrapperAliasOfMInt => {} } + + match (e) { WrapperAliasOfInt => {} } + + match (f) { WrapperAliasOfMInt => {} } + match (f) { Wrapper => {} } + match (f) { Wrapper => {} } + // match (f) { WrapperAlias => {} } + match (f) { WrapperAlias => {} } + + __expect_type(a, "WrapperAlias"); + __expect_type(b, "WrapperAlias"); + __expect_type(c, "Wrapper"); + __expect_type(d, "Wrapper"); + __expect_type(e, "WrapperAliasOfInt"); + __expect_type(f, "WrapperAliasOfMInt"); + __expect_type(g, "WrapperOfInt"); + __expect_type(h, "WrapperOfMInt"); + + __expect_type(g.myFn(), "int1"); + __expect_type(h.myFn(), "int2"); + __expect_type(e.myFn(), "int3"); + __expect_type(f.myFn(), "int4"); +} + +type Payload14 = MInt | MBool | PointAlias2 + +fun test14(i: MInt, b: MBool, c: PointAlias2) { + var p: Payload14 = i; + p = b; + p = c; + p = i as int; + p = b as bool; + p = c as Point; + p = c as PointAlias; +} + +type MInt1 = int +type MInt2 = MInt1 + +fun int.plus0(self) { return self + 0 } +fun MInt1.plus1(self) { return self + 1 } +fun MInt2.plus2(self) { return self + 2 } + +@method_id(115) +fun test15() { + var i: int = 0; + var m: MInt1 = 10; + var t: MInt2 = 20; + + if (0) { + i = m; i = t; + m = i; m = t; + t = i; t = m; + } + + return ( + i.plus0(), i.plus1(), i.plus2(), + m.plus0(), m.plus1(), m.plus2(), + t.plus0(), t.plus1(), t.plus2(), + ) +} + +type StrangeUnionInt = int | UserId +type StrangeUnionInt2 = UserId | OwnerId + +struct ManyUnionFields { + f1: int | UserId + f2: StrangeUnionInt + f3: UserId | OwnerId + f4: OwnerId | UserId + f5: int | StrangeUnionInt2 + f6: StrangeUnionInt2 + + f10: slice | int | UserId + f11: slice | UserId | int + f12: OwnerId | () | int | UserId | slice + f13: OwnerId | cell | StrangeUnionInt2? +} + +fun test16(a: ManyUnionFields) { + __expect_type(a.f1, "int"); + __expect_type(a.f2, "StrangeUnionInt"); + __expect_type(a.f3, "UserId"); + __expect_type(a.f5, "int"); + __expect_type(a.f6, "StrangeUnionInt2"); + a.f1 + a.f2 + a.f3 + a.f4 + a.f5 + a.f6; + + __expect_type(a.f10, "slice | int"); + __expect_type(a.f11, "slice | UserId"); + __expect_type(a.f12, "OwnerId | () | slice"); + __expect_type(a.f13, "OwnerId | cell | null"); + + match (a.f13) { + int => {} + cell => {} + null => {} + } +} + +type SnakedCell = cell + +fun test17(a: SnakedCell, b: SnakedCell, c: SnakedCell) { + a = a; a = b; a = c; + b = a; + c = a; + + a as SnakedCell; + a as SnakedCell; + a as SnakedCell; + b as SnakedCell; + b as SnakedCell; + b as SnakedCell; + c as SnakedCell; + c as SnakedCell; + c as SnakedCell; +} + fun main(x: MInt, y: MInt?) { return y == null ? x : x + y; @@ -209,6 +400,7 @@ fun main(x: MInt, y: MInt?) { @testcase | 110 | | 41 @testcase | 112 | 0 | [ 1 1 ] @testcase | 112 | -1 | [ 2 1 ] +@testcase | 115 | | 0 1 2 10 11 12 20 21 22 @fif_codegen """ diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/union-types-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/union-types-tests.tolk index 7b9d52b6..0f649f75 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/union-types-tests.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/union-types-tests.tolk @@ -222,7 +222,7 @@ fun test21() { __expect_type(a, "never"); } if (a is slice || a is int) { - __expect_type(a, "slice | int") + __expect_type(a, "int | slice") } else { __expect_type(a, "never") } @@ -254,12 +254,12 @@ fun test23(p2: bool) { if (p is (int, int) && 10 < 0) { __expect_type(p, "(int, int)"); } - __expect_type(p, "(int, int) | (int, int, int)"); + __expect_type(p, "Pair2Or3"); if (p is int) { __expect_type(p, "never"); return (0, 0); } - __expect_type(p, "(int, int) | (int, int, int)"); + __expect_type(p, "Pair2Or3"); if (p !is (int, int)) { __expect_type(p, "(int, int, int)"); p = (10, 20); @@ -552,7 +552,7 @@ fun test48(a: IntOrInt8OrInt16) { var b = a; __expect_type(b, "IntOrInt8OrInt16"); match (b) { MInt => {} int8 => {} int16 => {} } - __expect_type(b, "int | int8 | int16"); + __expect_type(b, "IntOrInt8OrInt16"); match (b) { int => {}, int8 => { return ((0, null), (0, null)); }, int16 => "" }; __expect_type(b, "int | int16"); return (checkGeneric3(a), checkGeneric3(5 as int | slice | builder)); @@ -579,10 +579,10 @@ fun test50() { test49(match (a) { int => a, slice => a, null => a, }); __expect_type(b, "VeryComplexType"); match (b) { int => {} Pair2 => {} builder => {} null => {} } - __expect_type(b, "int | (int, int) | builder | null"); - __expect_type(b!, "int | (int, int) | builder"); + __expect_type(b, "VeryComplexType"); + __expect_type(b!, "int | (MInt, int) | builder"); match (b) { int => 100, Pair2 => {} builder => return -2, null => "null" } - __expect_type(b, "int | (int, int) | null"); + __expect_type(b, "int | Pair2 | null"); if (b !is null) { match (b) { int => {}, Pair2 => {} } } else { @@ -796,7 +796,7 @@ fun main() { @testcase | 107 | 3 | 6 7 8 typeid-2 6 7 8 typeid-2 @testcase | 108 | | (null) 2 3 typeid-1 (null) (null) (null) 0 (null) 2 3 typeid-1 (null) 2 3 typeid-1 (null) (null) (null) 0 @testcase | 109 | | 6 7 8 typeid-2 0 (null) -1 (null) (null) (null) 0 -1 (null) (null) (null) 0 -@testcase | 110 | | 1 (null) (null) 0 (null) (null) 0 typeid-7 +@testcase | 110 | | 1 (null) (null) 0 (null) (null) 0 typeid-3 @testcase | 120 | | 5 @testcase | 121 | | -1 0 0 -1 -1 0 @testcase | 122 | 0 0 1 | 36 @@ -810,19 +810,19 @@ fun main() { @testcase | 127 | | 1 2 5 1 @testcase | 128 | | (null) (null) 0 777 (null) 2 1 777 3 4 typeid-1 @testcase | 129 | | (null) (null) 0 (null) 5 1 -@testcase | 130 | 0 | (null) 5 1 777 (null) (null) 0 777 1 2 typeid-1 777 (null) [ 6 ] typeid-8 777 (null) (null) 0 -@testcase | 130 | -1 | (null) (null) 0 777 (null) (null) 0 777 1 2 typeid-1 777 (null) [ 6 ] typeid-8 777 (null) (null) 0 +@testcase | 130 | 0 | (null) 5 1 777 (null) (null) 0 777 1 2 typeid-1 777 (null) [ 6 ] typeid-4 777 (null) (null) 0 +@testcase | 130 | -1 | (null) (null) 0 777 (null) (null) 0 777 1 2 typeid-1 777 (null) [ 6 ] typeid-4 777 (null) (null) 0 @testcase | 131 | | (null) 777 5 777 1 2 777 (null) 777 5 777 (null) (null) 0 777 1 2 typeid-1 @testcase | 132 | | (null) (null) 5 1 777 (null) (null) 5 42 777 (null) 4 5 typeid-1 777 (null) (null) (null) 0 @testcase | 133 | | (null) (null) 5 1 777 (null) (null) 5 42 777 (null) 4 5 typeid-1 777 (null) (null) (null) 0 @testcase | 134 | | (null) 5 1 777 (null) 5 1 777 1 2 typeid-1 777 1 2 typeid-1 777 (null) 5 42 777 5 42 777 5 42 @testcase | 135 | | (null) 5 1 777 (null) (null) 1 2 typeid-1 777 (null) (null) 1 2 typeid-1 777 (null) (null) 0 -@testcase | 136 | | 1 2 typeid-1 777 1 2 typeid-12 777 1 2 typeid-13 777 (null) 1 2 typeid-14 +@testcase | 136 | | 1 2 typeid-1 777 1 2 typeid-5 777 1 2 typeid-6 777 (null) 1 2 typeid-7 @testcase | 137 | 1 2 | 1 2 777 1 2 777 1 2 777 [ 1 2 ] -@testcase | 138 | | 1 (null) (null) (null) (null) 0 2 typeid-18 777 1 (null) 0 2 typeid-19 777 1 5 1 2 typeid-19 777 1 (null) 5 1 2 typeid-20 +@testcase | 138 | | 1 (null) (null) (null) (null) 0 2 typeid-9 777 1 (null) 0 2 typeid-10 777 1 5 1 2 typeid-10 777 1 (null) 5 1 2 typeid-11 @testcase | 139 | | 1 (null) (null) (null) (null) 0 2 777 1 (null) 0 2 777 1 5 1 2 777 1 (null) 5 1 2 @testcase | 140 | 5 | 5 42 -@testcase | 140 | 15 | 15 typeid-3 +@testcase | 140 | 15 | 15 typeid-12 @testcase | 140 | 90 | 90 49 @testcase | 141 | 7 8 | 7 7 1 8 8 1 @testcase | 141 | null null | (null) (null) 0 (null) (null) 0 @@ -843,9 +843,9 @@ fun main() { @testcase | 154 | | 100 1 @testcase | 155 | | 5 1 5 1 @testcase | 156 | 1 2 -1 | 2 44 2 2 44 2 2 44 2 44 2 2 2 -@testcase | 157 | 1 | (null) 0 (null) typeid-21 777 1 2 typeid-1 777 typeid-21 typeid-21 777 0 0 -1 0 -1 0 0 777 -1 0 -@testcase | 157 | 0 | (null) 0 (null) typeid-21 777 (null) (null) typeid-21 777 0 0 777 0 0 -1 0 0 -1 0 777 0 -1 -@testcase | 158 | | (null) (null) typeid-21 (null) (null) typeid-21 (null) 0 (null) +@testcase | 157 | 1 | (null) 0 (null) typeid-14 777 1 2 typeid-1 777 typeid-14 typeid-14 777 0 0 -1 0 -1 0 0 777 -1 0 +@testcase | 157 | 0 | (null) 0 (null) typeid-14 777 (null) (null) typeid-14 777 0 0 777 0 0 -1 0 0 -1 0 777 0 -1 +@testcase | 158 | | (null) (null) typeid-14 (null) (null) typeid-14 (null) 0 (null) @testcase | 159 | 0 4 | 456 @testcase | 159 | null 0 | 123 @testcase | 160 | | 10 0 diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/use-before-declare.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/use-before-declare.tolk index 0c59c671..d5ecd407 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/use-before-declare.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/use-before-declare.tolk @@ -23,6 +23,7 @@ fun demo1(v: int): int { global demo_var: int; const demo_10: int = 10; +@method_id(101) fun test1(): int { var demo_var: int = demo_10; var demo_slice: int = demo_20; @@ -33,6 +34,7 @@ fun test1(): int { return demo_var + demo_slice; } +@method_id(102) fun test2() { return second; } diff --git a/server/src/e2e/tolk/testcases/unresolved-identifiers/var-apply-tests.tolk b/server/src/e2e/tolk/testcases/unresolved-identifiers/var-apply-tests.tolk index 702c9fdd..e11235ed 100644 --- a/server/src/e2e/tolk/testcases/unresolved-identifiers/var-apply-tests.tolk +++ b/server/src/e2e/tolk/testcases/unresolved-identifiers/var-apply-tests.tolk @@ -309,7 +309,7 @@ fun testMethodsOfGenericStruct() { __expect_type(w2, "Wrapper"); var eq = Wrapper.equalsNotNull; __expect_type(eq, "(Wrapper, Wrapper) -> bool"); - var creator3 = Wrapper.createFromNull; + var creator3 = Wrapper.createFromNull; __expect_type(creator3, "() -> Wrapper"); return (eq(w1, w2), w2.value = w1.value as int, eq(w1, w2), creator3()); } @@ -350,7 +350,7 @@ fun main() {} @testcase | 107 | | 65537 @testcase | 108 | | 4 @testcase | 109 | | 0 3 0 7 -@testcase | 110 | 5 | 5 10 100 100 100 typeid-6 +@testcase | 110 | 5 | 5 10 100 100 100 typeid-2 @testcase | 110 | 0 | (null) (null) (null) (null) (null) 0 @testcase | 111 | 2 3 9 | -1 @testcase | 111 | 11 22 44 | -1 @@ -360,7 +360,7 @@ fun main() {} @testcase | 112 | -1 -10 -20 | -1 @testcase | 113 | 0 | 12 12 @testcase | 113 | -1 | 12 -1 -@testcase | 114 | -1 | 2 3 typeid-3 +@testcase | 114 | -1 | 2 3 typeid-1 @testcase | 114 | 0 | (null) 12 1 @testcase | 115 | | 7 6 7 @testcase | 116 | | 7 11 80 diff --git a/server/src/e2e/tolk/tolk-stdlib/common.tolk b/server/src/e2e/tolk/tolk-stdlib/common.tolk index a7271824..5a240ae3 100644 --- a/server/src/e2e/tolk/tolk-stdlib/common.tolk +++ b/server/src/e2e/tolk/tolk-stdlib/common.tolk @@ -1,14 +1,112 @@ // Standard library for Tolk (LGPL licence). // It contains common functions that are available out of the box, the user doesn't have to import anything. -// More specific functions are required to be imported explicitly, like "@stdlib/tvm-dicts". -tolk 0.99 +// More specific functions are required to be imported explicitly, like "@stdlib/gas-payments". +tolk 1.0 -/// In Tolk v1.x there would be a type `map`. -/// Currently, working with dictionaries is still low-level, with raw cells. -/// But just for clarity, we use "dict" instead of a "cell?" where a cell-dictionary is assumed. -/// Every dictionary object can be null. TVM NULL is essentially "empty dictionary". +/** + Built-in types. + */ + +/// `int` is the primitive 257-bit signed integer type. +type int = builtin + +/// `bool` is a classic boolean type, which can hold only two values: `true` and `false`. +/// At the TVM (TON virtual machine) level, it's an integer -1 or 0. +/// Note: `boolVar as int` is possible, but remember, that true is -1, not 1! +type bool = builtin + +/// `cell` is a data structure, which can hold of up to 1023 bits (not bytes!) +/// and up to 4 references (refs) to other cells. +/// Both contract code and contract state are represented by a tree of cells. +/// See docs: https://docs.ton.org/v3/documentation/data-formats/tlb/cell-boc +type cell = builtin + +/// `slice` is a "cell opened for reading". +/// When you call [cell.beginParse], you get a `slice`, from which you can load binary data +/// or high-level structures with [T.fromSlice]. +type slice = builtin + +/// `builder` is a "cell at the stage of creation". +/// When you call [beginCell], you get a `builder`, populate it with binary data or structures, +/// and after [builder.endCell], you get a `cell`. +type builder = builtin + +/// `continuation` are "executable cells" representing executable TVM bytecode. +/// They are used to manage execution flow in TVM programs and serve as +/// the basis for function calls, exception handling, and control flow operations. +type continuation = builtin + +/// `tuple` is a collection from 0 to 255 elements of any type. +/// You can push, pop, access individual elements as `someTuple.0`. +/// A tuple occupies one stack slot regardless of its size. +type tuple = builtin + +/// `address` represents an internal/external/none address. +/// Most likely, you'll use it for internal addresses — "an address of a smart contract". +/// It's `slice` under the hood. `someAddress as slice` is also possible. +/// See docs: https://docs.ton.org/learn/overviews/addresses#address-of-smart-contract +type address = builtin + +/// `never` is a special type that represents computations that never complete normally. +/// A functions that always throw an exception returns `never`, and the compiler knows +/// that any code after it is unreachable. +type never = builtin + +/// `int8`, `int32`, `int222`, etc. is "a fixed-width signed integer with N bits", N <= 257. +/// Note: it's still `int` at runtime, you can assign "100500" to "int8": +/// overflow will happen at serialization to a cell/builder, NOT at assignment. +type intN = builtin + +/// `uint32`, `uint64`, `uint111`, etc. is "a fixed-width unsigned integer with N bits", N <= 256. +/// Note: it's still `int` at runtime, you can assign "100500" to "uint8": +/// overflow will happen at serialization to a cell/builder, NOT at assignment. +type uintN = builtin + +/// `coins` is a special primitive representing "nanotoncoins". One TON = 10^9 nanotoncoins. +/// You can create coins with `ton()` function: `ton("0.05")` (actually, `int` 50000000 at runtime). +/// Arithmetic operations on `coins` degrade to 257-bit `int` type. +type coins = builtin + +/// `varint16` is `int` at runtime, but serialized as "variadic signed int", -2^119 <= X < 2^119. +type varint16 = builtin + +/// `varuint16` is `int` at runtime, but serialized as "variadic unsigned int", 0 <= X < 2^120. +type varuint16 = builtin + +/// `varint32` is `int` at runtime, but serialized as "variadic signed int", -2^247 <= X < 2^247. +type varint32 = builtin + +/// `varuint32` is `int` at runtime, but serialized as "variadic unsigned int", 0 <= X < 2^248. +type varuint32 = builtin + +/// `bits256`, `bits111`, etc. is "a fixed-width slice with N bits and 0 refs", N <= 1023. +/// Note: use `as` operator to convert `slice` to `bitsN`: `someSlice as bits256` +/// (manually writing `as` enforces you to think that this conversion is correct). +/// Note: similar to `intN`, you can assign an invalid slice to `bitsN`, +/// an error will be fired at serialization with [T.toCell] and similar, NOT at assignment. +type bitsN = builtin + +/// `bytes8`, `bytes99`, etc. is a convenient alias for `bits(N*8)` +type bytesN = builtin + +/// `map` is "a map from a key K to a value V". +/// Internally, it's an "optional cell": an empty map is `null`, a non-empty points to a root cell. +/// Restrictions for K and V types: +/// - a key must be fixed-with; valid: int32, uint64, address, bits256, Point; invalid: int, coins +/// - a value must be serializable; valid: int32, coins, AnyStruct, Cell; invalid: int, builder +struct map {} + +/// `dict` is a low-level TVM dictionary. +/// Think of it as "a map with unknown keys and unknown values". +/// Prefer using `map`, not `dict`. type dict = cell? +/// `void` is the unit type representing the absence of a meaningful value. +/// It's similar to both `void` and `unit` in other languages. +/// Note: a function without return type means "auto infer", NOT "void". +type void = builtin + + /** Tuple manipulation primitives. Elements of a tuple can be of arbitrary type. @@ -150,11 +248,9 @@ fun contract.getAddress(): address fun contract.getOriginalBalance(): coins asm "BALANCE" "FIRST" -/// Same as [contract.getOriginalBalance], but returns a tuple: -/// `int` — balance in nanotoncoins; -/// `dict` — a dictionary with 32-bit keys representing the balance of "extra currencies". +/// Same as [contract.getOriginalBalance], but returns a tuple: balance in nanotoncoins and extra currencies @pure -fun contract.getOriginalBalanceWithExtraCurrencies(): [coins, dict] +fun contract.getOriginalBalanceWithExtraCurrencies(): [coins, ExtraCurrenciesMap] asm "BALANCE" /// Returns the persistent contract storage cell. It can be parsed or modified with slice and builder primitives later. @@ -614,6 +710,275 @@ fun debug.dumpStack(): void builtin +/* + Implementation of `map` and methods over it. + + Note: maps in TVM are called "dictionaries". They are stored as nested cells + (each value in a separate cell, probably with intermediate cells-nodes). + Constructing even small dicts is gas-expensive, because cell creation costs a lot. + + `map` is a "human-readable interface" over low-level dictionaries. The compiler, + knowing types of K and V, effectively generates asm instructions for storing/loading values. + But still, of course, it's a tree of cells, it's just a TVM dictionary. + */ + +/// Returns an empty types map. It's essentially "PUSHNULL", since null represents an empty map. +/// Example: +/// ``` +/// var m: map = createEmptyMap(); +/// ``` +@pure +fun createEmptyMap(): map + builtin + +/// Converts a low-level TVM dictionary to a typed map. +/// Actually, does nothing: accepts an "optional cell" and returns the same "optional cell", +/// so if you specify key/value types incorrectly, it will fail later, at [map.get] and similar. +@pure +fun createMapFromLowLevelDict(d: dict): map + builtin + +/// Converts a high-level map to a low-level TVM dictionary. +/// Actually, does nothing: returns the same "optional cell". +@pure +fun map.toLowLevelDict(self): dict + asm "NOP" + +/// Checks whether a map is empty (whether a cell is null). +/// Note: a check `m == null` will not work, use `m.isEmpty()`. +@pure +fun map.isEmpty(self): bool + asm "DICTEMPTY" + +/// Checks whether a key exists in a map. +@pure +fun map.exists(self, key: K): bool + builtin + +/// Gets an element by key. If not found, does NOT throw, just returns isFound = false. +/// Example: +/// ``` +/// val r = m.get(123); +/// if (r.isFound) { +/// r.loadValue() +/// } +/// ``` +@pure +fun map.get(self, key: K): MapLookupResult + builtin + +/// Gets an element by key and throws if it doesn't exist. +@pure +fun map.mustGet(self, key: K, throwIfNotFound: int = 9): V + builtin + +/// Sets an element by key. +/// Example: +/// ``` +/// m.set(k, 3); +/// ``` +/// Since it returns `self`, calls may be chained. +@pure +fun map.set(mutate self, key: K, value: V): self + builtin + +/// Sets an element and returns previous element at that key. If previous doesn't exist, isFound = false. +/// Example: +/// ``` +/// val prev = m.setAndGetPrevious(k, 3); +/// if (prev.isFound) { +/// prev.loadValue() +/// } +/// ``` +@pure +fun map.setAndGetPrevious(mutate self, key: K, value: V): MapLookupResult + builtin + +/// Sets an element only if the key already exists. Returns whether an element was replaced. +@pure +fun map.replaceIfExists(mutate self, key: K, value: V): bool + builtin + +/// Sets an element only if the key already exists and returns previous element at that key. +@pure +fun map.replaceAndGetPrevious(mutate self, key: K, value: V): MapLookupResult + builtin + +/// Sets an element only if the key does not exist. Returns whether an element was added. +@pure +fun map.addIfNotExists(mutate self, key: K, value: V): bool + builtin + +/// Sets an element only if the key does not exist. If exists, returns an old value. +@pure +fun map.addOrGetExisting(mutate self, key: K, value: V): MapLookupResult + builtin + +/// Delete an element at the key. Returns whether an element was deleted. +@pure +fun map.delete(mutate self, key: K): bool + builtin + +/// Delete an element at the key and returns the deleted element. If not exists, isFound = false. +/// Example: +/// ``` +/// val prev = m.deleteAndGetDeleted(k); +/// if (prev.isFound) { +/// prev.loadValue() +/// } +/// ``` +@pure +fun map.deleteAndGetDeleted(mutate self, key: K): MapLookupResult + builtin + +/// Finds the first (minimal) element in a map. If key are integers, it's the minimal integer. +/// It keys are addresses or complex structures (represented as slices), it's lexicographically smallest. +/// For an empty map, just returns isFound = false. +/// Useful for iterating over a map: +/// ``` +/// var r = m.findFirst(); +/// while (r.isFound) { +/// ... use r.getKey() and r.loadValue() +/// r = m.iterateNext(r) +/// } +/// ``` +@pure +fun map.findFirst(self): MapEntry + builtin + +/// Finds the last (minimal) element in a map. If key are integers, it's the maximal integer. +/// It keys are addresses or complex structures (represented as slices), it's lexicographically largest. +/// For an empty map, just returns isFound = false. +/// Useful for iterating over a map: +/// ``` +/// var r = m.findLast(); +/// while (r.isFound) { +/// ... use r.getKey() and r.loadValue() +/// r = m.iteratePrev(r) +/// } +/// ``` +@pure +fun map.findLast(self): MapEntry + builtin + +/// Finds an element with key > pivotKey. +/// Don't forget to check `isFound` before using `getKey()` and `loadValue()` of the result. +@pure +fun map.findKeyGreater(self, pivotKey: K): MapEntry + builtin + +/// Finds an element with key >= pivotKey. +/// Don't forget to check `isFound` before using `getKey()` and `loadValue()` of the result. +@pure +fun map.findKeyGreaterOrEqual(self, pivotKey: K): MapEntry + builtin + +/// Finds an element with key < pivotKey. +/// Don't forget to check `isFound` before using `getKey()` and `loadValue()` of the result. +@pure +fun map.findKeyLess(self, pivotKey: K): MapEntry + builtin + +/// Finds an element with key <= pivotKey. +/// Don't forget to check `isFound` before using `getKey()` and `loadValue()` of the result. +@pure +fun map.findKeyLessOrEqual(self, pivotKey: K): MapEntry + builtin + +/// Iterate over a map in ascending order. +/// Example: +/// ``` +/// // iterate for all keys >= 10 up to the end +/// var r = m.findKeyGreaterOrEqual(10); +/// while (r.isFound) { +/// ... use r.getKey() and r.loadValue() +/// r = m.iterateNext(r) +/// } +/// ``` +@pure +fun map.iterateNext(self, current: MapEntry): MapEntry + builtin + +/// Iterate over a map in reverse order. +/// Example: +/// ``` +/// // iterate for all keys < 10 down lo lowest +/// var r = m.findKeyLess(10); +/// while (r.isFound) { +/// ... use r.getKey() and r.loadValue() +/// r = m.iteratePrev(r) +/// } +/// ```@pure +fun map.iteratePrev(self, current: MapEntry): MapEntry + builtin + +/// MapLookupResult is a return value of [map.get], [map.setAndGetPrevious], and similar. +/// Instead of returning a nullable value (that you'd check on null before usage), +/// this struct is returned (and you check on `isFound` before usage). +/// Example: +/// ``` +/// val r = m.get(key); +/// if (r.isFound) { +/// r.loadValue() // unpacks a returned slice +/// } +/// ``` +struct MapLookupResult { + rawSlice: slice? // holds encoded value, present if isFound + isFound: bool +} + +@pure +fun MapLookupResult.loadValue(self): TValue { + // it's assumed that you check for `isFound` before calling `loadValue()`; + // note that `assertEnd` is called to ensure no remaining data left besides TValue + return TValue.fromSlice(self.rawSlice!, { + assertEndAfterReading: true + }) +} + +@pure +fun MapLookupResult.loadValue(self): slice { + return self.rawSlice! +} + +/// MapEntry is a return value of [map.findFirst], [map.iterateNext], and similar. +/// You should check for `isFound` before calling `getKey()` and `loadValue()`. +/// Example: +/// ``` +/// var r = m.findFirst(); +/// while (r.isFound) { +/// ... use r.getKey() and r.loadValue() +/// r = m.iterateNext(r) +/// } +/// ``` +struct MapEntry { + rawValue: slice? // holds encoded value, present if isFound + key: K + isFound: bool +} + +@pure +fun MapEntry.getKey(self): K { + // it's assumed that you check for `isFound` before calling `getKey()`; + // (otherwise, it will contain an incorrect stack slot with `null` value, not K) + return self.key +} + +@pure +fun MapEntry.loadValue(self): V { + // it's assumed that you check for `isFound` before calling `loadValue()`; + // note that `assertEnd` is inserted to ensure no remaining data left besides TValue + return V.fromSlice(self.rawValue!, { + assertEndAfterReading: true + }) +} + +@pure +fun MapEntry.loadValue(self): slice { + return self.rawValue! +} + + /** Slice primitives: parsing cells. When you _load_ some data, you mutate the slice (shifting an internal pointer on the stack). @@ -715,7 +1080,6 @@ fun slice.getMiddleBits(self, offset: int, len: int): slice asm "SDSUBSTR" /// Loads a dictionary (TL HashMapE structure, represented as TVM cell) from a slice. -/// Returns `null` if `nothing` constructor is used. @pure fun slice.loadDict(mutate self): dict asm( -> 1 0) "LDDICT" @@ -993,7 +1357,7 @@ const SEND_MODE_ESTIMATE_FEE_ONLY = 1024 /// +64 substitutes the entire balance of the incoming message as an outcoming value (slightly inaccurate, gas expenses that cannot be estimated before the computation is completed are not taken into account). /// +128 substitutes the value of the entire balance of the contract before the start of the computation phase (slightly inaccurate, since gas expenses that cannot be estimated before the completion of the computation phase are not taken into account). -type ExtraCurrenciesDict = dict +type ExtraCurrenciesMap = map // todo or uint32? /// ContractState is "code + data" of a contract. /// Used in outgoing messages (StateInit) to initialize a destination contract. @@ -1062,7 +1426,7 @@ struct CreateMessageOptions { /// whether a message will bounce back on error bounce: bool /// message value: attached tons (or tons + extra currencies) - value: coins | (coins, ExtraCurrenciesDict) + value: coins | (coins, ExtraCurrenciesMap) /// destination is either a provided address, or is auto-calculated by stateInit dest: address // either just send a message to some address | builder // ... or a manually constructed builder with a valid address @@ -1312,7 +1676,7 @@ fun sendRawMessage(msg: cell, mode: int): void struct InMessage { senderAddress: address // an internal address from which the message arrived valueCoins: coins // ton amount attached to an incoming message - valueExtra: dict // extra currencies attached to an incoming message + valueExtra: ExtraCurrenciesMap // extra currencies attached to an incoming message originalForwardFee: coins // fee that was paid by the sender createdLt: uint64 // logical time when a message was created createdAt: uint32 // unixtime when a message was created @@ -1332,7 +1696,7 @@ struct InMessage { struct InMessageBounced { senderAddress: address // an internal address from which the message was bounced valueCoins: coins // ton amount attached to a message - valueExtra: dict // extra currencies attached to a message + valueExtra: ExtraCurrenciesMap // extra currencies attached to a message originalForwardFee: coins // comission that the sender has payed to send this message createdLt: uint64 // logical time when a message was created (and bounced) createdAt: uint32 // unixtime when a message was created (and bounced) diff --git a/server/src/e2e/tolk/tolk-stdlib/gas-payments.tolk b/server/src/e2e/tolk/tolk-stdlib/gas-payments.tolk index 977fa6f2..916cefc6 100644 --- a/server/src/e2e/tolk/tolk-stdlib/gas-payments.tolk +++ b/server/src/e2e/tolk/tolk-stdlib/gas-payments.tolk @@ -1,5 +1,5 @@ // A part of standard library for Tolk -tolk 0.99 +tolk 1.0 /** Gas and payment related primitives. diff --git a/server/src/e2e/tolk/tolk-stdlib/lisp-lists.tolk b/server/src/e2e/tolk/tolk-stdlib/lisp-lists.tolk index 2853abef..212586b1 100644 --- a/server/src/e2e/tolk/tolk-stdlib/lisp-lists.tolk +++ b/server/src/e2e/tolk/tolk-stdlib/lisp-lists.tolk @@ -1,5 +1,5 @@ // A part of standard library for Tolk -tolk 0.99 +tolk 1.0 /** Lisp-style lists are nested 2-elements tuples: `[1, [2, [3, null]]]` represents list `[1, 2, 3]`. diff --git a/server/src/e2e/tolk/tolk-stdlib/tvm-dicts.tolk b/server/src/e2e/tolk/tolk-stdlib/tvm-dicts.tolk index 14723682..289c58e6 100644 --- a/server/src/e2e/tolk/tolk-stdlib/tvm-dicts.tolk +++ b/server/src/e2e/tolk/tolk-stdlib/tvm-dicts.tolk @@ -1,9 +1,12 @@ // A part of standard library for Tolk -tolk 0.99 +tolk 1.0 /** - Dictionaries are represented as `cell` data type (cells can store anything, dicts in particular). - Currently, they have very low-level API very close to TVM internals. + Low-level API working with TVM dictionaries. + Not recommended to use, since it's very complicated and error-prone. + Use `map` instead, it's even more efficient then low-level dict API. + + Dictionaries are represented as `cell` data type. Most of functions are duplicated for three common cases: - iDict* - dicts with signed integer keys - uDict* - dicts with unsigned integer keys @@ -86,28 +89,28 @@ fun dict.uDictSetIfExists(mutate self, keyLen: int, key: int, value: slice): boo @pure -fun dict.iDictGetRef(self, keyLen: int, key: int): (dict, bool) +fun dict.iDictGetRef(self, keyLen: int, key: int): (cell?, bool) asm(key self keyLen) "DICTIGETREF" "NULLSWAPIFNOT" @pure -fun dict.uDictGetRef(self, keyLen: int, key: int): (dict, bool) +fun dict.uDictGetRef(self, keyLen: int, key: int): (cell?, bool) asm(key self keyLen) "DICTUGETREF" "NULLSWAPIFNOT" @pure -fun dict.sDictGetRef(self, keyLen: int, key: slice): (dict, bool) +fun dict.sDictGetRef(self, keyLen: int, key: slice): (cell?, bool) asm(key self keyLen) "DICTGETREF" "NULLSWAPIFNOT" @pure -fun dict.iDictGetRefOrNull(self, keyLen: int, key: int): dict +fun dict.iDictGetRefOrNull(self, keyLen: int, key: int): cell? asm(key self keyLen) "DICTIGETOPTREF" @pure -fun dict.uDictGetRefOrNull(self, keyLen: int, key: int): dict +fun dict.uDictGetRefOrNull(self, keyLen: int, key: int): cell? asm(key self keyLen) "DICTUGETOPTREF" @pure -fun dict.sDictGetRefOrNull(self, keyLen: int, key: slice): dict +fun dict.sDictGetRefOrNull(self, keyLen: int, key: slice): cell? asm(key self keyLen) "DICTGETOPTREF" @@ -138,11 +141,11 @@ fun dict.sDictSetAndGet(mutate self, keyLen: int, key: slice, value: slice): (sl @pure -fun dict.iDictSetAndGetRefOrNull(mutate self, keyLen: int, key: int, value: cell): dict +fun dict.iDictSetAndGetRefOrNull(mutate self, keyLen: int, key: int, value: cell): cell? asm(value key self keyLen) "DICTISETGETOPTREF" @pure -fun dict.uDictSetAndGetRefOrNull(mutate self, keyLen: int, key: int, value: cell): dict +fun dict.uDictSetAndGetRefOrNull(mutate self, keyLen: int, key: int, value: cell): cell? asm(value key self keyLen) "DICTUSETGETOPTREF" @@ -228,15 +231,15 @@ fun dict.sDictGetFirst(self, keyLen: int): (slice?, slice?, bool) asm (-> 1 0 2) "DICTMIN" "NULLSWAPIFNOT2" @pure -fun dict.iDictGetFirstAsRef(self, keyLen: int): (int?, dict, bool) +fun dict.iDictGetFirstAsRef(self, keyLen: int): (int?, cell?, bool) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2" @pure -fun dict.uDictGetFirstAsRef(self, keyLen: int): (int?, dict, bool) +fun dict.uDictGetFirstAsRef(self, keyLen: int): (int?, cell?, bool) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2" @pure -fun dict.sDictGetFirstAsRef(self, keyLen: int): (slice?, dict, bool) +fun dict.sDictGetFirstAsRef(self, keyLen: int): (slice?, cell?, bool) asm (-> 1 0 2) "DICTMINREF" "NULLSWAPIFNOT2" @@ -253,15 +256,15 @@ fun dict.sDictGetLast(self, keyLen: int): (slice?, slice?, bool) asm (-> 1 0 2) "DICTMAX" "NULLSWAPIFNOT2" @pure -fun dict.iDictGetLastAsRef(self, keyLen: int): (int?, dict, bool) +fun dict.iDictGetLastAsRef(self, keyLen: int): (int?, cell?, bool) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2" @pure -fun dict.uDictGetLastAsRef(self, keyLen: int): (int?, dict, bool) +fun dict.uDictGetLastAsRef(self, keyLen: int): (int?, cell?, bool) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2" @pure -fun dict.sDictGetLastAsRef(self, keyLen: int): (slice?, dict, bool) +fun dict.sDictGetLastAsRef(self, keyLen: int): (slice?, cell?, bool) asm (-> 1 0 2) "DICTMAXREF" "NULLSWAPIFNOT2" @@ -273,6 +276,10 @@ fun dict.iDictGetNext(self, keyLen: int, pivot: int): (int?, slice?, bool) fun dict.uDictGetNext(self, keyLen: int, pivot: int): (int?, slice?, bool) asm(pivot self keyLen -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2" +@pure +fun dict.sDictGetNext(self, keyLen: int, pivot: slice): (slice?, slice?, bool) + asm(pivot self keyLen -> 1 0 2) "DICTGETNEXT" "NULLSWAPIFNOT2" + @pure fun dict.iDictGetNextOrEqual(self, keyLen: int, pivot: int): (int?, slice?, bool) asm(pivot self keyLen -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2" @@ -281,6 +288,10 @@ fun dict.iDictGetNextOrEqual(self, keyLen: int, pivot: int): (int?, slice?, bool fun dict.uDictGetNextOrEqual(self, keyLen: int, pivot: int): (int?, slice?, bool) asm(pivot self keyLen -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2" +@pure +fun dict.sDictGetNextOrEqual(self, keyLen: int, pivot: slice): (slice?, slice?, bool) + asm(pivot self keyLen -> 1 0 2) "DICTGETNEXTEQ" "NULLSWAPIFNOT2" + @pure fun dict.iDictGetPrev(self, keyLen: int, pivot: int): (int?, slice?, bool) @@ -290,6 +301,10 @@ fun dict.iDictGetPrev(self, keyLen: int, pivot: int): (int?, slice?, bool) fun dict.uDictGetPrev(self, keyLen: int, pivot: int): (int?, slice?, bool) asm(pivot self keyLen -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2" +@pure +fun dict.sDictGetPrev(self, keyLen: int, pivot: slice): (slice?, slice?, bool) + asm(pivot self keyLen -> 1 0 2) "DICTGETPREV" "NULLSWAPIFNOT2" + @pure fun dict.iDictGetPrevOrEqual(self, keyLen: int, pivot: int): (int?, slice?, bool) asm(pivot self keyLen -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2" @@ -298,6 +313,10 @@ fun dict.iDictGetPrevOrEqual(self, keyLen: int, pivot: int): (int?, slice?, bool fun dict.uDictGetPrevOrEqual(self, keyLen: int, pivot: int): (int?, slice?, bool) asm(pivot self keyLen -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2" +@pure +fun dict.sDictGetPrevOrEqual(self, keyLen: int, pivot: slice): (slice?, slice?, bool) + asm(pivot self keyLen -> 1 0 2) "DICTGETPREVEQ" "NULLSWAPIFNOT2" + /** Prefix dictionary primitives. diff --git a/server/src/e2e/tolk/tolk-stdlib/tvm-lowlevel.tolk b/server/src/e2e/tolk/tolk-stdlib/tvm-lowlevel.tolk index 18a0e159..d578199c 100644 --- a/server/src/e2e/tolk/tolk-stdlib/tvm-lowlevel.tolk +++ b/server/src/e2e/tolk/tolk-stdlib/tvm-lowlevel.tolk @@ -1,5 +1,5 @@ // A part of standard library for Tolk -tolk 0.99 +tolk 1.0 /// Usually `c3` has a continuation initialized by the whole code of the contract. It is used for function calls. /// The primitive returns the current value of `c3`. diff --git a/server/src/languages/tolk/type-inference.ts b/server/src/languages/tolk/type-inference.ts index a5399882..2a7b2cae 100644 --- a/server/src/languages/tolk/type-inference.ts +++ b/server/src/languages/tolk/type-inference.ts @@ -76,11 +76,24 @@ export class GenericSubstitutions { } public static deduceTo(mapping: Map, paramTy: Ty, argTy: Ty): void { - if (paramTy instanceof InstantiationTy && argTy instanceof InstantiationTy) { - for (let i = 0; i < Math.min(paramTy.types.length, argTy.types.length); i++) { - this.deduceTo(mapping, paramTy.types[i], argTy.types[i]) + if (paramTy instanceof InstantiationTy) { + const unwrappedArgType = argTy.unwrapAlias() + if (unwrappedArgType instanceof InstantiationTy) { + for ( + let i = 0; + i < Math.min(paramTy.types.length, unwrappedArgType.types.length); + i++ + ) { + this.deduceTo(mapping, paramTy.types[i], unwrappedArgType.types[i]) + } + } + + if (argTy instanceof InstantiationTy) { + for (let i = 0; i < Math.min(paramTy.types.length, argTy.types.length); i++) { + this.deduceTo(mapping, paramTy.types[i], argTy.types[i]) + } + return } - return } if (paramTy instanceof FuncTy && argTy instanceof FuncTy) { diff --git a/server/src/languages/tolk/types/size-of.ts b/server/src/languages/tolk/types/size-of.ts index 1983406c..a50b5bf1 100644 --- a/server/src/languages/tolk/types/size-of.ts +++ b/server/src/languages/tolk/types/size-of.ts @@ -296,6 +296,10 @@ function calculateSizeOf(ty: Ty, ctx: EstimateContext): SizeOf { // Cell, same as cell return createSizeOf(0, 0, 1, 1) } + if (ty.innerTy.name() === "map") { + // mao, same as cell? + return createSizeOf(0, 1, 0, 1) + } if (ty.innerTy instanceof StructTy || ty.innerTy instanceof TypeAliasTy) { const parameters = ty.innerTy.anchor?.typeParameters() ?? [] diff --git a/server/src/languages/tolk/types/ty.ts b/server/src/languages/tolk/types/ty.ts index cd74ab9e..700e030e 100644 --- a/server/src/languages/tolk/types/ty.ts +++ b/server/src/languages/tolk/types/ty.ts @@ -59,6 +59,8 @@ export abstract class NamedTy implements Ty { public equals(other: Ty): boolean { if (this === other) return true if (other instanceof NamedTy) return this._name === other.name() + const otherUnwrapped = other.unwrapAlias() + if (otherUnwrapped instanceof NamedTy) return this._name === otherUnwrapped.name() return false } @@ -100,7 +102,8 @@ export abstract class NonNamedTy implements Ty { } public equals(other: Ty): boolean { - return this === other + const otherUnwrapped = other.unwrapAlias() + return this === otherUnwrapped } public canRhsBeAssigned(other: Ty): boolean { @@ -351,8 +354,7 @@ export class UnionTy extends NonNamedTy { } public contains(other: Ty): boolean { - const baseType = other.baseType() - return this.elements.some(it => it.baseType().equals(baseType)) + return this.elements.some(it => it.equals(other)) } public containsAll(other: UnionTy): boolean { @@ -886,7 +888,7 @@ export function subtractTypes(left: Ty | null, right: Ty): Ty { } } else if (left.contains(right)) { for (const leftVariant of left.elements) { - if (!leftVariant.baseType().equals(right.baseType())) { + if (!leftVariant.equals(right)) { restVariants.push(leftVariant) } }