chromium/base/types/expected.h

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_TYPES_EXPECTED_H_
#define BASE_TYPES_EXPECTED_H_

#include <concepts>
#include <string_view>
#include <type_traits>
#include <utility>

#include "base/check.h"
#include "base/strings/strcat.h"
#include "base/strings/to_string.h"
#include "base/types/expected_internal.h"  // IWYU pragma: export
#include "third_party/abseil-cpp/absl/utility/utility.h"

// Class template `expected<T, E>` is a vocabulary type which contains an
// expected value of type `T`, or an error `E`. The class skews towards behaving
// like a `T`, because its intended use is when the expected type is contained.
// When something unexpected occurs, more typing is required. When all is good,
// code mostly looks as if a `T` were being handled.
//
// Class template `expected<T, E>` contains either:
// * A value of type `T`, the expected value type; or
// * A value of type `E`, an error type used when an unexpected outcome
// occurred.
//
// The interface can be queried as to whether the underlying value is the
// expected value (of type `T`) or an unexpected value (of type `E`). The
// interface and the rational are based on `std::optional`. We consider
// `expected<T, E>` as a supplement to `optional<T>`, expressing why an expected
// value isn’t contained in the object.
//
// Example Usage:
//
// Before:
//   bool ParseInt32(std::string_view input,
//                   int32_t* output,
//                   ParseIntError* error);
//   ...
//
//   int32_t output;
//   ParseIntError error;
//   if (ParseInt32("...". &output, &error)) {
//     // process `output`
//   } else {
//     // process `error`
//   }
//
// After:
//
//   base::expected<int32_t, ParseIntError> ParseInt32(std::string_view input);
//   ...
//
//   if (auto parsed = ParseInt32("..."); parsed.has_value()) {
//     // process `parsed.value()`
//   } else {
//     // process `parsed.error()`
//   }
//
// For even less boilerplate, see expected_macros.h.
//
// Note that there are various transformation member functions. To avoid having
// to puzzle through the standard-ese on their documentation, here's a quick
// reference table, assuming a source `expected<T, E> ex;` and types U and G
// convertible from T and E, respectively:
//
//                        Return type     Val when ex = t  Val when ex = e
//                        -----------     ---------------  ---------------
// ex.value_or(t2)        T               t                t2
// ex.and_then(f)         expected<U, E>  f(t)             unexpected(e)
// ex.transform(f)        expected<U, E>  expected(f(t))   unexpected(e)
// ex.or_else(f)          expected<T, G>  expected(t)      f(e)
// ex.transform_error(f)  expected<T, G>  expected(t)      unexpected(f(e))
//
// References:
// * https://wg21.link/P0323
// * https://eel.is/c++draft/expected
namespace base {

// Note: base::unexpected and base::expected are C++17 compatible backports of
// C++23's std::unexpected and std::expected. They differ from the standard in
// the following ways:
//
// * Not all member functions can be used in a constexpr context. This is due to
//   limitations in both the C++17 language and the Abseil library used for the
//   implementation.
// * Since Chromium does not support exceptions, there is no bad_expected_access
//   exception and the program will just terminate when the exception would have
//   been thrown. Furthermore, all member functions are marked noexcept.
// * C++23 allows an implicit conversion from U to expected<T, E> if U is
//   implicitly convertible to T; the Chromium version only allows an implicit
//   conversion if U is implicitly convertible to T *and* U is *not* implicitly
//   convertible to E, to guard against bug-prone patterns such as:
//     // creates an expected value containing true, not an unexpected value
//     // containing 123L.
//     expected<bool, long> e = 123L;
// * Because of the above restriction, the Chromium version also introduces
//   `base::ok` as a complement to `base::unexpected` to simplify returning
//   success values when the implicit conversion above is disallowed.
// * Calling operator* or operator-> on an unexpected value results in program
//   termination, and not UB.
// * There is no operator bool due to bug-prone usage when the value type is
//   convertible to bool, see e.g. https://abseil.io/tips/141.
// * Moving out of an expected object will put it into a moved-from state.
//   Trying to use it before re-initializing it will result in program
//   termination.
// * The expected<void> specialization is done via a defaulted boolean template
//   parameter, due to the lack of requires clauses in C++17.
// * Since equality operators can not be defaulted in C++17, equality and
//   inequality operators are specified explicitly.
// * base::expected implements the monadic interface proposal
//   (https://wg21.link/P2505), which is currently only on track for C++26.

// Class template used as a type hint for constructing a `base::expected`
// containing a value (i.e. success). Useful when implicit conversion
// construction of `base::expected` is disallowed, e.g. due to ambiguity.
// Example usage:
//
//   base::expected<std::string, std::string> RunOp() {
//     std::string error;
//     std::string result = RunOpImpl(..., &error);
//     if (!error.empty()) {
//       return base::unexpected(std::move(error));
//
//     // The C++23 std::expected proposal allows this to be simply written as
//     //   return result;
//     //
//     // However, the Chromium version disallows this if E implicitly converts
//     // to T, so without base::ok(), this would have to be written as:
//     //   return base::expected<std::string, std::string>(std::move(result));
//
//     return base::ok(std::move(result));
//   }
template <typename T>
class ok final {};

ok<T>;

template <typename T, typename U>
constexpr bool operator==(const ok<T>& lhs, const ok<U>& rhs) noexcept {}

template <typename T, typename U>
constexpr bool operator!=(const ok<T>& lhs, const ok<U>& rhs) noexcept {}

template <typename T>
ok(T) -> ok<T>;

<deduction guide for ok>();

// [expected.un.object], class template unexpected
// https://eel.is/c++draft/expected#un.object
template <typename E>
class unexpected final {};

// [expected.un.eq] Equality operator
template <typename E, typename G>
constexpr bool operator==(const unexpected<E>& lhs,
                          const unexpected<G>& rhs) noexcept {}

template <typename E, typename G>
constexpr bool operator!=(const unexpected<E>& lhs,
                          const unexpected<G>& rhs) noexcept {}

template <typename E>
unexpected(E) -> unexpected<E>;

// [expected.expected], class template expected
// https://eel.is/c++draft/expected#expected
template <typename T, typename E>
class [[nodiscard]] expected final {};

// [expected.void], partial specialization of expected for void types
expected<T, E>;

// [expected.object.eq], equality operators
// [expected.void.eq], equality operators
template <typename T, typename E, typename U, typename G>
constexpr bool operator==(const expected<T, E>& x,
                          const expected<U, G>& y) noexcept {}

template <typename T, typename E, typename U>
  requires(!std::is_void_v<T>)
constexpr bool operator==(const expected<T, E>& x, const U& v) noexcept {}

template <typename T, typename E, typename U>
constexpr bool operator==(const expected<T, E>& x, const ok<U>& o) noexcept {}

template <typename T, typename E, typename G>
constexpr bool operator==(const expected<T, E>& x,
                          const unexpected<G>& e) noexcept {}

}  // namespace base

#endif  // BASE_TYPES_EXPECTED_H_