Flesh out Optional members swap, reset, emplace, has_value
[folly.git] / folly / Expected.h
index ee765cc92a154fcac6f9f8fe77202b6cd203c025..c2d4e42d07784fbcd5739198bc843e1a159fa186 100644 (file)
@@ -15,7 +15,7 @@
  */
 
 /**
- * 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)
  */
@@ -32,8 +32,9 @@
 #include <folly/Likely.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>
 
 #define FOLLY_EXPECTED_ID(X) FB_CONCATENATE(FB_CONCATENATE(Folly, X), __LINE__)
 
@@ -233,6 +234,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_;
   }
@@ -242,6 +248,7 @@ struct ExpectedStorage {
   Error&& error() && {
     return std::move(error_);
   }
+  FOLLY_POP_WARNING
 };
 
 template <class Value, class Error>
@@ -453,8 +460,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());
@@ -527,6 +535,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_;
   }
@@ -536,6 +549,7 @@ struct ExpectedStorage<Value, Error, StorageType::ePODStruct> {
   Error&& error() && {
     return std::move(error_);
   }
+  FOLLY_POP_WARNING
 };
 
 namespace expected_detail_ExpectedHelper {
@@ -577,13 +591,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());
   }
 
@@ -595,8 +610,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());
   }
 
@@ -608,15 +624,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 {};
@@ -1028,8 +1045,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())
+    if (this->uninitializedByException() || that.uninitializedByException()) {
       throw BadExpectedAccess();
+    }
     using std::swap;
     if (*this) {
       if (that) {
@@ -1167,8 +1185,9 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
       expected_detail::ExpectedHelper::then_(
           std::declval<const Base&>(),
           std::declval<Fns>()...)) {
-    if (this->uninitializedByException())
+    if (this->uninitializedByException()) {
       throw BadExpectedAccess();
+    }
     return expected_detail::ExpectedHelper::then_(
         base(), static_cast<Fns&&>(fns)...);
   }
@@ -1177,8 +1196,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())
+    if (this->uninitializedByException()) {
       throw BadExpectedAccess();
+    }
     return expected_detail::ExpectedHelper::then_(
         base(), static_cast<Fns&&>(fns)...);
   }
@@ -1187,8 +1207,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())
+    if (this->uninitializedByException()) {
       throw BadExpectedAccess();
+    }
     return expected_detail::ExpectedHelper::then_(
         std::move(base()), static_cast<Fns&&>(fns)...);
   }
@@ -1200,8 +1221,9 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
   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())
+    if (this->uninitializedByException()) {
       throw BadExpectedAccess();
+    }
     return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
         base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
   }
@@ -1210,8 +1232,9 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
   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())
+    if (this->uninitializedByException()) {
       throw BadExpectedAccess();
+    }
     return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
         base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
   }
@@ -1220,8 +1243,9 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
   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())
+    if (this->uninitializedByException()) {
       throw BadExpectedAccess();
+    }
     return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
         std::move(base()), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
   }
@@ -1229,8 +1253,9 @@ 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();
     }
   }
@@ -1251,13 +1276,16 @@ 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_))
+  if (UNLIKELY(lhs.which_ != rhs.which_)) {
     return UNLIKELY(lhs.uninitializedByException()) ? false
                                                     : throw BadExpectedAccess();
-  if (UNLIKELY(lhs.uninitializedByException()))
+  }
+  if (UNLIKELY(lhs.uninitializedByException())) {
     throw BadExpectedAccess();
-  if (UNLIKELY(lhs.hasError()))
+  }
+  if (UNLIKELY(lhs.hasError())) {
     return true; // All error states are considered equal
+  }
   return lhs.value_ == rhs.value_;
 }
 
@@ -1276,12 +1304,15 @@ operator<(
     const Expected<Value, Error>& lhs,
     const Expected<Value, Error>& rhs) {
   if (UNLIKELY(
-          lhs.uninitializedByException() || rhs.uninitializedByException()))
+          lhs.uninitializedByException() || rhs.uninitializedByException())) {
     throw BadExpectedAccess();
-  if (UNLIKELY(lhs.hasError()))
+  }
+  if (UNLIKELY(lhs.hasError())) {
     return !rhs.hasError();
-  if (UNLIKELY(rhs.hasError()))
+  }
+  if (UNLIKELY(rhs.hasError())) {
     return false;
+  }
   return lhs.value_ < rhs.value_;
 }