Fix folly::Function under C++17 exception specifier rules
authorChristopher Dykes <cdykes@fb.com>
Thu, 2 Nov 2017 03:33:39 +0000 (20:33 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Thu, 2 Nov 2017 03:36:21 +0000 (20:36 -0700)
Summary: Under C++17's exception specifier rules (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0012r1.html) we need additional specializations to achieve the desired effect.

Reviewed By: yfeldblum

Differential Revision: D6214359

fbshipit-source-id: a007c2fcea1496bdfe4fdf923b39c1611c6ad9bc

folly/Function.h
folly/Portability.h
folly/experimental/exception_tracer/ExceptionStackTraceLib.cpp

index 0ad0158..b8a287d 100644 (file)
@@ -237,6 +237,12 @@ template <typename ReturnType, typename... Args>
 Function<ReturnType(Args...) const> constCastFunction(
     Function<ReturnType(Args...)>&&) noexcept;
 
+#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE
+template <typename ReturnType, typename... Args>
+Function<ReturnType(Args...) const noexcept> constCastFunction(
+    Function<ReturnType(Args...) noexcept>&&) noexcept;
+#endif
+
 namespace detail {
 namespace function {
 
@@ -371,6 +377,98 @@ struct FunctionTraits<ReturnType(Args...) const> {
   };
 };
 
+#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE
+template <typename ReturnType, typename... Args>
+struct FunctionTraits<ReturnType(Args...) noexcept> {
+  using Call = ReturnType (*)(Data&, Args&&...) noexcept;
+  using IsConst = std::false_type;
+  using ConstSignature = ReturnType(Args...) const noexcept;
+  using NonConstSignature = ReturnType(Args...) noexcept;
+  using OtherSignature = ConstSignature;
+
+  template <typename F, typename G = typename std::decay<F>::type>
+  using ResultOf = decltype(
+      static_cast<ReturnType>(std::declval<G&>()(std::declval<Args>()...)));
+
+  template <typename Fun>
+  static ReturnType callSmall(Data& p, Args&&... args) noexcept {
+    return static_cast<ReturnType>((*static_cast<Fun*>(
+        static_cast<void*>(&p.tiny)))(static_cast<Args&&>(args)...));
+  }
+
+  template <typename Fun>
+  static ReturnType callBig(Data& p, Args&&... args) noexcept {
+    return static_cast<ReturnType>(
+        (*static_cast<Fun*>(p.big))(static_cast<Args&&>(args)...));
+  }
+
+  static ReturnType uninitCall(Data&, Args&&...) noexcept {
+    throw std::bad_function_call();
+  }
+
+  ReturnType operator()(Args... args) noexcept {
+    auto& fn = *static_cast<Function<NonConstSignature>*>(this);
+    return fn.call_(fn.data_, static_cast<Args&&>(args)...);
+  }
+
+  class SharedProxy {
+    std::shared_ptr<Function<NonConstSignature>> sp_;
+
+   public:
+    explicit SharedProxy(Function<NonConstSignature>&& func)
+        : sp_(std::make_shared<Function<NonConstSignature>>(std::move(func))) {}
+    ReturnType operator()(Args&&... args) const {
+      return (*sp_)(static_cast<Args&&>(args)...);
+    }
+  };
+};
+
+template <typename ReturnType, typename... Args>
+struct FunctionTraits<ReturnType(Args...) const noexcept> {
+  using Call = ReturnType (*)(Data&, Args&&...) noexcept;
+  using IsConst = std::true_type;
+  using ConstSignature = ReturnType(Args...) const noexcept;
+  using NonConstSignature = ReturnType(Args...) noexcept;
+  using OtherSignature = NonConstSignature;
+
+  template <typename F, typename G = typename std::decay<F>::type>
+  using ResultOf = decltype(static_cast<ReturnType>(
+      std::declval<const G&>()(std::declval<Args>()...)));
+
+  template <typename Fun>
+  static ReturnType callSmall(Data& p, Args&&... args) noexcept {
+    return static_cast<ReturnType>((*static_cast<const Fun*>(
+        static_cast<void*>(&p.tiny)))(static_cast<Args&&>(args)...));
+  }
+
+  template <typename Fun>
+  static ReturnType callBig(Data& p, Args&&... args) noexcept {
+    return static_cast<ReturnType>(
+        (*static_cast<const Fun*>(p.big))(static_cast<Args&&>(args)...));
+  }
+
+  static ReturnType uninitCall(Data&, Args&&...) noexcept {
+    throw std::bad_function_call();
+  }
+
+  ReturnType operator()(Args... args) const noexcept {
+    auto& fn = *static_cast<const Function<ConstSignature>*>(this);
+    return fn.call_(fn.data_, static_cast<Args&&>(args)...);
+  }
+
+  class SharedProxy {
+    std::shared_ptr<Function<ConstSignature>> sp_;
+
+   public:
+    explicit SharedProxy(Function<ConstSignature>&& func)
+        : sp_(std::make_shared<Function<ConstSignature>>(std::move(func))) {}
+    ReturnType operator()(Args&&... args) const {
+      return (*sp_)(static_cast<Args&&>(args)...);
+    }
+  };
+};
+#endif
+
 template <typename Fun>
 bool execSmall(Op o, Data* src, Data* dst) {
   switch (o) {
@@ -743,6 +841,21 @@ Function<ReturnType(Args...) const> constCastFunction(
   return std::move(that);
 }
 
+#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE
+template <typename ReturnType, typename... Args>
+Function<ReturnType(Args...) const noexcept> constCastFunction(
+    Function<ReturnType(Args...) noexcept>&& that) noexcept {
+  return Function<ReturnType(Args...) const noexcept>{
+      std::move(that), detail::function::CoerceTag{}};
+}
+
+template <typename ReturnType, typename... Args>
+Function<ReturnType(Args...) const noexcept> constCastFunction(
+    Function<ReturnType(Args...) const noexcept>&& that) noexcept {
+  return std::move(that);
+}
+#endif
+
 namespace detail {
 namespace function {
 template <typename Fun, typename FunctionType, typename = void>
index be5960d..15f393e 100644 (file)
@@ -429,3 +429,8 @@ constexpr auto kMscVer = 0;
 #if __cpp_coroutines >= 201703L || (_MSC_VER && _RESUMABLE_FUNCTIONS_SUPPORTED)
 #define FOLLY_HAS_COROUTINES 1
 #endif
+
+// MSVC 2017.5
+#if __cpp_noexcept_function_type >= 201510 || _MSC_FULL_VER >= 191225816
+#define FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE 1
+#endif
index 57f0fd6..cd569b5 100644 (file)
@@ -66,16 +66,20 @@ void moveTopException(StackTraceStack& from, StackTraceStack& to) {
 
 struct Initializer {
   Initializer() {
-    registerCxaThrowCallback(
-        [](void*, std::type_info*, void (*)(void*)) { addActiveException(); });
+    registerCxaThrowCallback([](
+        void*, std::type_info*, void (*)(void*)) noexcept {
+      addActiveException();
+    });
 
-    registerCxaBeginCatchCallback(
-        [](void*) { moveTopException(activeExceptions, caughtExceptions); });
+    registerCxaBeginCatchCallback([](void*) noexcept {
+      moveTopException(activeExceptions, caughtExceptions);
+    });
 
-    registerCxaRethrowCallback(
-        []() { moveTopException(caughtExceptions, activeExceptions); });
+    registerCxaRethrowCallback([]() noexcept {
+      moveTopException(caughtExceptions, activeExceptions);
+    });
 
-    registerCxaEndCatchCallback([]() {
+    registerCxaEndCatchCallback([]() noexcept {
       if (invalid) {
         return;
       }
@@ -97,8 +101,9 @@ struct Initializer {
       }
     });
 
-    registerRethrowExceptionCallback(
-        [](std::exception_ptr) { addActiveException(); });
+    registerRethrowExceptionCallback([](std::exception_ptr) noexcept {
+      addActiveException();
+    });
 
     try {
       ::folly::exception_tracer::installHandlers();