• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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