Adds unbounded queue test case
[folly.git] / folly / Expected.h
index 57098e28df18f3118816dd874887f0f61acaeb61..4b736c6b9b63a0b8f384715ebc6ef9946b0d9d7e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 Facebook, Inc.
+ * Copyright 2016-present Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 /**
  * Like folly::Optional, but can store a value *or* an error.
  *
 #include <type_traits>
 #include <utility>
 
+#include <glog/logging.h>
+
+#include <folly/CPortability.h>
+#include <folly/CppAttributes.h>
 #include <folly/Likely.h>
+#include <folly/Optional.h>
 #include <folly/Portability.h>
 #include <folly/Preprocessor.h>
 #include <folly/Traits.h>
 #include <folly/Unit.h>
 #include <folly/Utility.h>
+#include <folly/lang/ColdClass.h>
 
 #define FOLLY_EXPECTED_ID(X) FB_CONCATENATE(FB_CONCATENATE(Folly, X), __LINE__)
 
@@ -93,6 +98,10 @@ using ExpectedErrorType =
 
 // Details...
 namespace expected_detail {
+
+template <typename Value, typename Error>
+struct PromiseReturn;
+
 #ifdef _MSC_VER
 // MSVC 2015 can't handle the StrictConjunction, so we have
 // to use std::conjunction instead.
@@ -633,7 +642,7 @@ struct ExpectedHelper {
   }
   FOLLY_POP_WARNING
 };
-}
+} // namespace expected_detail_ExpectedHelper
 /* using override */ using expected_detail_ExpectedHelper::ExpectedHelper;
 
 struct UnexpectedTag {};
@@ -651,17 +660,23 @@ inline expected_detail::UnexpectedTag unexpected(
 /**
  * An exception type thrown by Expected on catastrophic logic errors.
  */
-class BadExpectedAccess : public std::logic_error {
+class FOLLY_EXPORT BadExpectedAccess : public std::logic_error {
  public:
   BadExpectedAccess() : std::logic_error("bad Expected access") {}
 };
 
+namespace expected_detail {
+
+[[noreturn]] void throwBadExpectedAccess();
+
+} // namespace expected_detail
+
 /**
  * Unexpected - a helper type used to disambiguate the construction of
  * Expected objects in the error state.
  */
 template <class Error>
-class Unexpected final {
+class Unexpected final : ColdClass {
   template <class E>
   friend class Unexpected;
   template <class V, class E>
@@ -674,7 +689,7 @@ class Unexpected final {
    * when the user tries to access the nested value but the Expected object is
    * actually storing an error code.
    */
-  class BadExpectedAccess : public folly::BadExpectedAccess {
+  class FOLLY_EXPORT BadExpectedAccess : public folly::BadExpectedAccess {
    public:
     explicit BadExpectedAccess(Error err)
         : folly::BadExpectedAccess{}, error_(std::move(err)) {}
@@ -1034,6 +1049,12 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
     return *this;
   }
 
+  // Used only when an Expected is used with coroutines on MSVC
+  /* implicit */ Expected(const expected_detail::PromiseReturn<Value, Error>& p)
+      : Expected{} {
+    p.promise_->value_ = this;
+  }
+
   template <class... Ts FOLLY_REQUIRES_TRAILING(
       std::is_constructible<Value, Ts&&...>::value)>
   void emplace(Ts&&... ts) {
@@ -1046,7 +1067,7 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
   void swap(Expected& that) noexcept(
       expected_detail::StrictAllOf<IsNothrowSwappable, Value, Error>::value) {
     if (this->uninitializedByException() || that.uninitializedByException()) {
-      throw BadExpectedAccess();
+      expected_detail::throwBadExpectedAccess();
     }
     using std::swap;
     if (*this) {
@@ -1087,11 +1108,11 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
    * Accessors
    */
   constexpr bool hasValue() const noexcept {
-    return expected_detail::Which::eValue == this->which_;
+    return LIKELY(expected_detail::Which::eValue == this->which_);
   }
 
   constexpr bool hasError() const noexcept {
-    return expected_detail::Which::eError == this->which_;
+    return UNLIKELY(expected_detail::Which::eError == this->which_);
   }
 
   using Base::uninitializedByException;
@@ -1186,7 +1207,7 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
           std::declval<const Base&>(),
           std::declval<Fns>()...)) {
     if (this->uninitializedByException()) {
-      throw BadExpectedAccess();
+      expected_detail::throwBadExpectedAccess();
     }
     return expected_detail::ExpectedHelper::then_(
         base(), static_cast<Fns&&>(fns)...);
@@ -1197,7 +1218,7 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
       std::declval<Base&>(),
       std::declval<Fns>()...)) {
     if (this->uninitializedByException()) {
-      throw BadExpectedAccess();
+      expected_detail::throwBadExpectedAccess();
     }
     return expected_detail::ExpectedHelper::then_(
         base(), static_cast<Fns&&>(fns)...);
@@ -1208,7 +1229,7 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
       std::declval<Base&&>(),
       std::declval<Fns>()...)) {
     if (this->uninitializedByException()) {
-      throw BadExpectedAccess();
+      expected_detail::throwBadExpectedAccess();
     }
     return expected_detail::ExpectedHelper::then_(
         std::move(base()), static_cast<Fns&&>(fns)...);
@@ -1222,7 +1243,7 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
       std::declval<Yes>()(std::declval<const Value&>())) {
     using Ret = decltype(std::declval<Yes>()(std::declval<const Value&>()));
     if (this->uninitializedByException()) {
-      throw BadExpectedAccess();
+      expected_detail::throwBadExpectedAccess();
     }
     return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
         base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
@@ -1233,7 +1254,7 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
       std::declval<Yes>()(std::declval<Value&>())) {
     using Ret = decltype(std::declval<Yes>()(std::declval<Value&>()));
     if (this->uninitializedByException()) {
-      throw BadExpectedAccess();
+      expected_detail::throwBadExpectedAccess();
     }
     return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
         base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
@@ -1244,7 +1265,7 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
       std::declval<Yes>()(std::declval<Value&&>())) {
     using Ret = decltype(std::declval<Yes>()(std::declval<Value&&>()));
     if (this->uninitializedByException()) {
-      throw BadExpectedAccess();
+      expected_detail::throwBadExpectedAccess();
     }
     return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
         std::move(base()), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
@@ -1256,13 +1277,13 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
       if (LIKELY(hasError())) {
         throw typename Unexpected<Error>::BadExpectedAccess(this->error_);
       }
-      throw BadExpectedAccess();
+      expected_detail::throwBadExpectedAccess();
     }
   }
 
   void requireError() const {
     if (UNLIKELY(!hasError())) {
-      throw BadExpectedAccess();
+      expected_detail::throwBadExpectedAccess();
     }
   }
 
@@ -1276,12 +1297,11 @@ inline typename std::enable_if<IsEqualityComparable<Value>::value, bool>::type
 operator==(
     const Expected<Value, Error>& lhs,
     const Expected<Value, Error>& rhs) {
-  if (UNLIKELY(lhs.which_ != rhs.which_)) {
-    return UNLIKELY(lhs.uninitializedByException()) ? false
-                                                    : throw BadExpectedAccess();
-  }
   if (UNLIKELY(lhs.uninitializedByException())) {
-    throw BadExpectedAccess();
+    expected_detail::throwBadExpectedAccess();
+  }
+  if (UNLIKELY(lhs.which_ != rhs.which_)) {
+    return false;
   }
   if (UNLIKELY(lhs.hasError())) {
     return true; // All error states are considered equal
@@ -1305,7 +1325,7 @@ operator<(
     const Expected<Value, Error>& rhs) {
   if (UNLIKELY(
           lhs.uninitializedByException() || rhs.uninitializedByException())) {
-    throw BadExpectedAccess();
+    expected_detail::throwBadExpectedAccess();
   }
   if (UNLIKELY(lhs.hasError())) {
     return !rhs.hasError();
@@ -1413,3 +1433,99 @@ bool operator>(const Value& other, const Expected<Value, Error>&) = delete;
 
 #undef FOLLY_REQUIRES
 #undef FOLLY_REQUIRES_TRAILING
+
+// Enable the use of folly::Expected with `co_await`
+// Inspired by https://github.com/toby-allsopp/coroutine_monad
+#if FOLLY_HAS_COROUTINES
+#include <experimental/coroutine>
+
+namespace folly {
+namespace expected_detail {
+template <typename Value, typename Error>
+struct Promise;
+
+template <typename Value, typename Error>
+struct PromiseReturn {
+  Optional<Expected<Value, Error>> storage_;
+  Promise<Value, Error>* promise_;
+  /* implicit */ PromiseReturn(Promise<Value, Error>& promise) noexcept
+      : promise_(&promise) {
+    promise_->value_ = &storage_;
+  }
+  PromiseReturn(PromiseReturn&& that) noexcept
+      : PromiseReturn{*that.promise_} {}
+  ~PromiseReturn() {}
+  /* implicit */ operator Expected<Value, Error>() & {
+    return std::move(*storage_);
+  }
+};
+
+template <typename Value, typename Error>
+struct Promise {
+  Optional<Expected<Value, Error>>* value_ = nullptr;
+  Promise() = default;
+  Promise(Promise const&) = delete;
+  // This should work regardless of whether the compiler generates:
+  //    folly::Expected<Value, Error> retobj{ p.get_return_object(); } // MSVC
+  // or:
+  //    auto retobj = p.get_return_object(); // clang
+  PromiseReturn<Value, Error> get_return_object() noexcept {
+    return *this;
+  }
+  std::experimental::suspend_never initial_suspend() const noexcept {
+    return {};
+  }
+  std::experimental::suspend_never final_suspend() const {
+    return {};
+  }
+  template <typename U>
+  void return_value(U&& u) {
+    value_->emplace(static_cast<U&&>(u));
+  }
+  void unhandled_exception() {
+    // Technically, throwing from unhandled_exception is underspecified:
+    // https://github.com/GorNishanov/CoroutineWording/issues/17
+    throw;
+  }
+};
+
+template <typename Value, typename Error>
+struct Awaitable {
+  Expected<Value, Error> o_;
+
+  explicit Awaitable(Expected<Value, Error> o) : o_(std::move(o)) {}
+
+  bool await_ready() const noexcept {
+    return o_.hasValue();
+  }
+  Value await_resume() {
+    return std::move(o_.value());
+  }
+
+  // Explicitly only allow suspension into a Promise
+  template <typename U>
+  void await_suspend(std::experimental::coroutine_handle<Promise<U, Error>> h) {
+    *h.promise().value_ = makeUnexpected(std::move(o_.error()));
+    // Abort the rest of the coroutine. resume() is not going to be called
+    h.destroy();
+  }
+};
+} // namespace expected_detail
+
+template <typename Value, typename Error>
+expected_detail::Awaitable<Value, Error>
+/* implicit */ operator co_await(Expected<Value, Error> o) {
+  return expected_detail::Awaitable<Value, Error>{std::move(o)};
+}
+} // namespace folly
+
+// This makes folly::Expected<Value> useable as a coroutine return type...
+namespace std {
+namespace experimental {
+template <typename Value, typename Error, typename... Args>
+struct coroutine_traits<folly::Expected<Value, Error>, Args...> {
+  using promise_type = folly::expected_detail::Promise<Value, Error>;
+};
+} // namespace experimental
+} // namespace std
+#endif // FOLLY_HAS_COROUTINES