Mark constexpr values needed within non-implicitly-capturing lambdas as static
[folly.git] / folly / ExceptionWrapper-inl.h
1 /*
2  * Copyright 2017-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 /*
17  *
18  * Author: Eric Niebler <eniebler@fb.com>
19  */
20
21 namespace folly {
22
23 template <class Fn>
24 struct exception_wrapper::arg_type_
25     : public arg_type_<decltype(&Fn::operator())> {
26 };
27 template <class Ret, class Class, class Arg>
28 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg)> {
29   using type = Arg;
30 };
31 template <class Ret, class Class, class Arg>
32 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg) const> {
33   using type = Arg;
34 };
35 template <class Ret, class Arg>
36 struct exception_wrapper::arg_type_<Ret (Arg)> {
37   using type = Arg;
38 };
39 template <class Ret, class Arg>
40 struct exception_wrapper::arg_type_<Ret (*)(Arg)> {
41   using type = Arg;
42 };
43 template <class Ret, class Class>
44 struct exception_wrapper::arg_type_<Ret (Class::*)(...)> {
45   using type = AnyException;
46 };
47 template <class Ret, class Class>
48 struct exception_wrapper::arg_type_<Ret (Class::*)(...) const> {
49   using type = AnyException;
50 };
51 template <class Ret>
52 struct exception_wrapper::arg_type_<Ret (...)> {
53   using type = AnyException;
54 };
55 template <class Ret>
56 struct exception_wrapper::arg_type_<Ret (*)(...)> {
57   using type = AnyException;
58 };
59
60 template <class Ret, class... Args>
61 inline Ret exception_wrapper::noop_(Args...) {
62   return Ret();
63 }
64
65 inline std::type_info const* exception_wrapper::uninit_type_(
66     exception_wrapper const*) {
67   return &typeid(void);
68 }
69
70 template <class Ex, class DEx>
71 inline exception_wrapper::Buffer::Buffer(in_place_t, Ex&& ex) {
72   ::new (static_cast<void*>(&buff_)) DEx(std::forward<Ex>(ex));
73 }
74
75 template <class Ex>
76 inline Ex& exception_wrapper::Buffer::as() noexcept {
77   return *static_cast<Ex*>(static_cast<void*>(&buff_));
78 }
79 template <class Ex>
80 inline Ex const& exception_wrapper::Buffer::as() const noexcept {
81   return *static_cast<Ex const*>(static_cast<void const*>(&buff_));
82 }
83
84 inline std::exception const* exception_wrapper::as_exception_or_null_(
85     std::exception const& ex) {
86   return &ex;
87 }
88 inline std::exception const* exception_wrapper::as_exception_or_null_(
89     AnyException) {
90   return nullptr;
91 }
92
93 inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_(
94     std::exception const& e) {
95   return reinterpret_cast<std::uintptr_t>(&e);
96 }
97 inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_(AnyException e) {
98   return reinterpret_cast<std::uintptr_t>(e.typeinfo_) + 1;
99 }
100 inline bool exception_wrapper::ExceptionPtr::has_exception_() const {
101   return 0 == exception_or_type_ % 2;
102 }
103 inline std::exception const* exception_wrapper::ExceptionPtr::as_exception_()
104     const {
105   return reinterpret_cast<std::exception const*>(exception_or_type_);
106 }
107 inline std::type_info const* exception_wrapper::ExceptionPtr::as_type_() const {
108   return reinterpret_cast<std::type_info const*>(exception_or_type_ - 1);
109 }
110
111 inline void exception_wrapper::ExceptionPtr::copy_(
112     exception_wrapper const* from, exception_wrapper* to) {
113   ::new (static_cast<void*>(&to->eptr_)) ExceptionPtr(from->eptr_);
114 }
115 inline void exception_wrapper::ExceptionPtr::move_(
116     exception_wrapper* from, exception_wrapper* to) {
117   ::new (static_cast<void*>(&to->eptr_))
118       ExceptionPtr(std::move(from->eptr_));
119   delete_(from);
120 }
121 inline void exception_wrapper::ExceptionPtr::delete_(
122     exception_wrapper* that) {
123   that->eptr_.~ExceptionPtr();
124   that->vptr_ = &uninit_;
125 }
126 [[noreturn]] inline void exception_wrapper::ExceptionPtr::throw_(
127     exception_wrapper const* that) {
128   std::rethrow_exception(that->eptr_.ptr_);
129 }
130 inline std::type_info const* exception_wrapper::ExceptionPtr::type_(
131     exception_wrapper const* that) {
132   if (auto e = get_exception_(that)) {
133     return &typeid(*e);
134   }
135   return that->eptr_.as_type_();
136 }
137 inline std::exception const* exception_wrapper::ExceptionPtr::get_exception_(
138     exception_wrapper const* that) {
139   return that->eptr_.has_exception_() ? that->eptr_.as_exception_()
140                                       : nullptr;
141 }
142 inline exception_wrapper exception_wrapper::ExceptionPtr::get_exception_ptr_(
143     exception_wrapper const* that) {
144   return *that;
145 }
146
147 template <class Ex>
148 inline void exception_wrapper::InPlace<Ex>::copy_(
149     exception_wrapper const* from, exception_wrapper* to) {
150   ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>())))
151       Ex(from->buff_.as<Ex>());
152 }
153 template <class Ex>
154 inline void exception_wrapper::InPlace<Ex>::move_(
155     exception_wrapper* from, exception_wrapper* to) {
156   ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>())))
157       Ex(std::move(from->buff_.as<Ex>()));
158   delete_(from);
159 }
160 template <class Ex>
161 inline void exception_wrapper::InPlace<Ex>::delete_(
162     exception_wrapper* that) {
163   that->buff_.as<Ex>().~Ex();
164   that->vptr_ = &uninit_;
165 }
166 template <class Ex>
167 [[noreturn]] inline void exception_wrapper::InPlace<Ex>::throw_(
168     exception_wrapper const* that) {
169   throw that->buff_.as<Ex>(); // @nolint
170 }
171 template <class Ex>
172 inline std::type_info const* exception_wrapper::InPlace<Ex>::type_(
173     exception_wrapper const*) {
174   return &typeid(Ex);
175 }
176 template <class Ex>
177 inline std::exception const* exception_wrapper::InPlace<Ex>::get_exception_(
178     exception_wrapper const* that) {
179   return as_exception_or_null_(that->buff_.as<Ex>());
180 }
181 template <class Ex>
182 inline exception_wrapper exception_wrapper::InPlace<Ex>::get_exception_ptr_(
183     exception_wrapper const* that) {
184   try {
185     throw_(that);
186   } catch (Ex const& ex) {
187     return exception_wrapper{std::current_exception(), ex};
188   }
189 }
190
191 template <class Ex>
192 [[noreturn]] inline void
193 exception_wrapper::SharedPtr::Impl<Ex>::throw_() const {
194   throw ex_; // @nolint
195 }
196 template <class Ex>
197 inline std::exception const*
198 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_() const noexcept {
199   return as_exception_or_null_(ex_);
200 }
201 template <class Ex>
202 inline exception_wrapper
203 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_ptr_() const noexcept {
204   try {
205     throw_();
206   } catch (Ex& ex) {
207     return exception_wrapper{std::current_exception(), ex};
208   }
209 }
210 inline void exception_wrapper::SharedPtr::copy_(
211     exception_wrapper const* from, exception_wrapper* to) {
212   ::new (static_cast<void*>(std::addressof(to->sptr_)))
213       SharedPtr(from->sptr_);
214 }
215 inline void exception_wrapper::SharedPtr::move_(
216     exception_wrapper* from, exception_wrapper* to) {
217   ::new (static_cast<void*>(std::addressof(to->sptr_)))
218       SharedPtr(std::move(from->sptr_));
219   delete_(from);
220 }
221 inline void exception_wrapper::SharedPtr::delete_(
222     exception_wrapper* that) {
223   that->sptr_.~SharedPtr();
224   that->vptr_ = &uninit_;
225 }
226 [[noreturn]] inline void exception_wrapper::SharedPtr::throw_(
227     exception_wrapper const* that) {
228   that->sptr_.ptr_->throw_();
229   folly::assume_unreachable();
230 }
231 inline std::type_info const* exception_wrapper::SharedPtr::type_(
232     exception_wrapper const* that) {
233   return that->sptr_.ptr_->info_;
234 }
235 inline std::exception const* exception_wrapper::SharedPtr::get_exception_(
236     exception_wrapper const* that) {
237   return that->sptr_.ptr_->get_exception_();
238 }
239 inline exception_wrapper exception_wrapper::SharedPtr::get_exception_ptr_(
240     exception_wrapper const* that) {
241   return that->sptr_.ptr_->get_exception_ptr_();
242 }
243
244 template <class Ex, class DEx>
245 inline exception_wrapper::exception_wrapper(Ex&& ex, OnHeapTag)
246     : sptr_{std::make_shared<SharedPtr::Impl<DEx>>(std::forward<Ex>(ex))},
247       vptr_(&SharedPtr::ops_) {}
248
249 template <class Ex, class DEx>
250 inline exception_wrapper::exception_wrapper(Ex&& ex, InSituTag)
251     : buff_{in_place, std::forward<Ex>(ex)}, vptr_(&InPlace<DEx>::ops_) {}
252
253 inline exception_wrapper::exception_wrapper(exception_wrapper&& that) noexcept
254     : exception_wrapper{} {
255   (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw
256 }
257
258 inline exception_wrapper::exception_wrapper(
259     exception_wrapper const& that) : exception_wrapper{} {
260   that.vptr_->copy_(&that, this); // could throw
261   vptr_ = that.vptr_;
262 }
263
264 // If `this == &that`, this move assignment operator leaves the object in a
265 // valid but unspecified state.
266 inline exception_wrapper& exception_wrapper::operator=(
267     exception_wrapper&& that) noexcept {
268   vptr_->delete_(this); // Free the current exception
269   (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw
270   return *this;
271 }
272
273 inline exception_wrapper& exception_wrapper::operator=(
274     exception_wrapper const& that) {
275   exception_wrapper(that).swap(*this);
276   return *this;
277 }
278
279 inline exception_wrapper::~exception_wrapper() {
280   reset();
281 }
282
283 template <class Ex>
284 inline exception_wrapper::exception_wrapper(std::exception_ptr ptr, Ex& ex)
285     : eptr_{std::move(ptr), ExceptionPtr::as_int_(ex)},
286       vptr_(&ExceptionPtr::ops_) {
287   assert(eptr_.ptr_);
288 }
289
290 namespace exception_wrapper_detail {
291 template <class Ex>
292 Ex&& dont_slice(Ex&& ex) {
293   assert(typeid(ex) == typeid(_t<std::decay<Ex>>) ||
294        !"Dynamic and static exception types don't match. Exception would "
295         "be sliced when storing in exception_wrapper.");
296   return std::forward<Ex>(ex);
297 }
298 }
299
300 template <
301     class Ex,
302     class Ex_,
303     FOLLY_REQUIRES_DEF(
304         Conjunction<
305             exception_wrapper::IsStdException<Ex_>,
306             exception_wrapper::IsRegularExceptionType<Ex_>>::value)>
307 inline exception_wrapper::exception_wrapper(Ex&& ex)
308     : exception_wrapper{
309         exception_wrapper_detail::dont_slice(std::forward<Ex>(ex)),
310         PlacementOf<Ex_>{}} {
311 }
312
313 template <
314     class Ex,
315     class Ex_,
316     FOLLY_REQUIRES_DEF(
317         exception_wrapper::IsRegularExceptionType<Ex_>::value)>
318 inline exception_wrapper::exception_wrapper(in_place_t, Ex&& ex)
319     : exception_wrapper{
320         exception_wrapper_detail::dont_slice(std::forward<Ex>(ex)),
321         PlacementOf<Ex_>{}} {
322 }
323
324 inline void exception_wrapper::swap(exception_wrapper& that) noexcept {
325   exception_wrapper tmp(std::move(that));
326   that = std::move(*this);
327   *this = std::move(tmp);
328 }
329
330 inline exception_wrapper::operator bool() const noexcept {
331   return vptr_ != &uninit_;
332 }
333
334 inline bool exception_wrapper::operator!() const noexcept {
335   return !static_cast<bool>(*this);
336 }
337
338 inline void exception_wrapper::reset() {
339   vptr_->delete_(this);
340 }
341
342 inline bool exception_wrapper::has_exception_ptr() const noexcept {
343   return vptr_ == &ExceptionPtr::ops_;
344 }
345
346 inline std::exception* exception_wrapper::get_exception() noexcept {
347   return const_cast<std::exception*>(vptr_->get_exception_(this));
348 }
349 inline std::exception const* exception_wrapper::get_exception() const noexcept {
350   return vptr_->get_exception_(this);
351 }
352
353 template <typename Ex>
354 inline Ex* exception_wrapper::get_object() noexcept {
355   Ex* object{nullptr};
356   with_exception([&](Ex& ex) { object = &ex; });
357   return object;
358 }
359
360 template <typename Ex>
361 inline Ex const* exception_wrapper::get_object() const noexcept {
362   Ex const* object{nullptr};
363   with_exception([&](Ex const& ex) { object = &ex; });
364   return object;
365 }
366
367 inline std::exception_ptr const& exception_wrapper::to_exception_ptr()
368     noexcept {
369   // Computing an exception_ptr is expensive so cache the result.
370   return (*this = vptr_->get_exception_ptr_(this)).eptr_.ptr_;
371 }
372 inline std::exception_ptr exception_wrapper::to_exception_ptr() const noexcept {
373   return vptr_->get_exception_ptr_(this).eptr_.ptr_;
374 }
375
376 inline std::type_info const& exception_wrapper::none() noexcept {
377   return typeid(void);
378 }
379 inline std::type_info const& exception_wrapper::unknown() noexcept {
380   return typeid(Unknown);
381 }
382
383 inline std::type_info const& exception_wrapper::type() const noexcept {
384   return *vptr_->type_(this);
385 }
386
387 inline folly::fbstring exception_wrapper::what() const {
388   if (auto e = get_exception()) {
389     return class_name() + ": " + e->what();
390   }
391   return class_name();
392 }
393
394 inline folly::fbstring exception_wrapper::class_name() const {
395   auto& ti = type();
396   return ti == none()
397       ? ""
398       : ti == unknown() ? "<unknown exception>" : folly::demangle(ti);
399 }
400
401 template <class Ex>
402 inline bool exception_wrapper::is_compatible_with() const noexcept {
403   return with_exception([](Ex const&) {});
404 }
405
406 [[noreturn]] inline void exception_wrapper::throw_exception() const {
407   vptr_->throw_(this);
408   onNoExceptionError();
409 }
410
411 template <class CatchFn, bool IsConst>
412 struct exception_wrapper::ExceptionTypeOf {
413   using type = arg_type<_t<std::decay<CatchFn>>>;
414   static_assert(
415       std::is_reference<type>::value,
416       "Always catch exceptions by reference.");
417   static_assert(
418       !IsConst || std::is_const<_t<std::remove_reference<type>>>::value,
419       "handle() or with_exception() called on a const exception_wrapper "
420       "and asked to catch a non-const exception. Handler will never fire. "
421       "Catch exception by const reference to fix this.");
422 };
423
424 // Nests a throw in the proper try/catch blocks
425 template <bool IsConst>
426 struct exception_wrapper::HandleReduce {
427   bool* handled_;
428
429   template <
430       class ThrowFn,
431       class CatchFn,
432       FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
433   auto operator()(ThrowFn&& th, CatchFn& ca) const {
434     using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
435     return [ th = std::forward<ThrowFn>(th), &ca, handled_ = handled_ ] {
436       try {
437         th();
438       } catch (Ex& e) {
439         // If we got here because a catch function threw, rethrow.
440         if (*handled_) {
441           throw;
442         }
443         *handled_ = true;
444         ca(e);
445       }
446     };
447   }
448
449   template <
450       class ThrowFn,
451       class CatchFn,
452       FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
453   auto operator()(ThrowFn&& th, CatchFn& ca) const {
454     return [ th = std::forward<ThrowFn>(th), &ca, handled_ = handled_ ] {
455       try {
456         th();
457       } catch (...) {
458         // If we got here because a catch function threw, rethrow.
459         if (*handled_) {
460           throw;
461         }
462         *handled_ = true;
463         ca();
464       }
465     };
466   }
467 };
468
469 // When all the handlers expect types derived from std::exception, we can
470 // sometimes invoke the handlers without throwing any exceptions.
471 template <bool IsConst>
472 struct exception_wrapper::HandleStdExceptReduce {
473   using StdEx = AddConstIf<IsConst, std::exception>;
474
475   template <
476       class ThrowFn,
477       class CatchFn,
478       FOLLY_REQUIRES(!IsCatchAll<CatchFn>::value)>
479   auto operator()(ThrowFn&& th, CatchFn& ca) const {
480     using Ex = _t<ExceptionTypeOf<CatchFn, IsConst>>;
481     return [ th = std::forward<ThrowFn>(th), &ca ](auto&& continuation)
482         -> StdEx* {
483       if (auto e = const_cast<StdEx*>(th(continuation))) {
484         if (auto e2 = dynamic_cast<_t<std::add_pointer<Ex>>>(e)) {
485           ca(*e2);
486         } else {
487           return e;
488         }
489       }
490       return nullptr;
491     };
492   }
493
494   template <
495       class ThrowFn,
496       class CatchFn,
497       FOLLY_REQUIRES(IsCatchAll<CatchFn>::value)>
498   auto operator()(ThrowFn&& th, CatchFn& ca) const {
499     return [ th = std::forward<ThrowFn>(th), &ca ](auto&&) -> StdEx* {
500       // The following continuation causes ca() to execute if *this contains
501       // an exception /not/ derived from std::exception.
502       auto continuation = [&ca](StdEx* e) {
503         return e != nullptr ? e : ((void)ca(), nullptr);
504       };
505       if (th(continuation) != nullptr) {
506         ca();
507       }
508       return nullptr;
509     };
510   }
511 };
512
513 // Called when some types in the catch clauses are not derived from
514 // std::exception.
515 template <class This, class... CatchFns>
516 inline void exception_wrapper::handle_(
517     std::false_type, This& this_, CatchFns&... fns) {
518   bool handled = false;
519   auto impl = exception_wrapper_detail::fold(
520       HandleReduce<std::is_const<This>::value>{&handled},
521       [&] { this_.throw_exception(); },
522       fns...);
523   impl();
524 }
525
526 // Called when all types in the catch clauses are either derived from
527 // std::exception or a catch-all clause.
528 template <class This, class... CatchFns>
529 inline void exception_wrapper::handle_(
530     std::true_type, This& this_, CatchFns&... fns) {
531   using StdEx = exception_wrapper_detail::
532       AddConstIf<std::is_const<This>::value, std::exception>;
533   auto impl = exception_wrapper_detail::fold(
534       HandleStdExceptReduce<std::is_const<This>::value>{},
535       [&](auto&& continuation) {
536         return continuation(
537             const_cast<StdEx*>(this_.vptr_->get_exception_(&this_)));
538       },
539       fns...);
540   // This continuation gets evaluated if CatchFns... does not include a
541   // catch-all handler. It is a no-op.
542   auto continuation = [](StdEx* ex) { return ex; };
543   if (StdEx* e = impl(continuation)) {
544     throw *e; // Not handled. Throw.
545   }
546 }
547
548 namespace exception_wrapper_detail {
549 template <class Ex, class Fn>
550 struct catch_fn {
551   Fn fn_;
552   auto operator()(Ex& ex) {
553     return fn_(ex);
554   }
555 };
556
557 template <class Ex, class Fn>
558 inline catch_fn<Ex, Fn> catch_(Ex*, Fn fn) {
559   return {std::move(fn)};
560 }
561 template <class Fn>
562 inline Fn catch_(void const*, Fn fn) {
563   return fn;
564 }
565 } // namespace exception_wrapper_detail
566
567 template <class Ex, class This, class Fn>
568 inline bool exception_wrapper::with_exception_(This& this_, Fn fn_) {
569   if (!this_) {
570     return false;
571   }
572   bool handled = true;
573   auto fn = exception_wrapper_detail::catch_(
574       static_cast<Ex*>(nullptr), std::move(fn_));
575   auto&& all = [&](...) { handled = false; };
576   handle_(IsStdException<arg_type<decltype(fn)>>{}, this_, fn, all);
577   return handled;
578 }
579
580 template <class Ex, class Fn>
581 inline bool exception_wrapper::with_exception(Fn fn) {
582   return with_exception_<Ex>(*this, std::move(fn));
583 }
584 template <class Ex, class Fn>
585 inline bool exception_wrapper::with_exception(Fn fn) const {
586   return with_exception_<Ex const>(*this, std::move(fn));
587 }
588
589 template <class... CatchFns>
590 inline void exception_wrapper::handle(CatchFns... fns) {
591   using AllStdEx =
592       exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
593   if (!*this) {
594     onNoExceptionError();
595   }
596   this->handle_(AllStdEx{}, *this, fns...);
597 }
598 template <class... CatchFns>
599 inline void exception_wrapper::handle(CatchFns... fns) const {
600   using AllStdEx =
601       exception_wrapper_detail::AllOf<IsStdException, arg_type<CatchFns>...>;
602   if (!*this) {
603     onNoExceptionError();
604   }
605   this->handle_(AllStdEx{}, *this, fns...);
606 }
607
608 } // namespace folly