Skip to content

Commit 1ebdc0a

Browse files
committed
[Tolk] Support numeric enums similar to TypeScript and C++
1 parent 6e9ce41 commit 1ebdc0a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1149
-123
lines changed

tolk-tester/tests/enums-tests.tolk

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
const SIX = 6
2+
3+
enum EEmpty {}
4+
5+
enum Color {
6+
Red,
7+
Green,
8+
Blue
9+
}
10+
11+
type ColorAlias = Color
12+
13+
struct WithColor {
14+
c1: Color
15+
c2: Color = Color.Green
16+
}
17+
18+
enum EPartialAssigned {
19+
M100 = -100,
20+
M99,
21+
P1 = (1 + 0) << 0;
22+
P2 = ((+1 + 1) << 1) / 2;
23+
P3
24+
P4
25+
P5 = 0b101,
26+
P6 = SIX;
27+
Max = 0x7FFFFFFF,
28+
}
29+
30+
enum Err {
31+
TooLowId = 0x100,
32+
TooHighId,
33+
DisabledId,
34+
}
35+
36+
struct Box<T> {
37+
value: T
38+
}
39+
40+
fun checkIsBlue(c: Color) {
41+
return c == Color.Blue
42+
}
43+
44+
fun Color.isBlue(self) {
45+
return self == Color.Blue
46+
}
47+
48+
fun Color.createBlue() {
49+
return Color.Blue
50+
}
51+
52+
const C_BLUE = Color.Blue
53+
const C_TEN = 5 * (Color.Green as int) * 2
54+
55+
// these methods do not bother Color.Red and other
56+
fun T.Red() { return 123 }
57+
fun T.Max() { return -C_TEN }
58+
fun int.Max(self) { return self }
59+
fun Color.Max(): EPartialAssigned { return ((EPartialAssigned.Max as int) + 1) as EPartialAssigned }
60+
61+
@noinline
62+
fun checkIsColor<T>(obj: Color | T) { return obj is Color }
63+
64+
@inline_ref
65+
fun Color.mapToNumber(self) {
66+
// match over enum is exhaustive, it's like "if Red {} else if Green {} else {}"
67+
return match (self) {
68+
Color.Red => 100,
69+
Color.Green => 200,
70+
Color.Blue => 300, // this works like `else` and handles invalid values at runtime also
71+
}
72+
}
73+
74+
75+
@method_id(101)
76+
fun test1() {
77+
var c = Color.Red;
78+
79+
return (
80+
checkIsBlue(Color.Blue), checkIsBlue(c), checkIsBlue(Color.createBlue()),
81+
Color.Blue.isBlue(), Color.Red.isBlue(), Color.createBlue()!!.isBlue(), C_BLUE.isBlue(),
82+
Color.createBlue()
83+
);
84+
}
85+
86+
@method_id(102)
87+
fun test2(c: Color) {
88+
return (c is Color, c !is Color, c is EPartialAssigned, c is EEmpty, c is int, c is int8);
89+
}
90+
91+
@method_id(103)
92+
fun test3() {
93+
assert (EPartialAssigned.P4 as int == 4) throw 123;
94+
assert (EPartialAssigned.M99 as int <= 0) throw 123;
95+
assert (EPartialAssigned.P3 == (3 as EPartialAssigned)) throw 123;
96+
assert (EPartialAssigned.P3 != (4 as EPartialAssigned)) throw 123;
97+
return (
98+
EPartialAssigned.M100,
99+
EPartialAssigned.M99,
100+
EPartialAssigned.P1,
101+
EPartialAssigned.P2,
102+
EPartialAssigned.P3,
103+
EPartialAssigned.P4,
104+
EPartialAssigned.P5,
105+
EPartialAssigned.P6,
106+
EPartialAssigned.Max,
107+
)
108+
}
109+
110+
@method_id(104)
111+
fun test4(chooseGreen: bool) {
112+
var c: ColorAlias = chooseGreen ? Color.Green : ColorAlias.Blue;
113+
return (c is Color, (c!) is ColorAlias, ColorAlias.Blue, ColorAlias.createBlue().isBlue(), c);
114+
}
115+
116+
@method_id(105)
117+
fun test5() {
118+
return (int.Red(), int8.Max(), 456.Max(), EPartialAssigned.Max, Color.Max());
119+
}
120+
121+
@method_id(106)
122+
fun test6(c: Color) {
123+
val p = c as EPartialAssigned;
124+
val e = c as EEmpty;
125+
match (e) { EEmpty => {} }
126+
return (p, e, c as int, c as int?, 100 as Color, -100 as EEmpty);
127+
}
128+
129+
@method_id(107)
130+
fun test7() {
131+
return (
132+
WithColor{c1: Color.Red},
133+
WithColor{c1: -100 as Color, c2: Color.createBlue()}
134+
)
135+
}
136+
137+
@method_id(108)
138+
fun test8() {
139+
var (c0: Color | int, c1: Color | int) = (Color.Red, 123);
140+
var (u1, u2) = (Color.Red as Color | int, 0 as Color | int);
141+
var (b1, b2, b3) = (Color.Red as Color | EPartialAssigned | builder, EPartialAssigned.Max as ColorAlias | builder | EPartialAssigned, beginCell() as builder | Color | EPartialAssigned);
142+
return (
143+
checkIsColor(u1), checkIsColor(u2),
144+
checkIsColor(b1), checkIsColor(b2), checkIsColor(b3),
145+
b2 is EPartialAssigned,
146+
u1,
147+
match (u1) { int => -100, Color => -200 }
148+
)
149+
}
150+
151+
@method_id(109)
152+
fun test9() {
153+
return (
154+
Color.Red.mapToNumber(), Color.Green.mapToNumber(), Color.Blue.mapToNumber(),
155+
// this is UB, the compiler may do any optimizations
156+
(100 as Color).mapToNumber(), (-99 as Color).mapToNumber(),
157+
);
158+
}
159+
160+
@method_id(110)
161+
fun test10(c: Color) {
162+
var t = createEmptyTuple();
163+
match (c) {
164+
Color.Red => { t.push(1) }
165+
else => { t.push(-100) }
166+
}
167+
match (c) {
168+
Color.Green => { t.push(2) }
169+
Color.Blue => { t.push(3) }
170+
Color.Red => { t.push(1) }
171+
}
172+
match (c is Color) {
173+
true => { t.push(-100) }
174+
}
175+
match (c) {
176+
Color => { t.push(88) }
177+
}
178+
match (c) {
179+
Color.Blue => { t.push(3) }
180+
Color.Green => { t.push(2) }
181+
else => { t.push(-100) }
182+
}
183+
match (c) {
184+
Color.Red => { t.push(1) }
185+
Color.Blue => { t.push(3) }
186+
Color.Green => { t.push(2) }
187+
}
188+
return t;
189+
}
190+
191+
@method_id(111)
192+
fun test11(b1Value: Color) {
193+
var b1: Box<Color> = { value: b1Value };
194+
var b2 = Box<Color> { value: Color.Green };
195+
var b3 = Box { value: Color.Blue };
196+
return (b1.value == b2.value, b1.value != b2.value, b1.value == b3.value, b1.value != b3.value)
197+
}
198+
199+
@method_id(112)
200+
fun test12(c1: Color, c2: Color) {
201+
var t = createEmptyTuple();
202+
203+
match (c1) {
204+
Color.Red => { t.push(10 + c2.mapToNumber()) }
205+
Color.Green => { t.push(20 + c2.mapToNumber()) }
206+
Color.Blue => { t.push(30 + c2.mapToNumber()) }
207+
}
208+
209+
match (c2) {
210+
Color.Red => {
211+
match (c1) {
212+
Color.Red => t.push(100),
213+
Color.Blue => t.push(300),
214+
Color.Green => t.push(200),
215+
}
216+
}
217+
Color.Blue => match (c1) {
218+
Color.Green => t.push(210),
219+
Color.Red => t.push(110),
220+
Color.Blue => t.push(310),
221+
},
222+
Color.Green => match (c1) {
223+
Color.Green => t.push(220),
224+
Color.Red => t.push(120),
225+
Color.Blue => t.push(320),
226+
}
227+
}
228+
229+
t.push((999 as Color).mapToNumber());
230+
return t;
231+
}
232+
233+
@method_id(113)
234+
fun test13(x: int) {
235+
try {
236+
if (x < 0) { throw Err.TooLowId }
237+
assert (x < 100) throw Err.TooHighId;
238+
assert (x != 10, Err.DisabledId);
239+
return (0, false);
240+
} catch (excno) {
241+
return (excno, excno as Err == Err.TooLowId);
242+
}
243+
}
244+
245+
246+
fun main(c: Color) {
247+
__expect_type(c, "Color");
248+
__expect_type(C_BLUE, "Color");
249+
__expect_type(Color.createBlue(), "Color");
250+
__expect_type(ColorAlias.Red, "Color");
251+
__expect_type(EPartialAssigned.Max, "EPartialAssigned");
252+
__expect_type(Box{value:Color.Red}, "Box<Color>");
253+
return c;
254+
}
255+
256+
257+
/**
258+
@testcase | 0 | 123 | 123
259+
@testcase | 101 | | -1 0 -1 -1 0 -1 -1 2
260+
@testcase | 102 | 1 | -1 0 0 0 0 0
261+
@testcase | 103 | | -100 -99 1 2 3 4 5 6 2147483647
262+
@testcase | 104 | -1 | -1 -1 2 -1 1
263+
@testcase | 105 | | 123 -10 456 2147483647 2147483648
264+
@testcase | 106 | 1 | 1 1 1 1 100 -100
265+
@testcase | 107 | | 0 1 -100 2
266+
@testcase | 108 | | -1 0 -1 0 0 -1 0 typeid-1 -200
267+
@testcase | 109 | | 100 200 300 300 300
268+
@testcase | 110 | 0 | [ 1 1 -100 88 -100 1 ]
269+
@testcase | 110 | 1 | [ -100 2 -100 88 2 2 ]
270+
@testcase | 110 | 2 | [ -100 3 -100 88 3 3 ]
271+
@testcase | 110 | 44 | [ -100 1 -100 88 -100 2 ]
272+
@testcase | 111 | 2 | 0 -1 -1 0
273+
@testcase | 112 | 0 1 | [ 210 120 300 ]
274+
@testcase | 112 | 2 4 | [ 330 320 300 ]
275+
@testcase | 113 | -8 | 256 -1
276+
@testcase | 113 | 111 | 257 0
277+
@testcase | 113 | 10 | 258 0
278+
@testcase | 113 | 50 | 0 0
279+
280+
@fif_codegen
281+
"""
282+
test1() PROC:<{
283+
TRUE
284+
FALSE
285+
TRUE
286+
TRUE
287+
FALSE
288+
TRUE
289+
TRUE
290+
2 PUSHINT
291+
}>
292+
"""
293+
294+
@fif_codegen
295+
"""
296+
test2() PROC:<{ // c
297+
DROP //
298+
-1 PUSHINT // '1=-1
299+
0 PUSHINT // '1=-1 '2
300+
0 PUSHINT // '1=-1 '2 '3=0
301+
s0 s0 s0 PUSH3 // '1=-1 '2 '3=0 '4=0 '5=0 '6=0
302+
}>
303+
"""
304+
305+
@fif_codegen DECLPROC checkIsColor<EPartialAssigned|builder>()
306+
307+
*/

tolk-tester/tests/generics-1.tolk

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fun tuplePush<T>(mutate t: tuple, value: T): void
1515
@method_id(101)
1616
fun test101(x: int) {
1717
var (a, b, c) = (x, (x,x), [x,x]);
18-
return (eq1(a), eq1(b), eq1(c), eq2(a), eq2(b), eq2(c), eq3(a), eq4(b), eq3(createEmptyTuple()));
18+
return (eq1(a), eq1(b), eq1(c), eq2(a), eq2(b), eq2(c), eq3(a), eq4(b), eq3(createEmptyTuple()), eq1(Color.Red));
1919
}
2020

2121
fun getTwo<X>(): X { return 2 as X; }
@@ -28,6 +28,7 @@ struct Wrapper<T> {
2828
struct Err<T> {
2929
errPayload: T
3030
}
31+
enum Color { Red, Green, Blue }
3132

3233
@method_id(102)
3334
fun test102(): (int, int, int, [int, int], Wrapper< Wrapper<int8> >) {
@@ -211,6 +212,7 @@ fun test113(a: int | slice) {
211212

212213
fun main(x: int): (int, [Tup2Int]) {
213214
__expect_type(test110, "() -> (Tensor2Int, Tensor2Int)");
215+
__expect_type(eq2(10 as Color), "Color");
214216

215217
try { if(x) { throw (1, x); } }
216218
catch (excNo, arg) { return (arg as int, [[eq2(arg as int), getTwo()]]); }
@@ -219,7 +221,7 @@ fun main(x: int): (int, [Tup2Int]) {
219221

220222
/**
221223
@testcase | 0 | 1 | 1 [ [ 1 2 ] ]
222-
@testcase | 101 | 0 | 0 0 0 [ 0 0 ] 0 0 0 [ 0 0 ] 0 0 0 []
224+
@testcase | 101 | 0 | 0 0 0 [ 0 0 ] 0 0 0 [ 0 0 ] 0 0 0 [] 0
223225
@testcase | 102 | | 2 2 2 [ 2 2 ] 2
224226
@testcase | 103 | 0 | 0 100 100
225227
@testcase | 104 | 0 | [ 1 (null) 2 ] [ 2 -1 0 ]
@@ -241,6 +243,7 @@ fun main(x: int): (int, [Tup2Int]) {
241243
@fif_codegen DECLPROC eq1<MInt?>()
242244
@fif_codegen DECLPROC eq1<int|slice|null>()
243245
@fif_codegen DECLPROC eq1<Wrapper<Wrapper<int8>>>()
246+
@fif_codegen DECLPROC eq1<Color>()
244247

245248
// was inlined
246249
@fif_codegen_avoid DECLPROC getTwo<int>()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
enum Color { Red = Color.Red }
3+
4+
/**
5+
@compilation_should_fail
6+
@stderr referencing other members in initializers is not allowed yet
7+
*/
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
enum Color { Red = ton("0.05") as int }
3+
4+
/**
5+
@compilation_should_fail
6+
@stderr unsupported operation in `enum` member value
7+
*/
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
enum Over {
2+
MaxIntMinus1 = +115792089237316195423570985008687907853269984665640564039457584007913129639935 - 1,
3+
MaxInt,
4+
MaxIntPlus1,
5+
}
6+
7+
/**
8+
@compilation_should_fail
9+
@stderr integer overflow
10+
@stderr MaxIntPlus1
11+
*/
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
enum Over {
2+
A = (+115792089237316195423570985008687907853269984665640564039457584007913129639934 << 1) * 2,
3+
}
4+
5+
/**
6+
@compilation_should_fail
7+
@stderr integer overflow
8+
*/
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
enum Color { Red, Green, Blue, }
2+
3+
fun main() {
4+
Color.Red = 123 as Color;
5+
}
6+
7+
/**
8+
@compilation_should_fail
9+
@stderr modifying immutable constant
10+
*/

0 commit comments

Comments
 (0)