1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2001-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(lc_SUITE).
21
22-export([all/0, suite/0, groups/0, init_per_suite/1, end_per_suite/1,
23	 init_per_group/2,end_per_group/2,
24	 init_per_testcase/2,end_per_testcase/2,
25	 basic/1,deeply_nested/1,no_generator/1,
26	 empty_generator/1,no_export/1,shadow/1,
27	 effect/1]).
28
29-include_lib("common_test/include/ct.hrl").
30
31suite() ->
32    [{ct_hooks,[ts_install_cth]},
33     {timetrap,{minutes,1}}].
34
35all() ->
36    [{group,p}].
37
38groups() ->
39    [{p,test_lib:parallel(),
40      [basic,
41       deeply_nested,
42       no_generator,
43       empty_generator,
44       no_export,
45       shadow,
46       effect
47      ]}].
48
49init_per_suite(Config) ->
50    test_lib:recompile(?MODULE),
51    Config.
52
53end_per_suite(_Config) ->
54    ok.
55
56init_per_group(_GroupName, Config) ->
57    Config.
58
59end_per_group(_GroupName, Config) ->
60    Config.
61
62
63init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
64    Config.
65
66end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
67    ok.
68
69basic(Config) when is_list(Config) ->
70    L0 = lists:seq(1, 10),
71    L1 = my_map(fun(X) -> {x,X} end, L0),
72    L1 = [{x,X} || X <- L0],
73    L0 = my_map(fun({x,X}) -> X end, L1),
74    [1,2,3,4,5] = [X || X <- L0, X < 6],
75    [4,5,6] = [X || X <- L0, X > 3, X < 7],
76    [] = [X || X <- L0, X > 32, X < 7],
77    [1,3,5,7,9] = [X || X <- L0, odd(X)],
78    [2,4,6,8,10] = [X || X <- L0, not odd(X)],
79    [1,3,5,9] = [X || X <- L0, odd(X), X =/= 7],
80    [2,4,8,10] = [X || X <- L0, not odd(X), X =/= 6],
81
82    %% Append is specially handled.
83    [1,3,5,9,2,4,8,10] = [X || X <- L0, odd(X), X =/= 7] ++
84	[X || X <- L0, not odd(X), X =/= 6],
85
86    %% Guards BIFs are evaluated in guard context. Weird, but true.
87    [{a,b,true},{x,y,true,true}] = [X || X <- tuple_list(), element(3, X)],
88
89    %% Filter expressions with andalso/orelse.
90    "abc123" = alphanum("?abc123.;"),
91
92    %% Aliased patterns.
93    [] = [t || {C=D}={_,_} <- []],
94    [] = [X || {X,{Y}={X,X}} <- []],
95    [t] = [t || "a"++"b" = "ab" <- ["ab"]],
96
97    %% Strange filter block.
98    [] = [{X,Y} || {X} <- [], begin Y = X, Y =:= X end],
99    [{a,a}] = [{X,Y} || {X} <- [{a}], begin Y = X, Y =:= X end],
100
101    %% Not matching.
102    [] = [3 || {3=4} <- []],
103
104    %% Error cases.
105    [] = [{xx,X} || X <- L0, element(2, X) == no_no_no],
106    {'EXIT',_} = (catch [X || X <- L1, list_to_atom(X) == dum]),
107    [] = [X || X <- L1, X+1 < 2],
108    {'EXIT',_} = (catch [X || X <- L1, odd(X)]),
109    {'EXIT',{{bad_generator,x},_}} = (catch [E || E <- id(x)]),
110    {'EXIT',{{bad_filter,not_bool},_}} = (catch [E || E <- [1,2], id(not_bool)]),
111
112    %% Make sure that line numbers point out the generator.
113    case ?MODULE of
114        lc_inline_SUITE ->
115            ok;
116        _ ->
117            {'EXIT',{{bad_generator,a},
118                     [{?MODULE,_,_,
119                       [{file,"bad_lc.erl"},{line,4}]}|_]}} =
120                (catch id(bad_generator(a))),
121
122            {'EXIT',{{bad_generator,a},
123                     [{?MODULE,_,_,
124                       [{file,"bad_lc.erl"},{line,7}]}|_]}} =
125                (catch id(bad_generator_bc(a))),
126
127            %% List comprehensions with improper lists.
128            {'EXIT',{{bad_generator,d},
129                     [{?MODULE,_,_,
130                       [{file,"bad_lc.erl"},{line,4}]}|_]}} =
131                (catch bad_generator(id([a,b,c|d])))
132    end,
133
134    ok.
135
136tuple_list() ->
137    [{a,b,true},[a,b,c],glurf,{a,b,false,xx},{a,b},{x,y,true,true},{a,b,d,ddd}].
138
139my_map(F, L) ->
140    [F(X) || X <- L].
141
142odd(X) ->
143    X rem 2 == 1.
144
145alphanum(Str) ->
146    [C || C <- Str, ((C >= $0) andalso (C =< $9))
147	      orelse ((C >= $a) andalso (C =< $z))
148	      orelse ((C >= $A) andalso (C =< $Z))].
149
150deeply_nested(Config) when is_list(Config) ->
151    [[99,98,97,96,42,17,1764,12,11,10,9,8,7,6,5,4,3,7,2,1]] =  deeply_nested_1(),
152    ok.
153
154deeply_nested_1() ->
155    %% This used to compile really, really SLOW before R11B-1...
156    [[X1,X2,X3,X4,X5,X6,X7(),X8,X9,X10,X11,X12,X13,X14,X15,X16,X17,X18(),X19,X20] ||
157        X1 <- [99],X2 <- [98],X3 <- [97],X4 <- [96],X5 <- [42],X6 <- [17],
158	X7 <- [fun() -> X5*X5 end],X8 <- [12],X9 <- [11],X10 <- [10],
159        X11 <- [9],X12 <- [8],X13 <- [7],X14 <- [6],X15 <- [5],
160	X16 <- [4],X17 <- [3],X18 <- [fun() -> X16+X17 end],X19 <- [2],X20 <- [1]].
161
162no_generator(Config) when is_list(Config) ->
163    Seq = lists:seq(-10, 17),
164    [no_gen_verify(no_gen(A, B), A, B) || A <- Seq, B <- Seq],
165
166    %% Literal expression, for coverage.
167    [a] = [a || true],
168    [a,b,c] = [a || true] ++ [b,c],
169    ok.
170
171no_gen(A, B) ->
172    [{A,B} || A+B =:= 0] ++
173	[{A,B} || A*B =:= 0] ++
174	[{A,B} || A rem B =:= 3] ++
175	[{A,B} || A =:= B] ++
176	[{one_more,A,B} || no_gen_one_more(A, B)] ++
177	[A || A =:= 1] ++
178	[A || A =:= 2] ++
179	[A || A =:= 3] ++
180	[A || A =:= 4] ++
181	[A || A =:= 5] ++
182	[A || A =:= 6] ++
183	[A || A =:= 7] ++
184	[A || A =:= 8] ++
185	[A || A =:= 9] ++
186	[B || B =:= 1] ++
187	[B || B =:= 2] ++
188	[B || B =:= 3] ++
189	[B || B =:= 4] ++
190	[B || B =:= 5] ++
191	[B || B =:= 6] ++
192	[B || B =:= 7] ++
193	[B || B =:= 8] ++
194	[B || B =:= 9].
195
196no_gen_verify(Res, A, B) ->
197    Pair = {A,B},
198    ShouldBe = no_gen_eval(fun() -> A+B =:= 0 end, Pair) ++
199	no_gen_eval(fun() -> A*B =:= 0 end, Pair) ++
200	no_gen_eval(fun() -> B =/= 0 andalso A rem B =:= 3 end, Pair) ++
201	no_gen_eval(fun() -> A =:= B end, Pair) ++
202	no_gen_eval(fun() -> A + 1 =:= B end, {one_more,A,B}) ++
203	no_gen_eval(fun() -> 1 =< A andalso A =< 9 end, A) ++
204	no_gen_eval(fun() -> 1 =< B andalso B =< 9 end, B),
205    case Res of
206	ShouldBe -> ok;
207	_ ->
208	    io:format("A = ~p; B = ~p; Expected = ~p, actual = ~p", [A,B,ShouldBe,Res]),
209	    ct:fail(failed)
210    end.
211
212no_gen_eval(Fun, Res) ->
213    case Fun() of
214	true -> [Res];
215	false -> []
216    end.
217
218no_gen_one_more(A, B) -> A + 1 =:= B.
219
220empty_generator(Config) when is_list(Config) ->
221    [] = [X || {X} <- [], (false or (X/0 > 3))],
222    ok.
223
224no_export(Config) when is_list(Config) ->
225    [] = [ _X = a || false ] ++ [ _X = a || false ],
226    ok.
227
228%% Test that variables in list comprehensions are
229%% correctly shadowed.
230
231shadow(Config) when is_list(Config) ->
232    Shadowed = nomatch,
233    _ = id(Shadowed),				%Eliminate warning.
234    L = [{Shadowed,Shadowed+1} || Shadowed <- lists:seq(7, 9)],
235    [{7,8},{8,9},{9,10}] = id(L),
236    [8,9] = id([Shadowed || {_,Shadowed} <- id(L),
237			    Shadowed < 10]),
238    ok.
239
240effect(Config) when is_list(Config) ->
241    ct:timetrap({minutes,10}),
242    [{42,{a,b,c}}] =
243	do_effect(fun(F, L) ->
244			  [F({V1,V2}) ||
245			      #{<<1:500>>:=V1,<<2:301>>:=V2} <- L],
246			  ok
247		  end, id([#{},x,#{<<1:500>>=>42,<<2:301>>=>{a,b,c}}])),
248
249    %% Will trigger the time-trap timeout if not tail-recursive.
250    case ?MODULE of
251	lc_SUITE ->
252	    _ = [{'EXIT',{badarg,_}} =
253		     (catch binary_to_atom(<<C/utf8>>, utf8)) ||
254		    C <- lists:seq(16#FF10000, 16#FFFFFFF)];
255	_ ->
256	    ok
257    end,
258
259    ok.
260
261do_effect(Lc, L) ->
262    put(?MODULE, []),
263    F = fun(V) -> put(?MODULE, [V|get(?MODULE)]) end,
264    ok = Lc(F, L),
265    lists:reverse(erase(?MODULE)).
266
267id(I) -> I.
268
269-file("bad_lc.erl", 1).
270bad_generator(List) ->                          %Line 2
271    [I ||                                       %Line 3
272        I <- List].                             %Line 4
273bad_generator_bc(List) ->                       %Line 5
274    << <<I:4>> ||                               %Line 6
275        I <- List>>.                            %Line 7
276