Adds unbounded queue test case
[folly.git] / folly / Expected.h
index cde4a04be2ad10aa404db50d0445a80b348adf37..4b736c6b9b63a0b8f384715ebc6ef9946b0d9d7e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 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,9 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 /**
- * Like folly::Optional, but can store a value *or* and error.
+ * Like folly::Optional, but can store a value *or* an error.
  *
  * @author Eric Niebler (eniebler@fb.com)
  */
 #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> // for construct_in_place_t
+#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__)
 
@@ -92,8 +98,19 @@ 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.
+template <template <class...> class Trait, class... Ts>
+using StrictAllOf = std::conjunction<Trait<Ts>...>;
+#else
 template <template <class...> class Trait, class... Ts>
 using StrictAllOf = StrictConjunction<Trait<Ts>...>;
+#endif
 
 template <class T>
 using IsCopyable = StrictConjunction<
@@ -174,11 +191,11 @@ struct ExpectedStorage {
   };
   Which which_;
 
-  template <class E = Error, class = decltype(E())>
-  constexpr ExpectedStorage() noexcept(noexcept(E()))
-      : error_(), which_(Which::eError) {}
+  template <class E = Error, class = decltype(E{})>
+  constexpr ExpectedStorage() noexcept(noexcept(E{}))
+      : error_{}, which_(Which::eError) {}
   explicit constexpr ExpectedStorage(EmptyTag) noexcept
-      : ch_(), which_(Which::eEmpty) {}
+      : ch_{}, which_(Which::eEmpty) {}
   template <class... Vs>
   explicit constexpr ExpectedStorage(ValueTag, Vs&&... vs) noexcept(
       noexcept(Value(static_cast<Vs&&>(vs)...)))
@@ -226,6 +243,11 @@ struct ExpectedStorage {
   Value&& value() && {
     return std::move(value_);
   }
+  // TODO (t17322426): remove when VS2015 support is deprecated
+  // VS2015 static analyzer incorrectly flags these as unreachable in certain
+  // circumstances. VS2017 does not have this problem on the same code.
+  FOLLY_PUSH_WARNING
+  FOLLY_MSVC_DISABLE_WARNING(4702) // unreachable code
   Error& error() & {
     return error_;
   }
@@ -235,6 +257,7 @@ struct ExpectedStorage {
   Error&& error() && {
     return std::move(error_);
   }
+  FOLLY_POP_WARNING
 };
 
 template <class Value, class Error>
@@ -242,12 +265,11 @@ struct ExpectedUnion {
   union {
     Value value_;
     Error error_;
-    char ch_;
+    char ch_{};
   };
-  Which which_;
+  Which which_ = Which::eEmpty;
 
-  explicit constexpr ExpectedUnion(EmptyTag = {}) noexcept
-      : ch_(), which_(Which::eEmpty) {}
+  explicit constexpr ExpectedUnion(EmptyTag) noexcept {}
   template <class... Vs>
   explicit constexpr ExpectedUnion(ValueTag, Vs&&... vs) noexcept(
       noexcept(Value(static_cast<Vs&&>(vs)...)))
@@ -287,28 +309,28 @@ struct ExpectedUnion {
 
 template <class Derived, bool, bool Noexcept>
 struct CopyConstructible {
-  CopyConstructible() = default;
+  constexpr CopyConstructible() = default;
   CopyConstructible(const CopyConstructible& that) noexcept(Noexcept) {
     static_cast<Derived*>(this)->assign(static_cast<const Derived&>(that));
   }
-  CopyConstructible(CopyConstructible&&) = default;
+  constexpr CopyConstructible(CopyConstructible&&) = default;
   CopyConstructible& operator=(const CopyConstructible&) = default;
   CopyConstructible& operator=(CopyConstructible&&) = default;
 };
 
 template <class Derived, bool Noexcept>
 struct CopyConstructible<Derived, false, Noexcept> {
-  CopyConstructible() = default;
+  constexpr CopyConstructible() = default;
   CopyConstructible(const CopyConstructible&) = delete;
-  CopyConstructible(CopyConstructible&&) = default;
+  constexpr CopyConstructible(CopyConstructible&&) = default;
   CopyConstructible& operator=(const CopyConstructible&) = default;
   CopyConstructible& operator=(CopyConstructible&&) = default;
 };
 
 template <class Derived, bool, bool Noexcept>
 struct MoveConstructible {
-  MoveConstructible() = default;
-  MoveConstructible(const MoveConstructible&) = default;
+  constexpr MoveConstructible() = default;
+  constexpr MoveConstructible(const MoveConstructible&) = default;
   MoveConstructible(MoveConstructible&& that) noexcept(Noexcept) {
     static_cast<Derived*>(this)->assign(std::move(static_cast<Derived&>(that)));
   }
@@ -318,8 +340,8 @@ struct MoveConstructible {
 
 template <class Derived, bool Noexcept>
 struct MoveConstructible<Derived, false, Noexcept> {
-  MoveConstructible() = default;
-  MoveConstructible(const MoveConstructible&) = default;
+  constexpr MoveConstructible() = default;
+  constexpr MoveConstructible(const MoveConstructible&) = default;
   MoveConstructible(MoveConstructible&&) = delete;
   MoveConstructible& operator=(const MoveConstructible&) = default;
   MoveConstructible& operator=(MoveConstructible&&) = default;
@@ -327,9 +349,9 @@ struct MoveConstructible<Derived, false, Noexcept> {
 
 template <class Derived, bool, bool Noexcept>
 struct CopyAssignable {
-  CopyAssignable() = default;
-  CopyAssignable(const CopyAssignable&) = default;
-  CopyAssignable(CopyAssignable&&) = default;
+  constexpr CopyAssignable() = default;
+  constexpr CopyAssignable(const CopyAssignable&) = default;
+  constexpr CopyAssignable(CopyAssignable&&) = default;
   CopyAssignable& operator=(const CopyAssignable& that) noexcept(Noexcept) {
     static_cast<Derived*>(this)->assign(static_cast<const Derived&>(that));
     return *this;
@@ -339,18 +361,18 @@ struct CopyAssignable {
 
 template <class Derived, bool Noexcept>
 struct CopyAssignable<Derived, false, Noexcept> {
-  CopyAssignable() = default;
-  CopyAssignable(const CopyAssignable&) = default;
-  CopyAssignable(CopyAssignable&&) = default;
+  constexpr CopyAssignable() = default;
+  constexpr CopyAssignable(const CopyAssignable&) = default;
+  constexpr CopyAssignable(CopyAssignable&&) = default;
   CopyAssignable& operator=(const CopyAssignable&) = delete;
   CopyAssignable& operator=(CopyAssignable&&) = default;
 };
 
 template <class Derived, bool, bool Noexcept>
 struct MoveAssignable {
-  MoveAssignable() = default;
-  MoveAssignable(const MoveAssignable&) = default;
-  MoveAssignable(MoveAssignable&&) = default;
+  constexpr MoveAssignable() = default;
+  constexpr MoveAssignable(const MoveAssignable&) = default;
+  constexpr MoveAssignable(MoveAssignable&&) = default;
   MoveAssignable& operator=(const MoveAssignable&) = default;
   MoveAssignable& operator=(MoveAssignable&& that) noexcept(Noexcept) {
     static_cast<Derived*>(this)->assign(std::move(static_cast<Derived&>(that)));
@@ -360,9 +382,9 @@ struct MoveAssignable {
 
 template <class Derived, bool Noexcept>
 struct MoveAssignable<Derived, false, Noexcept> {
-  MoveAssignable() = default;
-  MoveAssignable(const MoveAssignable&) = default;
-  MoveAssignable(MoveAssignable&&) = default;
+  constexpr MoveAssignable() = default;
+  constexpr MoveAssignable(const MoveAssignable&) = default;
+  constexpr MoveAssignable(MoveAssignable&&) = default;
   MoveAssignable& operator=(const MoveAssignable&) = default;
   MoveAssignable& operator=(MoveAssignable&& that) = delete;
 };
@@ -389,8 +411,8 @@ struct ExpectedStorage<Value, Error, StorageType::eUnion>
   using value_type = Value;
   using error_type = Error;
   using Base = ExpectedUnion<Value, Error>;
-  template <class E = Error, class = decltype(E())>
-  constexpr ExpectedStorage() noexcept(noexcept(E())) : Base{ErrorTag{}} {}
+  template <class E = Error, class = decltype(E{})>
+  constexpr ExpectedStorage() noexcept(noexcept(E{})) : Base{ErrorTag{}} {}
   ExpectedStorage(const ExpectedStorage&) = default;
   ExpectedStorage(ExpectedStorage&&) = default;
   ExpectedStorage& operator=(const ExpectedStorage&) = default;
@@ -439,7 +461,7 @@ struct ExpectedStorage<Value, Error, StorageType::eUnion>
       this->which_ = Which::eError;
     }
   }
-  bool isThis(const ExpectedStorage* that) const {
+  bool isSelfAssign(const ExpectedStorage* that) const {
     return this == that;
   }
   constexpr bool isSelfAssign(const void*) const {
@@ -447,8 +469,9 @@ struct ExpectedStorage<Value, Error, StorageType::eUnion>
   }
   template <class Other>
   void assign(Other&& that) {
-    if (isSelfAssign(&that))
+    if (isSelfAssign(&that)) {
       return;
+    }
     switch (that.which_) {
       case Which::eValue:
         this->assignValue(static_cast<Other&&>(that).value());
@@ -473,17 +496,17 @@ struct ExpectedStorage<Value, Error, StorageType::ePODStruct> {
   Value value_;
 
   constexpr ExpectedStorage() noexcept
-      : which_(Which::eError), error_(), value_() {}
+      : which_(Which::eError), error_{}, value_{} {}
   explicit constexpr ExpectedStorage(EmptyTag) noexcept
-      : which_(Which::eEmpty), error_(), value_() {}
+      : which_(Which::eEmpty), error_{}, value_{} {}
   template <class... Vs>
   explicit constexpr ExpectedStorage(ValueTag, Vs&&... vs) noexcept(
       noexcept(Value(static_cast<Vs&&>(vs)...)))
-      : which_(Which::eValue), error_(), value_(static_cast<Vs&&>(vs)...) {}
+      : which_(Which::eValue), error_{}, value_(static_cast<Vs&&>(vs)...) {}
   template <class... Es>
   explicit constexpr ExpectedStorage(ErrorTag, Es&&... es) noexcept(
       noexcept(Error(static_cast<Es&&>(es)...)))
-      : which_(Which::eError), error_(static_cast<Es&&>(es)...), value_() {}
+      : which_(Which::eError), error_(static_cast<Es&&>(es)...), value_{} {}
   void clear() noexcept {}
   constexpr static bool uninitializedByException() noexcept {
     return false;
@@ -521,6 +544,11 @@ struct ExpectedStorage<Value, Error, StorageType::ePODStruct> {
   Value&& value() && {
     return std::move(value_);
   }
+  // TODO (t17322426): remove when VS2015 support is deprecated
+  // VS2015 static analyzer incorrectly flags these as unreachable in certain
+  // circumstances. VS2017 does not have this problem on the same code.
+  FOLLY_PUSH_WARNING
+  FOLLY_MSVC_DISABLE_WARNING(4702) // unreachable code
   Error& error() & {
     return error_;
   }
@@ -530,6 +558,7 @@ struct ExpectedStorage<Value, Error, StorageType::ePODStruct> {
   Error&& error() && {
     return std::move(error_);
   }
+  FOLLY_POP_WARNING
 };
 
 namespace expected_detail_ExpectedHelper {
@@ -558,6 +587,9 @@ struct ExpectedHelper {
     return static_cast<This&&>(ex);
   }
 
+  FOLLY_PUSH_WARNING
+  // Don't warn about not using the overloaded comma operator.
+  FOLLY_MSVC_DISABLE_WARNING(4913)
   template <
       class This,
       class Fn,
@@ -568,13 +600,14 @@ struct ExpectedHelper {
       T::template return_<E>(
           (std::declval<Fn>()(std::declval<This>().value()), unit)),
       std::declval<Fns>()...)) {
-    if (LIKELY(ex.which_ == expected_detail::Which::eValue))
+    if (LIKELY(ex.which_ == expected_detail::Which::eValue)) {
       return T::then_(
           T::template return_<E>(
               // Uses the comma operator defined above IFF the lambda
               // returns non-void.
               (static_cast<Fn&&>(fn)(static_cast<This&&>(ex).value()), unit)),
           static_cast<Fns&&>(fns)...);
+    }
     return makeUnexpected(static_cast<This&&>(ex).error());
   }
 
@@ -586,8 +619,9 @@ struct ExpectedHelper {
       class Err = decltype(std::declval<No>()(std::declval<This>().error()))
           FOLLY_REQUIRES_TRAILING(!std::is_void<Err>::value)>
   static Ret thenOrThrow_(This&& ex, Yes&& yes, No&& no) {
-    if (LIKELY(ex.which_ == expected_detail::Which::eValue))
+    if (LIKELY(ex.which_ == expected_detail::Which::eValue)) {
       return Ret(static_cast<Yes&&>(yes)(static_cast<This&&>(ex).value()));
+    }
     throw static_cast<No&&>(no)(static_cast<This&&>(ex).error());
   }
 
@@ -599,14 +633,16 @@ struct ExpectedHelper {
       class Err = decltype(std::declval<No>()(std::declval<This&>().error()))
           FOLLY_REQUIRES_TRAILING(std::is_void<Err>::value)>
   static Ret thenOrThrow_(This&& ex, Yes&& yes, No&& no) {
-    if (LIKELY(ex.which_ == expected_detail::Which::eValue))
+    if (LIKELY(ex.which_ == expected_detail::Which::eValue)) {
       return Ret(static_cast<Yes&&>(yes)(static_cast<This&&>(ex).value()));
+    }
     static_cast<No&&>(no)(ex.error());
     throw typename Unexpected<ExpectedErrorType<This>>::MakeBadExpectedAccess()(
         static_cast<This&&>(ex).error());
   }
+  FOLLY_POP_WARNING
 };
-}
+} // namespace expected_detail_ExpectedHelper
 /* using override */ using expected_detail_ExpectedHelper::ExpectedHelper;
 
 struct UnexpectedTag {};
@@ -624,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>
@@ -647,10 +689,10 @@ 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)) {}
+        : folly::BadExpectedAccess{}, error_(std::move(err)) {}
     /**
      * The error code that was held by the Expected object when the user
      * erroneously requested the value.
@@ -701,18 +743,6 @@ class Unexpected final {
     return std::move(error_);
   }
 
-  /**
-   * Relational operators
-   */
-  FOLLY_REQUIRES(IsEqualityComparable<Error>::value)
-  friend bool operator==(const Unexpected& lhs, const Unexpected& rhs) {
-    return lhs.error() == rhs.error();
-  }
-  FOLLY_REQUIRES(IsEqualityComparable<Error>::value)
-  friend bool operator!=(const Unexpected& lhs, const Unexpected& rhs) {
-    return !(lhs == rhs);
-  }
-
  private:
   struct MakeBadExpectedAccess {
     template <class E>
@@ -724,6 +754,22 @@ class Unexpected final {
   Error error_;
 };
 
+template <
+    class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Error>::value)>
+inline bool operator==(
+    const Unexpected<Error>& lhs,
+    const Unexpected<Error>& rhs) {
+  return lhs.error() == rhs.error();
+}
+
+template <
+    class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Error>::value)>
+inline bool operator!=(
+    const Unexpected<Error>& lhs,
+    const Unexpected<Error>& rhs) {
+  return !(lhs == rhs);
+}
+
 /**
  * For constructing an Unexpected object from an error code. Unexpected objects
  * are implicitly convertible to Expected object in the error state. Usage is
@@ -877,7 +923,7 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
    * Constructors
    */
   template <class B = Base, class = decltype(B{})>
-  Expected() noexcept(noexcept(B{})) : Base() {}
+  Expected() noexcept(noexcept(B{})) : Base{} {}
   Expected(const Expected& that) = default;
   Expected(Expected&& that) = default;
 
@@ -1003,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) {
@@ -1014,8 +1066,9 @@ 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();
+    if (this->uninitializedByException() || that.uninitializedByException()) {
+      expected_detail::throwBadExpectedAccess();
+    }
     using std::swap;
     if (*this) {
       if (that) {
@@ -1044,60 +1097,22 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
   /**
    * Relational Operators
    */
-  FOLLY_REQUIRES(IsEqualityComparable<Value>::value)
-  friend bool operator==(const Expected& lhs, const Expected& rhs) {
-    if (UNLIKELY(lhs.which_ != rhs.which_))
-      return UNLIKELY(lhs.uninitializedByException())
-          ? false
-          : throw BadExpectedAccess();
-    if (UNLIKELY(lhs.uninitializedByException()))
-      throw BadExpectedAccess();
-    if (UNLIKELY(lhs.hasError()))
-      return true; // All error states are considered equal
-    return lhs.value_ == rhs.value_;
-  }
-
-  FOLLY_REQUIRES(IsEqualityComparable<Value>::value)
-  friend bool operator!=(const Expected& lhs, const Expected& rhs) {
-    return !(lhs == rhs);
-  }
-
-  FOLLY_REQUIRES(IsLessThanComparable<Value>::value)
-  friend bool operator<(const Expected& lhs, const Expected& rhs) {
-    if (UNLIKELY(
-            lhs.uninitializedByException() || rhs.uninitializedByException()))
-      throw BadExpectedAccess();
-    if (UNLIKELY(lhs.hasError()))
-      return !rhs.hasError();
-    if (UNLIKELY(rhs.hasError()))
-      return false;
-    return lhs.value_ < rhs.value_;
-  }
-
-  FOLLY_REQUIRES(IsLessThanComparable<Value>::value)
-  friend bool operator<=(const Expected& lhs, const Expected& rhs) {
-    return !(rhs < lhs);
-  }
-
-  FOLLY_REQUIRES(IsLessThanComparable<Value>::value)
-  friend bool operator>(const Expected& lhs, const Expected& rhs) {
-    return rhs < lhs;
-  }
-
-  FOLLY_REQUIRES(IsLessThanComparable<Value>::value)
-  friend bool operator>=(const Expected& lhs, const Expected& rhs) {
-    return !(lhs < rhs);
-  }
+  template <class Val, class Err>
+  friend typename std::enable_if<IsEqualityComparable<Val>::value, bool>::type
+  operator==(const Expected<Val, Err>& lhs, const Expected<Val, Err>& rhs);
+  template <class Val, class Err>
+  friend typename std::enable_if<IsLessThanComparable<Val>::value, bool>::type
+  operator<(const Expected<Val, Err>& lhs, const Expected<Val, Err>& rhs);
 
   /*
    * 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;
@@ -1191,8 +1206,9 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
       expected_detail::ExpectedHelper::then_(
           std::declval<const Base&>(),
           std::declval<Fns>()...)) {
-    if (this->uninitializedByException())
-      throw BadExpectedAccess();
+    if (this->uninitializedByException()) {
+      expected_detail::throwBadExpectedAccess();
+    }
     return expected_detail::ExpectedHelper::then_(
         base(), static_cast<Fns&&>(fns)...);
   }
@@ -1201,8 +1217,9 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
   auto then(Fns&&... fns) & -> decltype(expected_detail::ExpectedHelper::then_(
       std::declval<Base&>(),
       std::declval<Fns>()...)) {
-    if (this->uninitializedByException())
-      throw BadExpectedAccess();
+    if (this->uninitializedByException()) {
+      expected_detail::throwBadExpectedAccess();
+    }
     return expected_detail::ExpectedHelper::then_(
         base(), static_cast<Fns&&>(fns)...);
   }
@@ -1211,8 +1228,9 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
   auto then(Fns&&... fns) && -> decltype(expected_detail::ExpectedHelper::then_(
       std::declval<Base&&>(),
       std::declval<Fns>()...)) {
-    if (this->uninitializedByException())
-      throw BadExpectedAccess();
+    if (this->uninitializedByException()) {
+      expected_detail::throwBadExpectedAccess();
+    }
     return expected_detail::ExpectedHelper::then_(
         std::move(base()), static_cast<Fns&&>(fns)...);
   }
@@ -1221,31 +1239,34 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
    * thenOrThrow
    */
   template <class Yes, class No = MakeBadExpectedAccess>
-  auto thenOrThrow(Yes&& yes, No&& no = No()) const& -> decltype(
+  auto thenOrThrow(Yes&& yes, No&& no = No{}) const& -> decltype(
       std::declval<Yes>()(std::declval<const Value&>())) {
     using Ret = decltype(std::declval<Yes>()(std::declval<const Value&>()));
-    if (this->uninitializedByException())
-      throw BadExpectedAccess();
+    if (this->uninitializedByException()) {
+      expected_detail::throwBadExpectedAccess();
+    }
     return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
         base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
   }
 
   template <class Yes, class No = MakeBadExpectedAccess>
-  auto thenOrThrow(Yes&& yes, No&& no = No()) & -> decltype(
+  auto thenOrThrow(Yes&& yes, No&& no = No{}) & -> decltype(
       std::declval<Yes>()(std::declval<Value&>())) {
     using Ret = decltype(std::declval<Yes>()(std::declval<Value&>()));
-    if (this->uninitializedByException())
-      throw BadExpectedAccess();
+    if (this->uninitializedByException()) {
+      expected_detail::throwBadExpectedAccess();
+    }
     return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
         base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
   }
 
   template <class Yes, class No = MakeBadExpectedAccess>
-  auto thenOrThrow(Yes&& yes, No&& no = No()) && -> decltype(
+  auto thenOrThrow(Yes&& yes, No&& no = No{}) && -> decltype(
       std::declval<Yes>()(std::declval<Value&&>())) {
     using Ret = decltype(std::declval<Yes>()(std::declval<Value&&>()));
-    if (this->uninitializedByException())
-      throw BadExpectedAccess();
+    if (this->uninitializedByException()) {
+      expected_detail::throwBadExpectedAccess();
+    }
     return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
         std::move(base()), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
   }
@@ -1253,15 +1274,16 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
  private:
   void requireValue() const {
     if (UNLIKELY(!hasValue())) {
-      if (LIKELY(hasError()))
+      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();
     }
   }
 
@@ -1270,6 +1292,77 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
   }
 };
 
+template <class Value, class Error>
+inline typename std::enable_if<IsEqualityComparable<Value>::value, bool>::type
+operator==(
+    const Expected<Value, Error>& lhs,
+    const Expected<Value, Error>& rhs) {
+  if (UNLIKELY(lhs.uninitializedByException())) {
+    expected_detail::throwBadExpectedAccess();
+  }
+  if (UNLIKELY(lhs.which_ != rhs.which_)) {
+    return false;
+  }
+  if (UNLIKELY(lhs.hasError())) {
+    return true; // All error states are considered equal
+  }
+  return lhs.value_ == rhs.value_;
+}
+
+template <
+    class Value,
+    class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Value>::value)>
+inline bool operator!=(
+    const Expected<Value, Error>& lhs,
+    const Expected<Value, Error>& rhs) {
+  return !(rhs == lhs);
+}
+
+template <class Value, class Error>
+inline typename std::enable_if<IsLessThanComparable<Value>::value, bool>::type
+operator<(
+    const Expected<Value, Error>& lhs,
+    const Expected<Value, Error>& rhs) {
+  if (UNLIKELY(
+          lhs.uninitializedByException() || rhs.uninitializedByException())) {
+    expected_detail::throwBadExpectedAccess();
+  }
+  if (UNLIKELY(lhs.hasError())) {
+    return !rhs.hasError();
+  }
+  if (UNLIKELY(rhs.hasError())) {
+    return false;
+  }
+  return lhs.value_ < rhs.value_;
+}
+
+template <
+    class Value,
+    class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)>
+inline bool operator<=(
+    const Expected<Value, Error>& lhs,
+    const Expected<Value, Error>& rhs) {
+  return !(rhs < lhs);
+}
+
+template <
+    class Value,
+    class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)>
+inline bool operator>(
+    const Expected<Value, Error>& lhs,
+    const Expected<Value, Error>& rhs) {
+  return rhs < lhs;
+}
+
+template <
+    class Value,
+    class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)>
+inline bool operator>=(
+    const Expected<Value, Error>& lhs,
+    const Expected<Value, Error>& rhs) {
+  return !(lhs < rhs);
+}
+
 /**
  * swap Expected values
  */
@@ -1340,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