1%% -*- erlang-indent-level: 4 -*-
2%%
3%% Licensed under the Apache License, Version 2.0 (the "License");
4%% you may not use this file except in compliance with the License.
5%% You may obtain a copy of the License at
6%%
7%%     http://www.apache.org/licenses/LICENSE-2.0
8%%
9%% Unless required by applicable law or agreed to in writing, software
10%% distributed under the License is distributed on an "AS IS" BASIS,
11%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12%% See the License for the specific language governing permissions and
13%% limitations under the License.
14%%
15-module(erl_types_SUITE).
16
17-export([all/0,
18         consistency_and_to_string/1, map_multiple_representations/1]).
19
20%% Simplify calls into erl_types and avoid importing the entire module.
21-define(M, erl_types).
22
23-include_lib("common_test/include/ct.hrl").
24
25all() ->
26    [consistency_and_to_string, map_multiple_representations].
27
28consistency_and_to_string(_Config) ->
29    %% Check consistency of types
30    Atom1  = ?M:t_atom(),
31    Atom2  = ?M:t_atom(foo),
32    Atom3  = ?M:t_atom(bar),
33    true   = ?M:t_is_atom(Atom2),
34
35    True   = ?M:t_atom(true),
36    False  = ?M:t_atom(false),
37    Bool   = ?M:t_boolean(),
38    true   = ?M:t_is_boolean(True),
39    true   = ?M:t_is_boolean(Bool),
40    false  = ?M:t_is_boolean(Atom1),
41
42    Binary = ?M:t_binary(),
43    true   = ?M:t_is_binary(Binary),
44
45    Bitstr = ?M:t_bitstr(),
46    true   = ?M:t_is_bitstr(Bitstr),
47
48    Bitstr1 = ?M:t_bitstr(7, 3),
49    true   = ?M:t_is_bitstr(Bitstr1),
50    false  = ?M:t_is_binary(Bitstr1),
51
52    Bitstr2 = ?M:t_bitstr(16, 8),
53    true   = ?M:t_is_bitstr(Bitstr2),
54    true   = ?M:t_is_binary(Bitstr2),
55
56    BitStr816 = ?M:t_bitstr(8,16),
57    BitStr816 = ?M:t_subtract(?M:t_bitstr(4, 12), ?M:t_bitstr(8, 12)),
58
59    Int1   = ?M:t_integer(),
60    Int2   = ?M:t_integer(1),
61    Int3   = ?M:t_integer(16#ffffffff),
62    true   = ?M:t_is_integer(Int2),
63    true   = ?M:t_is_byte(Int2),
64    false  = ?M:t_is_byte(Int3),
65    false  = ?M:t_is_byte(?M:t_from_range(-1, 1)),
66    true   = ?M:t_is_byte(?M:t_from_range(1, 255)),
67
68    Tuple1 = ?M:t_tuple(),
69    Tuple2 = ?M:t_tuple(3),
70    Tuple3 = ?M:t_tuple([Atom1, Int1]),
71    Tuple4 = ?M:t_tuple([Tuple1, Tuple2]),
72    Tuple5 = ?M:t_tuple([Tuple3, Tuple4]),
73    Tuple6 = ?M:t_limit(Tuple5, 2),
74    Tuple7 = ?M:t_limit(Tuple5, 3),
75    true   = ?M:t_is_tuple(Tuple1),
76
77    Port   = ?M:t_port(),
78    Pid    = ?M:t_pid(),
79    Ref    = ?M:t_reference(),
80    Identifier = ?M:t_identifier(),
81    false  = ?M:t_is_reference(Port),
82    true   = ?M:t_is_identifier(Port),
83
84    Function1 = ?M:t_fun(),
85    Function2 = ?M:t_fun(Pid),
86    Function3 = ?M:t_fun([], Pid),
87    Function4 = ?M:t_fun([Port, Pid], Pid),
88    Function5 = ?M:t_fun([Pid, Atom1], Int2),
89    true      = ?M:t_is_fun(Function3),
90
91    List1 = ?M:t_list(),
92    List2 = ?M:t_list(?M:t_boolean()),
93    List3 = ?M:t_cons(?M:t_boolean(), List2),
94    List4 = ?M:t_cons(?M:t_boolean(), ?M:t_atom()),
95    List5 = ?M:t_cons(?M:t_boolean(), ?M:t_nil()),
96    List6 = ?M:t_cons_tl(List5),
97    List7 = ?M:t_sup(List4, List5),
98    List8 = ?M:t_inf(List7, ?M:t_list()),
99    List9 = ?M:t_cons(),
100    List10 = ?M:t_cons_tl(List9),
101    true  = ?M:t_is_boolean(?M:t_cons_hd(List5)),
102    true  = ?M:t_is_list(List5),
103    false = ?M:t_is_list(List4),
104
105    Product1 = ?M:t_product([Atom1, Atom2]),
106    Product2 = ?M:t_product([Atom3, Atom1]),
107    Product3 = ?M:t_product([Atom3, Atom2]),
108
109    Union1 = ?M:t_sup(Atom2, Atom3),
110    Union2 = ?M:t_sup(Tuple2, Tuple3),
111    Union3 = ?M:t_sup(Int2, Atom3),
112    Union4 = ?M:t_sup(Port, Pid),
113    Union5 = ?M:t_sup(Union4, Int1),
114    Union6 = ?M:t_sup(Function1, Function2),
115    Union7 = ?M:t_sup(Function4, Function5),
116    Union8 = ?M:t_sup(True, False),
117    true   = ?M:t_is_boolean(Union8),
118    Union9 = ?M:t_sup(Int2, ?M:t_integer(2)),
119    true   = ?M:t_is_byte(Union9),
120    Union10 = ?M:t_sup(?M:t_tuple([?M:t_atom(true), ?M:t_any()]),
121                       ?M:t_tuple([?M:t_atom(false), ?M:t_any()])),
122
123    Any   = ?M:t_any(),
124    Any   = ?M:t_sup(Product3, Function5),
125
126    Atom3  = ?M:t_inf(Union3, Atom1),
127    Union2 = ?M:t_inf(Union2, Tuple1),
128    Int2   = ?M:t_inf(Int1, Union3),
129    Union4 = ?M:t_inf(Union4, Identifier),
130    Port   = ?M:t_inf(Union5, Port),
131    Function4 = ?M:t_inf(Union7, Function4),
132    None   = ?M:t_none(),
133    None   = ?M:t_inf(Product2, Atom1),
134    Product3 = ?M:t_inf(Product1, Product2),
135    Function5 = ?M:t_inf(Union7, Function5),
136    true   = ?M:t_is_byte(?M:t_inf(Union9, ?M:t_number())),
137    true   = ?M:t_is_char(?M:t_inf(Union9, ?M:t_number())),
138
139    RecDict = #{{record, foo} => {{?FILE, ?LINE}, [{2, [{bar, [], ?M:t_any()},
140                                                        {baz, [], ?M:t_any()}]}]}},
141    Record1 = ?M:t_from_term({foo, [1,2], {1,2,3}}),
142
143    %% Check string representations
144    "atom()" = ?M:t_to_string(Atom1),
145    "'foo'"  = ?M:t_to_string(Atom2),
146    "'bar'"  = ?M:t_to_string(Atom3),
147
148    "binary()" = ?M:t_to_string(Binary),
149
150    "integer()" = ?M:t_to_string(Int1),
151    "1" = ?M:t_to_string(Int2),
152
153    "tuple()" = ?M:t_to_string(Tuple1),
154    "{_,_,_}" = ?M:t_to_string(Tuple2),
155    "{atom(),integer()}" = ?M:t_to_string(Tuple3),
156    "{tuple(),{_,_,_}}" = ?M:t_to_string(Tuple4),
157    "{{atom(),integer()},{tuple(),{_,_,_}}}" = ?M:t_to_string(Tuple5),
158    "{{_,_},{_,_}}" = ?M:t_to_string(Tuple6),
159    "{{atom(),integer()},{tuple(),{_,_,_}}}" = ?M:t_to_string(Tuple7),
160
161    "reference()" = ?M:t_to_string(Ref),
162    "port()" = ?M:t_to_string(Port),
163    "pid()" = ?M:t_to_string(Pid),
164    "identifier()" = ?M:t_to_string(Identifier),
165
166    "[any()]" = ?M:t_to_string(List1),
167    "[boolean()]" = ?M:t_to_string(List2),
168    "[boolean(),...]" = ?M:t_to_string(List3),
169    "nonempty_improper_list(boolean(),atom())" = ?M:t_to_string(List4),
170    "[boolean(),...]" = ?M:t_to_string(List5),
171    "[boolean()]" = ?M:t_to_string(List6),
172    "nonempty_maybe_improper_list(boolean(),atom() | [])" = ?M:t_to_string(List7),
173    "[boolean(),...]" = ?M:t_to_string(List8),
174    "nonempty_maybe_improper_list()" = ?M:t_to_string(List9),
175    "any()" = ?M:t_to_string(List10),
176
177    "fun()" = ?M:t_to_string(Function1),
178    "fun((...) -> pid())" = ?M:t_to_string(Function2),
179    "fun(() -> pid())" = ?M:t_to_string(Function3),
180    "fun((port(),pid()) -> pid())" = ?M:t_to_string(Function4),
181    "fun((pid(),atom()) -> 1)" = ?M:t_to_string(Function5),
182
183    "<atom(),'foo'>" = ?M:t_to_string(Product1),
184    "<'bar',atom()>" = ?M:t_to_string(Product2),
185
186    "#foo{bar::[1 | 2,...],baz::{1,2,3}}" = ?M:t_to_string(Record1, RecDict),
187
188    "'bar' | 'foo'" = ?M:t_to_string(Union1),
189    "{atom(),integer()} | {_,_,_}" = ?M:t_to_string(Union2),
190    "'bar' | 1" = ?M:t_to_string(Union3),
191    "pid() | port()" = ?M:t_to_string(Union4),
192    "pid() | port() | integer()" = ?M:t_to_string(Union5),
193    "fun()" = ?M:t_to_string(Union6),
194    "fun((pid() | port(),atom() | pid()) -> pid() | 1)" = ?M:t_to_string(Union7),
195    "boolean()" = ?M:t_to_string(Union8),
196    "{'false',_} | {'true',_}" = ?M:t_to_string(Union10),
197    "{'true',integer()}" = ?M:t_to_string(?M:t_inf(Union10, ?M:t_tuple([?M:t_atom(true), ?M:t_integer()]))).
198
199%% OTP-17537.
200map_multiple_representations(_Config) ->
201    DefV = erl_types:t_atom(),
202    fun() ->
203            P2 = {erl_types:t_integer(0), optional, DefV},
204            Ps = [P2],
205            DefK = erl_types:t_pos_integer(),
206            T = erl_types:t_map(Ps, DefK, DefV),
207            "#{non_neg_integer()=>atom()}" = erl_types:t_to_string(T)
208    end(),
209    fun() ->
210            P1 = {erl_types:t_integer(-1), optional, DefV},
211            P2 = {erl_types:t_integer(0), optional, DefV},
212            Ps = [P1, P2],
213            DefK = erl_types:t_pos_integer(),
214            T = erl_types:t_map(Ps, DefK, DefV),
215            "#{integer()=>atom()}" = erl_types:t_to_string(T)
216    end(),
217    fun() ->
218            P1 = {erl_types:t_integer(0), optional, DefV}, % integer()
219            P2 = {erl_types:t_integer(1), optional, DefV}, % extra
220            Ps = [P1, P2],
221            DefK = erl_types:t_neg_integer(),
222            T = erl_types:t_map(Ps, DefK, DefV),
223            "#{integer()=>atom()}" = erl_types:t_to_string(T)
224    end(),
225    fun() ->
226            P1 = {erl_types:t_nil(), optional, DefV},
227            Ps = [P1],
228            DefK = erl_types:t_nonempty_list(),
229            T = erl_types:t_map(Ps, DefK, DefV),
230            "#{[any()]=>atom()}" = erl_types:t_to_string(T)
231    end(),
232    fun() ->
233            P1 = {erl_types:t_nil(), optional, DefV},
234            Ps = [P1],
235            DefK = erl_types:t_nonempty_string(),
236            T = erl_types:t_map(Ps, DefK, DefV),
237            "#{string()=>atom()}" = erl_types:t_to_string(T)
238    end(),
239    fun() ->
240            P1 = {erl_types:t_nil(), optional, DefV},
241            Ps = [P1],
242            DefK = erl_types:t_sup(erl_types:t_nonempty_string(),
243                                   erl_types:t_nil()),
244            T = erl_types:t_map(Ps, DefK, DefV),
245            "#{string()=>atom()}" = erl_types:t_to_string(T)
246    end(),
247    fun() ->
248            P1 = {erl_types:t_nil(), optional, DefV},
249            Ps = [P1],
250            DefK = erl_types:t_sup(erl_types:t_nonempty_string(),
251                                   erl_types:t_atom()),
252            T = erl_types:t_map(Ps, DefK, DefV),
253            "#{atom() | string()=>atom()}" = erl_types:t_to_string(T)
254    end(),
255    fun() ->
256            P1 = {erl_types:t_integer(0), optional, DefV},
257            Ps = [P1],
258            DefK = erl_types:t_sup(erl_types:t_pos_integer(),
259                                   erl_types:t_atom()),
260            T = erl_types:t_map(Ps, DefK, DefV),
261            "#{atom() | non_neg_integer()=>atom()}" = erl_types:t_to_string(T)
262    end(),
263    fun() ->
264            P1 = {erl_types:t_integer(8), optional, DefV},
265            Ps = [P1],
266            DefK = erl_types:t_from_range(9, 12),
267            T = erl_types:t_map(Ps, DefK, DefV),
268            "#{8 | 9 | 10 | 11 | 12=>atom()}" = erl_types:t_to_string(T)
269
270    end(),
271    fun() ->
272            P1 = {erl_types:t_integer(13), optional, DefV},
273            Ps = [P1],
274            DefK = erl_types:t_from_range(9, 12),
275            T = erl_types:t_map(Ps, DefK, DefV),
276            "#{9 | 10 | 11 | 12 | 13=>atom()}" = erl_types:t_to_string(T)
277
278    end(),
279    fun() ->
280            P1 = {erl_types:t_atom(a), optional, DefV},
281            Ps = [P1],
282            DefK = erl_types:t_sup([erl_types:t_atom(a01),
283                                    erl_types:t_atom(a02),
284                                    erl_types:t_atom(a03),
285                                    erl_types:t_atom(a04),
286                                    erl_types:t_atom(a05),
287                                    erl_types:t_atom(a06),
288                                    erl_types:t_atom(a07),
289                                    erl_types:t_atom(a08),
290                                    erl_types:t_atom(a09),
291                                    erl_types:t_atom(a10),
292                                    erl_types:t_atom(a11),
293                                    erl_types:t_atom(a12),
294                                    erl_types:t_atom(a13)]),
295            T = erl_types:t_map(Ps, DefK, DefV),
296            "#{atom()=>atom()}" = erl_types:t_to_string(T)
297    end(),
298    fun() ->
299            P1 = {erl_types:t_atom(a), optional, DefV},
300            Ps = [P1],
301            DefK = erl_types:t_sup([erl_types:t_atom(b),
302                                    erl_types:t_atom(c)]),
303            T = erl_types:t_map(Ps, DefK, DefV),
304            "#{'a' | 'b' | 'c'=>atom()}" = erl_types:t_to_string(T)
305    end(),
306    fun() ->
307            P1 = {erl_types:t_atom(a), optional, DefV},
308            Ps = [P1],
309            DefK = erl_types:t_atom(b),
310            T = erl_types:t_map(Ps, DefK, DefV),
311            "#{'a'=>atom(), 'b'=>atom()}" = erl_types:t_to_string(T)
312    end(),
313    ok.
314