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 
constexpr_sum_fn()25 constexpr int constexpr_sum_fn() { return 0; }
26 
27 template <class ...Ints>
constexpr_sum_fn(int x1,Ints...rest)28 constexpr int constexpr_sum_fn(int x1, Ints... rest) { return x1 + constexpr_sum_fn(rest...); }
29 
30 struct ConstexprSumT {
31   constexpr ConstexprSumT() = default;
32   template <class ...Ints>
operator ()ConstexprSumT33   constexpr int operator()(Ints... values) const {
34       return constexpr_sum_fn(values...);
35   }
36 };
37 
38 
test_constexpr_evaluation()39 void test_constexpr_evaluation()
40 {
41     constexpr ConstexprSumT sum_obj{};
42     {
43         using Tup = std::tuple<>;
44         using Fn = int(&)();
45         constexpr Tup t;
46         static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 0, "");
47         static_assert(std::apply(sum_obj, t) == 0, "");
48     }
49     {
50         using Tup = std::tuple<int>;
51         using Fn = int(&)(int);
52         constexpr Tup t(42);
53         static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 42, "");
54         static_assert(std::apply(sum_obj, t) == 42, "");
55     }
56     {
57         using Tup = std::tuple<int, long>;
58         using Fn = int(&)(int, int);
59         constexpr Tup t(42, 101);
60         static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 143, "");
61         static_assert(std::apply(sum_obj, t) == 143, "");
62     }
63     {
64         using Tup = std::pair<int, long>;
65         using Fn = int(&)(int, int);
66         constexpr Tup t(42, 101);
67         static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 143, "");
68         static_assert(std::apply(sum_obj, t) == 143, "");
69     }
70     {
71         using Tup = std::tuple<int, long, int>;
72         using Fn = int(&)(int, int, int);
73         constexpr Tup t(42, 101, -1);
74         static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 142, "");
75         static_assert(std::apply(sum_obj, t) == 142, "");
76     }
77     {
78         using Tup = std::array<int, 3>;
79         using Fn = int(&)(int, int, int);
80         constexpr Tup t = {42, 101, -1};
81         static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 142, "");
82         static_assert(std::apply(sum_obj, t) == 142, "");
83     }
84 }
85 
86 
87 enum CallQuals {
88   CQ_None,
89   CQ_LValue,
90   CQ_ConstLValue,
91   CQ_RValue,
92   CQ_ConstRValue
93 };
94 
95 template <class Tuple>
96 struct CallInfo {
97   CallQuals quals;
98   TypeID const* arg_types;
99   Tuple args;
100 
101   template <class ...Args>
CallInfoCallInfo102   CallInfo(CallQuals q, Args&&... xargs)
103       : quals(q), arg_types(&makeArgumentID<Args&&...>()), args(std::forward<Args>(xargs)...)
104   {}
105 };
106 
107 template <class ...Args>
108 inline CallInfo<decltype(std::forward_as_tuple(std::declval<Args>()...))>
makeCallInfo(CallQuals quals,Args &&...args)109 makeCallInfo(CallQuals quals, Args&&... args) {
110     return {quals, std::forward<Args>(args)...};
111 }
112 
113 struct TrackedCallable {
114 
115   TrackedCallable() = default;
116 
operator ()TrackedCallable117   template <class ...Args> auto operator()(Args&&... xargs) &
118   { return makeCallInfo(CQ_LValue, std::forward<Args>(xargs)...); }
119 
operator ()TrackedCallable120   template <class ...Args> auto operator()(Args&&... xargs) const&
121   { return makeCallInfo(CQ_ConstLValue, std::forward<Args>(xargs)...); }
122 
operator ()TrackedCallable123   template <class ...Args> auto operator()(Args&&... xargs) &&
124   { return makeCallInfo(CQ_RValue, std::forward<Args>(xargs)...); }
125 
operator ()TrackedCallable126   template <class ...Args> auto operator()(Args&&... xargs) const&&
127   { return makeCallInfo(CQ_ConstRValue, std::forward<Args>(xargs)...); }
128 };
129 
130 template <class ...ExpectArgs, class Tuple>
check_apply_quals_and_types(Tuple && t)131 void check_apply_quals_and_types(Tuple&& t) {
132     TypeID const* const expect_args = &makeArgumentID<ExpectArgs...>();
133     TrackedCallable obj;
134     TrackedCallable const& cobj = obj;
135     {
136         auto ret = std::apply(obj, std::forward<Tuple>(t));
137         assert(ret.quals == CQ_LValue);
138         assert(ret.arg_types == expect_args);
139         assert(ret.args == t);
140     }
141     {
142         auto ret = std::apply(cobj, std::forward<Tuple>(t));
143         assert(ret.quals == CQ_ConstLValue);
144         assert(ret.arg_types == expect_args);
145         assert(ret.args == t);
146     }
147     {
148         auto ret = std::apply(std::move(obj), std::forward<Tuple>(t));
149         assert(ret.quals == CQ_RValue);
150         assert(ret.arg_types == expect_args);
151         assert(ret.args == t);
152     }
153     {
154         auto ret = std::apply(std::move(cobj), std::forward<Tuple>(t));
155         assert(ret.quals == CQ_ConstRValue);
156         assert(ret.arg_types == expect_args);
157         assert(ret.args == t);
158     }
159 }
160 
test_call_quals_and_arg_types()161 void test_call_quals_and_arg_types()
162 {
163     using Tup = std::tuple<int, int const&, unsigned&&>;
164     const int x = 42;
165     unsigned y = 101;
166     Tup t(-1, x, std::move(y));
167     Tup const& ct = t;
168     check_apply_quals_and_types<int&, int const&, unsigned&>(t);
169     check_apply_quals_and_types<int const&, int const&, unsigned&>(ct);
170     check_apply_quals_and_types<int&&, int const&, unsigned&&>(std::move(t));
171     check_apply_quals_and_types<int const&&, int const&, unsigned&&>(std::move(ct));
172 }
173 
174 
175 struct NothrowMoveable {
176   NothrowMoveable() noexcept = default;
NothrowMoveableNothrowMoveable177   NothrowMoveable(NothrowMoveable const&) noexcept(false) {}
NothrowMoveableNothrowMoveable178   NothrowMoveable(NothrowMoveable&&) noexcept {}
179 };
180 
181 template <bool IsNoexcept>
182 struct TestNoexceptCallable {
183   template <class ...Args>
operator ()TestNoexceptCallable184   NothrowMoveable operator()(Args...) const noexcept(IsNoexcept) { return {}; }
185 };
186 
test_noexcept()187 void test_noexcept()
188 {
189     TestNoexceptCallable<true> nec;
190     TestNoexceptCallable<false> tc;
191     {
192         // test that the functions noexcept-ness is propagated
193         using Tup = std::tuple<int, const char*, long>;
194         Tup t;
195         LIBCPP_ASSERT_NOEXCEPT(std::apply(nec, t));
196         ASSERT_NOT_NOEXCEPT(std::apply(tc, t));
197     }
198     {
199         // test that the noexcept-ness of the argument conversions is checked.
200         using Tup = std::tuple<NothrowMoveable, int>;
201         Tup t;
202         ASSERT_NOT_NOEXCEPT(std::apply(nec, t));
203         LIBCPP_ASSERT_NOEXCEPT(std::apply(nec, std::move(t)));
204     }
205 }
206 
207 namespace ReturnTypeTest {
208     static int my_int = 42;
209 
210     template <int N> struct index {};
211 
f(index<0>)212     void f(index<0>) {}
213 
f(index<1>)214     int f(index<1>) { return 0; }
215 
f(index<2>)216     int & f(index<2>) { return static_cast<int &>(my_int); }
f(index<3>)217     int const & f(index<3>) { return static_cast<int const &>(my_int); }
f(index<4>)218     int volatile & f(index<4>) { return static_cast<int volatile &>(my_int); }
f(index<5>)219     int const volatile & f(index<5>) { return static_cast<int const volatile &>(my_int); }
220 
f(index<6>)221     int && f(index<6>) { return static_cast<int &&>(my_int); }
f(index<7>)222     int const && f(index<7>) { return static_cast<int const &&>(my_int); }
f(index<8>)223     int volatile && f(index<8>) { return static_cast<int volatile &&>(my_int); }
f(index<9>)224     int const volatile && f(index<9>) { return static_cast<int const volatile &&>(my_int); }
225 
f(index<10>)226     int * f(index<10>) { return static_cast<int *>(&my_int); }
f(index<11>)227     int const * f(index<11>) { return static_cast<int const *>(&my_int); }
f(index<12>)228     int volatile * f(index<12>) { return static_cast<int volatile *>(&my_int); }
f(index<13>)229     int const volatile * f(index<13>) { return static_cast<int const volatile *>(&my_int); }
230 
231     template <int Func, class Expect>
test()232     void test()
233     {
234         using RawInvokeResult = decltype(f(index<Func>{}));
235         static_assert(std::is_same<RawInvokeResult, Expect>::value, "");
236         using FnType = RawInvokeResult (*) (index<Func>);
237         FnType fn = f;
238         std::tuple<index<Func>> t; ((void)t);
239         using InvokeResult = decltype(std::apply(fn, t));
240         static_assert(std::is_same<InvokeResult, Expect>::value, "");
241     }
242 } // end namespace ReturnTypeTest
243 
test_return_type()244 void test_return_type()
245 {
246     using ReturnTypeTest::test;
247     test<0, void>();
248     test<1, int>();
249     test<2, int &>();
250     test<3, int const &>();
251     test<4, int volatile &>();
252     test<5, int const volatile &>();
253     test<6, int &&>();
254     test<7, int const &&>();
255     test<8, int volatile &&>();
256     test<9, int const volatile &&>();
257     test<10, int *>();
258     test<11, int const *>();
259     test<12, int volatile *>();
260     test<13, int const volatile *>();
261 }
262 
main(int,char **)263 int main(int, char**) {
264     test_constexpr_evaluation();
265     test_call_quals_and_arg_types();
266     test_return_type();
267     test_noexcept();
268 
269   return 0;
270 }
271