diff --git a/folly/lang/Exception.h b/folly/lang/Exception.h index 9bc93c0bf96b0ed919b3f7be9658c670497d3658..8bb52cd59f43fdaa9f130896654b0adcf2b386bb 100644 --- a/folly/lang/Exception.h +++ b/folly/lang/Exception.h @@ -23,6 +23,7 @@ #include <folly/CPortability.h> #include <folly/CppAttributes.h> #include <folly/Portability.h> +#include <folly/Traits.h> namespace folly { @@ -48,52 +49,75 @@ template <typename Ex> throw_exception(static_cast<Ex&&>(ex)); } -// clang-format off namespace detail { -template <typename T> -FOLLY_ERASE T&& to_exception_arg_(T&& t) { - return static_cast<T&&>(t); -} -template <std::size_t N> -FOLLY_ERASE char const* to_exception_arg_( - char const (&array)[N]) { - return static_cast<char const*>(array); -} + +struct throw_exception_arg_array_ { + template <typename R> + using v = std::remove_extent_t<std::remove_reference_t<R>>; + template <typename R> + using apply = std::enable_if_t<std::is_same<char const, v<R>>::value, v<R>*>; +}; +struct throw_exception_arg_trivial_ { + template <typename R> + using apply = remove_cvref_t<R>; +}; +struct throw_exception_arg_base_ { + template <typename R> + using apply = R; +}; +template <typename R> +using throw_exception_arg_ = // + conditional_t< + std::is_array<std::remove_reference_t<R>>::value, + throw_exception_arg_array_, + conditional_t< + is_trivially_copyable_v<remove_cvref_t<R>>, + throw_exception_arg_trivial_, + throw_exception_arg_base_>>; +template <typename R> +using throw_exception_arg_t = + typename throw_exception_arg_<R>::template apply<R>; + template <typename Ex, typename... Args> -[[noreturn]] FOLLY_NOINLINE FOLLY_COLD void throw_exception_(Args&&... args) { - throw_exception(Ex(static_cast<Args&&>(args)...)); +[[noreturn]] FOLLY_NOINLINE FOLLY_COLD void throw_exception_(Args... args) { + throw_exception(Ex(static_cast<Args>(args)...)); } template <typename Ex, typename... Args> [[noreturn]] FOLLY_NOINLINE FOLLY_COLD void terminate_with_( - Args&&... args) noexcept { - throw_exception(Ex(static_cast<Args&&>(args)...)); + Args... args) noexcept { + throw_exception(Ex(static_cast<Args>(args)...)); } + } // namespace detail -// clang-format on /// throw_exception /// /// Construct and throw an exception if exceptions are enabled, or terminate if /// compiled with -fno-exceptions. /// -/// Converts any arguments of type `char const[N]` to `char const*`. +/// Does not perfectly forward all its arguments. Instead, in the interest of +/// minimizing common-case inline code size, decays its arguments as follows: +/// * refs to arrays of char const are decayed to char const* +/// * refs to arrays are otherwise invalid +/// * refs to trivial types are decayed to values +/// +/// The reason for treating refs to arrays as invalid is to avoid having two +/// behaviors for refs to arrays, one for the general case and one for where the +/// inner type is char const. Having two behaviors can be surprising, so avoid. template <typename Ex, typename... Args> [[noreturn]] FOLLY_ERASE void throw_exception(Args&&... args) { - detail::throw_exception_<Ex>( - detail::to_exception_arg_(static_cast<Args&&>(args))...); + detail::throw_exception_<Ex, detail::throw_exception_arg_t<Args&&>...>( + static_cast<Args&&>(args)...); } /// terminate_with /// -/// Terminates as if by forwarding to throw_exception but in a noexcept context. -// clang-format off +/// Terminates as if by forwarding to throw_exception within a noexcept context. template <typename Ex, typename... Args> -[[noreturn]] FOLLY_ERASE void -terminate_with(Args&&... args) noexcept { - detail::terminate_with_<Ex>( - detail::to_exception_arg_(static_cast<Args&&>(args))...); +[[noreturn]] FOLLY_ERASE void terminate_with(Args&&... args) { + detail::terminate_with_<Ex, detail::throw_exception_arg_t<Args>...>( + static_cast<Args&&>(args)...); } -// clang-format on /// invoke_cold ///