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
constexpr_sum_fn()31 constexpr int constexpr_sum_fn() { return 0; }
32
33 template <class ...Ints>
constexpr_sum_fn(int x1,Ints...rest)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>
operator ()ConstexprSumT39 constexpr int operator()(Ints... values) const {
40 return constexpr_sum_fn(values...);
41 }
42 };
43
44
test_constexpr_evaluation()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>
CallInfoCallInfo108 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>()...))>
makeCallInfo(CallQuals quals,Args &&...args)115 makeCallInfo(CallQuals quals, Args&&... args) {
116 return {quals, std::forward<Args>(args)...};
117 }
118
119 struct TrackedCallable {
120
121 TrackedCallable() = default;
122
operator ()TrackedCallable123 template <class ...Args> auto operator()(Args&&... xargs) &
124 { return makeCallInfo(CQ_LValue, std::forward<Args>(xargs)...); }
125
operator ()TrackedCallable126 template <class ...Args> auto operator()(Args&&... xargs) const&
127 { return makeCallInfo(CQ_ConstLValue, std::forward<Args>(xargs)...); }
128
operator ()TrackedCallable129 template <class ...Args> auto operator()(Args&&... xargs) &&
130 { return makeCallInfo(CQ_RValue, std::forward<Args>(xargs)...); }
131
operator ()TrackedCallable132 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>
check_apply_quals_and_types(Tuple && t)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
test_call_quals_and_arg_types()167 void test_call_quals_and_arg_types()
168 {
169 using Tup = std::tuple<int, int const&, unsigned&&>;
170 const int x = 42;
171 unsigned y = 101;
172 Tup t(-1, x, std::move(y));
173 Tup const& ct = t;
174 check_apply_quals_and_types<int&, int const&, unsigned&>(t);
175 check_apply_quals_and_types<int const&, int const&, unsigned&>(ct);
176 check_apply_quals_and_types<int&&, int const&, unsigned&&>(std::move(t));
177 check_apply_quals_and_types<int const&&, int const&, unsigned&&>(std::move(ct));
178 }
179
180
181 struct NothrowMoveable {
182 NothrowMoveable() noexcept = default;
NothrowMoveableNothrowMoveable183 NothrowMoveable(NothrowMoveable const&) noexcept(false) {}
NothrowMoveableNothrowMoveable184 NothrowMoveable(NothrowMoveable&&) noexcept {}
185 };
186
187 template <bool IsNoexcept>
188 struct TestNoexceptCallable {
189 template <class ...Args>
operator ()TestNoexceptCallable190 NothrowMoveable operator()(Args...) const noexcept(IsNoexcept) { return {}; }
191 };
192
test_noexcept()193 void test_noexcept()
194 {
195 TestNoexceptCallable<true> nec;
196 TestNoexceptCallable<false> tc;
197 {
198 // test that the functions noexcept-ness is propagated
199 using Tup = std::tuple<int, const char*, long>;
200 Tup t;
201 LIBCPP_ASSERT_NOEXCEPT(std::apply(nec, t));
202 ASSERT_NOT_NOEXCEPT(std::apply(tc, t));
203 }
204 {
205 // test that the noexcept-ness of the argument conversions is checked.
206 using Tup = std::tuple<NothrowMoveable, int>;
207 Tup t;
208 ASSERT_NOT_NOEXCEPT(std::apply(nec, t));
209 LIBCPP_ASSERT_NOEXCEPT(std::apply(nec, std::move(t)));
210 }
211 }
212
213 namespace ReturnTypeTest {
214 static int my_int = 42;
215
216 template <int N> struct index {};
217
f(index<0>)218 void f(index<0>) {}
219
f(index<1>)220 int f(index<1>) { return 0; }
221
f(index<2>)222 int & f(index<2>) { return static_cast<int &>(my_int); }
f(index<3>)223 int const & f(index<3>) { return static_cast<int const &>(my_int); }
f(index<4>)224 int volatile & f(index<4>) { return static_cast<int volatile &>(my_int); }
f(index<5>)225 int const volatile & f(index<5>) { return static_cast<int const volatile &>(my_int); }
226
f(index<6>)227 int && f(index<6>) { return static_cast<int &&>(my_int); }
f(index<7>)228 int const && f(index<7>) { return static_cast<int const &&>(my_int); }
f(index<8>)229 int volatile && f(index<8>) { return static_cast<int volatile &&>(my_int); }
f(index<9>)230 int const volatile && f(index<9>) { return static_cast<int const volatile &&>(my_int); }
231
f(index<10>)232 int * f(index<10>) { return static_cast<int *>(&my_int); }
f(index<11>)233 int const * f(index<11>) { return static_cast<int const *>(&my_int); }
f(index<12>)234 int volatile * f(index<12>) { return static_cast<int volatile *>(&my_int); }
f(index<13>)235 int const volatile * f(index<13>) { return static_cast<int const volatile *>(&my_int); }
236
237 template <int Func, class Expect>
test()238 void test()
239 {
240 using RawInvokeResult = decltype(f(index<Func>{}));
241 static_assert(std::is_same<RawInvokeResult, Expect>::value, "");
242 using FnType = RawInvokeResult (*) (index<Func>);
243 FnType fn = f;
244 std::tuple<index<Func>> t; ((void)t);
245 using InvokeResult = decltype(std::apply(fn, t));
246 static_assert(std::is_same<InvokeResult, Expect>::value, "");
247 }
248 } // end namespace ReturnTypeTest
249
test_return_type()250 void test_return_type()
251 {
252 using ReturnTypeTest::test;
253 test<0, void>();
254 test<1, int>();
255 test<2, int &>();
256 test<3, int const &>();
257 test<4, int volatile &>();
258 test<5, int const volatile &>();
259 test<6, int &&>();
260 test<7, int const &&>();
261 test<8, int volatile &&>();
262 test<9, int const volatile &&>();
263 test<10, int *>();
264 test<11, int const *>();
265 test<12, int volatile *>();
266 test<13, int const volatile *>();
267 }
268
main()269 int main() {
270 test_constexpr_evaluation();
271 test_call_quals_and_arg_types();
272 test_return_type();
273 test_noexcept();
274 }
275