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