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 | // type_traits |
12 | |
13 | // is_nothrow_invocable |
14 | |
15 | #include <type_traits> |
16 | #include <functional> |
17 | #include <vector> |
18 | |
19 | #include "test_macros.h" |
20 | |
21 | struct Tag {}; |
22 | |
23 | struct Implicit { |
24 | Implicit(int) noexcept {} |
25 | }; |
26 | |
27 | struct ThrowsImplicit { |
28 | ThrowsImplicit(int) {} |
29 | }; |
30 | |
31 | struct Explicit { |
32 | explicit Explicit(int) noexcept {} |
33 | }; |
34 | |
35 | template <bool IsNoexcept, class Ret, class... Args> |
36 | struct CallObject { |
37 | Ret operator()(Args&&...) const noexcept(IsNoexcept); |
38 | }; |
39 | |
40 | struct Sink { |
41 | template <class... Args> |
42 | void operator()(Args&&...) const noexcept {} |
43 | }; |
44 | |
45 | template <class Fn, class... Args> |
46 | constexpr bool throws_invocable() { |
47 | return std::is_invocable<Fn, Args...>::value && |
48 | !std::is_nothrow_invocable<Fn, Args...>::value; |
49 | } |
50 | |
51 | template <class Ret, class Fn, class... Args> |
52 | constexpr bool throws_invocable_r() { |
53 | return std::is_invocable_r<Ret, Fn, Args...>::value && |
54 | !std::is_nothrow_invocable_r<Ret, Fn, Args...>::value; |
55 | } |
56 | |
57 | void test_noexcept_function_pointers() { |
58 | struct Dummy { |
59 | void foo() noexcept {} |
60 | static void bar() noexcept {} |
61 | }; |
62 | // Check that PMF's and function pointers actually work and that |
63 | // is_nothrow_invocable returns true for noexcept PMF's and function |
64 | // pointers. |
65 | static_assert(std::is_nothrow_invocable<decltype(&Dummy::foo), Dummy&>::value, "" ); |
66 | static_assert(std::is_nothrow_invocable<decltype(&Dummy::bar)>::value, "" ); |
67 | } |
68 | |
69 | int main(int, char**) { |
70 | using AbominableFunc = void(...) const noexcept; |
71 | // Non-callable things |
72 | { |
73 | static_assert(!std::is_nothrow_invocable<void>::value, "" ); |
74 | static_assert(!std::is_nothrow_invocable<const void>::value, "" ); |
75 | static_assert(!std::is_nothrow_invocable<volatile void>::value, "" ); |
76 | static_assert(!std::is_nothrow_invocable<const volatile void>::value, "" ); |
77 | static_assert(!std::is_nothrow_invocable<std::nullptr_t>::value, "" ); |
78 | static_assert(!std::is_nothrow_invocable<int>::value, "" ); |
79 | static_assert(!std::is_nothrow_invocable<double>::value, "" ); |
80 | |
81 | static_assert(!std::is_nothrow_invocable<int[]>::value, "" ); |
82 | static_assert(!std::is_nothrow_invocable<int[3]>::value, "" ); |
83 | |
84 | static_assert(!std::is_nothrow_invocable<int*>::value, "" ); |
85 | static_assert(!std::is_nothrow_invocable<const int*>::value, "" ); |
86 | static_assert(!std::is_nothrow_invocable<int const*>::value, "" ); |
87 | |
88 | static_assert(!std::is_nothrow_invocable<int&>::value, "" ); |
89 | static_assert(!std::is_nothrow_invocable<const int&>::value, "" ); |
90 | static_assert(!std::is_nothrow_invocable<int&&>::value, "" ); |
91 | |
92 | static_assert(!std::is_nothrow_invocable<int, std::vector<int> >::value, |
93 | "" ); |
94 | static_assert(!std::is_nothrow_invocable<int, std::vector<int*> >::value, |
95 | "" ); |
96 | static_assert(!std::is_nothrow_invocable<int, std::vector<int**> >::value, |
97 | "" ); |
98 | |
99 | static_assert(!std::is_nothrow_invocable<AbominableFunc>::value, "" ); |
100 | |
101 | // with parameters |
102 | static_assert(!std::is_nothrow_invocable<int, int>::value, "" ); |
103 | static_assert(!std::is_nothrow_invocable<int, double, float>::value, "" ); |
104 | static_assert(!std::is_nothrow_invocable<int, char, float, double>::value, |
105 | "" ); |
106 | static_assert(!std::is_nothrow_invocable<Sink, AbominableFunc>::value, "" ); |
107 | static_assert(!std::is_nothrow_invocable<Sink, void>::value, "" ); |
108 | static_assert(!std::is_nothrow_invocable<Sink, const volatile void>::value, |
109 | "" ); |
110 | |
111 | static_assert(!std::is_nothrow_invocable_r<int, void>::value, "" ); |
112 | static_assert(!std::is_nothrow_invocable_r<int, const void>::value, "" ); |
113 | static_assert(!std::is_nothrow_invocable_r<int, volatile void>::value, "" ); |
114 | static_assert(!std::is_nothrow_invocable_r<int, const volatile void>::value, |
115 | "" ); |
116 | static_assert(!std::is_nothrow_invocable_r<int, std::nullptr_t>::value, "" ); |
117 | static_assert(!std::is_nothrow_invocable_r<int, int>::value, "" ); |
118 | static_assert(!std::is_nothrow_invocable_r<int, double>::value, "" ); |
119 | |
120 | static_assert(!std::is_nothrow_invocable_r<int, int[]>::value, "" ); |
121 | static_assert(!std::is_nothrow_invocable_r<int, int[3]>::value, "" ); |
122 | |
123 | static_assert(!std::is_nothrow_invocable_r<int, int*>::value, "" ); |
124 | static_assert(!std::is_nothrow_invocable_r<int, const int*>::value, "" ); |
125 | static_assert(!std::is_nothrow_invocable_r<int, int const*>::value, "" ); |
126 | |
127 | static_assert(!std::is_nothrow_invocable_r<int, int&>::value, "" ); |
128 | static_assert(!std::is_nothrow_invocable_r<int, const int&>::value, "" ); |
129 | static_assert(!std::is_nothrow_invocable_r<int, int&&>::value, "" ); |
130 | |
131 | static_assert(!std::is_nothrow_invocable_r<int, std::vector<int> >::value, |
132 | "" ); |
133 | static_assert(!std::is_nothrow_invocable_r<int, std::vector<int*> >::value, |
134 | "" ); |
135 | static_assert(!std::is_nothrow_invocable_r<int, std::vector<int**> >::value, |
136 | "" ); |
137 | static_assert(!std::is_nothrow_invocable_r<void, AbominableFunc>::value, |
138 | "" ); |
139 | |
140 | // with parameters |
141 | static_assert(!std::is_nothrow_invocable_r<int, int, int>::value, "" ); |
142 | static_assert(!std::is_nothrow_invocable_r<int, int, double, float>::value, |
143 | "" ); |
144 | static_assert( |
145 | !std::is_nothrow_invocable_r<int, int, char, float, double>::value, "" ); |
146 | static_assert( |
147 | !std::is_nothrow_invocable_r<void, Sink, AbominableFunc>::value, "" ); |
148 | static_assert(!std::is_nothrow_invocable_r<void, Sink, void>::value, "" ); |
149 | static_assert( |
150 | !std::is_nothrow_invocable_r<void, Sink, const volatile void>::value, |
151 | "" ); |
152 | } |
153 | |
154 | { |
155 | // Check that the conversion to the return type is properly checked |
156 | using Fn = CallObject<true, int>; |
157 | static_assert(std::is_nothrow_invocable_r<Implicit, Fn>::value, "" ); |
158 | static_assert(std::is_nothrow_invocable_r<double, Fn>::value, "" ); |
159 | static_assert(std::is_nothrow_invocable_r<const volatile void, Fn>::value, |
160 | "" ); |
161 | static_assert(throws_invocable_r<ThrowsImplicit, Fn>(), "" ); |
162 | static_assert(!std::is_nothrow_invocable<Fn(), Explicit>(), "" ); |
163 | } |
164 | { |
165 | // Check that the conversion to the parameters is properly checked |
166 | using Fn = CallObject<true, void, const Implicit&, const ThrowsImplicit&>; |
167 | static_assert( |
168 | std::is_nothrow_invocable<Fn, Implicit&, ThrowsImplicit&>::value, "" ); |
169 | static_assert(std::is_nothrow_invocable<Fn, int, ThrowsImplicit&>::value, |
170 | "" ); |
171 | static_assert(throws_invocable<Fn, int, int>(), "" ); |
172 | static_assert(!std::is_nothrow_invocable<Fn>::value, "" ); |
173 | } |
174 | { |
175 | // Check that the noexcept-ness of function objects is checked. |
176 | using Fn = CallObject<true, void>; |
177 | using Fn2 = CallObject<false, void>; |
178 | static_assert(std::is_nothrow_invocable<Fn>::value, "" ); |
179 | static_assert(throws_invocable<Fn2>(), "" ); |
180 | } |
181 | { |
182 | // Check that PMD derefs are noexcept |
183 | using Fn = int(Tag::*); |
184 | static_assert(std::is_nothrow_invocable<Fn, Tag&>::value, "" ); |
185 | static_assert(std::is_nothrow_invocable_r<Implicit, Fn, Tag&>::value, "" ); |
186 | static_assert(throws_invocable_r<ThrowsImplicit, Fn, Tag&>(), "" ); |
187 | } |
188 | { |
189 | // Check that it's fine if the result type is non-moveable. |
190 | struct CantMove { |
191 | CantMove() = default; |
192 | CantMove(CantMove&&) = delete; |
193 | }; |
194 | |
195 | static_assert(!std::is_move_constructible_v<CantMove>); |
196 | static_assert(!std::is_copy_constructible_v<CantMove>); |
197 | |
198 | using Fn = CantMove() noexcept; |
199 | |
200 | static_assert(std::is_nothrow_invocable_r<CantMove, Fn>::value); |
201 | static_assert(!std::is_nothrow_invocable_r<CantMove, Fn, int>::value); |
202 | |
203 | static_assert(std::is_nothrow_invocable_r_v<CantMove, Fn>); |
204 | static_assert(!std::is_nothrow_invocable_r_v<CantMove, Fn, int>); |
205 | } |
206 | { |
207 | // Check for is_nothrow_invocable_v |
208 | using Fn = CallObject<true, int>; |
209 | static_assert(std::is_nothrow_invocable_v<Fn>, "" ); |
210 | static_assert(!std::is_nothrow_invocable_v<Fn, int>, "" ); |
211 | } |
212 | { |
213 | // Check for is_nothrow_invocable_r_v |
214 | using Fn = CallObject<true, int>; |
215 | static_assert(std::is_nothrow_invocable_r_v<void, Fn>, "" ); |
216 | static_assert(!std::is_nothrow_invocable_r_v<int, Fn, int>, "" ); |
217 | } |
218 | test_noexcept_function_pointers(); |
219 | |
220 | return 0; |
221 | } |
222 | |