folly/folly/Expected.h

/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * 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.
 */

#pragma once

#include <cassert>
#include <cstddef>
#include <initializer_list>
#include <new>
#include <stdexcept>
#include <type_traits>
#include <utility>

#include <folly/CPortability.h>
#include <folly/CppAttributes.h>
#include <folly/Likely.h>
#include <folly/Portability.h>
#include <folly/Preprocessor.h>
#include <folly/Traits.h>
#include <folly/Unit.h>
#include <folly/Utility.h>
#include <folly/lang/Exception.h>
#include <folly/lang/Hint.h>

#define FOLLY_EXPECTED_ID(X)

#define FOLLY_REQUIRES_IMPL(...)

#define FOLLY_REQUIRES_TRAILING

#define FOLLY_REQUIRES

namespace folly {

namespace expected_detail {
namespace expected_detail_ExpectedHelper {
struct ExpectedHelper;
}
/* using override */ ExpectedHelper;
} // namespace expected_detail

/**
 * Unexpected - a helper type used to disambiguate the construction of
 * Expected objects in the error state.
 */
template <class Error>
class FOLLY_NODISCARD Unexpected final {};

template <
    class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Error>::value)>
inline bool operator==(
    const Unexpected<Error>& lhs, const Unexpected<Error>& rhs) {}

template <
    class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Error>::value)>
inline bool operator!=(
    const Unexpected<Error>& lhs, const Unexpected<Error>& rhs) {}

/**
 * For constructing an Unexpected object from an error code. Unexpected objects
 * are implicitly convertible to Expected object in the error state. Usage is
 * as follows:
 *
 * enum class MyErrorCode { BAD_ERROR, WORSE_ERROR };
 * Expected<int, MyErrorCode> myAPI() {
 *   int i = // ...;
 *   return i ? makeExpected<MyErrorCode>(i)
 *            : makeUnexpected(MyErrorCode::BAD_ERROR);
 * }
 */
template <class Error>
constexpr Unexpected<typename std::decay<Error>::type> makeUnexpected(
    Error&& err) {}

template <class Error>
class FOLLY_EXPORT BadExpectedAccess;

/**
 * A common base type for exceptions thrown by Expected when the caller
 * erroneously requests a value.
 */
template <>
class FOLLY_EXPORT BadExpectedAccess<void> : public std::exception {};

/**
 * An exception type thrown by Expected on catastrophic logic errors, i.e., when
 * the caller tries to access the value within an Expected but when the Expected
 * instead contains an error.
 */
template <class Error>
class FOLLY_EXPORT BadExpectedAccess : public BadExpectedAccess<void> {};

/**
 * Forward declarations
 */
template <class Value, class Error>
class Expected;

template <class Error, class Value>
FOLLY_NODISCARD constexpr Expected<typename std::decay<Value>::type, Error>
makeExpected(Value&&);

/**
 * Alias for an Expected type's associated value_type
 */
ExpectedValueType;

/**
 * Alias for an Expected type's associated error_type
 */
ExpectedErrorType;

// Details...
namespace expected_detail {

template <typename Value, typename Error>
struct Promise;
template <typename Value, typename Error>
struct PromiseReturn;

StrictAllOf;

IsCopyable;

IsMovable;

IsNothrowCopyable;

IsNothrowMovable;

IsConvertible;

template <class T, class U>
auto doEmplaceAssign(int, T& t, U&& u)
    -> decltype(void(t = static_cast<U&&>(u))) {}

template <class T, class U>
auto doEmplaceAssign(long, T& t, U&& u)
    -> decltype(void(T(static_cast<U&&>(u)))) {}

template <class T, class... Us>
auto doEmplaceAssign(int, T& t, Us&&... us)
    -> decltype(void(t = T(static_cast<Us&&>(us)...))) {}

template <class T, class... Us>
auto doEmplaceAssign(long, T& t, Us&&... us)
    -> decltype(void(T(static_cast<Us&&>(us)...))) {}

struct EmptyTag {};
struct ValueTag {};
struct ErrorTag {};
enum class Which : unsigned char {};
enum class StorageType {};

template <class Value, class Error>
constexpr StorageType getStorageType() {}

template <
    class Value,
    class Error,
    StorageType = expected_detail::getStorageType<Value, Error>()> // ePODUnion
struct ExpectedStorage {};

template <class Value, class Error>
struct ExpectedUnion {};

template <class Derived, bool, bool Noexcept>
struct CopyConstructible {};

CopyConstructible<Derived, false, Noexcept>;

template <class Derived, bool, bool Noexcept>
struct MoveConstructible {};

MoveConstructible<Derived, false, Noexcept>;

template <class Derived, bool, bool Noexcept>
struct CopyAssignable {};

CopyAssignable<Derived, false, Noexcept>;

template <class Derived, bool, bool Noexcept>
struct MoveAssignable {};

MoveAssignable<Derived, false, Noexcept>;

ExpectedStorage<Value, Error, StorageType::eUnion>;

// For small (pointer-sized) trivial types, a struct is faster than a union.
ExpectedStorage<Value, Error, StorageType::ePODStruct>;

namespace expected_detail_ExpectedHelper {
// Tricky hack so that Expected::then can handle lambdas that return void
template <class T>
inline T&& operator,(T&& t, Unit) noexcept {}

struct ExpectedHelper {};
} // namespace expected_detail_ExpectedHelper

struct UnexpectedTag {};

} // namespace expected_detail

unexpected_t;

inline expected_detail::UnexpectedTag unexpected(
    expected_detail::UnexpectedTag = {}

namespace expected_detail {
// empty
} // namespace expected_detail

/**
 * Expected - For holding a value or an error. Useful as an alternative to
 * exceptions, for APIs where throwing on failure would be too expensive.
 *
 * Expected<Value, Error> is a variant over the types Value and Error.
 *
 * Expected does not offer support for references. Use
 * Expected<std::reference_wrapper<T>, Error> if your API needs to return a
 * reference or an error.
 *
 * Expected offers a continuation-based interface to reduce the boilerplate
 * of checking error codes. The Expected::then member function takes a lambda
 * that is to execute should the Expected object contain a value. The return
 * value of the lambda is wrapped in an Expected and returned. If the lambda is
 * not executed because the Expected contains an error, the error is returned
 * immediately in a new Expected object.
 *
 * Expected<int, Error> funcTheFirst();
 * Expected<std::string, Error> funcTheSecond() {
 *   return funcTheFirst().then([](int i) { return std::to_string(i); });
 * }
 *
 * The above line of code could more verbosely written as:
 *
 * Expected<std::string, Error> funcTheSecond() {
 *   if (auto ex = funcTheFirst()) {
 *     return std::to_string(*ex);
 *   }
 *   return makeUnexpected(ex.error());
 * }
 *
 * Continuations can chain, like:
 *
 * Expected<D, Error> maybeD = someFunc()
 *     .then([](A a){return B(a);})
 *     .then([](B b){return C(b);})
 *     .then([](C c){return D(c);});
 *
 * To avoid the redundant error checking that would happen if a call at the
 * front of the chain returns an error, these call chains can be collaped into
 * a single call to .then:
 *
 * Expected<D, Error> maybeD = someFunc()
 *     .then([](A a){return B(a);},
 *           [](B b){return C(b);},
 *           [](C c){return D(c);});
 *
 * The result of .then() is wrapped into Expected< ~, Error > if it isn't
 * of that form already. Consider the following code:
 *
 * extern Expected<std::string, Error> readLineFromIO();
 * extern Expected<int, Error> parseInt(std::string);
 * extern int increment(int);
 *
 * Expected<int, Error> x = readLineFromIO().then(parseInt).then(increment);
 *
 * From the code above, we see that .then() works both with functions that
 * return an Expected< ~, Error > (like parseInt) and with ones that return
 * a plain value (like increment). In the case of parseInt, .then() returns
 * the result of parseInt as-is. In the case of increment, it wraps the int
 * that increment returns into an Expected< int, Error >.
 *
 * Sometimes when using a continuation you would prefer an exception to be
 * thrown for a value-less Expected. For that you can use .thenOrThrow, as
 * follows:
 *
 * B b = someFunc()
 *     .thenOrThrow([](A a){return B(a);});
 *
 * The above call to thenOrThrow will invoke the lambda if the Expected returned
 * by someFunc() contains a value. Otherwise, it will throw an exception of type
 * Unexpected<Error>::BadExpectedAccess. If you prefer it throw an exception of
 * a different type, you can pass a second lambda to thenOrThrow:
 *
 * B b = someFunc()
 *     .thenOrThrow([](A a){return B(a);},
 *                  [](Error e) {throw MyException(e);});
 *
 * Like C++17's std::variant, Expected offers the almost-never-empty guarantee;
 * that is, an Expected<Value, Error> almost always contains either a Value or
 * and Error. Partially-formed Expected objects occur when an assignment to
 * an Expected object that would change the type of the contained object (Value-
 * to-Error or vice versa) throws. Trying to access either the contained value
 * or error object causes Expected to throw folly::BadExpectedAccess.
 *
 * Expected models OptionalPointee, so calling 'get_pointer(ex)' will return a
 * pointer to nullptr if the 'ex' is in the error state, and a pointer to the
 * value otherwise:
 *
 *  Expected<int, Error> maybeInt = ...;
 *  if (int* v = get_pointer(maybeInt)) {
 *    cout << *v << endl;
 *  }
 */
template <class Value, class Error>
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) {}

template <
    class Value,
    class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Value>::value)>
inline bool operator!=(
    const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) {}

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) {}

template <
    class Value,
    class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)>
inline bool operator<=(
    const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) {}

template <
    class Value,
    class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)>
inline bool operator>(
    const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) {}

template <
    class Value,
    class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)>
inline bool operator>=(
    const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) {}

/**
 * swap Expected values
 */
template <class Value, class Error>
void swap(Expected<Value, Error>& lhs, Expected<Value, Error>& rhs) noexcept(
    std::is_nothrow_swappable_v<Value> && std::is_nothrow_swappable_v<Error>) {}

template <class Value, class Error>
const Value* get_pointer(const Expected<Value, Error>& ex) noexcept {}

template <class Value, class Error>
Value* get_pointer(Expected<Value, Error>& ex) noexcept {}

/**
 * For constructing an Expected object from a value, with the specified
 * Error type. Usage is as follows:
 *
 * enum MyErrorCode { BAD_ERROR, WORSE_ERROR };
 * Expected<int, MyErrorCode> myAPI() {
 *   int i = // ...;
 *   return i ? makeExpected<MyErrorCode>(i) : makeUnexpected(BAD_ERROR);
 * }
 */
template <class Error, class Value>
FOLLY_NODISCARD constexpr Expected<typename std::decay<Value>::type, Error>
makeExpected(Value&& val) {}

// Suppress comparability of Optional<T> with T, despite implicit conversion.
template <class Value, class Error>
bool operator==(const Expected<Value, Error>&, const Value& other) = delete;
template <class Value, class Error>
bool operator!=(const Expected<Value, Error>&, const Value& other) = delete;
template <class Value, class Error>
bool operator<(const Expected<Value, Error>&, const Value& other) = delete;
template <class Value, class Error>
bool operator<=(const Expected<Value, Error>&, const Value& other) = delete;
template <class Value, class Error>
bool operator>=(const Expected<Value, Error>&, const Value& other) = delete;
template <class Value, class Error>
bool operator>(const Expected<Value, Error>&, const Value& other) = delete;
template <class Value, class Error>
bool operator==(const Value& other, const Expected<Value, Error>&) = delete;
template <class Value, class Error>
bool operator!=(const Value& other, const Expected<Value, Error>&) = delete;
template <class Value, class Error>
bool operator<(const Value& other, const Expected<Value, Error>&) = delete;
template <class Value, class Error>
bool operator<=(const Value& other, const Expected<Value, Error>&) = delete;
template <class Value, class Error>
bool operator>=(const Value& other, const Expected<Value, Error>&) = delete;
template <class Value, class Error>
bool operator>(const Value& other, const Expected<Value, Error>&) = delete;

} // namespace folly

#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 <folly/experimental/coro/Coroutine.h>

namespace folly {
namespace expected_detail {
template <typename Value, typename Error>
struct PromiseBase;

template <typename Value, typename Error>
struct PromiseReturn {
  Expected<Value, Error> storage_{EmptyTag{}};
  Expected<Value, Error>*& pointer_;

  /* implicit */ PromiseReturn(PromiseBase<Value, Error>& p) noexcept
      : pointer_{p.value_} {
    pointer_ = &storage_;
  }
  PromiseReturn(PromiseReturn const&) = delete;
  // letting dtor be trivial makes the coroutine crash
  // TODO: fix clang/llvm codegen
  ~PromiseReturn() {}
  /* implicit */ operator Expected<Value, Error>() {
    // handle both deferred and eager return-object conversion behaviors
    // see docs for detect_promise_return_object_eager_conversion
    if (folly::coro::detect_promise_return_object_eager_conversion()) {
      assert(storage_.which_ == expected_detail::Which::eEmpty);
      return Expected<Value, Error>{EmptyTag{}, pointer_}; // eager
    } else {
      assert(storage_.which_ != expected_detail::Which::eEmpty);
      return std::move(storage_); // deferred
    }
  }
};

template <typename Value, typename Error>
struct PromiseBase {
  Expected<Value, Error>* value_ = nullptr;

  PromiseBase() = default;
  PromiseBase(PromiseBase const&) = delete;
  void operator=(PromiseBase const&) = delete;

  [[nodiscard]] coro::suspend_never initial_suspend() const noexcept {
    return {};
  }
  [[nodiscard]] coro::suspend_never final_suspend() const noexcept {
    return {};
  }
  [[noreturn]] void unhandled_exception() {
    // Technically, throwing from unhandled_exception is underspecified:
    // https://github.com/GorNishanov/CoroutineWording/issues/17
    rethrow_current_exception();
  }

  PromiseReturn<Value, Error> get_return_object() noexcept { return *this; }
};

template <typename Value>
inline constexpr bool ReturnsVoid =
    std::is_trivial_v<Value> && std::is_empty_v<Value>;

template <typename Value, typename Error>
struct PromiseReturnsValue : public PromiseBase<Value, Error> {
  template <typename U = Value>
  void return_value(U&& u) {
    auto& v = *this->value_;
    ExpectedHelper::assume_empty(v);
    v = static_cast<U&&>(u);
  }
};

template <typename Value, typename Error>
struct PromiseReturnsVoid : public PromiseBase<Value, Error> {
  // When the coroutine uses `return;` you can fail via `co_await err`.
  void return_void() { this->value_->emplace(Value{}); }
};

template <typename Value, typename Error>
struct Promise //
    : conditional_t<
          ReturnsVoid<Value>,
          PromiseReturnsVoid<Value, Error>,
          PromiseReturnsValue<Value, Error>> {};

template <typename Error>
struct UnexpectedAwaitable {
  Unexpected<Error> o_;

  explicit UnexpectedAwaitable(Unexpected<Error> o) : o_(std::move(o)) {}

  constexpr std::false_type await_ready() const noexcept { return {}; }
  void await_resume() { compiler_may_unsafely_assume_unreachable(); }

  template <typename U>
  FOLLY_ALWAYS_INLINE void await_suspend(
      coro::coroutine_handle<Promise<U, Error>> h) {
    auto& v = *h.promise().value_;
    ExpectedHelper::assume_empty(v);
    v = std::move(o_);
    h.destroy();
  }
};

template <typename Value, typename Error>
struct ExpectedAwaitable {
  Expected<Value, Error> o_;

  explicit ExpectedAwaitable(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>
  FOLLY_ALWAYS_INLINE void await_suspend(
      coro::coroutine_handle<Promise<U, Error>> h) {
    auto& v = *h.promise().value_;
    ExpectedHelper::assume_empty(v);
    v = 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 Error>
expected_detail::UnexpectedAwaitable<Error>
/* implicit */ operator co_await(Unexpected<Error> o) {
  return expected_detail::UnexpectedAwaitable<Error>{std::move(o)};
}

template <typename Value, typename Error>
expected_detail::ExpectedAwaitable<Value, Error>
/* implicit */ operator co_await(Expected<Value, Error> o) {
  return expected_detail::ExpectedAwaitable<Value, Error>{std::move(o)};
}

} // namespace folly
#endif // FOLLY_HAS_COROUTINES