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 30 constexpr int constexpr_sum_fn() { return 0; } 31 32 template <class ...Ints> 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> 38 constexpr int operator()(Ints... values) const { 39 return constexpr_sum_fn(values...); 40 } 41 }; 42 43 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> 107 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>()...))> 114 makeCallInfo(CallQuals quals, Args&&... args) { 115 return {quals, std::forward<Args>(args)...}; 116 } 117 118 struct TrackedCallable { 119 120 TrackedCallable() = default; 121 122 template <class ...Args> auto operator()(Args&&... xargs) & 123 { return makeCallInfo(CQ_LValue, std::forward<Args>(xargs)...); } 124 125 template <class ...Args> auto operator()(Args&&... xargs) const& 126 { return makeCallInfo(CQ_ConstLValue, std::forward<Args>(xargs)...); } 127 128 template <class ...Args> auto operator()(Args&&... xargs) && 129 { return makeCallInfo(CQ_RValue, std::forward<Args>(xargs)...); } 130 131 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> 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 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; 182 NothrowMoveable(NothrowMoveable const&) noexcept(false) {} 183 NothrowMoveable(NothrowMoveable&&) noexcept {} 184 }; 185 186 template <bool IsNoexcept> 187 struct TestNoexceptCallable { 188 template <class ...Args> 189 NothrowMoveable operator()(Args...) const noexcept(IsNoexcept) { return {}; } 190 }; 191 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 217 void f(index<0>) {} 218 219 int f(index<1>) { return 0; } 220 221 int & f(index<2>) { return static_cast<int &>(my_int); } 222 int const & f(index<3>) { return static_cast<int const &>(my_int); } 223 int volatile & f(index<4>) { return static_cast<int volatile &>(my_int); } 224 int const volatile & f(index<5>) { return static_cast<int const volatile &>(my_int); } 225 226 int && f(index<6>) { return static_cast<int &&>(my_int); } 227 int const && f(index<7>) { return static_cast<int const &&>(my_int); } 228 int volatile && f(index<8>) { return static_cast<int volatile &&>(my_int); } 229 int const volatile && f(index<9>) { return static_cast<int const volatile &&>(my_int); } 230 231 int * f(index<10>) { return static_cast<int *>(&my_int); } 232 int const * f(index<11>) { return static_cast<int const *>(&my_int); } 233 int volatile * f(index<12>) { return static_cast<int volatile *>(&my_int); } 234 int const volatile * f(index<13>) { return static_cast<int const volatile *>(&my_int); } 235 236 template <int Func, class Expect> 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 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 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