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(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> 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&>(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; 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(nec, t)); 199 #endif 200 ASSERT_NOT_NOEXCEPT(std::apply(tc, 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(nec, t)); 207 #if TEST_STD_VER >= 23 208 ASSERT_NOEXCEPT(std::apply(nec, std::move(t))); 209 #else 210 LIBCPP_ASSERT_NOEXCEPT(std::apply(nec, 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