| 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 &&) noexcept(see below) // noexcept since C++23 |
| 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 | constexpr int constexpr_sum_fn() { return 0; } |
| 26 | |
| 27 | template <class ...Ints> |
| 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> |
| 33 | constexpr int operator()(Ints... values) const { |
| 34 | return constexpr_sum_fn(values...); |
| 35 | } |
| 36 | }; |
| 37 | |
| 38 | |
| 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(f&: static_cast<Fn>(constexpr_sum_fn), t: t) == 0, "" ); |
| 47 | static_assert(std::apply(f: sum_obj, t: 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(f&: static_cast<Fn>(constexpr_sum_fn), t: t) == 42, "" ); |
| 54 | static_assert(std::apply(f: sum_obj, t: 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(f&: static_cast<Fn>(constexpr_sum_fn), t: t) == 143, "" ); |
| 61 | static_assert(std::apply(f: sum_obj, t: 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(f&: static_cast<Fn>(constexpr_sum_fn), t: t) == 143, "" ); |
| 68 | static_assert(std::apply(f: sum_obj, t: 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(f&: static_cast<Fn>(constexpr_sum_fn), t: t) == 142, "" ); |
| 75 | static_assert(std::apply(f: sum_obj, t: 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(f&: static_cast<Fn>(constexpr_sum_fn), t: t) == 142, "" ); |
| 82 | static_assert(std::apply(f: sum_obj, t: 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> |
| 102 | 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>()...))> |
| 109 | makeCallInfo(CallQuals quals, Args&&... args) { |
| 110 | return {quals, std::forward<Args>(args)...}; |
| 111 | } |
| 112 | |
| 113 | struct TrackedCallable { |
| 114 | |
| 115 | TrackedCallable() = default; |
| 116 | |
| 117 | template <class ...Args> auto operator()(Args&&... xargs) & |
| 118 | { return makeCallInfo(CQ_LValue, std::forward<Args>(xargs)...); } |
| 119 | |
| 120 | template <class ...Args> auto operator()(Args&&... xargs) const& |
| 121 | { return makeCallInfo(CQ_ConstLValue, std::forward<Args>(xargs)...); } |
| 122 | |
| 123 | template <class ...Args> auto operator()(Args&&... xargs) && |
| 124 | { return makeCallInfo(CQ_RValue, std::forward<Args>(xargs)...); } |
| 125 | |
| 126 | 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> |
| 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 | |
| 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&>(t: ct); |
| 170 | check_apply_quals_and_types<int&&, int const&, unsigned&&>(t: std::move(t)); |
| 171 | check_apply_quals_and_types<int const&&, int const&, unsigned&&>(t: std::move(ct)); |
| 172 | } |
| 173 | |
| 174 | |
| 175 | struct NothrowMoveable { |
| 176 | NothrowMoveable() noexcept = default; |
| 177 | NothrowMoveable(NothrowMoveable const&) noexcept(false) {} |
| 178 | NothrowMoveable(NothrowMoveable&&) noexcept {} |
| 179 | }; |
| 180 | |
| 181 | template <bool IsNoexcept> |
| 182 | struct TestNoexceptCallable { |
| 183 | template <class ...Args> |
| 184 | NothrowMoveable operator()(Args...) const noexcept(IsNoexcept) { return {}; } |
| 185 | }; |
| 186 | |
| 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 | #if TEST_STD_VER >= 23 |
| 196 | ASSERT_NOEXCEPT(std::apply(nec, t)); |
| 197 | #else |
| 198 | LIBCPP_ASSERT_NOEXCEPT(std::apply(f&: nec, t&: t)); |
| 199 | #endif |
| 200 | ASSERT_NOT_NOEXCEPT(std::apply(f&: tc, t&: t)); |
| 201 | } |
| 202 | { |
| 203 | // test that the noexcept-ness of the argument conversions is checked. |
| 204 | using Tup = std::tuple<NothrowMoveable, int>; |
| 205 | Tup t; |
| 206 | ASSERT_NOT_NOEXCEPT(std::apply(f&: nec, t&: t)); |
| 207 | #if TEST_STD_VER >= 23 |
| 208 | ASSERT_NOEXCEPT(std::apply(nec, std::move(t))); |
| 209 | #else |
| 210 | LIBCPP_ASSERT_NOEXCEPT(std::apply(f&: nec, t: std::move(t))); |
| 211 | #endif |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | namespace ReturnTypeTest { |
| 216 | static int my_int = 42; |
| 217 | |
| 218 | template <int N> struct index {}; |
| 219 | |
| 220 | void f(index<0>) {} |
| 221 | |
| 222 | int f(index<1>) { return 0; } |
| 223 | |
| 224 | int & f(index<2>) { return static_cast<int &>(my_int); } |
| 225 | int const & f(index<3>) { return static_cast<int const &>(my_int); } |
| 226 | int volatile & f(index<4>) { return static_cast<int volatile &>(my_int); } |
| 227 | int const volatile & f(index<5>) { return static_cast<int const volatile &>(my_int); } |
| 228 | |
| 229 | int && f(index<6>) { return static_cast<int &&>(my_int); } |
| 230 | int const && f(index<7>) { return static_cast<int const &&>(my_int); } |
| 231 | int volatile && f(index<8>) { return static_cast<int volatile &&>(my_int); } |
| 232 | int const volatile && f(index<9>) { return static_cast<int const volatile &&>(my_int); } |
| 233 | |
| 234 | int * f(index<10>) { return static_cast<int *>(&my_int); } |
| 235 | int const * f(index<11>) { return static_cast<int const *>(&my_int); } |
| 236 | int volatile * f(index<12>) { return static_cast<int volatile *>(&my_int); } |
| 237 | int const volatile * f(index<13>) { return static_cast<int const volatile *>(&my_int); } |
| 238 | |
| 239 | template <int Func, class Expect> |
| 240 | void test() |
| 241 | { |
| 242 | using RawInvokeResult = decltype(f(index<Func>{})); |
| 243 | static_assert(std::is_same<RawInvokeResult, Expect>::value, "" ); |
| 244 | using FnType = RawInvokeResult (*) (index<Func>); |
| 245 | FnType fn = f; |
| 246 | std::tuple<index<Func>> t; ((void)t); |
| 247 | using InvokeResult = decltype(std::apply(fn, t)); |
| 248 | static_assert(std::is_same<InvokeResult, Expect>::value, "" ); |
| 249 | } |
| 250 | } // namespace ReturnTypeTest |
| 251 | |
| 252 | void test_return_type() |
| 253 | { |
| 254 | using ReturnTypeTest::test; |
| 255 | test<0, void>(); |
| 256 | test<1, int>(); |
| 257 | test<2, int &>(); |
| 258 | test<3, int const &>(); |
| 259 | test<4, int volatile &>(); |
| 260 | test<5, int const volatile &>(); |
| 261 | test<6, int &&>(); |
| 262 | test<7, int const &&>(); |
| 263 | test<8, int volatile &&>(); |
| 264 | test<9, int const volatile &&>(); |
| 265 | test<10, int *>(); |
| 266 | test<11, int const *>(); |
| 267 | test<12, int volatile *>(); |
| 268 | test<13, int const volatile *>(); |
| 269 | } |
| 270 | |
| 271 | int main(int, char**) { |
| 272 | test_constexpr_evaluation(); |
| 273 | test_call_quals_and_arg_types(); |
| 274 | test_return_type(); |
| 275 | test_noexcept(); |
| 276 | |
| 277 | return 0; |
| 278 | } |
| 279 | |