diff options
Diffstat (limited to 'absl/status')
-rw-r--r-- | absl/status/BUILD.bazel | 40 | ||||
-rw-r--r-- | absl/status/CMakeLists.txt | 35 | ||||
-rw-r--r-- | absl/status/internal/status_internal.h | 51 | ||||
-rw-r--r-- | absl/status/internal/statusor_internal.h | 399 | ||||
-rw-r--r-- | absl/status/status.cc | 18 | ||||
-rw-r--r-- | absl/status/status.h | 615 | ||||
-rw-r--r-- | absl/status/status_payload_printer.cc | 15 | ||||
-rw-r--r-- | absl/status/status_test.cc | 57 | ||||
-rw-r--r-- | absl/status/statusor.cc | 71 | ||||
-rw-r--r-- | absl/status/statusor.h | 760 | ||||
-rw-r--r-- | absl/status/statusor_test.cc | 1800 |
11 files changed, 3731 insertions, 130 deletions
diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel index 2b83077d..189bd73d 100644 --- a/absl/status/BUILD.bazel +++ b/absl/status/BUILD.bazel @@ -26,11 +26,12 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "status", srcs = [ + "internal/status_internal.h", "status.cc", "status_payload_printer.cc", ], @@ -40,6 +41,7 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, deps = [ + "//absl/base:atomic_hook", "//absl/base:config", "//absl/base:core_headers", "//absl/base:raw_logging_internal", @@ -63,3 +65,39 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_library( + name = "statusor", + srcs = [ + "internal/statusor_internal.h", + "statusor.cc", + ], + hdrs = [ + "statusor.h", + ], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":status", + "//absl/base:core_headers", + "//absl/base:raw_logging_internal", + "//absl/meta:type_traits", + "//absl/strings", + "//absl/types:variant", + "//absl/utility", + ], +) + +cc_test( + name = "statusor_test", + size = "small", + srcs = ["statusor_test.cc"], + deps = [ + ":status", + ":statusor", + "//absl/base", + "//absl/memory", + "//absl/types:any", + "//absl/utility", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt index f05cee5e..66728551 100644 --- a/absl/status/CMakeLists.txt +++ b/absl/status/CMakeLists.txt @@ -19,12 +19,14 @@ absl_cc_library( HDRS "status.h" SRCS + "internal/status_internal.h" "status.cc" "status_payload_printer.h" "status_payload_printer.cc" COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::atomic_hook absl::config absl::core_headers absl::raw_logging_internal @@ -50,3 +52,36 @@ absl_cc_test( absl::strings gmock_main ) + +absl_cc_library( + NAME + statusor + HDRS + "statusor.h" + SRCS + "statusor.cc" + "internal/statusor_internal.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::core_headers + absl::raw_logging_internal + absl::type_traits + absl::strings + absl::utility + absl::variant + PUBLIC +) + +absl_cc_test( + NAME + statusor_test + SRCS + "statusor_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::status + absl::statusor + gmock_main +) diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h new file mode 100644 index 00000000..1f82b8e4 --- /dev/null +++ b/absl/status/internal/status_internal.h @@ -0,0 +1,51 @@ +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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. +#ifndef ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_ +#define ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_ + +#include <string> + +#include "absl/container/inlined_vector.h" +#include "absl/strings/cord.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +enum class StatusCode : int; + +namespace status_internal { + +// Container for status payloads. +struct Payload { + std::string type_url; + absl::Cord payload; +}; + +using Payloads = absl::InlinedVector<Payload, 1>; + +// Reference-counted representation of Status data. +struct StatusRep { + std::atomic<int32_t> ref; + absl::StatusCode code; + std::string message; + std::unique_ptr<status_internal::Payloads> payloads; +}; + +absl::StatusCode MapToLocalCode(int value); +} // namespace status_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_ diff --git a/absl/status/internal/statusor_internal.h b/absl/status/internal/statusor_internal.h new file mode 100644 index 00000000..96e41da5 --- /dev/null +++ b/absl/status/internal/statusor_internal.h @@ -0,0 +1,399 @@ +// Copyright 2020 The Abseil Authors. +// +// 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 +// +// https://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. +#ifndef ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_ +#define ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_ + +#include <type_traits> +#include <utility> + +#include "absl/meta/type_traits.h" +#include "absl/status/status.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +template <typename T> +class ABSL_MUST_USE_RESULT StatusOr; + +namespace internal_statusor { + +// Detects whether `U` has conversion operator to `StatusOr<T>`, i.e. `operator +// StatusOr<T>()`. +template <typename T, typename U, typename = void> +struct HasConversionOperatorToStatusOr : std::false_type {}; + +template <typename T, typename U> +void test(char (*)[sizeof(std::declval<U>().operator absl::StatusOr<T>())]); + +template <typename T, typename U> +struct HasConversionOperatorToStatusOr<T, U, decltype(test<T, U>(0))> + : std::true_type {}; + +// Detects whether `T` is constructible or convertible from `StatusOr<U>`. +template <typename T, typename U> +using IsConstructibleOrConvertibleFromStatusOr = + absl::disjunction<std::is_constructible<T, StatusOr<U>&>, + std::is_constructible<T, const StatusOr<U>&>, + std::is_constructible<T, StatusOr<U>&&>, + std::is_constructible<T, const StatusOr<U>&&>, + std::is_convertible<StatusOr<U>&, T>, + std::is_convertible<const StatusOr<U>&, T>, + std::is_convertible<StatusOr<U>&&, T>, + std::is_convertible<const StatusOr<U>&&, T>>; + +// Detects whether `T` is constructible or convertible or assignable from +// `StatusOr<U>`. +template <typename T, typename U> +using IsConstructibleOrConvertibleOrAssignableFromStatusOr = + absl::disjunction<IsConstructibleOrConvertibleFromStatusOr<T, U>, + std::is_assignable<T&, StatusOr<U>&>, + std::is_assignable<T&, const StatusOr<U>&>, + std::is_assignable<T&, StatusOr<U>&&>, + std::is_assignable<T&, const StatusOr<U>&&>>; + +// Detects whether direct initializing `StatusOr<T>` from `U` is ambiguous, i.e. +// when `U` is `StatusOr<V>` and `T` is constructible or convertible from `V`. +template <typename T, typename U> +struct IsDirectInitializationAmbiguous + : public absl::conditional_t< + std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, + U>::value, + std::false_type, + IsDirectInitializationAmbiguous< + T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {}; + +template <typename T, typename V> +struct IsDirectInitializationAmbiguous<T, absl::StatusOr<V>> + : public IsConstructibleOrConvertibleFromStatusOr<T, V> {}; + +// Checks against the constraints of the direction initialization, i.e. when +// `StatusOr<T>::StatusOr(U&&)` should participate in overload resolution. +template <typename T, typename U> +using IsDirectInitializationValid = absl::disjunction< + // Short circuits if T is basically U. + std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>, + absl::negation<absl::disjunction< + std::is_same<absl::StatusOr<T>, + absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<absl::Status, + absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<absl::in_place_t, + absl::remove_cv_t<absl::remove_reference_t<U>>>, + IsDirectInitializationAmbiguous<T, U>>>>; + +// This trait detects whether `StatusOr<T>::operator=(U&&)` is ambiguous, which +// is equivalent to whether all the following conditions are met: +// 1. `U` is `StatusOr<V>`. +// 2. `T` is constructible and assignable from `V`. +// 3. `T` is constructible and assignable from `U` (i.e. `StatusOr<V>`). +// For example, the following code is considered ambiguous: +// (`T` is `bool`, `U` is `StatusOr<bool>`, `V` is `bool`) +// StatusOr<bool> s1 = true; // s1.ok() && s1.ValueOrDie() == true +// StatusOr<bool> s2 = false; // s2.ok() && s2.ValueOrDie() == false +// s1 = s2; // ambiguous, `s1 = s2.ValueOrDie()` or `s1 = bool(s2)`? +template <typename T, typename U> +struct IsForwardingAssignmentAmbiguous + : public absl::conditional_t< + std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, + U>::value, + std::false_type, + IsForwardingAssignmentAmbiguous< + T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {}; + +template <typename T, typename U> +struct IsForwardingAssignmentAmbiguous<T, absl::StatusOr<U>> + : public IsConstructibleOrConvertibleOrAssignableFromStatusOr<T, U> {}; + +// Checks against the constraints of the forwarding assignment, i.e. whether +// `StatusOr<T>::operator(U&&)` should participate in overload resolution. +template <typename T, typename U> +using IsForwardingAssignmentValid = absl::disjunction< + // Short circuits if T is basically U. + std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>, + absl::negation<absl::disjunction< + std::is_same<absl::StatusOr<T>, + absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<absl::Status, + absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<absl::in_place_t, + absl::remove_cv_t<absl::remove_reference_t<U>>>, + IsForwardingAssignmentAmbiguous<T, U>>>>; + +class Helper { + public: + // Move type-agnostic error handling to the .cc. + static void HandleInvalidStatusCtorArg(Status*); + static void Crash(const absl::Status& status); +}; + +// Construct an instance of T in `p` through placement new, passing Args... to +// the constructor. +// This abstraction is here mostly for the gcc performance fix. +template <typename T, typename... Args> +void PlacementNew(void* p, Args&&... args) { +#if defined(__GNUC__) && !defined(__clang__) + // Teach gcc that 'p' cannot be null, fixing code size issues. + if (p == nullptr) __builtin_unreachable(); +#endif + new (p) T(std::forward<Args>(args)...); +} + +// Helper base class to hold the data and all operations. +// We move all this to a base class to allow mixing with the appropriate +// TraitsBase specialization. +template <typename T> +class StatusOrData { + template <typename U> + friend class StatusOrData; + + public: + StatusOrData() = delete; + + StatusOrData(const StatusOrData& other) { + if (other.ok()) { + MakeValue(other.data_); + MakeStatus(); + } else { + MakeStatus(other.status_); + } + } + + StatusOrData(StatusOrData&& other) noexcept { + if (other.ok()) { + MakeValue(std::move(other.data_)); + MakeStatus(); + } else { + MakeStatus(std::move(other.status_)); + } + } + + template <typename U> + explicit StatusOrData(const StatusOrData<U>& other) { + if (other.ok()) { + MakeValue(other.data_); + MakeStatus(); + } else { + MakeStatus(other.status_); + } + } + + template <typename U> + explicit StatusOrData(StatusOrData<U>&& other) { + if (other.ok()) { + MakeValue(std::move(other.data_)); + MakeStatus(); + } else { + MakeStatus(std::move(other.status_)); + } + } + + template <typename... Args> + explicit StatusOrData(absl::in_place_t, Args&&... args) + : data_(std::forward<Args>(args)...) { + MakeStatus(); + } + + explicit StatusOrData(const T& value) : data_(value) { + MakeStatus(); + } + explicit StatusOrData(T&& value) : data_(std::move(value)) { + MakeStatus(); + } + + template <typename U, + absl::enable_if_t<std::is_constructible<absl::Status, U&&>::value, + int> = 0> + explicit StatusOrData(U&& v) : status_(v) { + EnsureNotOk(); + } + + StatusOrData& operator=(const StatusOrData& other) { + if (this == &other) return *this; + if (other.ok()) + Assign(other.data_); + else + AssignStatus(other.status_); + return *this; + } + + StatusOrData& operator=(StatusOrData&& other) { + if (this == &other) return *this; + if (other.ok()) + Assign(std::move(other.data_)); + else + AssignStatus(std::move(other.status_)); + return *this; + } + + ~StatusOrData() { + if (ok()) { + status_.~Status(); + data_.~T(); + } else { + status_.~Status(); + } + } + + template <typename U> + void Assign(U&& value) { + if (ok()) { + data_ = std::forward<U>(value); + } else { + MakeValue(std::forward<U>(value)); + status_ = OkStatus(); + } + } + + template <typename U> + void AssignStatus(U&& v) { + Clear(); + status_ = static_cast<absl::Status>(std::forward<U>(v)); + EnsureNotOk(); + } + + bool ok() const { return status_.ok(); } + + protected: + // status_ will always be active after the constructor. + // We make it a union to be able to initialize exactly how we need without + // waste. + // Eg. in the copy constructor we use the default constructor of Status in + // the ok() path to avoid an extra Ref call. + union { + Status status_; + }; + + // data_ is active iff status_.ok()==true + struct Dummy {}; + union { + // When T is const, we need some non-const object we can cast to void* for + // the placement new. dummy_ is that object. + Dummy dummy_; + T data_; + }; + + void Clear() { + if (ok()) data_.~T(); + } + + void EnsureOk() const { + if (ABSL_PREDICT_FALSE(!ok())) Helper::Crash(status_); + } + + void EnsureNotOk() { + if (ABSL_PREDICT_FALSE(ok())) Helper::HandleInvalidStatusCtorArg(&status_); + } + + // Construct the value (ie. data_) through placement new with the passed + // argument. + template <typename... Arg> + void MakeValue(Arg&&... arg) { + internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg)...); + } + + // Construct the status (ie. status_) through placement new with the passed + // argument. + template <typename... Args> + void MakeStatus(Args&&... args) { + internal_statusor::PlacementNew<Status>(&status_, + std::forward<Args>(args)...); + } +}; + +// Helper base classes to allow implicitly deleted constructors and assignment +// operators in `StatusOr`. For example, `CopyCtorBase` will explicitly delete +// the copy constructor when T is not copy constructible and `StatusOr` will +// inherit that behavior implicitly. +template <typename T, bool = std::is_copy_constructible<T>::value> +struct CopyCtorBase { + CopyCtorBase() = default; + CopyCtorBase(const CopyCtorBase&) = default; + CopyCtorBase(CopyCtorBase&&) = default; + CopyCtorBase& operator=(const CopyCtorBase&) = default; + CopyCtorBase& operator=(CopyCtorBase&&) = default; +}; + +template <typename T> +struct CopyCtorBase<T, false> { + CopyCtorBase() = default; + CopyCtorBase(const CopyCtorBase&) = delete; + CopyCtorBase(CopyCtorBase&&) = default; + CopyCtorBase& operator=(const CopyCtorBase&) = default; + CopyCtorBase& operator=(CopyCtorBase&&) = default; +}; + +template <typename T, bool = std::is_move_constructible<T>::value> +struct MoveCtorBase { + MoveCtorBase() = default; + MoveCtorBase(const MoveCtorBase&) = default; + MoveCtorBase(MoveCtorBase&&) = default; + MoveCtorBase& operator=(const MoveCtorBase&) = default; + MoveCtorBase& operator=(MoveCtorBase&&) = default; +}; + +template <typename T> +struct MoveCtorBase<T, false> { + MoveCtorBase() = default; + MoveCtorBase(const MoveCtorBase&) = default; + MoveCtorBase(MoveCtorBase&&) = delete; + MoveCtorBase& operator=(const MoveCtorBase&) = default; + MoveCtorBase& operator=(MoveCtorBase&&) = default; +}; + +template <typename T, bool = std::is_copy_constructible<T>::value&& + std::is_copy_assignable<T>::value> +struct CopyAssignBase { + CopyAssignBase() = default; + CopyAssignBase(const CopyAssignBase&) = default; + CopyAssignBase(CopyAssignBase&&) = default; + CopyAssignBase& operator=(const CopyAssignBase&) = default; + CopyAssignBase& operator=(CopyAssignBase&&) = default; +}; + +template <typename T> +struct CopyAssignBase<T, false> { + CopyAssignBase() = default; + CopyAssignBase(const CopyAssignBase&) = default; + CopyAssignBase(CopyAssignBase&&) = default; + CopyAssignBase& operator=(const CopyAssignBase&) = delete; + CopyAssignBase& operator=(CopyAssignBase&&) = default; +}; + +template <typename T, bool = std::is_move_constructible<T>::value&& + std::is_move_assignable<T>::value> +struct MoveAssignBase { + MoveAssignBase() = default; + MoveAssignBase(const MoveAssignBase&) = default; + MoveAssignBase(MoveAssignBase&&) = default; + MoveAssignBase& operator=(const MoveAssignBase&) = default; + MoveAssignBase& operator=(MoveAssignBase&&) = default; +}; + +template <typename T> +struct MoveAssignBase<T, false> { + MoveAssignBase() = default; + MoveAssignBase(const MoveAssignBase&) = default; + MoveAssignBase(MoveAssignBase&&) = default; + MoveAssignBase& operator=(const MoveAssignBase&) = default; + MoveAssignBase& operator=(MoveAssignBase&&) = delete; +}; + +ABSL_ATTRIBUTE_NORETURN void ThrowBadStatusOrAccess(absl::Status status); + +} // namespace internal_statusor +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_ diff --git a/absl/status/status.cc b/absl/status/status.cc index bbc1895e..a27fd8b3 100644 --- a/absl/status/status.cc +++ b/absl/status/status.cc @@ -27,8 +27,6 @@ namespace absl { ABSL_NAMESPACE_BEGIN -// The implementation was intentionally kept same as util::error::Code_Name() -// to ease the migration. std::string StatusCodeToString(StatusCode code) { switch (code) { case StatusCode::kOk: @@ -80,7 +78,7 @@ static int FindPayloadIndexByUrl(const Payloads* payloads, absl::string_view type_url) { if (payloads == nullptr) return -1; - for (int i = 0; i < payloads->size(); ++i) { + for (size_t i = 0; i < payloads->size(); ++i) { if ((*payloads)[i].type_url == type_url) return i; } @@ -147,7 +145,15 @@ void Status::SetPayload(absl::string_view type_url, absl::Cord payload) { bool Status::ErasePayload(absl::string_view type_url) { int index = status_internal::FindPayloadIndexByUrl(GetPayloads(), type_url); if (index != -1) { + PrepareToModify(); GetPayloads()->erase(GetPayloads()->begin() + index); + if (GetPayloads()->empty() && message().empty()) { + // Special case: If this can be represented inlined, it MUST be + // inlined (EqualsSlow depends on this behavior). + StatusCode c = static_cast<StatusCode>(raw_code()); + Unref(rep_); + rep_ = CodeToInlinedRep(c); + } return true; } @@ -161,15 +167,15 @@ void Status::ForEachPayload( bool in_reverse = payloads->size() > 1 && reinterpret_cast<uintptr_t>(payloads) % 13 > 6; - for (int index = 0; index < payloads->size(); ++index) { + for (size_t index = 0; index < payloads->size(); ++index) { const auto& elem = (*payloads)[in_reverse ? payloads->size() - 1 - index : index]; #ifdef NDEBUG visitor(elem.type_url, elem.payload); #else - // In debug mode invaldiate the type url to prevent users from relying on - // this std::string lifetime. + // In debug mode invalidate the type url to prevent users from relying on + // this string lifetime. // NOLINTNEXTLINE intentional extra conversion to force temporary. visitor(std::string(elem.type_url), elem.payload); diff --git a/absl/status/status.h b/absl/status/status.h index 9706d4ba..42f634e0 100644 --- a/absl/status/status.h +++ b/absl/status/status.h @@ -11,6 +11,43 @@ // 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. +// +// ----------------------------------------------------------------------------- +// File: status.h +// ----------------------------------------------------------------------------- +// +// This header file defines the Abseil `status` library, consisting of: +// +// * An `absl::Status` class for holding error handling information +// * A set of canonical `absl::StatusCode` error codes, and associated +// utilities for generating and propagating status codes. +// * A set of helper functions for creating status codes and checking their +// values +// +// Within Google, `absl::Status` is the primary mechanism for gracefully +// handling errors across API boundaries (and in particular across RPC +// boundaries). Some of these errors may be recoverable, but others may not. +// Most functions that can produce a recoverable error should be designed to +// return an `absl::Status` (or `absl::StatusOr`). +// +// Example: +// +// absl::Status myFunction(absl::string_view fname, ...) { +// ... +// // encounter error +// if (error condition) { +// return absl::InvalidArgumentError("bad mode"); +// } +// // else, return OK +// return absl::OkStatus(); +// } +// +// An `absl::Status` is designed to either return "OK" or one of a number of +// different error codes, corresponding to typical error conditions. +// In almost all cases, when using `absl::Status` you should use the canonical +// error codes (of type `absl::StatusCode`) enumerated in this header file. +// These canonical codes are understood across the codebase and will be +// accepted across all API and RPC boundaries. #ifndef ABSL_STATUS_STATUS_H_ #define ABSL_STATUS_STATUS_H_ @@ -18,165 +55,477 @@ #include <string> #include "absl/container/inlined_vector.h" +#include "absl/status/internal/status_internal.h" #include "absl/strings/cord.h" #include "absl/types/optional.h" namespace absl { ABSL_NAMESPACE_BEGIN +// absl::StatusCode +// +// An `absl::StatusCode` is an enumerated type indicating either no error ("OK") +// or an error condition. In most cases, an `absl::Status` indicates a +// recoverable error, and the purpose of signalling an error is to indicate what +// action to take in response to that error. These error codes map to the proto +// RPC error codes indicated in https://cloud.google.com/apis/design/errors. +// +// The errors listed below are the canonical errors associated with +// `absl::Status` and are used throughout the codebase. As a result, these +// error codes are somewhat generic. +// +// In general, try to return the most specific error that applies if more than +// one error may pertain. For example, prefer `kOutOfRange` over +// `kFailedPrecondition` if both codes apply. Similarly prefer `kNotFound` or +// `kAlreadyExists` over `kFailedPrecondition`. +// +// Because these errors may travel RPC boundaries, these codes are tied to the +// `google.rpc.Code` definitions within +// https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto +// The string value of these RPC codes is denoted within each enum below. +// +// If your error handling code requires more context, you can attach payloads +// to your status. See `absl::Status::SetPayload()` and +// `absl::Status::GetPayload()` below. enum class StatusCode : int { + // StatusCode::kOk + // + // kOK (gRPC code "OK") does not indicate an error; this value is returned on + // success. It is typical to check for this value before proceeding on any + // given call across an API or RPC boundary. To check this value, use the + // `absl::Status::ok()` member function rather than inspecting the raw code. kOk = 0, + + // StatusCode::kCancelled + // + // kCanelled (gRPC code "CANCELLED") indicates the operation was cancelled, + // typically by the caller. kCancelled = 1, + + // StatusCode::kUnknown + // + // kUnknown (gRPC code "UNKNOWN") indicates an unknown error occurred. In + // general, more specific errors should be raised, if possible. Errors raised + // by APIs that do not return enough error information may be converted to + // this error. kUnknown = 2, + + // StatusCode::kInvalidArgument + // + // kInvalidArgument (gRPC code "INVALID_ARGUMENT") indicates the caller + // specified an invalid argument, such a malformed filename. Note that such + // errors should be narrowly limited to indicate to the invalid nature of the + // arguments themselves. Errors with validly formed arguments that may cause + // errors with the state of the receiving system should be denoted with + // `kFailedPrecondition` instead. kInvalidArgument = 3, + + // StatusCode::kDeadlineExceeded + // + // kDeadlineExceeded (gRPC code "DEADLINE_EXCEEDED") indicates a deadline + // expired before the operation could complete. For operations that may change + // state within a system, this error may be returned even if the operation has + // completed successfully. For example, a successful response from a server + // could have been delayed long enough for the deadline to expire. kDeadlineExceeded = 4, + + // StatusCode::kNotFound + // + // kNotFound (gRPC code "NOT_FOUND") indicates some requested entity (such as + // a file or directory) was not found. + // + // `kNotFound` is useful if a request should be denied for an entire class of + // users, such as during a gradual feature rollout or undocumented allow list. + // If, instead, a request should be denied for specific sets of users, such as + // through user-based access control, use `kPermissionDenied` instead. kNotFound = 5, + + // StatusCode::kAlreadyExists + // + // kAlreadyExists (gRPC code "ALREADY_EXISTS") indicates the entity that a + // caller attempted to create (such as file or directory) is already present. kAlreadyExists = 6, + + // StatusCode::kPermissionDenied + // + // kPermissionDenied (gRPC code "PERMISSION_DENIED") indicates that the caller + // does not have permission to execute the specified operation. Note that this + // error is different than an error due to an *un*authenticated user. This + // error code does not imply the request is valid or the requested entity + // exists or satisfies any other pre-conditions. + // + // `kPermissionDenied` must not be used for rejections caused by exhausting + // some resource. Instead, use `kResourceExhausted` for those errors. + // `kPermissionDenied` must not be used if the caller cannot be identified. + // Instead, use `kUnauthenticated` for those errors. kPermissionDenied = 7, + + // StatusCode::kResourceExhausted + // + // kResourceExhausted (gRPC code "RESOURCE_EXHAUSTED") indicates some resource + // has been exhausted, perhaps a per-user quota, or perhaps the entire file + // system is out of space. kResourceExhausted = 8, + + // StatusCode::kFailedPrecondition + // + // kFailedPrecondition (gRPC code "FAILED_PRECONDITION") indicates that the + // operation was rejected because the system is not in a state required for + // the operation's execution. For example, a directory to be deleted may be + // non-empty, an "rmdir" operation is applied to a non-directory, etc. + // + // Some guidelines that may help a service implementer in deciding between + // `kFailedPrecondition`, `kAborted`, and `kUnavailable`: + // + // (a) Use `kUnavailable` if the client can retry just the failing call. + // (b) Use `kAborted` if the client should retry at a higher transaction + // level (such as when a client-specified test-and-set fails, indicating + // the client should restart a read-modify-write sequence). + // (c) Use `kFailedPrecondition` if the client should not retry until + // the system state has been explicitly fixed. For example, if an "rmdir" + // fails because the directory is non-empty, `kFailedPrecondition` + // should be returned since the client should not retry unless + // the files are deleted from the directory. kFailedPrecondition = 9, + + // StatusCode::kAborted + // + // kAborted (gRPC code "ABORTED") indicates the operation was aborted, + // typically due to a concurrency issue such as a sequencer check failure or a + // failed transaction. + // + // See the guidelines above for deciding between `kFailedPrecondition`, + // `kAborted`, and `kUnavailable`. kAborted = 10, + + // StatusCode::kOutofRange + // + // kOutofRange (gRPC code "OUT_OF_RANGE") indicates the operation was + // attempted past the valid range, such as seeking or reading past an + // end-of-file. + // + // Unlike `kInvalidArgument`, this error indicates a problem that may + // be fixed if the system state changes. For example, a 32-bit file + // system will generate `kInvalidArgument` if asked to read at an + // offset that is not in the range [0,2^32-1], but it will generate + // `kOutOfRange` if asked to read from an offset past the current + // file size. + // + // There is a fair bit of overlap between `kFailedPrecondition` and + // `kOutOfRange`. We recommend using `kOutOfRange` (the more specific + // error) when it applies so that callers who are iterating through + // a space can easily look for an `kOutOfRange` error to detect when + // they are done. kOutOfRange = 11, + + // StatusCode::kUnimplemented + // + // kUnimplemented (gRPC code "UNIMPLEMENTED") indicates the operation is not + // implemented or supported in this service. In this case, the operation + // should not be re-attempted. kUnimplemented = 12, + + // StatusCode::kInternal + // + // kInternal (gRPC code "INTERNAL") indicates an internal error has occurred + // and some invariants expected by the underlying system have not been + // satisfied. This error code is reserved for serious errors. kInternal = 13, + + // StatusCode::kUnavailable + // + // kUnavailable (gRPC code "UNAVAILABLE") indicates the service is currently + // unavailable and that this is most likely a transient condition. An error + // such as this can be corrected by retrying with a backoff scheme. Note that + // it is not always safe to retry non-idempotent operations. + // + // See the guidelines above for deciding between `kFailedPrecondition`, + // `kAborted`, and `kUnavailable`. kUnavailable = 14, + + // StatusCode::kDataLoss + // + // kDataLoss (gRPC code "DATA_LOSS") indicates that unrecoverable data loss or + // corruption has occurred. As this error is serious, proper alerting should + // be attached to errors such as this. kDataLoss = 15, + + // StatusCode::kUnauthenticated + // + // kUnauthenticated (gRPC code "UNAUTHENTICATED") indicates that the request + // does not have valid authentication credentials for the operation. Correct + // the authentication and try again. kUnauthenticated = 16, + + // StatusCode::DoNotUseReservedForFutureExpansionUseDefaultInSwitchInstead_ + // + // NOTE: this error code entry should not be used and you should not rely on + // its value, which may change. + // + // The purpose of this enumerated value is to force people who handle status + // codes with `switch()` statements to *not* simply enumerate all possible + // values, but instead provide a "default:" case. Providing such a default + // case ensures that code will compile when new codes are added. kDoNotUseReservedForFutureExpansionUseDefaultInSwitchInstead_ = 20 }; +// StatusCodeToString() +// // Returns the name for the status code, or "" if it is an unknown value. std::string StatusCodeToString(StatusCode code); +// operator<< +// // Streams StatusCodeToString(code) to `os`. std::ostream& operator<<(std::ostream& os, StatusCode code); -namespace status_internal { - -// Container for status payloads. -struct Payload { - std::string type_url; - absl::Cord payload; -}; - -using Payloads = absl::InlinedVector<Payload, 1>; - -// Reference-counted representation of Status data. -struct StatusRep { - std::atomic<int32_t> ref; - absl::StatusCode code; - std::string message; - std::unique_ptr<status_internal::Payloads> payloads; -}; - -absl::StatusCode MapToLocalCode(int value); -} // namespace status_internal - +// absl::Status +// +// The `absl::Status` class is generally used to gracefully handle errors +// across API boundaries (and in particular across RPC boundaries). Some of +// these errors may be recoverable, but others may not. Most +// functions which can produce a recoverable error should be designed to return +// either an `absl::Status` (or the similar `absl::StatusOr<T>`, which holds +// either an object of type `T` or an error). +// +// API developers should construct their functions to return `absl::OkStatus()` +// upon success, or an `absl::StatusCode` upon another type of error (e.g +// an `absl::StatusCode::kInvalidArgument` error). The API provides convenience +// functions to constuct each status code. +// +// Example: +// +// absl::Status myFunction(absl::string_view fname, ...) { +// ... +// // encounter error +// if (error condition) { +// // Construct an absl::StatusCode::kInvalidArgument error +// return absl::InvalidArgumentError("bad mode"); +// } +// // else, return OK +// return absl::OkStatus(); +// } +// +// Users handling status error codes should prefer checking for an OK status +// using the `ok()` member function. Handling multiple error codes may justify +// use of switch statement, but only check for error codes you know how to +// handle; do not try to exhaustively match against all canonical error codes. +// Errors that cannot be handled should be logged and/or propagated for higher +// levels to deal with. If you do use a switch statement, make sure that you +// also provide a `default:` switch case, so that code does not break as other +// canonical codes are added to the API. +// +// Example: +// +// absl::Status result = DoSomething(); +// if (!result.ok()) { +// LOG(ERROR) << result; +// } +// +// // Provide a default if switching on multiple error codes +// switch (result.code()) { +// // The user hasn't authenticated. Ask them to reauth +// case absl::StatusCode::kUnauthenticated: +// DoReAuth(); +// break; +// // The user does not have permission. Log an error. +// case absl::StatusCode::kPermissionDenied: +// LOG(ERROR) << result; +// break; +// // Propagate the error otherwise. +// default: +// return true; +// } +// +// An `absl::Status` can optionally include a payload with more information +// about the error. Typically, this payload serves one of several purposes: +// +// * It may provide more fine-grained semantic information about the error to +// facilitate actionable remedies. +// * It may provide human-readable contexual information that is more +// appropriate to display to an end user. +// +// Example: +// +// absl::Status result = DoSomething(); +// // Inform user to retry after 30 seconds +// // See more error details in googleapis/google/rpc/error_details.proto +// if (absl::IsResourceExhausted(result)) { +// google::rpc::RetryInfo info; +// info.retry_delay().seconds() = 30; +// // Payloads require a unique key (a URL to ensure no collisions with +// // other payloads), and an `absl::Cord` to hold the encoded data. +// absl::string_view url = "type.googleapis.com/google.rpc.RetryInfo"; +// result.SetPayload(url, info.SerializeAsCord()); +// return result; +// } +// class ABSL_MUST_USE_RESULT Status final { public: - // Creates an OK status with no message or payload. + // Constructors + + // This default constructor creates an OK status with no message or payload. + // Avoid this constructor and prefer explicit construction of an OK status + // with `absl::OkStatus()`. Status(); - // Create a status in the canonical error space with the specified code and - // error message. If `code == util::error::OK`, `msg` is ignored and an - // object identical to an OK status is constructed. + // Creates a status in the canonical error space with the specified + // `absl::StatusCode` and error message. If `code == absl::StatusCode::kOk`, + // `msg` is ignored and an object identical to an OK status is constructed. // - // `msg` must be in UTF-8. The implementation may complain (e.g., + // The `msg` string must be in UTF-8. The implementation may complain (e.g., // by printing a warning) if it is not. Status(absl::StatusCode code, absl::string_view msg); Status(const Status&); Status& operator=(const Status& x); - // Move operations. + // Move operators + // The moved-from state is valid but unspecified. Status(Status&&) noexcept; Status& operator=(Status&&); ~Status(); - // If `this->ok()`, stores `new_status` into *this. If `!this->ok()`, - // preserves the current data. May, in the future, augment the current status - // with additional information about `new_status`. + // Status::Update() + // + // Updates the existing status with `new_status` provided that `this->ok()`. + // If the existing status already contains a non-OK error, this update has no + // effect and preserves the current data. Note that this behavior may change + // in the future to augment a current non-ok status with additional + // information about `new_status`. // - // Convenient way of keeping track of the first error encountered. - // Instead of: - // if (overall_status.ok()) overall_status = new_status - // Use: + // `Update()` provides a convenient way of keeping track of the first error + // encountered. + // + // Example: + // // Instead of "if (overall_status.ok()) overall_status = new_status" // overall_status.Update(new_status); // - // Style guide exception for rvalue reference granted in CL 153567220. void Update(const Status& new_status); void Update(Status&& new_status); - // Returns true if the Status is OK. + // Status::ok() + // + // Returns `true` if `this->ok()`. Prefer checking for an OK status using this + // member function. ABSL_MUST_USE_RESULT bool ok() const; - // Returns the (canonical) error code. + // Status::code() + // + // Returns the canonical error code of type `absl::StatusCode` of this status. absl::StatusCode code() const; - // Returns the raw (canonical) error code which could be out of the range of - // the local `absl::StatusCode` enum. NOTE: This should only be called when - // converting to wire format. Use `code` for error handling. + // Status::raw_code() + // + // Returns a raw (canonical) error code corresponding to the enum value of + // `google.rpc.Code` definitions within + // https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto. + // These values could be out of the range of canonical `absl::StatusCode` + // enum values. + // + // NOTE: This function should only be called when converting to an associated + // wire format. Use `Status::code()` for error handling. int raw_code() const; - // Returns the error message. Note: prefer ToString() for debug logging. - // This message rarely describes the error code. It is not unusual for the - // error message to be the empty std::string. + // Status::message() + // + // Returns the error message associated with this error code, if available. + // Note that this message rarely describes the error code. It is not unusual + // for the error message to be the empty string. As a result, prefer + // `Status::ToString()` for debug logging. absl::string_view message() const; friend bool operator==(const Status&, const Status&); friend bool operator!=(const Status&, const Status&); - // Returns a combination of the error code name, the message and the payloads. - // You can expect the code name and the message to be substrings of the - // result, and the payloads to be printed by the registered printer extensions - // if they are recognized. - // WARNING: Do not depend on the exact format of the result of `ToString()` - // which is subject to change. + // Status::ToString() + // + // Returns a combination of the error code name, the message and any + // associated payload messages. This string is designed simply to be human + // readable and its exact format should not be load bearing. Do not depend on + // the exact format of the result of `ToString()` which is subject to change. + // + // The printed code name and the message are generally substrings of the + // result, and the payloads to be printed use the status payload printer + // mechanism (which is internal). std::string ToString() const; + // Status::IgnoreError() + // // Ignores any errors. This method does nothing except potentially suppress // complaints from any tools that are checking that errors are not dropped on // the floor. void IgnoreError() const; - // Swap the contents of `a` with `b` + // swap() + // + // Swap the contents of one status with another. friend void swap(Status& a, Status& b); - // Payload management APIs - - // Type URL should be unique and follow the naming convention below: - // The idea of type URL comes from `google.protobuf.Any` - // (https://developers.google.com/protocol-buffers/docs/proto3#any). The - // type URL should be globally unique and follow the format of URL - // (https://en.wikipedia.org/wiki/URL). The default type URL for a given - // protobuf message type is "type.googleapis.com/packagename.messagename". For - // other custom wire formats, users should define the format of type URL in a - // similar practice so as to minimize the chance of conflict between type - // URLs. Users should make sure that the type URL can be mapped to a concrete - // C++ type if they want to deserialize the payload and read it effectively. + //---------------------------------------------------------------------------- + // Payload Management APIs + //---------------------------------------------------------------------------- - // Gets the payload based for `type_url` key, if it is present. + // A payload may be attached to a status to provide additional context to an + // error that may not be satisifed by an existing `absl::StatusCode`. + // Typically, this payload serves one of several purposes: + // + // * It may provide more fine-grained semantic information about the error + // to facilitate actionable remedies. + // * It may provide human-readable contexual information that is more + // appropriate to display to an end user. + // + // A payload consists of a [key,value] pair, where the key is a string + // referring to a unique "type URL" and the value is an object of type + // `absl::Cord` to hold the contextual data. + // + // The "type URL" should be unique and follow the format of a URL + // (https://en.wikipedia.org/wiki/URL) and, ideally, provide some + // documentation or schema on how to interpret its associated data. For + // example, the default type URL for a protobuf message type is + // "type.googleapis.com/packagename.messagename". Other custom wire formats + // should define the format of type URL in a similar practice so as to + // minimize the chance of conflict between type URLs. + // Users should ensure that the type URL can be mapped to a concrete + // C++ type if they want to deserialize the payload and read it effectively. + // + // To attach a payload to a status object, call `Status::SetPayload()`, + // passing it the type URL and an `absl::Cord` of associated data. Similarly, + // to extract the payload from a status, call `Status::GetPayload()`. You + // may attach multiple payloads (with differing type URLs) to any given + // status object, provided that the status is currently exhibiting an error + // code (i.e. is not OK). + + // Status::GetPayload() + // + // Gets the payload of a status given its unique `type_url` key, if present. absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const; - // Sets the payload for `type_url` key for a non-ok status, overwriting any - // existing payload for `type_url`. + // Status::SetPayload() + // + // Sets the payload for a non-ok status using a `type_url` key, overwriting + // any existing payload for that `type_url`. // - // NOTE: Does nothing if the Status is ok. + // NOTE: This function does nothing if the Status is ok. void SetPayload(absl::string_view type_url, absl::Cord payload); - // Erases the payload corresponding to the `type_url` key. Returns true if + // Status::ErasePayload() + // + // Erases the payload corresponding to the `type_url` key. Returns `true` if // the payload was present. bool ErasePayload(absl::string_view type_url); - // Iterates over the stored payloads and calls `visitor(type_key, payload)` - // for each one. + // Status::ForEachPayload() // - // NOTE: The order of calls to `visitor` is not specified and may change at + // Iterates over the stored payloads and calls the + // `visitor(type_key, payload)` callable for each one. + // + // NOTE: The order of calls to `visitor()` is not specified and may change at // any time. // - // NOTE: Any mutation on the same 'Status' object during visitation is + // NOTE: Any mutation on the same 'absl::Status' object during visitation is // forbidden and could result in undefined behavior. void ForEachPayload( const std::function<void(absl::string_view, const absl::Cord&)>& visitor) @@ -231,7 +580,7 @@ class ABSL_MUST_USE_RESULT Status final { static uintptr_t PointerToRep(status_internal::StatusRep* r); static status_internal::StatusRep* RepToPointer(uintptr_t r); - // Returns std::string for non-ok Status. + // Returns string for non-ok Status. std::string ToStringSlow() const; // Status supports two different representations. @@ -245,14 +594,93 @@ class ABSL_MUST_USE_RESULT Status final { uintptr_t rep_; }; -// Returns an OK status, equivalent to a default constructed instance. +// OkStatus() +// +// Returns an OK status, equivalent to a default constructed instance. Prefer +// usage of `absl::OkStatus()` when constructing such an OK status. Status OkStatus(); +// operator<<() +// // Prints a human-readable representation of `x` to `os`. std::ostream& operator<<(std::ostream& os, const Status& x); -// ----------------------------------------------------------------- +// IsAborted() +// IsAlreadyExists() +// IsCancelled() +// IsDataLoss() +// IsDeadlineExceeded() +// IsFailedPrecondition() +// IsInternal() +// IsInvalidArgument() +// IsNotFound() +// IsOutOfRange() +// IsPermissionDenied() +// IsResourceExhausted() +// IsUnauthenticated() +// IsUnavailable() +// IsUnimplemented() +// IsUnknown() +// +// These convenience functions return `true` if a given status matches the +// `absl::StatusCode` error code of its associated function. +ABSL_MUST_USE_RESULT bool IsAborted(const Status& status); +ABSL_MUST_USE_RESULT bool IsAlreadyExists(const Status& status); +ABSL_MUST_USE_RESULT bool IsCancelled(const Status& status); +ABSL_MUST_USE_RESULT bool IsDataLoss(const Status& status); +ABSL_MUST_USE_RESULT bool IsDeadlineExceeded(const Status& status); +ABSL_MUST_USE_RESULT bool IsFailedPrecondition(const Status& status); +ABSL_MUST_USE_RESULT bool IsInternal(const Status& status); +ABSL_MUST_USE_RESULT bool IsInvalidArgument(const Status& status); +ABSL_MUST_USE_RESULT bool IsNotFound(const Status& status); +ABSL_MUST_USE_RESULT bool IsOutOfRange(const Status& status); +ABSL_MUST_USE_RESULT bool IsPermissionDenied(const Status& status); +ABSL_MUST_USE_RESULT bool IsResourceExhausted(const Status& status); +ABSL_MUST_USE_RESULT bool IsUnauthenticated(const Status& status); +ABSL_MUST_USE_RESULT bool IsUnavailable(const Status& status); +ABSL_MUST_USE_RESULT bool IsUnimplemented(const Status& status); +ABSL_MUST_USE_RESULT bool IsUnknown(const Status& status); + +// AbortedError() +// AlreadyExistsError() +// CancelledError() +// DataLossError() +// DeadlineExceededError() +// FailedPreconditionError() +// InternalError() +// InvalidArgumentError() +// NotFoundError() +// OutOfRangeError() +// PermissionDeniedError() +// ResourceExhaustedError() +// UnauthenticatedError() +// UnavailableError() +// UnimplementedError() +// UnknownError() +// +// These convenience functions create an `absl::Status` object with an error +// code as indicated by the associated function name, using the error message +// passed in `message`. +Status AbortedError(absl::string_view message); +Status AlreadyExistsError(absl::string_view message); +Status CancelledError(absl::string_view message); +Status DataLossError(absl::string_view message); +Status DeadlineExceededError(absl::string_view message); +Status FailedPreconditionError(absl::string_view message); +Status InternalError(absl::string_view message); +Status InvalidArgumentError(absl::string_view message); +Status NotFoundError(absl::string_view message); +Status OutOfRangeError(absl::string_view message); +Status PermissionDeniedError(absl::string_view message); +Status ResourceExhaustedError(absl::string_view message); +Status UnauthenticatedError(absl::string_view message); +Status UnavailableError(absl::string_view message); +Status UnimplementedError(absl::string_view message); +Status UnknownError(absl::string_view message); + +//------------------------------------------------------------------------------ // Implementation details follow +//------------------------------------------------------------------------------ inline Status::Status() : rep_(CodeToInlinedRep(absl::StatusCode::kOk)) {} @@ -378,50 +806,11 @@ inline void Status::Unref(uintptr_t rep) { inline Status OkStatus() { return Status(); } -// Each of the functions below creates a Status object with a particular error -// code and the given message. The error code of the returned status object -// matches the name of the function. -Status AbortedError(absl::string_view message); -Status AlreadyExistsError(absl::string_view message); -Status CancelledError(absl::string_view message); -Status DataLossError(absl::string_view message); -Status DeadlineExceededError(absl::string_view message); -Status FailedPreconditionError(absl::string_view message); -Status InternalError(absl::string_view message); -Status InvalidArgumentError(absl::string_view message); -Status NotFoundError(absl::string_view message); -Status OutOfRangeError(absl::string_view message); -Status PermissionDeniedError(absl::string_view message); -Status ResourceExhaustedError(absl::string_view message); -Status UnauthenticatedError(absl::string_view message); -Status UnavailableError(absl::string_view message); -Status UnimplementedError(absl::string_view message); -Status UnknownError(absl::string_view message); - // Creates a `Status` object with the `absl::StatusCode::kCancelled` error code // and an empty message. It is provided only for efficiency, given that // message-less kCancelled errors are common in the infrastructure. inline Status CancelledError() { return Status(absl::StatusCode::kCancelled); } -// Each of the functions below returns true if the given status matches the -// error code implied by the function's name. -ABSL_MUST_USE_RESULT bool IsAborted(const Status& status); -ABSL_MUST_USE_RESULT bool IsAlreadyExists(const Status& status); -ABSL_MUST_USE_RESULT bool IsCancelled(const Status& status); -ABSL_MUST_USE_RESULT bool IsDataLoss(const Status& status); -ABSL_MUST_USE_RESULT bool IsDeadlineExceeded(const Status& status); -ABSL_MUST_USE_RESULT bool IsFailedPrecondition(const Status& status); -ABSL_MUST_USE_RESULT bool IsInternal(const Status& status); -ABSL_MUST_USE_RESULT bool IsInvalidArgument(const Status& status); -ABSL_MUST_USE_RESULT bool IsNotFound(const Status& status); -ABSL_MUST_USE_RESULT bool IsOutOfRange(const Status& status); -ABSL_MUST_USE_RESULT bool IsPermissionDenied(const Status& status); -ABSL_MUST_USE_RESULT bool IsResourceExhausted(const Status& status); -ABSL_MUST_USE_RESULT bool IsUnauthenticated(const Status& status); -ABSL_MUST_USE_RESULT bool IsUnavailable(const Status& status); -ABSL_MUST_USE_RESULT bool IsUnimplemented(const Status& status); -ABSL_MUST_USE_RESULT bool IsUnknown(const Status& status); - ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/status/status_payload_printer.cc b/absl/status/status_payload_printer.cc index ad96d76a..a47aea11 100644 --- a/absl/status/status_payload_printer.cc +++ b/absl/status/status_payload_printer.cc @@ -16,26 +16,21 @@ #include <atomic> #include "absl/base/attributes.h" +#include "absl/base/internal/atomic_hook.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace status_internal { -namespace { -// Tried constant initialized global variable but it doesn't work with Lexan -// (MSVC's `std::atomic` has trouble constant initializing). -std::atomic<StatusPayloadPrinter>& GetStatusPayloadPrinterStorage() { - ABSL_CONST_INIT static std::atomic<StatusPayloadPrinter> instance{nullptr}; - return instance; -} -} // namespace +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES +static absl::base_internal::AtomicHook<StatusPayloadPrinter> storage; void SetStatusPayloadPrinter(StatusPayloadPrinter printer) { - GetStatusPayloadPrinterStorage().store(printer, std::memory_order_relaxed); + storage.Store(printer); } StatusPayloadPrinter GetStatusPayloadPrinter() { - return GetStatusPayloadPrinterStorage().load(std::memory_order_relaxed); + return storage.Load(); } } // namespace status_internal diff --git a/absl/status/status_test.cc b/absl/status/status_test.cc index 7cc65e45..ca9488ad 100644 --- a/absl/status/status_test.cc +++ b/absl/status/status_test.cc @@ -204,6 +204,25 @@ TEST(Status, TestComparePayloads) { EXPECT_EQ(bad_status1, bad_status2); } +TEST(Status, TestComparePayloadsAfterErase) { + absl::Status payload_status(absl::StatusCode::kInternal, ""); + payload_status.SetPayload(kUrl1, absl::Cord(kPayload1)); + payload_status.SetPayload(kUrl2, absl::Cord(kPayload2)); + + absl::Status empty_status(absl::StatusCode::kInternal, ""); + + // Different payloads, not equal + EXPECT_NE(payload_status, empty_status); + EXPECT_TRUE(payload_status.ErasePayload(kUrl1)); + + // Still Different payloads, still not equal. + EXPECT_NE(payload_status, empty_status); + EXPECT_TRUE(payload_status.ErasePayload(kUrl2)); + + // Both empty payloads, should be equal + EXPECT_EQ(payload_status, empty_status); +} + PayloadsVec AllVisitedPayloads(const absl::Status& s) { PayloadsVec result; @@ -261,6 +280,36 @@ TEST(Status, ToString) { HasSubstr("[bar='\\xff']"))); } +absl::Status EraseAndReturn(const absl::Status& base) { + absl::Status copy = base; + EXPECT_TRUE(copy.ErasePayload(kUrl1)); + return copy; +} + +TEST(Status, CopyOnWriteForErasePayload) { + { + absl::Status base(absl::StatusCode::kInvalidArgument, "fail"); + base.SetPayload(kUrl1, absl::Cord(kPayload1)); + EXPECT_TRUE(base.GetPayload(kUrl1).has_value()); + absl::Status copy = EraseAndReturn(base); + EXPECT_TRUE(base.GetPayload(kUrl1).has_value()); + EXPECT_FALSE(copy.GetPayload(kUrl1).has_value()); + } + { + absl::Status base(absl::StatusCode::kInvalidArgument, "fail"); + base.SetPayload(kUrl1, absl::Cord(kPayload1)); + absl::Status copy = base; + + EXPECT_TRUE(base.GetPayload(kUrl1).has_value()); + EXPECT_TRUE(copy.GetPayload(kUrl1).has_value()); + + EXPECT_TRUE(base.ErasePayload(kUrl1)); + + EXPECT_FALSE(base.GetPayload(kUrl1).has_value()); + EXPECT_TRUE(copy.GetPayload(kUrl1).has_value()); + } +} + TEST(Status, CopyConstructor) { { absl::Status status; @@ -300,6 +349,14 @@ TEST(Status, CopyAssignment) { } } +TEST(Status, CopyAssignmentIsNotRef) { + const absl::Status status_orig(absl::StatusCode::kInvalidArgument, "message"); + absl::Status status_copy = status_orig; + EXPECT_EQ(status_orig, status_copy); + status_copy.SetPayload(kUrl1, absl::Cord(kPayload1)); + EXPECT_NE(status_orig, status_copy); +} + TEST(Status, MoveConstructor) { { absl::Status status; diff --git a/absl/status/statusor.cc b/absl/status/statusor.cc new file mode 100644 index 00000000..b954b45e --- /dev/null +++ b/absl/status/statusor.cc @@ -0,0 +1,71 @@ +// Copyright 2020 The Abseil Authors. +// +// 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 +// +// https://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. +#include "absl/status/statusor.h" + +#include <cstdlib> +#include <utility> + +#include "absl/base/internal/raw_logging.h" +#include "absl/status/status.h" +#include "absl/strings/str_cat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +BadStatusOrAccess::BadStatusOrAccess(absl::Status status) + : status_(std::move(status)) {} + +BadStatusOrAccess::~BadStatusOrAccess() = default; +const char* BadStatusOrAccess::what() const noexcept { + return "Bad StatusOr access"; +} + +const absl::Status& BadStatusOrAccess::status() const { return status_; } + +namespace internal_statusor { + +void Helper::HandleInvalidStatusCtorArg(absl::Status* status) { + const char* kMessage = + "An OK status is not a valid constructor argument to StatusOr<T>"; +#ifdef NDEBUG + ABSL_INTERNAL_LOG(ERROR, kMessage); +#else + ABSL_INTERNAL_LOG(FATAL, kMessage); +#endif + // In optimized builds, we will fall back to InternalError. + *status = absl::InternalError(kMessage); +} + +void Helper::Crash(const absl::Status& status) { + ABSL_INTERNAL_LOG( + FATAL, + absl::StrCat("Attempting to fetch value instead of handling error ", + status.ToString())); +} + +void ThrowBadStatusOrAccess(absl::Status status) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw absl::BadStatusOrAccess(std::move(status)); +#else + ABSL_INTERNAL_LOG( + FATAL, + absl::StrCat("Attempting to fetch value instead of handling error ", + status.ToString())); + std::abort(); +#endif +} + +} // namespace internal_statusor +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/status/statusor.h b/absl/status/statusor.h new file mode 100644 index 00000000..bdf6039d --- /dev/null +++ b/absl/status/statusor.h @@ -0,0 +1,760 @@ +// Copyright 2020 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: statusor.h +// ----------------------------------------------------------------------------- +// +// An `absl::StatusOr<T>` represents a union of an `absl::Status` object +// and an object of type `T`. The `absl::StatusOr<T>` will either contain an +// object of type `T` (indicating a successful operation), or an error (of type +// `absl::Status`) explaining why such a value is not present. +// +// In general, check the success of an operation returning an +// `absl::StatusOr<T>` like you would an `absl::Status` by using the `ok()` +// member function. +// +// Example: +// +// StatusOr<Foo> result = Calculation(); +// if (result.ok()) { +// result->DoSomethingCool(); +// } else { +// LOG(ERROR) << result.status(); +// } +#ifndef ABSL_STATUS_STATUSOR_H_ +#define ABSL_STATUS_STATUSOR_H_ + +#include <exception> +#include <initializer_list> +#include <new> +#include <string> +#include <type_traits> +#include <utility> + +#include "absl/base/attributes.h" +#include "absl/meta/type_traits.h" +#include "absl/status/internal/statusor_internal.h" +#include "absl/status/status.h" +#include "absl/types/variant.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// BadStatusOrAccess +// +// This class defines the type of object to throw (if exceptions are enabled), +// when accessing the value of an `absl::StatusOr<T>` object that does not +// contain a value. This behavior is analogous to that of +// `std::bad_optional_access` in the case of accessing an invalid +// `std::optional` value. +// +// Example: +// +// try { +// absl::StatusOr<int> v = FetchInt(); +// DoWork(v.value()); // Accessing value() when not "OK" may throw +// } catch (absl::BadStatusOrAccess& ex) { +// LOG(ERROR) << ex.status(); +// } +class BadStatusOrAccess : public std::exception { + public: + explicit BadStatusOrAccess(absl::Status status); + ~BadStatusOrAccess() override; + + // BadStatusOrAccess::what() + // + // Returns the associated explanatory string of the `absl::StatusOr<T>` + // object's error code. This function only returns the string literal "Bad + // StatusOr Access" for cases when evaluating general exceptions. + // + // The pointer of this string is guaranteed to be valid until any non-const + // function is invoked on the exception object. + const char* what() const noexcept override; + + // BadStatusOrAccess::status() + // + // Returns the associated `absl::Status` of the `absl::StatusOr<T>` object's + // error. + const absl::Status& status() const; + + private: + absl::Status status_; +}; + +// Returned StatusOr objects may not be ignored. +template <typename T> +class ABSL_MUST_USE_RESULT StatusOr; + +// absl::StatusOr<T> +// +// The `absl::StatusOr<T>` class template is a union of an `absl::Status` object +// and an object of type `T`. The `absl::StatusOr<T>` models an object that is +// either a usable object, or an error (of type `absl::Status`) explaining why +// such an object is not present. An `absl::StatusOr<T>` is typically the return +// value of a function which may fail. +// +// An `absl::StatusOr<T>` can never hold an "OK" status (an +// `absl::StatusCode::kOk` value); instead, the presence of an object of type +// `T` indicates success. Instead of checking for a `kOk` value, use the +// `absl::StatusOr<T>::ok()` member function. (It is for this reason, and code +// readability, that using the `ok()` function is preferred for `absl::Status` +// as well.) +// +// Example: +// +// StatusOr<Foo> result = DoBigCalculationThatCouldFail(); +// if (result.ok()) { +// result->DoSomethingCool(); +// } else { +// LOG(ERROR) << result.status(); +// } +// +// Accessing the object held by an `absl::StatusOr<T>` should be performed via +// `operator*` or `operator->`, after a call to `ok()` confirms that the +// `absl::StatusOr<T>` holds an object of type `T`: +// +// Example: +// +// absl::StatusOr<int> i = GetCount(); +// if (foo.ok()) { +// updated_total += *i +// } +// +// NOTE: using `absl::StatusOr<T>::value()` when no valid value is present will +// throw an exception if exceptions are enabled or terminate the process when +// execeptions are not enabled. +// +// Example: +// +// StatusOr<Foo> result = DoBigCalculationThatCouldFail(); +// const Foo& foo = result.value(); // Crash/exception if no value present +// foo.DoSomethingCool(); +// +// A `absl::StatusOr<T*>` can be constructed from a null pointer like any other +// pointer value, and the result will be that `ok()` returns `true` and +// `value()` returns `nullptr`. Checking the value of pointer in an +// `absl::StatusOr<T>` generally requires a bit more care, to ensure both that a +// value is present and that value is not null: +// +// StatusOr<std::unique_ptr<Foo>> result = FooFactory::MakeNewFoo(arg); +// if (!result.ok()) { +// LOG(ERROR) << result.status(); +// } else if (*result == nullptr) { +// LOG(ERROR) << "Unexpected null pointer"; +// } else { +// (*result)->DoSomethingCool(); +// } +// +// Example factory implementation returning StatusOr<T>: +// +// StatusOr<Foo> FooFactory::MakeFoo(int arg) { +// if (arg <= 0) { +// return absl::Status(absl::StatusCode::kInvalidArgument, +// "Arg must be positive"); +// } +// return Foo(arg); +// } +template <typename T> +class StatusOr : private internal_statusor::StatusOrData<T>, + private internal_statusor::CopyCtorBase<T>, + private internal_statusor::MoveCtorBase<T>, + private internal_statusor::CopyAssignBase<T>, + private internal_statusor::MoveAssignBase<T> { + template <typename U> + friend class StatusOr; + + typedef internal_statusor::StatusOrData<T> Base; + + public: + // StatusOr<T>::value_type + // + // This instance data provides a generic `value_type` member for use within + // generic programming. This usage is analogous to that of + // `optional::value_type` in the case of `std::optional`. + typedef T value_type; + + // Constructors + + // Constructs a new `absl::StatusOr` with an `absl::StatusCode::kUnknown` + // status. This constructor is marked 'explicit' to prevent usages in return + // values such as 'return {};', under the misconception that + // `absl::StatusOr<std::vector<int>>` will be initialized with an empty + // vector, instead of an `absl::StatusCode::kUnknown` error code. + explicit StatusOr(); + + // `StatusOr<T>` is copy constructible if `T` is copy constructible. + StatusOr(const StatusOr&) = default; + // `StatusOr<T>` is copy assignable if `T` is copy constructible and copy + // assignable. + StatusOr& operator=(const StatusOr&) = default; + + // `StatusOr<T>` is move constructible if `T` is move constructible. + StatusOr(StatusOr&&) = default; + // `StatusOr<T>` is moveAssignable if `T` is move constructible and move + // assignable. + StatusOr& operator=(StatusOr&&) = default; + + // Converting Constructors + + // Constructs a new `absl::StatusOr<T>` from an `absl::StatusOr<U>`, when `T` + // is constructible from `U`. To avoid ambiguity, these constructors are + // disabled if `T` is also constructible from `StatusOr<U>.`. This constructor + // is explicit if and only if the corresponding construction of `T` from `U` + // is explicit. (This constructor inherits its explicitness from the + // underlying constructor.) + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation<std::is_same<T, U>>, + std::is_constructible<T, const U&>, + std::is_convertible<const U&, T>, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr(const StatusOr<U>& other) // NOLINT + : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {} + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation<std::is_same<T, U>>, + std::is_constructible<T, const U&>, + absl::negation<std::is_convertible<const U&, T>>, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + explicit StatusOr(const StatusOr<U>& other) + : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {} + + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, + std::is_convertible<U&&, T>, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr(StatusOr<U>&& other) // NOLINT + : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {} + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, + absl::negation<std::is_convertible<U&&, T>>, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + explicit StatusOr(StatusOr<U>&& other) + : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {} + + // Converting Assignment Operators + + // Creates an `absl::StatusOr<T>` through assignment from an + // `absl::StatusOr<U>` when: + // + // * Both `absl::StatusOr<T>` and `absl::StatusOr<U>` are OK by assigning + // `U` to `T` directly. + // * `absl::StatusOr<T>` is OK and `absl::StatusOr<U>` contains an error + // code by destroying `absl::StatusOr<T>`'s value and assigning from + // `absl::StatusOr<U>' + // * `absl::StatusOr<T>` contains an error code and `absl::StatusOr<U>` is + // OK by directly initializing `T` from `U`. + // * Both `absl::StatusOr<T>` and `absl::StatusOr<U>` contain an error + // code by assigning the `Status` in `absl::StatusOr<U>` to + // `absl::StatusOr<T>` + // + // These overloads only apply if `absl::StatusOr<T>` is constructible and + // assignable from `absl::StatusOr<U>` and `StatusOr<T>` cannot be directly + // assigned from `StatusOr<U>`. + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation<std::is_same<T, U>>, + std::is_constructible<T, const U&>, + std::is_assignable<T, const U&>, + absl::negation< + internal_statusor:: + IsConstructibleOrConvertibleOrAssignableFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr& operator=(const StatusOr<U>& other) { + this->Assign(other); + return *this; + } + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, + std::is_assignable<T, U&&>, + absl::negation< + internal_statusor:: + IsConstructibleOrConvertibleOrAssignableFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr& operator=(StatusOr<U>&& other) { + this->Assign(std::move(other)); + return *this; + } + + // Constructs a new `absl::StatusOr<T>` with a non-ok status. After calling + // this constructor, `this->ok()` will be `false` and calls to `value()` will + // crash, or produce an exception if exceptions are enabled. + // + // The constructor also takes any type `U` that is convertible to + // `absl::Status`. This constructor is explicit if an only if `U` is not of + // type `absl::Status` and the conversion from `U` to `Status` is explicit. + // + // REQUIRES: !Status(std::forward<U>(v)).ok(). This requirement is DCHECKed. + // In optimized builds, passing absl::OkStatus() here will have the effect + // of passing absl::StatusCode::kInternal as a fallback. + template < + typename U = absl::Status, + absl::enable_if_t< + absl::conjunction< + std::is_convertible<U&&, absl::Status>, + std::is_constructible<absl::Status, U&&>, + absl::negation<std::is_same<absl::decay_t<U>, absl::StatusOr<T>>>, + absl::negation<std::is_same<absl::decay_t<U>, T>>, + absl::negation<std::is_same<absl::decay_t<U>, absl::in_place_t>>, + absl::negation<internal_statusor::HasConversionOperatorToStatusOr< + T, U&&>>>::value, + int> = 0> + StatusOr(U&& v) : Base(std::forward<U>(v)) {} + + template < + typename U = absl::Status, + absl::enable_if_t< + absl::conjunction< + absl::negation<std::is_convertible<U&&, absl::Status>>, + std::is_constructible<absl::Status, U&&>, + absl::negation<std::is_same<absl::decay_t<U>, absl::StatusOr<T>>>, + absl::negation<std::is_same<absl::decay_t<U>, T>>, + absl::negation<std::is_same<absl::decay_t<U>, absl::in_place_t>>, + absl::negation<internal_statusor::HasConversionOperatorToStatusOr< + T, U&&>>>::value, + int> = 0> + explicit StatusOr(U&& v) : Base(std::forward<U>(v)) {} + + template < + typename U = absl::Status, + absl::enable_if_t< + absl::conjunction< + std::is_convertible<U&&, absl::Status>, + std::is_constructible<absl::Status, U&&>, + absl::negation<std::is_same<absl::decay_t<U>, absl::StatusOr<T>>>, + absl::negation<std::is_same<absl::decay_t<U>, T>>, + absl::negation<std::is_same<absl::decay_t<U>, absl::in_place_t>>, + absl::negation<internal_statusor::HasConversionOperatorToStatusOr< + T, U&&>>>::value, + int> = 0> + StatusOr& operator=(U&& v) { + this->AssignStatus(std::forward<U>(v)); + return *this; + } + + // Perfect-forwarding value assignment operator. + + // If `*this` contains a `T` value before the call, the contained value is + // assigned from `std::forward<U>(v)`; Otherwise, it is directly-initialized + // from `std::forward<U>(v)`. + // This function does not participate in overload unless: + // 1. `std::is_constructible_v<T, U>` is true, + // 2. `std::is_assignable_v<T&, U>` is true. + // 3. `std::is_same_v<StatusOr<T>, std::remove_cvref_t<U>>` is false. + // 4. Assigning `U` to `T` is not ambiguous: + // If `U` is `StatusOr<V>` and `T` is constructible and assignable from + // both `StatusOr<V>` and `V`, the assignment is considered bug-prone and + // ambiguous thus will fail to compile. For example: + // StatusOr<bool> s1 = true; // s1.ok() && *s1 == true + // StatusOr<bool> s2 = false; // s2.ok() && *s2 == false + // s1 = s2; // ambiguous, `s1 = *s2` or `s1 = bool(s2)`? + template < + typename U = T, + typename = typename std::enable_if<absl::conjunction< + std::is_constructible<T, U&&>, std::is_assignable<T&, U&&>, + absl::disjunction< + std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, T>, + absl::conjunction< + absl::negation<std::is_convertible<U&&, absl::Status>>, + absl::negation<internal_statusor:: + HasConversionOperatorToStatusOr<T, U&&>>>>, + internal_statusor::IsForwardingAssignmentValid<T, U&&>>::value>::type> + StatusOr& operator=(U&& v) { + this->Assign(std::forward<U>(v)); + return *this; + } + + // Constructs the inner value `T` in-place using the provided args, using the + // `T(args...)` constructor. + template <typename... Args> + explicit StatusOr(absl::in_place_t, Args&&... args); + template <typename U, typename... Args> + explicit StatusOr(absl::in_place_t, std::initializer_list<U> ilist, + Args&&... args); + + // Constructs the inner value `T` in-place using the provided args, using the + // `T(U)` (direct-initialization) constructor. This constructor is only valid + // if `T` can be constructed from a `U`. Can accept move or copy constructors. + // + // This constructor is explicit if `U` is not convertible to `T`. To avoid + // ambiguity, this constuctor is disabled if `U` is a `StatusOr<J>`, where `J` + // is convertible to `T`. + template < + typename U = T, + absl::enable_if_t< + absl::conjunction< + internal_statusor::IsDirectInitializationValid<T, U&&>, + std::is_constructible<T, U&&>, std::is_convertible<U&&, T>, + absl::disjunction< + std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, + T>, + absl::conjunction< + absl::negation<std::is_convertible<U&&, absl::Status>>, + absl::negation< + internal_statusor::HasConversionOperatorToStatusOr< + T, U&&>>>>>::value, + int> = 0> + StatusOr(U&& u) // NOLINT + : StatusOr(absl::in_place, std::forward<U>(u)) { + } + + template < + typename U = T, + absl::enable_if_t< + absl::conjunction< + internal_statusor::IsDirectInitializationValid<T, U&&>, + absl::disjunction< + std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, + T>, + absl::conjunction< + absl::negation<std::is_constructible<absl::Status, U&&>>, + absl::negation< + internal_statusor::HasConversionOperatorToStatusOr< + T, U&&>>>>, + std::is_constructible<T, U&&>, + absl::negation<std::is_convertible<U&&, T>>>::value, + int> = 0> + explicit StatusOr(U&& u) // NOLINT + : StatusOr(absl::in_place, std::forward<U>(u)) { + } + + // StatusOr<T>::ok() + // + // Returns whether or not this `absl::StatusOr<T>` holds a `T` value. This + // member function is analagous to `absl::Status::ok()` and should be used + // similarly to check the status of return values. + // + // Example: + // + // StatusOr<Foo> result = DoBigCalculationThatCouldFail(); + // if (result.ok()) { + // // Handle result + // else { + // // Handle error + // } + ABSL_MUST_USE_RESULT bool ok() const { return this->status_.ok(); } + + // StatusOr<T>::status() + // + // Returns a reference to the current `absl::Status` contained within the + // `absl::StatusOr<T>`. If `absl::StatusOr<T>` contains a `T`, then this + // function returns `absl::OkStatus()`. + const Status& status() const &; + Status status() &&; + + // StatusOr<T>::value() + // + // Returns a reference to the held value if `this->ok()`. Otherwise, throws + // `absl::BadStatusOrAccess` if exceptions are enabled, or is guaranteed to + // terminate the process if exceptions are disabled. + // + // If you have already checked the status using `this->ok()`, you probably + // want to use `operator*()` or `operator->()` to access the value instead of + // `value`. + // + // Note: for value types that are cheap to copy, prefer simple code: + // + // T value = statusor.value(); + // + // Otherwise, if the value type is expensive to copy, but can be left + // in the StatusOr, simply assign to a reference: + // + // T& value = statusor.value(); // or `const T&` + // + // Otherwise, if the value type supports an efficient move, it can be + // used as follows: + // + // T value = std::move(statusor).value(); + // + // The `std::move` on statusor instead of on the whole expression enables + // warnings about possible uses of the statusor object after the move. + const T& value() const&; + T& value() &; + const T&& value() const&&; + T&& value() &&; + + // StatusOr<T>:: operator*() + // + // Returns a reference to the current value. + // + // REQUIRES: `this->ok() == true`, otherwise the behavior is undefined. + // + // Use `this->ok()` to verify that there is a current value within the + // `absl::StatusOr<T>`. Alternatively, see the `value()` member function for a + // similar API that guarantees crashing or throwing an exception if there is + // no current value. + const T& operator*() const&; + T& operator*() &; + const T&& operator*() const&&; + T&& operator*() &&; + + // StatusOr<T>::operator->() + // + // Returns a pointer to the current value. + // + // REQUIRES: `this->ok() == true`, otherwise the behavior is undefined. + // + // Use `this->ok()` to verify that there is a current value. + const T* operator->() const; + T* operator->(); + + // StatusOr<T>::value_or() + // + // Returns the current value of `this->ok() == true`. Otherwise constructs a + // value using the provided `default_value`. + // + // Unlike `value`, this function returns by value, copying the current value + // if necessary. If the value type supports an efficient move, it can be used + // as follows: + // + // T value = std::move(statusor).value_or(def); + // + // Unlike with `value`, calling `std::move()` on the result of `value_or` will + // still trigger a copy. + template <typename U> + T value_or(U&& default_value) const&; + template <typename U> + T value_or(U&& default_value) &&; + + // StatusOr<T>::IgnoreError() + // + // Ignores any errors. This method does nothing except potentially suppress + // complaints from any tools that are checking that errors are not dropped on + // the floor. + void IgnoreError() const; + + // StatusOr<T>::emplace() + // + // Reconstructs the inner value T in-place using the provided args, using the + // T(args...) constructor. Returns reference to the reconstructed `T`. + template <typename... Args> + T& emplace(Args&&... args) { + if (ok()) { + this->Clear(); + this->MakeValue(std::forward<Args>(args)...); + } else { + this->MakeValue(std::forward<Args>(args)...); + this->status_ = absl::OkStatus(); + } + return this->data_; + } + + template < + typename U, typename... Args, + absl::enable_if_t< + std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value, + int> = 0> + T& emplace(std::initializer_list<U> ilist, Args&&... args) { + if (ok()) { + this->Clear(); + this->MakeValue(ilist, std::forward<Args>(args)...); + } else { + this->MakeValue(ilist, std::forward<Args>(args)...); + this->status_ = absl::OkStatus(); + } + return this->data_; + } + + private: + using internal_statusor::StatusOrData<T>::Assign; + template <typename U> + void Assign(const absl::StatusOr<U>& other); + template <typename U> + void Assign(absl::StatusOr<U>&& other); +}; + +// operator==() +// +// This operator checks the equality of two `absl::StatusOr<T>` objects. +template <typename T> +bool operator==(const StatusOr<T>& lhs, const StatusOr<T>& rhs) { + if (lhs.ok() && rhs.ok()) return *lhs == *rhs; + return lhs.status() == rhs.status(); +} + +// operator!=() +// +// This operator checks the inequality of two `absl::StatusOr<T>` objects. +template <typename T> +bool operator!=(const StatusOr<T>& lhs, const StatusOr<T>& rhs) { + return !(lhs == rhs); +} + +//------------------------------------------------------------------------------ +// Implementation details for StatusOr<T> +//------------------------------------------------------------------------------ + +// TODO(sbenza): avoid the string here completely. +template <typename T> +StatusOr<T>::StatusOr() : Base(Status(absl::StatusCode::kUnknown, "")) {} + +template <typename T> +template <typename U> +inline void StatusOr<T>::Assign(const StatusOr<U>& other) { + if (other.ok()) { + this->Assign(*other); + } else { + this->AssignStatus(other.status()); + } +} + +template <typename T> +template <typename U> +inline void StatusOr<T>::Assign(StatusOr<U>&& other) { + if (other.ok()) { + this->Assign(*std::move(other)); + } else { + this->AssignStatus(std::move(other).status()); + } +} +template <typename T> +template <typename... Args> +StatusOr<T>::StatusOr(absl::in_place_t, Args&&... args) + : Base(absl::in_place, std::forward<Args>(args)...) {} + +template <typename T> +template <typename U, typename... Args> +StatusOr<T>::StatusOr(absl::in_place_t, std::initializer_list<U> ilist, + Args&&... args) + : Base(absl::in_place, ilist, std::forward<Args>(args)...) {} + +template <typename T> +const Status& StatusOr<T>::status() const & { return this->status_; } +template <typename T> +Status StatusOr<T>::status() && { + return ok() ? OkStatus() : std::move(this->status_); +} + +template <typename T> +const T& StatusOr<T>::value() const& { + if (!this->ok()) internal_statusor::ThrowBadStatusOrAccess(this->status_); + return this->data_; +} + +template <typename T> +T& StatusOr<T>::value() & { + if (!this->ok()) internal_statusor::ThrowBadStatusOrAccess(this->status_); + return this->data_; +} + +template <typename T> +const T&& StatusOr<T>::value() const&& { + if (!this->ok()) { + internal_statusor::ThrowBadStatusOrAccess(std::move(this->status_)); + } + return std::move(this->data_); +} + +template <typename T> +T&& StatusOr<T>::value() && { + if (!this->ok()) { + internal_statusor::ThrowBadStatusOrAccess(std::move(this->status_)); + } + return std::move(this->data_); +} + +template <typename T> +const T& StatusOr<T>::operator*() const& { + this->EnsureOk(); + return this->data_; +} + +template <typename T> +T& StatusOr<T>::operator*() & { + this->EnsureOk(); + return this->data_; +} + +template <typename T> +const T&& StatusOr<T>::operator*() const&& { + this->EnsureOk(); + return std::move(this->data_); +} + +template <typename T> +T&& StatusOr<T>::operator*() && { + this->EnsureOk(); + return std::move(this->data_); +} + +template <typename T> +const T* StatusOr<T>::operator->() const { + this->EnsureOk(); + return &this->data_; +} + +template <typename T> +T* StatusOr<T>::operator->() { + this->EnsureOk(); + return &this->data_; +} + +template <typename T> +template <typename U> +T StatusOr<T>::value_or(U&& default_value) const& { + if (ok()) { + return this->data_; + } + return std::forward<U>(default_value); +} + +template <typename T> +template <typename U> +T StatusOr<T>::value_or(U&& default_value) && { + if (ok()) { + return std::move(this->data_); + } + return std::forward<U>(default_value); +} + +template <typename T> +void StatusOr<T>::IgnoreError() const { + // no-op +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STATUS_STATUSOR_H_ diff --git a/absl/status/statusor_test.cc b/absl/status/statusor_test.cc new file mode 100644 index 00000000..5e4b2687 --- /dev/null +++ b/absl/status/statusor_test.cc @@ -0,0 +1,1800 @@ +// Copyright 2020 The Abseil Authors. +// +// 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 +// +// https://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. + +#include "absl/status/statusor.h" + +#include <array> +#include <initializer_list> +#include <memory> +#include <type_traits> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/casts.h" +#include "absl/memory/memory.h" +#include "absl/status/status.h" +#include "absl/types/any.h" +#include "absl/utility/utility.h" + +namespace { + +using ::testing::AllOf; +using ::testing::AnyWith; +using ::testing::ElementsAre; +using ::testing::Field; +using ::testing::Ne; +using ::testing::Not; +using ::testing::Pointee; +using ::testing::VariantWith; + +#ifdef GTEST_HAS_STATUS_MATCHERS +using ::testing::status::IsOk; +using ::testing::status::IsOkAndHolds; +#else // GTEST_HAS_STATUS_MATCHERS +inline const ::absl::Status& GetStatus(const ::absl::Status& status) { + return status; +} + +template <typename T> +inline const ::absl::Status& GetStatus(const ::absl::StatusOr<T>& status) { + return status.status(); +} + +// Monomorphic implementation of matcher IsOkAndHolds(m). StatusOrType is a +// reference to StatusOr<T>. +template <typename StatusOrType> +class IsOkAndHoldsMatcherImpl + : public ::testing::MatcherInterface<StatusOrType> { + public: + typedef + typename std::remove_reference<StatusOrType>::type::value_type value_type; + + template <typename InnerMatcher> + explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher) + : inner_matcher_(::testing::SafeMatcherCast<const value_type&>( + std::forward<InnerMatcher>(inner_matcher))) {} + + void DescribeTo(std::ostream* os) const override { + *os << "is OK and has a value that "; + inner_matcher_.DescribeTo(os); + } + + void DescribeNegationTo(std::ostream* os) const override { + *os << "isn't OK or has a value that "; + inner_matcher_.DescribeNegationTo(os); + } + + bool MatchAndExplain( + StatusOrType actual_value, + ::testing::MatchResultListener* result_listener) const override { + if (!actual_value.ok()) { + *result_listener << "which has status " << actual_value.status(); + return false; + } + + ::testing::StringMatchResultListener inner_listener; + const bool matches = + inner_matcher_.MatchAndExplain(*actual_value, &inner_listener); + const std::string inner_explanation = inner_listener.str(); + if (!inner_explanation.empty()) { + *result_listener << "which contains value " + << ::testing::PrintToString(*actual_value) << ", " + << inner_explanation; + } + return matches; + } + + private: + const ::testing::Matcher<const value_type&> inner_matcher_; +}; + +// Implements IsOkAndHolds(m) as a polymorphic matcher. +template <typename InnerMatcher> +class IsOkAndHoldsMatcher { + public: + explicit IsOkAndHoldsMatcher(InnerMatcher inner_matcher) + : inner_matcher_(std::move(inner_matcher)) {} + + // Converts this polymorphic matcher to a monomorphic matcher of the + // given type. StatusOrType can be either StatusOr<T> or a + // reference to StatusOr<T>. + template <typename StatusOrType> + operator ::testing::Matcher<StatusOrType>() const { // NOLINT + return ::testing::Matcher<StatusOrType>( + new IsOkAndHoldsMatcherImpl<const StatusOrType&>(inner_matcher_)); + } + + private: + const InnerMatcher inner_matcher_; +}; + +// Monomorphic implementation of matcher IsOk() for a given type T. +// T can be Status, StatusOr<>, or a reference to either of them. +template <typename T> +class MonoIsOkMatcherImpl : public ::testing::MatcherInterface<T> { + public: + void DescribeTo(std::ostream* os) const override { *os << "is OK"; } + void DescribeNegationTo(std::ostream* os) const override { + *os << "is not OK"; + } + bool MatchAndExplain(T actual_value, + ::testing::MatchResultListener*) const override { + return GetStatus(actual_value).ok(); + } +}; + +// Implements IsOk() as a polymorphic matcher. +class IsOkMatcher { + public: + template <typename T> + operator ::testing::Matcher<T>() const { // NOLINT + return ::testing::Matcher<T>(new MonoIsOkMatcherImpl<T>()); + } +}; + +// Macros for testing the results of functions that return absl::Status or +// absl::StatusOr<T> (for any type T). +#define EXPECT_OK(expression) EXPECT_THAT(expression, IsOk()) + +// Returns a gMock matcher that matches a StatusOr<> whose status is +// OK and whose value matches the inner matcher. +template <typename InnerMatcher> +IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type> IsOkAndHolds( + InnerMatcher&& inner_matcher) { + return IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type>( + std::forward<InnerMatcher>(inner_matcher)); +} + +// Returns a gMock matcher that matches a Status or StatusOr<> which is OK. +inline IsOkMatcher IsOk() { return IsOkMatcher(); } +#endif // GTEST_HAS_STATUS_MATCHERS + +struct CopyDetector { + CopyDetector() = default; + explicit CopyDetector(int xx) : x(xx) {} + CopyDetector(CopyDetector&& d) noexcept + : x(d.x), copied(false), moved(true) {} + CopyDetector(const CopyDetector& d) : x(d.x), copied(true), moved(false) {} + CopyDetector& operator=(const CopyDetector& c) { + x = c.x; + copied = true; + moved = false; + return *this; + } + CopyDetector& operator=(CopyDetector&& c) noexcept { + x = c.x; + copied = false; + moved = true; + return *this; + } + int x = 0; + bool copied = false; + bool moved = false; +}; + +testing::Matcher<const CopyDetector&> CopyDetectorHas(int a, bool b, bool c) { + return AllOf(Field(&CopyDetector::x, a), Field(&CopyDetector::moved, b), + Field(&CopyDetector::copied, c)); +} + +class Base1 { + public: + virtual ~Base1() {} + int pad; +}; + +class Base2 { + public: + virtual ~Base2() {} + int yetotherpad; +}; + +class Derived : public Base1, public Base2 { + public: + virtual ~Derived() {} + int evenmorepad; +}; + +class CopyNoAssign { + public: + explicit CopyNoAssign(int value) : foo(value) {} + CopyNoAssign(const CopyNoAssign& other) : foo(other.foo) {} + int foo; + + private: + const CopyNoAssign& operator=(const CopyNoAssign&); +}; + +absl::StatusOr<std::unique_ptr<int>> ReturnUniquePtr() { + // Uses implicit constructor from T&& + return absl::make_unique<int>(0); +} + +TEST(StatusOr, ElementType) { + static_assert(std::is_same<absl::StatusOr<int>::value_type, int>(), ""); + static_assert(std::is_same<absl::StatusOr<char>::value_type, char>(), ""); +} + +TEST(StatusOr, TestMoveOnlyInitialization) { + absl::StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr()); + ASSERT_TRUE(thing.ok()); + EXPECT_EQ(0, **thing); + int* previous = thing->get(); + + thing = ReturnUniquePtr(); + EXPECT_TRUE(thing.ok()); + EXPECT_EQ(0, **thing); + EXPECT_NE(previous, thing->get()); +} + +TEST(StatusOr, TestMoveOnlyValueExtraction) { + absl::StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr()); + ASSERT_TRUE(thing.ok()); + std::unique_ptr<int> ptr = *std::move(thing); + EXPECT_EQ(0, *ptr); + + thing = std::move(ptr); + ptr = std::move(*thing); + EXPECT_EQ(0, *ptr); +} + +TEST(StatusOr, TestMoveOnlyInitializationFromTemporaryByValueOrDie) { + std::unique_ptr<int> ptr(*ReturnUniquePtr()); + EXPECT_EQ(0, *ptr); +} + +TEST(StatusOr, TestValueOrDieOverloadForConstTemporary) { + static_assert( + std::is_same<const int&&, + decltype( + std::declval<const absl::StatusOr<int>&&>().value())>(), + "value() for const temporaries should return const T&&"); +} + +TEST(StatusOr, TestMoveOnlyConversion) { + absl::StatusOr<std::unique_ptr<const int>> const_thing(ReturnUniquePtr()); + EXPECT_TRUE(const_thing.ok()); + EXPECT_EQ(0, **const_thing); + + // Test rvalue converting assignment + const int* const_previous = const_thing->get(); + const_thing = ReturnUniquePtr(); + EXPECT_TRUE(const_thing.ok()); + EXPECT_EQ(0, **const_thing); + EXPECT_NE(const_previous, const_thing->get()); +} + +TEST(StatusOr, TestMoveOnlyVector) { + // Sanity check that absl::StatusOr<MoveOnly> works in vector. + std::vector<absl::StatusOr<std::unique_ptr<int>>> vec; + vec.push_back(ReturnUniquePtr()); + vec.resize(2); + auto another_vec = std::move(vec); + EXPECT_EQ(0, **another_vec[0]); + EXPECT_EQ(absl::UnknownError(""), another_vec[1].status()); +} + +TEST(StatusOr, TestDefaultCtor) { + absl::StatusOr<int> thing; + EXPECT_FALSE(thing.ok()); + EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown); +} + +// Define `EXPECT_DEATH_OR_THROW` to test the behavior of `StatusOr::value`, +// which either throws `BadStatusOrAccess` or `LOG(FATAL)` based on whether +// exceptions are enabled. +#ifdef ABSL_HAVE_EXCEPTIONS +#define EXPECT_DEATH_OR_THROW(statement, status_) \ + EXPECT_THROW( \ + { \ + try { \ + statement; \ + } catch (const absl::BadStatusOrAccess& e) { \ + EXPECT_EQ(e.status(), status_); \ + throw; \ + } \ + }, \ + absl::BadStatusOrAccess); +#else // ABSL_HAVE_EXCEPTIONS +#define EXPECT_DEATH_OR_THROW(statement, status) \ + EXPECT_DEATH_IF_SUPPORTED(statement, status.ToString()); +#endif // ABSL_HAVE_EXCEPTIONS + +TEST(StatusOrDeathTest, TestDefaultCtorValue) { + absl::StatusOr<int> thing; + EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError("")); + const absl::StatusOr<int> thing2; + EXPECT_DEATH_OR_THROW(thing2.value(), absl::UnknownError("")); +} + +TEST(StatusOrDeathTest, TestValueNotOk) { + absl::StatusOr<int> thing(absl::CancelledError()); + EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError()); +} + +TEST(StatusOrDeathTest, TestValueNotOkConst) { + const absl::StatusOr<int> thing(absl::UnknownError("")); + EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError("")); +} + +TEST(StatusOrDeathTest, TestPointerDefaultCtorValue) { + absl::StatusOr<int*> thing; + EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError("")); +} + +TEST(StatusOrDeathTest, TestPointerValueNotOk) { + absl::StatusOr<int*> thing(absl::CancelledError()); + EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError()); +} + +TEST(StatusOrDeathTest, TestPointerValueNotOkConst) { + const absl::StatusOr<int*> thing(absl::CancelledError()); + EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError()); +} + +#if GTEST_HAS_DEATH_TEST +TEST(StatusOrDeathTest, TestStatusCtorStatusOk) { + EXPECT_DEBUG_DEATH( + { + // This will DCHECK + absl::StatusOr<int> thing(absl::OkStatus()); + // In optimized mode, we are actually going to get error::INTERNAL for + // status here, rather than crashing, so check that. + EXPECT_FALSE(thing.ok()); + EXPECT_EQ(thing.status().code(), absl::StatusCode::kInternal); + }, + "An OK status is not a valid constructor argument"); +} + +TEST(StatusOrDeathTest, TestPointerStatusCtorStatusOk) { + EXPECT_DEBUG_DEATH( + { + absl::StatusOr<int*> thing(absl::OkStatus()); + // In optimized mode, we are actually going to get error::INTERNAL for + // status here, rather than crashing, so check that. + EXPECT_FALSE(thing.ok()); + EXPECT_EQ(thing.status().code(), absl::StatusCode::kInternal); + }, + "An OK status is not a valid constructor argument"); +} +#endif + +TEST(StatusOr, ValueAccessor) { + const int kIntValue = 110; + { + absl::StatusOr<int> status_or(kIntValue); + EXPECT_EQ(kIntValue, status_or.value()); + EXPECT_EQ(kIntValue, std::move(status_or).value()); + } + { + absl::StatusOr<CopyDetector> status_or(kIntValue); + EXPECT_THAT(status_or, + IsOkAndHolds(CopyDetectorHas(kIntValue, false, false))); + CopyDetector copy_detector = status_or.value(); + EXPECT_THAT(copy_detector, CopyDetectorHas(kIntValue, false, true)); + copy_detector = std::move(status_or).value(); + EXPECT_THAT(copy_detector, CopyDetectorHas(kIntValue, true, false)); + } +} + +TEST(StatusOr, BadValueAccess) { + const absl::Status kError = absl::CancelledError("message"); + absl::StatusOr<int> status_or(kError); + EXPECT_DEATH_OR_THROW(status_or.value(), kError); +} + +TEST(StatusOr, TestStatusCtor) { + absl::StatusOr<int> thing(absl::CancelledError()); + EXPECT_FALSE(thing.ok()); + EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled); +} + + + +TEST(StatusOr, TestValueCtor) { + const int kI = 4; + const absl::StatusOr<int> thing(kI); + EXPECT_TRUE(thing.ok()); + EXPECT_EQ(kI, *thing); +} + +struct Foo { + const int x; + explicit Foo(int y) : x(y) {} +}; + +TEST(StatusOr, InPlaceConstruction) { + EXPECT_THAT(absl::StatusOr<Foo>(absl::in_place, 10), + IsOkAndHolds(Field(&Foo::x, 10))); +} + +struct InPlaceHelper { + InPlaceHelper(std::initializer_list<int> xs, std::unique_ptr<int> yy) + : x(xs), y(std::move(yy)) {} + const std::vector<int> x; + std::unique_ptr<int> y; +}; + +TEST(StatusOr, InPlaceInitListConstruction) { + absl::StatusOr<InPlaceHelper> status_or(absl::in_place, {10, 11, 12}, + absl::make_unique<int>(13)); + EXPECT_THAT(status_or, IsOkAndHolds(AllOf( + Field(&InPlaceHelper::x, ElementsAre(10, 11, 12)), + Field(&InPlaceHelper::y, Pointee(13))))); +} + +TEST(StatusOr, Emplace) { + absl::StatusOr<Foo> status_or_foo(10); + status_or_foo.emplace(20); + EXPECT_THAT(status_or_foo, IsOkAndHolds(Field(&Foo::x, 20))); + status_or_foo = absl::InvalidArgumentError("msg"); + EXPECT_FALSE(status_or_foo.ok()); + EXPECT_EQ(status_or_foo.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(status_or_foo.status().message(), "msg"); + status_or_foo.emplace(20); + EXPECT_THAT(status_or_foo, IsOkAndHolds(Field(&Foo::x, 20))); +} + +TEST(StatusOr, EmplaceInitializerList) { + absl::StatusOr<InPlaceHelper> status_or(absl::in_place, {10, 11, 12}, + absl::make_unique<int>(13)); + status_or.emplace({1, 2, 3}, absl::make_unique<int>(4)); + EXPECT_THAT(status_or, + IsOkAndHolds(AllOf(Field(&InPlaceHelper::x, ElementsAre(1, 2, 3)), + Field(&InPlaceHelper::y, Pointee(4))))); + status_or = absl::InvalidArgumentError("msg"); + EXPECT_FALSE(status_or.ok()); + EXPECT_EQ(status_or.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(status_or.status().message(), "msg"); + status_or.emplace({1, 2, 3}, absl::make_unique<int>(4)); + EXPECT_THAT(status_or, + IsOkAndHolds(AllOf(Field(&InPlaceHelper::x, ElementsAre(1, 2, 3)), + Field(&InPlaceHelper::y, Pointee(4))))); +} + +TEST(StatusOr, TestCopyCtorStatusOk) { + const int kI = 4; + const absl::StatusOr<int> original(kI); + const absl::StatusOr<int> copy(original); + EXPECT_OK(copy.status()); + EXPECT_EQ(*original, *copy); +} + +TEST(StatusOr, TestCopyCtorStatusNotOk) { + absl::StatusOr<int> original(absl::CancelledError()); + absl::StatusOr<int> copy(original); + EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, TestCopyCtorNonAssignable) { + const int kI = 4; + CopyNoAssign value(kI); + absl::StatusOr<CopyNoAssign> original(value); + absl::StatusOr<CopyNoAssign> copy(original); + EXPECT_OK(copy.status()); + EXPECT_EQ(original->foo, copy->foo); +} + +TEST(StatusOr, TestCopyCtorStatusOKConverting) { + const int kI = 4; + absl::StatusOr<int> original(kI); + absl::StatusOr<double> copy(original); + EXPECT_OK(copy.status()); + EXPECT_DOUBLE_EQ(*original, *copy); +} + +TEST(StatusOr, TestCopyCtorStatusNotOkConverting) { + absl::StatusOr<int> original(absl::CancelledError()); + absl::StatusOr<double> copy(original); + EXPECT_EQ(copy.status(), original.status()); +} + +TEST(StatusOr, TestAssignmentStatusOk) { + // Copy assignmment + { + const auto p = std::make_shared<int>(17); + absl::StatusOr<std::shared_ptr<int>> source(p); + + absl::StatusOr<std::shared_ptr<int>> target; + target = source; + + ASSERT_TRUE(target.ok()); + EXPECT_OK(target.status()); + EXPECT_EQ(p, *target); + + ASSERT_TRUE(source.ok()); + EXPECT_OK(source.status()); + EXPECT_EQ(p, *source); + } + + // Move asssignment + { + const auto p = std::make_shared<int>(17); + absl::StatusOr<std::shared_ptr<int>> source(p); + + absl::StatusOr<std::shared_ptr<int>> target; + target = std::move(source); + + ASSERT_TRUE(target.ok()); + EXPECT_OK(target.status()); + EXPECT_EQ(p, *target); + + ASSERT_TRUE(source.ok()); + EXPECT_OK(source.status()); + EXPECT_EQ(nullptr, *source); + } +} + +TEST(StatusOr, TestAssignmentStatusNotOk) { + // Copy assignment + { + const absl::Status expected = absl::CancelledError(); + absl::StatusOr<int> source(expected); + + absl::StatusOr<int> target; + target = source; + + EXPECT_FALSE(target.ok()); + EXPECT_EQ(expected, target.status()); + + EXPECT_FALSE(source.ok()); + EXPECT_EQ(expected, source.status()); + } + + // Move assignment + { + const absl::Status expected = absl::CancelledError(); + absl::StatusOr<int> source(expected); + + absl::StatusOr<int> target; + target = std::move(source); + + EXPECT_FALSE(target.ok()); + EXPECT_EQ(expected, target.status()); + + EXPECT_FALSE(source.ok()); + EXPECT_EQ(source.status().code(), absl::StatusCode::kInternal); + } +} + +TEST(StatusOr, TestAssignmentStatusOKConverting) { + // Copy assignment + { + const int kI = 4; + absl::StatusOr<int> source(kI); + + absl::StatusOr<double> target; + target = source; + + ASSERT_TRUE(target.ok()); + EXPECT_OK(target.status()); + EXPECT_DOUBLE_EQ(kI, *target); + + ASSERT_TRUE(source.ok()); + EXPECT_OK(source.status()); + EXPECT_DOUBLE_EQ(kI, *source); + } + + // Move assignment + { + const auto p = new int(17); + absl::StatusOr<std::unique_ptr<int>> source(absl::WrapUnique(p)); + + absl::StatusOr<std::shared_ptr<int>> target; + target = std::move(source); + + ASSERT_TRUE(target.ok()); + EXPECT_OK(target.status()); + EXPECT_EQ(p, target->get()); + + ASSERT_TRUE(source.ok()); + EXPECT_OK(source.status()); + EXPECT_EQ(nullptr, source->get()); + } +} + +struct A { + int x; +}; + +struct ImplicitConstructibleFromA { + int x; + bool moved; + ImplicitConstructibleFromA(const A& a) // NOLINT + : x(a.x), moved(false) {} + ImplicitConstructibleFromA(A&& a) // NOLINT + : x(a.x), moved(true) {} +}; + +TEST(StatusOr, ImplicitConvertingConstructor) { + EXPECT_THAT( + absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromA>>( + absl::StatusOr<A>(A{11})), + IsOkAndHolds(AllOf(Field(&ImplicitConstructibleFromA::x, 11), + Field(&ImplicitConstructibleFromA::moved, true)))); + absl::StatusOr<A> a(A{12}); + EXPECT_THAT( + absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromA>>(a), + IsOkAndHolds(AllOf(Field(&ImplicitConstructibleFromA::x, 12), + Field(&ImplicitConstructibleFromA::moved, false)))); +} + +struct ExplicitConstructibleFromA { + int x; + bool moved; + explicit ExplicitConstructibleFromA(const A& a) : x(a.x), moved(false) {} + explicit ExplicitConstructibleFromA(A&& a) : x(a.x), moved(true) {} +}; + +TEST(StatusOr, ExplicitConvertingConstructor) { + EXPECT_FALSE( + (std::is_convertible<const absl::StatusOr<A>&, + absl::StatusOr<ExplicitConstructibleFromA>>::value)); + EXPECT_FALSE( + (std::is_convertible<absl::StatusOr<A>&&, + absl::StatusOr<ExplicitConstructibleFromA>>::value)); + EXPECT_THAT( + absl::StatusOr<ExplicitConstructibleFromA>(absl::StatusOr<A>(A{11})), + IsOkAndHolds(AllOf(Field(&ExplicitConstructibleFromA::x, 11), + Field(&ExplicitConstructibleFromA::moved, true)))); + absl::StatusOr<A> a(A{12}); + EXPECT_THAT( + absl::StatusOr<ExplicitConstructibleFromA>(a), + IsOkAndHolds(AllOf(Field(&ExplicitConstructibleFromA::x, 12), + Field(&ExplicitConstructibleFromA::moved, false)))); +} + +struct ImplicitConstructibleFromBool { + ImplicitConstructibleFromBool(bool y) : x(y) {} // NOLINT + bool x = false; +}; + +struct ConvertibleToBool { + explicit ConvertibleToBool(bool y) : x(y) {} + operator bool() const { return x; } // NOLINT + bool x = false; +}; + +TEST(StatusOr, ImplicitBooleanConstructionWithImplicitCasts) { + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(false)), + IsOkAndHolds(false)); + EXPECT_THAT( + absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromBool>>( + absl::StatusOr<bool>(false)), + IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false))); + EXPECT_FALSE((std::is_convertible< + absl::StatusOr<ConvertibleToBool>, + absl::StatusOr<ImplicitConstructibleFromBool>>::value)); +} + +TEST(StatusOr, BooleanConstructionWithImplicitCasts) { + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(false)), + IsOkAndHolds(false)); + EXPECT_THAT( + absl::StatusOr<ImplicitConstructibleFromBool>{ + absl::StatusOr<bool>(false)}, + IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false))); + EXPECT_THAT( + absl::StatusOr<ImplicitConstructibleFromBool>{ + absl::StatusOr<bool>(absl::InvalidArgumentError(""))}, + Not(IsOk())); + + EXPECT_THAT( + absl::StatusOr<ImplicitConstructibleFromBool>{ + absl::StatusOr<ConvertibleToBool>(ConvertibleToBool{false})}, + IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false))); + EXPECT_THAT( + absl::StatusOr<ImplicitConstructibleFromBool>{ + absl::StatusOr<ConvertibleToBool>(absl::InvalidArgumentError(""))}, + Not(IsOk())); +} + +TEST(StatusOr, ConstImplicitCast) { + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<bool>>( + absl::StatusOr<const bool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<bool>>( + absl::StatusOr<const bool>(false)), + IsOkAndHolds(false)); + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const bool>>( + absl::StatusOr<bool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const bool>>( + absl::StatusOr<bool>(false)), + IsOkAndHolds(false)); + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const std::string>>( + absl::StatusOr<std::string>("foo")), + IsOkAndHolds("foo")); + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<std::string>>( + absl::StatusOr<const std::string>("foo")), + IsOkAndHolds("foo")); + EXPECT_THAT( + absl::implicit_cast<absl::StatusOr<std::shared_ptr<const std::string>>>( + absl::StatusOr<std::shared_ptr<std::string>>( + std::make_shared<std::string>("foo"))), + IsOkAndHolds(Pointee(std::string("foo")))); +} + +TEST(StatusOr, ConstExplicitConstruction) { + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<const bool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<const bool>(false)), + IsOkAndHolds(false)); + EXPECT_THAT(absl::StatusOr<const bool>(absl::StatusOr<bool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::StatusOr<const bool>(absl::StatusOr<bool>(false)), + IsOkAndHolds(false)); +} + +struct ExplicitConstructibleFromInt { + int x; + explicit ExplicitConstructibleFromInt(int y) : x(y) {} +}; + +TEST(StatusOr, ExplicitConstruction) { + EXPECT_THAT(absl::StatusOr<ExplicitConstructibleFromInt>(10), + IsOkAndHolds(Field(&ExplicitConstructibleFromInt::x, 10))); +} + +TEST(StatusOr, ImplicitConstruction) { + // Check implicit casting works. + auto status_or = + absl::implicit_cast<absl::StatusOr<absl::variant<int, std::string>>>(10); + EXPECT_THAT(status_or, IsOkAndHolds(VariantWith<int>(10))); +} + +TEST(StatusOr, ImplicitConstructionFromInitliazerList) { + // Note: dropping the explicit std::initializer_list<int> is not supported + // by absl::StatusOr or absl::optional. + auto status_or = + absl::implicit_cast<absl::StatusOr<std::vector<int>>>({{10, 20, 30}}); + EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30))); +} + +TEST(StatusOr, UniquePtrImplicitConstruction) { + auto status_or = absl::implicit_cast<absl::StatusOr<std::unique_ptr<Base1>>>( + absl::make_unique<Derived>()); + EXPECT_THAT(status_or, IsOkAndHolds(Ne(nullptr))); +} + +TEST(StatusOr, NestedStatusOrCopyAndMoveConstructorTests) { + absl::StatusOr<absl::StatusOr<CopyDetector>> status_or = CopyDetector(10); + absl::StatusOr<absl::StatusOr<CopyDetector>> status_error = + absl::InvalidArgumentError("foo"); + EXPECT_THAT(status_or, + IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false)))); + absl::StatusOr<absl::StatusOr<CopyDetector>> a = status_or; + EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); + absl::StatusOr<absl::StatusOr<CopyDetector>> a_err = status_error; + EXPECT_THAT(a_err, Not(IsOk())); + + const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref = status_or; + absl::StatusOr<absl::StatusOr<CopyDetector>> b = cref; // NOLINT + EXPECT_THAT(b, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); + const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref_err = status_error; + absl::StatusOr<absl::StatusOr<CopyDetector>> b_err = cref_err; // NOLINT + EXPECT_THAT(b_err, Not(IsOk())); + + absl::StatusOr<absl::StatusOr<CopyDetector>> c = std::move(status_or); + EXPECT_THAT(c, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false)))); + absl::StatusOr<absl::StatusOr<CopyDetector>> c_err = std::move(status_error); + EXPECT_THAT(c_err, Not(IsOk())); +} + +TEST(StatusOr, NestedStatusOrCopyAndMoveAssignment) { + absl::StatusOr<absl::StatusOr<CopyDetector>> status_or = CopyDetector(10); + absl::StatusOr<absl::StatusOr<CopyDetector>> status_error = + absl::InvalidArgumentError("foo"); + absl::StatusOr<absl::StatusOr<CopyDetector>> a; + a = status_or; + EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); + a = status_error; + EXPECT_THAT(a, Not(IsOk())); + + const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref = status_or; + a = cref; + EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); + const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref_err = status_error; + a = cref_err; + EXPECT_THAT(a, Not(IsOk())); + a = std::move(status_or); + EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false)))); + a = std::move(status_error); + EXPECT_THAT(a, Not(IsOk())); +} + +struct Copyable { + Copyable() {} + Copyable(const Copyable&) {} + Copyable& operator=(const Copyable&) { return *this; } +}; + +struct MoveOnly { + MoveOnly() {} + MoveOnly(MoveOnly&&) {} + MoveOnly& operator=(MoveOnly&&) { return *this; } +}; + +struct NonMovable { + NonMovable() {} + NonMovable(const NonMovable&) = delete; + NonMovable(NonMovable&&) = delete; + NonMovable& operator=(const NonMovable&) = delete; + NonMovable& operator=(NonMovable&&) = delete; +}; + +TEST(StatusOr, CopyAndMoveAbility) { + EXPECT_TRUE(std::is_copy_constructible<Copyable>::value); + EXPECT_TRUE(std::is_copy_assignable<Copyable>::value); + EXPECT_TRUE(std::is_move_constructible<Copyable>::value); + EXPECT_TRUE(std::is_move_assignable<Copyable>::value); + EXPECT_FALSE(std::is_copy_constructible<MoveOnly>::value); + EXPECT_FALSE(std::is_copy_assignable<MoveOnly>::value); + EXPECT_TRUE(std::is_move_constructible<MoveOnly>::value); + EXPECT_TRUE(std::is_move_assignable<MoveOnly>::value); + EXPECT_FALSE(std::is_copy_constructible<NonMovable>::value); + EXPECT_FALSE(std::is_copy_assignable<NonMovable>::value); + EXPECT_FALSE(std::is_move_constructible<NonMovable>::value); + EXPECT_FALSE(std::is_move_assignable<NonMovable>::value); +} + +TEST(StatusOr, StatusOrAnyCopyAndMoveConstructorTests) { + absl::StatusOr<absl::any> status_or = CopyDetector(10); + absl::StatusOr<absl::any> status_error = absl::InvalidArgumentError("foo"); + EXPECT_THAT( + status_or, + IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false)))); + absl::StatusOr<absl::any> a = status_or; + EXPECT_THAT( + a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); + absl::StatusOr<absl::any> a_err = status_error; + EXPECT_THAT(a_err, Not(IsOk())); + + const absl::StatusOr<absl::any>& cref = status_or; + // No lint for no-change copy. + absl::StatusOr<absl::any> b = cref; // NOLINT + EXPECT_THAT( + b, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); + const absl::StatusOr<absl::any>& cref_err = status_error; + // No lint for no-change copy. + absl::StatusOr<absl::any> b_err = cref_err; // NOLINT + EXPECT_THAT(b_err, Not(IsOk())); + + absl::StatusOr<absl::any> c = std::move(status_or); + EXPECT_THAT( + c, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false)))); + absl::StatusOr<absl::any> c_err = std::move(status_error); + EXPECT_THAT(c_err, Not(IsOk())); +} + +TEST(StatusOr, StatusOrAnyCopyAndMoveAssignment) { + absl::StatusOr<absl::any> status_or = CopyDetector(10); + absl::StatusOr<absl::any> status_error = absl::InvalidArgumentError("foo"); + absl::StatusOr<absl::any> a; + a = status_or; + EXPECT_THAT( + a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); + a = status_error; + EXPECT_THAT(a, Not(IsOk())); + + const absl::StatusOr<absl::any>& cref = status_or; + a = cref; + EXPECT_THAT( + a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); + const absl::StatusOr<absl::any>& cref_err = status_error; + a = cref_err; + EXPECT_THAT(a, Not(IsOk())); + a = std::move(status_or); + EXPECT_THAT( + a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false)))); + a = std::move(status_error); + EXPECT_THAT(a, Not(IsOk())); +} + +TEST(StatusOr, StatusOrCopyAndMoveTestsConstructor) { + absl::StatusOr<CopyDetector> status_or(10); + ASSERT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(10, false, false))); + absl::StatusOr<CopyDetector> a(status_or); + EXPECT_THAT(a, IsOkAndHolds(CopyDetectorHas(10, false, true))); + const absl::StatusOr<CopyDetector>& cref = status_or; + absl::StatusOr<CopyDetector> b(cref); // NOLINT + EXPECT_THAT(b, IsOkAndHolds(CopyDetectorHas(10, false, true))); + absl::StatusOr<CopyDetector> c(std::move(status_or)); + EXPECT_THAT(c, IsOkAndHolds(CopyDetectorHas(10, true, false))); +} + +TEST(StatusOr, StatusOrCopyAndMoveTestsAssignment) { + absl::StatusOr<CopyDetector> status_or(10); + ASSERT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(10, false, false))); + absl::StatusOr<CopyDetector> a; + a = status_or; + EXPECT_THAT(a, IsOkAndHolds(CopyDetectorHas(10, false, true))); + const absl::StatusOr<CopyDetector>& cref = status_or; + absl::StatusOr<CopyDetector> b; + b = cref; + EXPECT_THAT(b, IsOkAndHolds(CopyDetectorHas(10, false, true))); + absl::StatusOr<CopyDetector> c; + c = std::move(status_or); + EXPECT_THAT(c, IsOkAndHolds(CopyDetectorHas(10, true, false))); +} + +TEST(StatusOr, AbslAnyAssignment) { + EXPECT_FALSE((std::is_assignable<absl::StatusOr<absl::any>, + absl::StatusOr<int>>::value)); + absl::StatusOr<absl::any> status_or; + status_or = absl::InvalidArgumentError("foo"); + EXPECT_THAT(status_or, Not(IsOk())); +} + +TEST(StatusOr, ImplicitAssignment) { + absl::StatusOr<absl::variant<int, std::string>> status_or; + status_or = 10; + EXPECT_THAT(status_or, IsOkAndHolds(VariantWith<int>(10))); +} + +TEST(StatusOr, SelfDirectInitAssignment) { + absl::StatusOr<std::vector<int>> status_or = {{10, 20, 30}}; + status_or = *status_or; + EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30))); +} + +TEST(StatusOr, ImplicitCastFromInitializerList) { + absl::StatusOr<std::vector<int>> status_or = {{10, 20, 30}}; + EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30))); +} + +TEST(StatusOr, UniquePtrImplicitAssignment) { + absl::StatusOr<std::unique_ptr<Base1>> status_or; + status_or = absl::make_unique<Derived>(); + EXPECT_THAT(status_or, IsOkAndHolds(Ne(nullptr))); +} + +TEST(StatusOr, Pointer) { + struct A {}; + struct B : public A {}; + struct C : private A {}; + + EXPECT_TRUE((std::is_constructible<absl::StatusOr<A*>, B*>::value)); + EXPECT_TRUE((std::is_convertible<B*, absl::StatusOr<A*>>::value)); + EXPECT_FALSE((std::is_constructible<absl::StatusOr<A*>, C*>::value)); + EXPECT_FALSE((std::is_convertible<C*, absl::StatusOr<A*>>::value)); +} + +TEST(StatusOr, TestAssignmentStatusNotOkConverting) { + // Copy assignment + { + const absl::Status expected = absl::CancelledError(); + absl::StatusOr<int> source(expected); + + absl::StatusOr<double> target; + target = source; + + EXPECT_FALSE(target.ok()); + EXPECT_EQ(expected, target.status()); + + EXPECT_FALSE(source.ok()); + EXPECT_EQ(expected, source.status()); + } + + // Move assignment + { + const absl::Status expected = absl::CancelledError(); + absl::StatusOr<int> source(expected); + + absl::StatusOr<double> target; + target = std::move(source); + + EXPECT_FALSE(target.ok()); + EXPECT_EQ(expected, target.status()); + + EXPECT_FALSE(source.ok()); + EXPECT_EQ(source.status().code(), absl::StatusCode::kInternal); + } +} + +TEST(StatusOr, SelfAssignment) { + // Copy-assignment, status OK + { + // A string long enough that it's likely to defeat any inline representation + // optimization. + const std::string long_str(128, 'a'); + + absl::StatusOr<std::string> so = long_str; + so = *&so; + + ASSERT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(long_str, *so); + } + + // Copy-assignment, error status + { + absl::StatusOr<int> so = absl::NotFoundError("taco"); + so = *&so; + + EXPECT_FALSE(so.ok()); + EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound); + EXPECT_EQ(so.status().message(), "taco"); + } + + // Move-assignment with copyable type, status OK + { + absl::StatusOr<int> so = 17; + + // Fool the compiler, which otherwise complains. + auto& same = so; + so = std::move(same); + + ASSERT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(17, *so); + } + + // Move-assignment with copyable type, error status + { + absl::StatusOr<int> so = absl::NotFoundError("taco"); + + // Fool the compiler, which otherwise complains. + auto& same = so; + so = std::move(same); + + EXPECT_FALSE(so.ok()); + EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound); + EXPECT_EQ(so.status().message(), "taco"); + } + + // Move-assignment with non-copyable type, status OK + { + const auto raw = new int(17); + absl::StatusOr<std::unique_ptr<int>> so = absl::WrapUnique(raw); + + // Fool the compiler, which otherwise complains. + auto& same = so; + so = std::move(same); + + ASSERT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(raw, so->get()); + } + + // Move-assignment with non-copyable type, error status + { + absl::StatusOr<std::unique_ptr<int>> so = absl::NotFoundError("taco"); + + // Fool the compiler, which otherwise complains. + auto& same = so; + so = std::move(same); + + EXPECT_FALSE(so.ok()); + EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound); + EXPECT_EQ(so.status().message(), "taco"); + } +} + +// These types form the overload sets of the constructors and the assignment +// operators of `MockValue`. They distinguish construction from assignment, +// lvalue from rvalue. +struct FromConstructibleAssignableLvalue {}; +struct FromConstructibleAssignableRvalue {}; +struct FromImplicitConstructibleOnly {}; +struct FromAssignableOnly {}; + +// This class is for testing the forwarding value assignments of `StatusOr`. +// `from_rvalue` indicates whether the constructor or the assignment taking +// rvalue reference is called. `from_assignment` indicates whether any +// assignment is called. +struct MockValue { + // Constructs `MockValue` from `FromConstructibleAssignableLvalue`. + MockValue(const FromConstructibleAssignableLvalue&) // NOLINT + : from_rvalue(false), assigned(false) {} + // Constructs `MockValue` from `FromConstructibleAssignableRvalue`. + MockValue(FromConstructibleAssignableRvalue&&) // NOLINT + : from_rvalue(true), assigned(false) {} + // Constructs `MockValue` from `FromImplicitConstructibleOnly`. + // `MockValue` is not assignable from `FromImplicitConstructibleOnly`. + MockValue(const FromImplicitConstructibleOnly&) // NOLINT + : from_rvalue(false), assigned(false) {} + // Assigns `FromConstructibleAssignableLvalue`. + MockValue& operator=(const FromConstructibleAssignableLvalue&) { + from_rvalue = false; + assigned = true; + return *this; + } + // Assigns `FromConstructibleAssignableRvalue` (rvalue only). + MockValue& operator=(FromConstructibleAssignableRvalue&&) { + from_rvalue = true; + assigned = true; + return *this; + } + // Assigns `FromAssignableOnly`, but not constructible from + // `FromAssignableOnly`. + MockValue& operator=(const FromAssignableOnly&) { + from_rvalue = false; + assigned = true; + return *this; + } + bool from_rvalue; + bool assigned; +}; + +// operator=(U&&) +TEST(StatusOr, PerfectForwardingAssignment) { + // U == T + constexpr int kValue1 = 10, kValue2 = 20; + absl::StatusOr<CopyDetector> status_or; + CopyDetector lvalue(kValue1); + status_or = lvalue; + EXPECT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(kValue1, false, true))); + status_or = CopyDetector(kValue2); + EXPECT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(kValue2, true, false))); + + // U != T + EXPECT_TRUE( + (std::is_assignable<absl::StatusOr<MockValue>&, + const FromConstructibleAssignableLvalue&>::value)); + EXPECT_TRUE((std::is_assignable<absl::StatusOr<MockValue>&, + FromConstructibleAssignableLvalue&&>::value)); + EXPECT_FALSE( + (std::is_assignable<absl::StatusOr<MockValue>&, + const FromConstructibleAssignableRvalue&>::value)); + EXPECT_TRUE((std::is_assignable<absl::StatusOr<MockValue>&, + FromConstructibleAssignableRvalue&&>::value)); + EXPECT_TRUE( + (std::is_assignable<absl::StatusOr<MockValue>&, + const FromImplicitConstructibleOnly&>::value)); + EXPECT_FALSE((std::is_assignable<absl::StatusOr<MockValue>&, + const FromAssignableOnly&>::value)); + + absl::StatusOr<MockValue> from_lvalue(FromConstructibleAssignableLvalue{}); + EXPECT_FALSE(from_lvalue->from_rvalue); + EXPECT_FALSE(from_lvalue->assigned); + from_lvalue = FromConstructibleAssignableLvalue{}; + EXPECT_FALSE(from_lvalue->from_rvalue); + EXPECT_TRUE(from_lvalue->assigned); + + absl::StatusOr<MockValue> from_rvalue(FromConstructibleAssignableRvalue{}); + EXPECT_TRUE(from_rvalue->from_rvalue); + EXPECT_FALSE(from_rvalue->assigned); + from_rvalue = FromConstructibleAssignableRvalue{}; + EXPECT_TRUE(from_rvalue->from_rvalue); + EXPECT_TRUE(from_rvalue->assigned); + + absl::StatusOr<MockValue> from_implicit_constructible( + FromImplicitConstructibleOnly{}); + EXPECT_FALSE(from_implicit_constructible->from_rvalue); + EXPECT_FALSE(from_implicit_constructible->assigned); + // construct a temporary `StatusOr` object and invoke the `StatusOr` move + // assignment operator. + from_implicit_constructible = FromImplicitConstructibleOnly{}; + EXPECT_FALSE(from_implicit_constructible->from_rvalue); + EXPECT_FALSE(from_implicit_constructible->assigned); +} + +TEST(StatusOr, TestStatus) { + absl::StatusOr<int> good(4); + EXPECT_TRUE(good.ok()); + absl::StatusOr<int> bad(absl::CancelledError()); + EXPECT_FALSE(bad.ok()); + EXPECT_EQ(bad.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, OperatorStarRefQualifiers) { + static_assert( + std::is_same<const int&, + decltype(*std::declval<const absl::StatusOr<int>&>())>(), + "Unexpected ref-qualifiers"); + static_assert( + std::is_same<int&, decltype(*std::declval<absl::StatusOr<int>&>())>(), + "Unexpected ref-qualifiers"); + static_assert( + std::is_same<const int&&, + decltype(*std::declval<const absl::StatusOr<int>&&>())>(), + "Unexpected ref-qualifiers"); + static_assert( + std::is_same<int&&, decltype(*std::declval<absl::StatusOr<int>&&>())>(), + "Unexpected ref-qualifiers"); +} + +TEST(StatusOr, OperatorStar) { + const absl::StatusOr<std::string> const_lvalue("hello"); + EXPECT_EQ("hello", *const_lvalue); + + absl::StatusOr<std::string> lvalue("hello"); + EXPECT_EQ("hello", *lvalue); + + // Note: Recall that std::move() is equivalent to a static_cast to an rvalue + // reference type. + const absl::StatusOr<std::string> const_rvalue("hello"); + EXPECT_EQ("hello", *std::move(const_rvalue)); // NOLINT + + absl::StatusOr<std::string> rvalue("hello"); + EXPECT_EQ("hello", *std::move(rvalue)); +} + +TEST(StatusOr, OperatorArrowQualifiers) { + static_assert( + std::is_same< + const int*, + decltype(std::declval<const absl::StatusOr<int>&>().operator->())>(), + "Unexpected qualifiers"); + static_assert( + std::is_same< + int*, decltype(std::declval<absl::StatusOr<int>&>().operator->())>(), + "Unexpected qualifiers"); + static_assert( + std::is_same< + const int*, + decltype(std::declval<const absl::StatusOr<int>&&>().operator->())>(), + "Unexpected qualifiers"); + static_assert( + std::is_same< + int*, decltype(std::declval<absl::StatusOr<int>&&>().operator->())>(), + "Unexpected qualifiers"); +} + +TEST(StatusOr, OperatorArrow) { + const absl::StatusOr<std::string> const_lvalue("hello"); + EXPECT_EQ(std::string("hello"), const_lvalue->c_str()); + + absl::StatusOr<std::string> lvalue("hello"); + EXPECT_EQ(std::string("hello"), lvalue->c_str()); +} + +TEST(StatusOr, RValueStatus) { + absl::StatusOr<int> so(absl::NotFoundError("taco")); + const absl::Status s = std::move(so).status(); + + EXPECT_EQ(s.code(), absl::StatusCode::kNotFound); + EXPECT_EQ(s.message(), "taco"); + + // Check that !ok() still implies !status().ok(), even after moving out of the + // object. See the note on the rvalue ref-qualified status method. + EXPECT_FALSE(so.ok()); // NOLINT + EXPECT_FALSE(so.status().ok()); + EXPECT_EQ(so.status().code(), absl::StatusCode::kInternal); + EXPECT_EQ(so.status().message(), "Status accessed after move."); +} + +TEST(StatusOr, TestValue) { + const int kI = 4; + absl::StatusOr<int> thing(kI); + EXPECT_EQ(kI, *thing); +} + +TEST(StatusOr, TestValueConst) { + const int kI = 4; + const absl::StatusOr<int> thing(kI); + EXPECT_EQ(kI, *thing); +} + +TEST(StatusOr, TestPointerDefaultCtor) { + absl::StatusOr<int*> thing; + EXPECT_FALSE(thing.ok()); + EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown); +} + + + +TEST(StatusOr, TestPointerStatusCtor) { + absl::StatusOr<int*> thing(absl::CancelledError()); + EXPECT_FALSE(thing.ok()); + EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, TestPointerValueCtor) { + const int kI = 4; + + // Construction from a non-null pointer + { + absl::StatusOr<const int*> so(&kI); + EXPECT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(&kI, *so); + } + + // Construction from a null pointer constant + { + absl::StatusOr<const int*> so(nullptr); + EXPECT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(nullptr, *so); + } + + // Construction from a non-literal null pointer + { + const int* const p = nullptr; + + absl::StatusOr<const int*> so(p); + EXPECT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(nullptr, *so); + } +} + +TEST(StatusOr, TestPointerCopyCtorStatusOk) { + const int kI = 0; + absl::StatusOr<const int*> original(&kI); + absl::StatusOr<const int*> copy(original); + EXPECT_OK(copy.status()); + EXPECT_EQ(*original, *copy); +} + +TEST(StatusOr, TestPointerCopyCtorStatusNotOk) { + absl::StatusOr<int*> original(absl::CancelledError()); + absl::StatusOr<int*> copy(original); + EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, TestPointerCopyCtorStatusOKConverting) { + Derived derived; + absl::StatusOr<Derived*> original(&derived); + absl::StatusOr<Base2*> copy(original); + EXPECT_OK(copy.status()); + EXPECT_EQ(static_cast<const Base2*>(*original), *copy); +} + +TEST(StatusOr, TestPointerCopyCtorStatusNotOkConverting) { + absl::StatusOr<Derived*> original(absl::CancelledError()); + absl::StatusOr<Base2*> copy(original); + EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, TestPointerAssignmentStatusOk) { + const int kI = 0; + absl::StatusOr<const int*> source(&kI); + absl::StatusOr<const int*> target; + target = source; + EXPECT_OK(target.status()); + EXPECT_EQ(*source, *target); +} + +TEST(StatusOr, TestPointerAssignmentStatusNotOk) { + absl::StatusOr<int*> source(absl::CancelledError()); + absl::StatusOr<int*> target; + target = source; + EXPECT_EQ(target.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, TestPointerAssignmentStatusOKConverting) { + Derived derived; + absl::StatusOr<Derived*> source(&derived); + absl::StatusOr<Base2*> target; + target = source; + EXPECT_OK(target.status()); + EXPECT_EQ(static_cast<const Base2*>(*source), *target); +} + +TEST(StatusOr, TestPointerAssignmentStatusNotOkConverting) { + absl::StatusOr<Derived*> source(absl::CancelledError()); + absl::StatusOr<Base2*> target; + target = source; + EXPECT_EQ(target.status(), source.status()); +} + +TEST(StatusOr, TestPointerStatus) { + const int kI = 0; + absl::StatusOr<const int*> good(&kI); + EXPECT_TRUE(good.ok()); + absl::StatusOr<const int*> bad(absl::CancelledError()); + EXPECT_EQ(bad.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, TestPointerValue) { + const int kI = 0; + absl::StatusOr<const int*> thing(&kI); + EXPECT_EQ(&kI, *thing); +} + +TEST(StatusOr, TestPointerValueConst) { + const int kI = 0; + const absl::StatusOr<const int*> thing(&kI); + EXPECT_EQ(&kI, *thing); +} + +TEST(StatusOr, StatusOrVectorOfUniquePointerCanReserveAndResize) { + using EvilType = std::vector<std::unique_ptr<int>>; + static_assert(std::is_copy_constructible<EvilType>::value, ""); + std::vector<::absl::StatusOr<EvilType>> v(5); + v.reserve(v.capacity() + 10); + v.resize(v.capacity() + 10); +} + +TEST(StatusOr, ConstPayload) { + // A reduced version of a problematic type found in the wild. All of the + // operations below should compile. + absl::StatusOr<const int> a; + + // Copy-construction + absl::StatusOr<const int> b(a); + + // Copy-assignment + EXPECT_FALSE(std::is_copy_assignable<absl::StatusOr<const int>>::value); + + // Move-construction + absl::StatusOr<const int> c(std::move(a)); + + // Move-assignment + EXPECT_FALSE(std::is_move_assignable<absl::StatusOr<const int>>::value); +} + +TEST(StatusOr, MapToStatusOrUniquePtr) { + // A reduced version of a problematic type found in the wild. All of the + // operations below should compile. + using MapType = std::map<std::string, absl::StatusOr<std::unique_ptr<int>>>; + + MapType a; + + // Move-construction + MapType b(std::move(a)); + + // Move-assignment + a = std::move(b); +} + +TEST(StatusOr, ValueOrOk) { + const absl::StatusOr<int> status_or = 0; + EXPECT_EQ(status_or.value_or(-1), 0); +} + +TEST(StatusOr, ValueOrDefault) { + const absl::StatusOr<int> status_or = absl::CancelledError(); + EXPECT_EQ(status_or.value_or(-1), -1); +} + +TEST(StatusOr, MoveOnlyValueOrOk) { + EXPECT_THAT(absl::StatusOr<std::unique_ptr<int>>(absl::make_unique<int>(0)) + .value_or(absl::make_unique<int>(-1)), + Pointee(0)); +} + +TEST(StatusOr, MoveOnlyValueOrDefault) { + EXPECT_THAT(absl::StatusOr<std::unique_ptr<int>>(absl::CancelledError()) + .value_or(absl::make_unique<int>(-1)), + Pointee(-1)); +} + +static absl::StatusOr<int> MakeStatus() { return 100; } + +TEST(StatusOr, TestIgnoreError) { MakeStatus().IgnoreError(); } + +TEST(StatusOr, EqualityOperator) { + constexpr int kNumCases = 4; + std::array<absl::StatusOr<int>, kNumCases> group1 = { + absl::StatusOr<int>(1), absl::StatusOr<int>(2), + absl::StatusOr<int>(absl::InvalidArgumentError("msg")), + absl::StatusOr<int>(absl::InternalError("msg"))}; + std::array<absl::StatusOr<int>, kNumCases> group2 = { + absl::StatusOr<int>(1), absl::StatusOr<int>(2), + absl::StatusOr<int>(absl::InvalidArgumentError("msg")), + absl::StatusOr<int>(absl::InternalError("msg"))}; + for (int i = 0; i < kNumCases; ++i) { + for (int j = 0; j < kNumCases; ++j) { + if (i == j) { + EXPECT_TRUE(group1[i] == group2[j]); + EXPECT_FALSE(group1[i] != group2[j]); + } else { + EXPECT_FALSE(group1[i] == group2[j]); + EXPECT_TRUE(group1[i] != group2[j]); + } + } + } +} + +struct MyType { + bool operator==(const MyType&) const { return true; } +}; + +enum class ConvTraits { kNone = 0, kImplicit = 1, kExplicit = 2 }; + +// This class has conversion operator to `StatusOr<T>` based on value of +// `conv_traits`. +template <typename T, ConvTraits conv_traits = ConvTraits::kNone> +struct StatusOrConversionBase {}; + +template <typename T> +struct StatusOrConversionBase<T, ConvTraits::kImplicit> { + operator absl::StatusOr<T>() const& { // NOLINT + return absl::InvalidArgumentError("conversion to absl::StatusOr"); + } + operator absl::StatusOr<T>() && { // NOLINT + return absl::InvalidArgumentError("conversion to absl::StatusOr"); + } +}; + +template <typename T> +struct StatusOrConversionBase<T, ConvTraits::kExplicit> { + explicit operator absl::StatusOr<T>() const& { + return absl::InvalidArgumentError("conversion to absl::StatusOr"); + } + explicit operator absl::StatusOr<T>() && { + return absl::InvalidArgumentError("conversion to absl::StatusOr"); + } +}; + +// This class has conversion operator to `T` based on the value of +// `conv_traits`. +template <typename T, ConvTraits conv_traits = ConvTraits::kNone> +struct ConversionBase {}; + +template <typename T> +struct ConversionBase<T, ConvTraits::kImplicit> { + operator T() const& { return t; } // NOLINT + operator T() && { return std::move(t); } // NOLINT + T t; +}; + +template <typename T> +struct ConversionBase<T, ConvTraits::kExplicit> { + explicit operator T() const& { return t; } + explicit operator T() && { return std::move(t); } + T t; +}; + +// This class has conversion operator to `absl::Status` based on the value of +// `conv_traits`. +template <ConvTraits conv_traits = ConvTraits::kNone> +struct StatusConversionBase {}; + +template <> +struct StatusConversionBase<ConvTraits::kImplicit> { + operator absl::Status() const& { // NOLINT + return absl::InternalError("conversion to Status"); + } + operator absl::Status() && { // NOLINT + return absl::InternalError("conversion to Status"); + } +}; + +template <> +struct StatusConversionBase<ConvTraits::kExplicit> { + explicit operator absl::Status() const& { // NOLINT + return absl::InternalError("conversion to Status"); + } + explicit operator absl::Status() && { // NOLINT + return absl::InternalError("conversion to Status"); + } +}; + +static constexpr int kConvToStatus = 1; +static constexpr int kConvToStatusOr = 2; +static constexpr int kConvToT = 4; +static constexpr int kConvExplicit = 8; + +constexpr ConvTraits GetConvTraits(int bit, int config) { + return (config & bit) == 0 + ? ConvTraits::kNone + : ((config & kConvExplicit) == 0 ? ConvTraits::kImplicit + : ConvTraits::kExplicit); +} + +// This class conditionally has conversion operator to `absl::Status`, `T`, +// `StatusOr<T>`, based on values of the template parameters. +template <typename T, int config> +struct CustomType + : StatusOrConversionBase<T, GetConvTraits(kConvToStatusOr, config)>, + ConversionBase<T, GetConvTraits(kConvToT, config)>, + StatusConversionBase<GetConvTraits(kConvToStatus, config)> {}; + +struct ConvertibleToAnyStatusOr { + template <typename T> + operator absl::StatusOr<T>() const { // NOLINT + return absl::InvalidArgumentError("Conversion to absl::StatusOr"); + } +}; + +// Test the rank of overload resolution for `StatusOr<T>` constructor and +// assignment, from highest to lowest: +// 1. T/Status +// 2. U that has conversion operator to absl::StatusOr<T> +// 3. U that is convertible to Status +// 4. U that is convertible to T +TEST(StatusOr, ConstructionFromT) { + // Construct absl::StatusOr<T> from T when T is convertible to + // absl::StatusOr<T> + { + ConvertibleToAnyStatusOr v; + absl::StatusOr<ConvertibleToAnyStatusOr> statusor(v); + EXPECT_TRUE(statusor.ok()); + } + { + ConvertibleToAnyStatusOr v; + absl::StatusOr<ConvertibleToAnyStatusOr> statusor = v; + EXPECT_TRUE(statusor.ok()); + } + // Construct absl::StatusOr<T> from T when T is explicitly convertible to + // Status + { + CustomType<MyType, kConvToStatus | kConvExplicit> v; + absl::StatusOr<CustomType<MyType, kConvToStatus | kConvExplicit>> statusor( + v); + EXPECT_TRUE(statusor.ok()); + } + { + CustomType<MyType, kConvToStatus | kConvExplicit> v; + absl::StatusOr<CustomType<MyType, kConvToStatus | kConvExplicit>> statusor = + v; + EXPECT_TRUE(statusor.ok()); + } +} + +// Construct absl::StatusOr<T> from U when U is explicitly convertible to T +TEST(StatusOr, ConstructionFromTypeConvertibleToT) { + { + CustomType<MyType, kConvToT | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_TRUE(statusor.ok()); + } + { + CustomType<MyType, kConvToT> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_TRUE(statusor.ok()); + } +} + +// Construct absl::StatusOr<T> from U when U has explicit conversion operator to +// absl::StatusOr<T> +TEST(StatusOr, ConstructionFromTypeWithConversionOperatorToStatusOrT) { + { + CustomType<MyType, kConvToStatusOr | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToT | kConvToStatusOr | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToStatusOr | kConvToStatus | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, + kConvToT | kConvToStatusOr | kConvToStatus | kConvExplicit> + v; + absl::StatusOr<MyType> statusor(v); + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToStatusOr> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToT | kConvToStatusOr> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToStatusOr | kConvToStatus> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToT | kConvToStatusOr | kConvToStatus> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } +} + +TEST(StatusOr, ConstructionFromTypeConvertibleToStatus) { + // Construction fails because conversion to `Status` is explicit. + { + CustomType<MyType, kConvToStatus | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } + { + CustomType<MyType, kConvToT | kConvToStatus | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } + { + CustomType<MyType, kConvToStatus> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } + { + CustomType<MyType, kConvToT | kConvToStatus> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } +} + +TEST(StatusOr, AssignmentFromT) { + // Assign to absl::StatusOr<T> from T when T is convertible to + // absl::StatusOr<T> + { + ConvertibleToAnyStatusOr v; + absl::StatusOr<ConvertibleToAnyStatusOr> statusor; + statusor = v; + EXPECT_TRUE(statusor.ok()); + } + // Assign to absl::StatusOr<T> from T when T is convertible to Status + { + CustomType<MyType, kConvToStatus> v; + absl::StatusOr<CustomType<MyType, kConvToStatus>> statusor; + statusor = v; + EXPECT_TRUE(statusor.ok()); + } +} + +TEST(StatusOr, AssignmentFromTypeConvertibleToT) { + // Assign to absl::StatusOr<T> from U when U is convertible to T + { + CustomType<MyType, kConvToT> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_TRUE(statusor.ok()); + } +} + +TEST(StatusOr, AssignmentFromTypeWithConversionOperatortoStatusOrT) { + // Assign to absl::StatusOr<T> from U when U has conversion operator to + // absl::StatusOr<T> + { + CustomType<MyType, kConvToStatusOr> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToT | kConvToStatusOr> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToStatusOr | kConvToStatus> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToT | kConvToStatusOr | kConvToStatus> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } +} + +TEST(StatusOr, AssignmentFromTypeConvertibleToStatus) { + // Assign to absl::StatusOr<T> from U when U is convertible to Status + { + CustomType<MyType, kConvToStatus> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } + { + CustomType<MyType, kConvToT | kConvToStatus> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } +} + +} // namespace |