1 //===----------------------------------------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // UNSUPPORTED: c++98, c++03, c++11, c++14
11 
12 // <tuple>
13 
14 // template <class F, class T> constexpr decltype(auto) apply(F &&, T &&)
15 
16 // Test with different ref/ptr/cv qualified argument types.
17 
18 #include <tuple>
19 #include <array>
20 #include <utility>
21 #include <cassert>
22 
23 #include "test_macros.h"
24 #include "type_id.h"
25 
26 // std::array is explicitly allowed to be initialized with A a = { init-list };.
27 // Disable the missing braces warning for this reason.
28 #include "disable_missing_braces_warning.h"
29 
30 
31 constexpr int constexpr_sum_fn() { return 0; }
32 
33 template <class ...Ints>
34 constexpr int constexpr_sum_fn(int x1, Ints... rest) { return x1 + constexpr_sum_fn(rest...); }
35 
36 struct ConstexprSumT {
37   constexpr ConstexprSumT() = default;
38   template <class ...Ints>
39   constexpr int operator()(Ints... values) const {
40       return constexpr_sum_fn(values...);
41   }
42 };
43 
44 
45 void test_constexpr_evaluation()
46 {
47     constexpr ConstexprSumT sum_obj{};
48     {
49         using Tup = std::tuple<>;
50         using Fn = int(&)();
51         constexpr Tup t;
52         static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 0, "");
53         static_assert(std::apply(sum_obj, t) == 0, "");
54     }
55     {
56         using Tup = std::tuple<int>;
57         using Fn = int(&)(int);
58         constexpr Tup t(42);
59         static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 42, "");
60         static_assert(std::apply(sum_obj, t) == 42, "");
61     }
62     {
63         using Tup = std::tuple<int, long>;
64         using Fn = int(&)(int, int);
65         constexpr Tup t(42, 101);
66         static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 143, "");
67         static_assert(std::apply(sum_obj, t) == 143, "");
68     }
69     {
70         using Tup = std::pair<int, long>;
71         using Fn = int(&)(int, int);
72         constexpr Tup t(42, 101);
73         static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 143, "");
74         static_assert(std::apply(sum_obj, t) == 143, "");
75     }
76     {
77         using Tup = std::tuple<int, long, int>;
78         using Fn = int(&)(int, int, int);
79         constexpr Tup t(42, 101, -1);
80         static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 142, "");
81         static_assert(std::apply(sum_obj, t) == 142, "");
82     }
83     {
84         using Tup = std::array<int, 3>;
85         using Fn = int(&)(int, int, int);
86         constexpr Tup t = {42, 101, -1};
87         static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 142, "");
88         static_assert(std::apply(sum_obj, t) == 142, "");
89     }
90 }
91 
92 
93 enum CallQuals {
94   CQ_None,
95   CQ_LValue,
96   CQ_ConstLValue,
97   CQ_RValue,
98   CQ_ConstRValue
99 };
100 
101 template <class Tuple>
102 struct CallInfo {
103   CallQuals quals;
104   TypeID const* arg_types;
105   Tuple args;
106 
107   template <class ...Args>
108   CallInfo(CallQuals q, Args&&... xargs)
109       : quals(q), arg_types(&makeArgumentID<Args&&...>()), args(std::forward<Args>(xargs)...)
110   {}
111 };
112 
113 template <class ...Args>
114 inline CallInfo<decltype(std::forward_as_tuple(std::declval<Args>()...))>
115 makeCallInfo(CallQuals quals, Args&&... args) {
116     return {quals, std::forward<Args>(args)...};
117 }
118 
119 struct TrackedCallable {
120 
121   TrackedCallable() = default;
122 
123   template <class ...Args> auto operator()(Args&&... xargs) &
124   { return makeCallInfo(CQ_LValue, std::forward<Args>(xargs)...); }
125 
126   template <class ...Args> auto operator()(Args&&... xargs) const&
127   { return makeCallInfo(CQ_ConstLValue, std::forward<Args>(xargs)...); }
128 
129   template <class ...Args> auto operator()(Args&&... xargs) &&
130   { return makeCallInfo(CQ_RValue, std::forward<Args>(xargs)...); }
131 
132   template <class ...Args> auto operator()(Args&&... xargs) const&&
133   { return makeCallInfo(CQ_ConstRValue, std::forward<Args>(xargs)...); }
134 };
135 
136 template <class ...ExpectArgs, class Tuple>
137 void check_apply_quals_and_types(Tuple&& t) {
138     TypeID const* const expect_args = &makeArgumentID<ExpectArgs...>();
139     TrackedCallable obj;
140     TrackedCallable const& cobj = obj;
141     {
142         auto ret = std::apply(obj, std::forward<Tuple>(t));
143         assert(ret.quals == CQ_LValue);
144         assert(ret.arg_types == expect_args);
145         assert(ret.args == t);
146     }
147     {
148         auto ret = std::apply(cobj, std::forward<Tuple>(t));
149         assert(ret.quals == CQ_ConstLValue);
150         assert(ret.arg_types == expect_args);
151         assert(ret.args == t);
152     }
153     {
154         auto ret = std::apply(std::move(obj), std::forward<Tuple>(t));
155         assert(ret.quals == CQ_RValue);
156         assert(ret.arg_types == expect_args);
157         assert(ret.args == t);
158     }
159     {
160         auto ret = std::apply(std::move(cobj), std::forward<Tuple>(t));
161         assert(ret.quals == CQ_ConstRValue);
162         assert(ret.arg_types == expect_args);
163         assert(ret.args == t);
164     }
165 }
166 
167 void test_call_quals_and_arg_types()
168 {
169     TrackedCallable obj;
170     using Tup = std::tuple<int, int const&, unsigned&&>;
171     const int x = 42;
172     unsigned y = 101;
173     Tup t(-1, x, std::move(y));
174     Tup const& ct = t;
175     check_apply_quals_and_types<int&, int const&, unsigned&>(t);
176     check_apply_quals_and_types<int const&, int const&, unsigned&>(ct);
177     check_apply_quals_and_types<int&&, int const&, unsigned&&>(std::move(t));
178     check_apply_quals_and_types<int const&&, int const&, unsigned&&>(std::move(ct));
179 }
180 
181 
182 struct NothrowMoveable {
183   NothrowMoveable() noexcept = default;
184   NothrowMoveable(NothrowMoveable const&) noexcept(false) {}
185   NothrowMoveable(NothrowMoveable&&) noexcept {}
186 };
187 
188 template <bool IsNoexcept>
189 struct TestNoexceptCallable {
190   template <class ...Args>
191   NothrowMoveable operator()(Args...) const noexcept(IsNoexcept) { return {}; }
192 };
193 
194 void test_noexcept()
195 {
196     TestNoexceptCallable<true> nec;
197     TestNoexceptCallable<false> tc;
198     {
199         // test that the functions noexcept-ness is propagated
200         using Tup = std::tuple<int, const char*, long>;
201         Tup t;
202         ASSERT_NOEXCEPT(std::apply(nec, t));
203         ASSERT_NOT_NOEXCEPT(std::apply(tc, t));
204     }
205     {
206         // test that the noexcept-ness of the argument conversions is checked.
207         using Tup = std::tuple<NothrowMoveable, int>;
208         Tup t;
209         ASSERT_NOT_NOEXCEPT(std::apply(nec, t));
210         ASSERT_NOEXCEPT(std::apply(nec, std::move(t)));
211     }
212 }
213 
214 namespace ReturnTypeTest {
215     static int my_int = 42;
216 
217     template <int N> struct index {};
218 
219     void f(index<0>) {}
220 
221     int f(index<1>) { return 0; }
222 
223     int & f(index<2>) { return static_cast<int &>(my_int); }
224     int const & f(index<3>) { return static_cast<int const &>(my_int); }
225     int volatile & f(index<4>) { return static_cast<int volatile &>(my_int); }
226     int const volatile & f(index<5>) { return static_cast<int const volatile &>(my_int); }
227 
228     int && f(index<6>) { return static_cast<int &&>(my_int); }
229     int const && f(index<7>) { return static_cast<int const &&>(my_int); }
230     int volatile && f(index<8>) { return static_cast<int volatile &&>(my_int); }
231     int const volatile && f(index<9>) { return static_cast<int const volatile &&>(my_int); }
232 
233     int * f(index<10>) { return static_cast<int *>(&my_int); }
234     int const * f(index<11>) { return static_cast<int const *>(&my_int); }
235     int volatile * f(index<12>) { return static_cast<int volatile *>(&my_int); }
236     int const volatile * f(index<13>) { return static_cast<int const volatile *>(&my_int); }
237 
238     template <int Func, class Expect>
239     void test()
240     {
241         using RawInvokeResult = decltype(f(index<Func>{}));
242         static_assert(std::is_same<RawInvokeResult, Expect>::value, "");
243         using FnType = RawInvokeResult (*) (index<Func>);
244         FnType fn = f;
245         std::tuple<index<Func>> t; ((void)t);
246         using InvokeResult = decltype(std::apply(fn, t));
247         static_assert(std::is_same<InvokeResult, Expect>::value, "");
248     }
249 } // end namespace ReturnTypeTest
250 
251 void test_return_type()
252 {
253     using ReturnTypeTest::test;
254     test<0, void>();
255     test<1, int>();
256     test<2, int &>();
257     test<3, int const &>();
258     test<4, int volatile &>();
259     test<5, int const volatile &>();
260     test<6, int &&>();
261     test<7, int const &&>();
262     test<8, int volatile &&>();
263     test<9, int const volatile &&>();
264     test<10, int *>();
265     test<11, int const *>();
266     test<12, int volatile *>();
267     test<13, int const volatile *>();
268 }
269 
270 int main() {
271     test_constexpr_evaluation();
272     test_call_quals_and_arg_types();
273     test_return_type();
274     test_noexcept();
275 }
276