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
31constexpr int constexpr_sum_fn() { return 0; }
32
33template <class ...Ints>
34constexpr int constexpr_sum_fn(int x1, Ints... rest) { return x1 + constexpr_sum_fn(rest...); }
35
36struct 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
45void 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
93enum CallQuals {
94  CQ_None,
95  CQ_LValue,
96  CQ_ConstLValue,
97  CQ_RValue,
98  CQ_ConstRValue
99};
100
101template <class Tuple>
102struct 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
113template <class ...Args>
114inline CallInfo<decltype(std::forward_as_tuple(std::declval<Args>()...))>
115makeCallInfo(CallQuals quals, Args&&... args) {
116    return {quals, std::forward<Args>(args)...};
117}
118
119struct 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
136template <class ...ExpectArgs, class Tuple>
137void 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
167void 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
181struct NothrowMoveable {
182  NothrowMoveable() noexcept = default;
183  NothrowMoveable(NothrowMoveable const&) noexcept(false) {}
184  NothrowMoveable(NothrowMoveable&&) noexcept {}
185};
186
187template <bool IsNoexcept>
188struct TestNoexceptCallable {
189  template <class ...Args>
190  NothrowMoveable operator()(Args...) const noexcept(IsNoexcept) { return {}; }
191};
192
193void 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
213namespace ReturnTypeTest {
214    static int my_int = 42;
215
216    template <int N> struct index {};
217
218    void f(index<0>) {}
219
220    int f(index<1>) { return 0; }
221
222    int & f(index<2>) { return static_cast<int &>(my_int); }
223    int const & f(index<3>) { return static_cast<int const &>(my_int); }
224    int volatile & f(index<4>) { return static_cast<int volatile &>(my_int); }
225    int const volatile & f(index<5>) { return static_cast<int const volatile &>(my_int); }
226
227    int && f(index<6>) { return static_cast<int &&>(my_int); }
228    int const && f(index<7>) { return static_cast<int const &&>(my_int); }
229    int volatile && f(index<8>) { return static_cast<int volatile &&>(my_int); }
230    int const volatile && f(index<9>) { return static_cast<int const volatile &&>(my_int); }
231
232    int * f(index<10>) { return static_cast<int *>(&my_int); }
233    int const * f(index<11>) { return static_cast<int const *>(&my_int); }
234    int volatile * f(index<12>) { return static_cast<int volatile *>(&my_int); }
235    int const volatile * f(index<13>) { return static_cast<int const volatile *>(&my_int); }
236
237    template <int Func, class Expect>
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
250void 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
269int main() {
270    test_constexpr_evaluation();
271    test_call_quals_and_arg_types();
272    test_return_type();
273    test_noexcept();
274}
275