diff options
Diffstat (limited to 'third_party/abseil_cpp/absl/status')
-rw-r--r-- | third_party/abseil_cpp/absl/status/BUILD.bazel | 103 | ||||
-rw-r--r-- | third_party/abseil_cpp/absl/status/CMakeLists.txt | 88 | ||||
-rw-r--r-- | third_party/abseil_cpp/absl/status/internal/status_internal.h | 58 | ||||
-rw-r--r-- | third_party/abseil_cpp/absl/status/internal/statusor_internal.h | 396 | ||||
-rw-r--r-- | third_party/abseil_cpp/absl/status/status.cc | 442 | ||||
-rw-r--r-- | third_party/abseil_cpp/absl/status/status.h | 817 | ||||
-rw-r--r-- | third_party/abseil_cpp/absl/status/status_payload_printer.cc | 38 | ||||
-rw-r--r-- | third_party/abseil_cpp/absl/status/status_payload_printer.h | 51 | ||||
-rw-r--r-- | third_party/abseil_cpp/absl/status/status_test.cc | 458 | ||||
-rw-r--r-- | third_party/abseil_cpp/absl/status/statusor.cc | 71 | ||||
-rw-r--r-- | third_party/abseil_cpp/absl/status/statusor.h | 760 | ||||
-rw-r--r-- | third_party/abseil_cpp/absl/status/statusor_internals.h | 250 | ||||
-rw-r--r-- | third_party/abseil_cpp/absl/status/statusor_test.cc | 1811 |
13 files changed, 5343 insertions, 0 deletions
diff --git a/third_party/abseil_cpp/absl/status/BUILD.bazel b/third_party/abseil_cpp/absl/status/BUILD.bazel new file mode 100644 index 000000000000..189bd73d0e46 --- /dev/null +++ b/third_party/abseil_cpp/absl/status/BUILD.bazel @@ -0,0 +1,103 @@ +# +# Copyright 2017 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. + +# This package contains `absl::Status`. +# It will expand later to have utilities around `Status` like `StatusOr`, +# `StatusBuilder` and macros. + +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") +load( + "//absl:copts/configure_copts.bzl", + "ABSL_DEFAULT_COPTS", + "ABSL_TEST_COPTS", +) + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +cc_library( + name = "status", + srcs = [ + "internal/status_internal.h", + "status.cc", + "status_payload_printer.cc", + ], + hdrs = [ + "status.h", + "status_payload_printer.h", + ], + copts = ABSL_DEFAULT_COPTS, + deps = [ + "//absl/base:atomic_hook", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:raw_logging_internal", + "//absl/container:inlined_vector", + "//absl/debugging:stacktrace", + "//absl/debugging:symbolize", + "//absl/strings", + "//absl/strings:cord", + "//absl/strings:str_format", + "//absl/types:optional", + ], +) + +cc_test( + name = "status_test", + srcs = ["status_test.cc"], + copts = ABSL_TEST_COPTS, + deps = [ + ":status", + "//absl/strings", + "@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/third_party/abseil_cpp/absl/status/CMakeLists.txt b/third_party/abseil_cpp/absl/status/CMakeLists.txt new file mode 100644 index 000000000000..f0d798a3732d --- /dev/null +++ b/third_party/abseil_cpp/absl/status/CMakeLists.txt @@ -0,0 +1,88 @@ +# +# 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. +# +absl_cc_library( + NAME + status + 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 + absl::inlined_vector + absl::stacktrace + absl::symbolize + absl::strings + absl::cord + absl::str_format + absl::optional + PUBLIC +) + +absl_cc_test( + NAME + status_test + SRCS + "status_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::status + 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::status + 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/third_party/abseil_cpp/absl/status/internal/status_internal.h b/third_party/abseil_cpp/absl/status/internal/status_internal.h new file mode 100644 index 000000000000..279f8f55bef0 --- /dev/null +++ b/third_party/abseil_cpp/absl/status/internal/status_internal.h @@ -0,0 +1,58 @@ +// 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 { + StatusRep(absl::StatusCode code, std::string message, + std::unique_ptr<status_internal::Payloads> payloads) + : ref(int32_t{1}), + code(code), + message(std::move(message)), + payloads(std::move(payloads)) {} + + 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/third_party/abseil_cpp/absl/status/internal/statusor_internal.h b/third_party/abseil_cpp/absl/status/internal/statusor_internal.h new file mode 100644 index 000000000000..eaac2c0b14c6 --- /dev/null +++ b/third_party/abseil_cpp/absl/status/internal/statusor_internal.h @@ -0,0 +1,396 @@ +// 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/base/attributes.h" +#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*); + ABSL_ATTRIBUTE_NORETURN 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> +ABSL_ATTRIBUTE_NONNULL(1) void PlacementNew(void* p, Args&&... args) { + 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_(std::forward<U>(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/third_party/abseil_cpp/absl/status/status.cc b/third_party/abseil_cpp/absl/status/status.cc new file mode 100644 index 000000000000..c71de846827c --- /dev/null +++ b/third_party/abseil_cpp/absl/status/status.cc @@ -0,0 +1,442 @@ +// 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. +#include "absl/status/status.h" + +#include <cassert> + +#include "absl/base/internal/raw_logging.h" +#include "absl/debugging/stacktrace.h" +#include "absl/debugging/symbolize.h" +#include "absl/status/status_payload_printer.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_split.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +std::string StatusCodeToString(StatusCode code) { + switch (code) { + case StatusCode::kOk: + return "OK"; + case StatusCode::kCancelled: + return "CANCELLED"; + case StatusCode::kUnknown: + return "UNKNOWN"; + case StatusCode::kInvalidArgument: + return "INVALID_ARGUMENT"; + case StatusCode::kDeadlineExceeded: + return "DEADLINE_EXCEEDED"; + case StatusCode::kNotFound: + return "NOT_FOUND"; + case StatusCode::kAlreadyExists: + return "ALREADY_EXISTS"; + case StatusCode::kPermissionDenied: + return "PERMISSION_DENIED"; + case StatusCode::kUnauthenticated: + return "UNAUTHENTICATED"; + case StatusCode::kResourceExhausted: + return "RESOURCE_EXHAUSTED"; + case StatusCode::kFailedPrecondition: + return "FAILED_PRECONDITION"; + case StatusCode::kAborted: + return "ABORTED"; + case StatusCode::kOutOfRange: + return "OUT_OF_RANGE"; + case StatusCode::kUnimplemented: + return "UNIMPLEMENTED"; + case StatusCode::kInternal: + return "INTERNAL"; + case StatusCode::kUnavailable: + return "UNAVAILABLE"; + case StatusCode::kDataLoss: + return "DATA_LOSS"; + default: + return ""; + } +} + +std::ostream& operator<<(std::ostream& os, StatusCode code) { + return os << StatusCodeToString(code); +} + +namespace status_internal { + +static int FindPayloadIndexByUrl(const Payloads* payloads, + absl::string_view type_url) { + if (payloads == nullptr) return -1; + + for (size_t i = 0; i < payloads->size(); ++i) { + if ((*payloads)[i].type_url == type_url) return i; + } + + return -1; +} + +// Convert canonical code to a value known to this binary. +absl::StatusCode MapToLocalCode(int value) { + absl::StatusCode code = static_cast<absl::StatusCode>(value); + switch (code) { + case absl::StatusCode::kOk: + case absl::StatusCode::kCancelled: + case absl::StatusCode::kUnknown: + case absl::StatusCode::kInvalidArgument: + case absl::StatusCode::kDeadlineExceeded: + case absl::StatusCode::kNotFound: + case absl::StatusCode::kAlreadyExists: + case absl::StatusCode::kPermissionDenied: + case absl::StatusCode::kResourceExhausted: + case absl::StatusCode::kFailedPrecondition: + case absl::StatusCode::kAborted: + case absl::StatusCode::kOutOfRange: + case absl::StatusCode::kUnimplemented: + case absl::StatusCode::kInternal: + case absl::StatusCode::kUnavailable: + case absl::StatusCode::kDataLoss: + case absl::StatusCode::kUnauthenticated: + return code; + default: + return absl::StatusCode::kUnknown; + } +} +} // namespace status_internal + +absl::optional<absl::Cord> Status::GetPayload( + absl::string_view type_url) const { + const auto* payloads = GetPayloads(); + int index = status_internal::FindPayloadIndexByUrl(payloads, type_url); + if (index != -1) return (*payloads)[index].payload; + + return absl::nullopt; +} + +void Status::SetPayload(absl::string_view type_url, absl::Cord payload) { + if (ok()) return; + + PrepareToModify(); + + status_internal::StatusRep* rep = RepToPointer(rep_); + if (!rep->payloads) { + rep->payloads = absl::make_unique<status_internal::Payloads>(); + } + + int index = + status_internal::FindPayloadIndexByUrl(rep->payloads.get(), type_url); + if (index != -1) { + (*rep->payloads)[index].payload = std::move(payload); + return; + } + + rep->payloads->push_back({std::string(type_url), std::move(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; + } + + return false; +} + +void Status::ForEachPayload( + const std::function<void(absl::string_view, const absl::Cord&)>& visitor) + const { + if (auto* payloads = GetPayloads()) { + bool in_reverse = + payloads->size() > 1 && reinterpret_cast<uintptr_t>(payloads) % 13 > 6; + + 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 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); +#endif // NDEBUG + } + } +} + +const std::string* Status::EmptyString() { + static std::string* empty_string = new std::string(); + return empty_string; +} + +constexpr const char Status::kMovedFromString[]; + +const std::string* Status::MovedFromString() { + static std::string* moved_from_string = new std::string(kMovedFromString); + return moved_from_string; +} + +void Status::UnrefNonInlined(uintptr_t rep) { + status_internal::StatusRep* r = RepToPointer(rep); + // Fast path: if ref==1, there is no need for a RefCountDec (since + // this is the only reference and therefore no other thread is + // allowed to be mucking with r). + if (r->ref.load(std::memory_order_acquire) == 1 || + r->ref.fetch_sub(1, std::memory_order_acq_rel) - 1 == 0) { + delete r; + } +} + +uintptr_t Status::NewRep(absl::StatusCode code, absl::string_view msg, + std::unique_ptr<status_internal::Payloads> payloads) { + status_internal::StatusRep* rep = new status_internal::StatusRep( + code, std::string(msg.data(), msg.size()), std::move(payloads)); + return PointerToRep(rep); +} + +Status::Status(absl::StatusCode code, absl::string_view msg) + : rep_(CodeToInlinedRep(code)) { + if (code != absl::StatusCode::kOk && !msg.empty()) { + rep_ = NewRep(code, msg, nullptr); + } +} + +int Status::raw_code() const { + if (IsInlined(rep_)) { + return static_cast<int>(InlinedRepToCode(rep_)); + } + status_internal::StatusRep* rep = RepToPointer(rep_); + return static_cast<int>(rep->code); +} + +absl::StatusCode Status::code() const { + return status_internal::MapToLocalCode(raw_code()); +} + +void Status::PrepareToModify() { + ABSL_RAW_CHECK(!ok(), "PrepareToModify shouldn't be called on OK status."); + if (IsInlined(rep_)) { + rep_ = NewRep(static_cast<absl::StatusCode>(raw_code()), + absl::string_view(), nullptr); + return; + } + + uintptr_t rep_i = rep_; + status_internal::StatusRep* rep = RepToPointer(rep_); + if (rep->ref.load(std::memory_order_acquire) != 1) { + std::unique_ptr<status_internal::Payloads> payloads; + if (rep->payloads) { + payloads = absl::make_unique<status_internal::Payloads>(*rep->payloads); + } + rep_ = NewRep(rep->code, message(), std::move(payloads)); + UnrefNonInlined(rep_i); + } +} + +bool Status::EqualsSlow(const absl::Status& a, const absl::Status& b) { + if (IsInlined(a.rep_) != IsInlined(b.rep_)) return false; + if (a.message() != b.message()) return false; + if (a.raw_code() != b.raw_code()) return false; + if (a.GetPayloads() == b.GetPayloads()) return true; + + const status_internal::Payloads no_payloads; + const status_internal::Payloads* larger_payloads = + a.GetPayloads() ? a.GetPayloads() : &no_payloads; + const status_internal::Payloads* smaller_payloads = + b.GetPayloads() ? b.GetPayloads() : &no_payloads; + if (larger_payloads->size() < smaller_payloads->size()) { + std::swap(larger_payloads, smaller_payloads); + } + if ((larger_payloads->size() - smaller_payloads->size()) > 1) return false; + // Payloads can be ordered differently, so we can't just compare payload + // vectors. + for (const auto& payload : *larger_payloads) { + + bool found = false; + for (const auto& other_payload : *smaller_payloads) { + if (payload.type_url == other_payload.type_url) { + if (payload.payload != other_payload.payload) { + return false; + } + found = true; + break; + } + } + if (!found) return false; + } + return true; +} + +std::string Status::ToStringSlow() const { + std::string text; + absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message()); + status_internal::StatusPayloadPrinter printer = + status_internal::GetStatusPayloadPrinter(); + this->ForEachPayload([&](absl::string_view type_url, + const absl::Cord& payload) { + absl::optional<std::string> result; + if (printer) result = printer(type_url, payload); + absl::StrAppend( + &text, " [", type_url, "='", + result.has_value() ? *result : absl::CHexEscape(std::string(payload)), + "']"); + }); + + return text; +} + +std::ostream& operator<<(std::ostream& os, const Status& x) { + os << x.ToString(); + return os; +} + +Status AbortedError(absl::string_view message) { + return Status(absl::StatusCode::kAborted, message); +} + +Status AlreadyExistsError(absl::string_view message) { + return Status(absl::StatusCode::kAlreadyExists, message); +} + +Status CancelledError(absl::string_view message) { + return Status(absl::StatusCode::kCancelled, message); +} + +Status DataLossError(absl::string_view message) { + return Status(absl::StatusCode::kDataLoss, message); +} + +Status DeadlineExceededError(absl::string_view message) { + return Status(absl::StatusCode::kDeadlineExceeded, message); +} + +Status FailedPreconditionError(absl::string_view message) { + return Status(absl::StatusCode::kFailedPrecondition, message); +} + +Status InternalError(absl::string_view message) { + return Status(absl::StatusCode::kInternal, message); +} + +Status InvalidArgumentError(absl::string_view message) { + return Status(absl::StatusCode::kInvalidArgument, message); +} + +Status NotFoundError(absl::string_view message) { + return Status(absl::StatusCode::kNotFound, message); +} + +Status OutOfRangeError(absl::string_view message) { + return Status(absl::StatusCode::kOutOfRange, message); +} + +Status PermissionDeniedError(absl::string_view message) { + return Status(absl::StatusCode::kPermissionDenied, message); +} + +Status ResourceExhaustedError(absl::string_view message) { + return Status(absl::StatusCode::kResourceExhausted, message); +} + +Status UnauthenticatedError(absl::string_view message) { + return Status(absl::StatusCode::kUnauthenticated, message); +} + +Status UnavailableError(absl::string_view message) { + return Status(absl::StatusCode::kUnavailable, message); +} + +Status UnimplementedError(absl::string_view message) { + return Status(absl::StatusCode::kUnimplemented, message); +} + +Status UnknownError(absl::string_view message) { + return Status(absl::StatusCode::kUnknown, message); +} + +bool IsAborted(const Status& status) { + return status.code() == absl::StatusCode::kAborted; +} + +bool IsAlreadyExists(const Status& status) { + return status.code() == absl::StatusCode::kAlreadyExists; +} + +bool IsCancelled(const Status& status) { + return status.code() == absl::StatusCode::kCancelled; +} + +bool IsDataLoss(const Status& status) { + return status.code() == absl::StatusCode::kDataLoss; +} + +bool IsDeadlineExceeded(const Status& status) { + return status.code() == absl::StatusCode::kDeadlineExceeded; +} + +bool IsFailedPrecondition(const Status& status) { + return status.code() == absl::StatusCode::kFailedPrecondition; +} + +bool IsInternal(const Status& status) { + return status.code() == absl::StatusCode::kInternal; +} + +bool IsInvalidArgument(const Status& status) { + return status.code() == absl::StatusCode::kInvalidArgument; +} + +bool IsNotFound(const Status& status) { + return status.code() == absl::StatusCode::kNotFound; +} + +bool IsOutOfRange(const Status& status) { + return status.code() == absl::StatusCode::kOutOfRange; +} + +bool IsPermissionDenied(const Status& status) { + return status.code() == absl::StatusCode::kPermissionDenied; +} + +bool IsResourceExhausted(const Status& status) { + return status.code() == absl::StatusCode::kResourceExhausted; +} + +bool IsUnauthenticated(const Status& status) { + return status.code() == absl::StatusCode::kUnauthenticated; +} + +bool IsUnavailable(const Status& status) { + return status.code() == absl::StatusCode::kUnavailable; +} + +bool IsUnimplemented(const Status& status) { + return status.code() == absl::StatusCode::kUnimplemented; +} + +bool IsUnknown(const Status& status) { + return status.code() == absl::StatusCode::kUnknown; +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil_cpp/absl/status/status.h b/third_party/abseil_cpp/absl/status/status.h new file mode 100644 index 000000000000..c4d6fce09050 --- /dev/null +++ b/third_party/abseil_cpp/absl/status/status.h @@ -0,0 +1,817 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// 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_ + +#include <iostream> +#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 + // + // kCancelled (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); + +// 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: + // 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(); + + // 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. + // + // 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 operators + + // The moved-from state is valid but unspecified. + Status(Status&&) noexcept; + Status& operator=(Status&&); + + ~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`. + // + // `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); + // + void Update(const Status& new_status); + void Update(Status&& new_status); + + // Status::ok() + // + // Returns `true` if `this->ok()`. Prefer checking for an OK status using this + // member function. + ABSL_MUST_USE_RESULT bool ok() const; + + // Status::code() + // + // Returns the canonical error code of type `absl::StatusCode` of this status. + absl::StatusCode code() const; + + // 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; + + // 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&); + + // 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() + // + // Swap the contents of one status with another. + friend void swap(Status& a, Status& b); + + //---------------------------------------------------------------------------- + // Payload Management APIs + //---------------------------------------------------------------------------- + + // 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; + + // Status::SetPayload() + // + // Sets the payload for a non-ok status using a `type_url` key, overwriting + // any existing payload for that `type_url`. + // + // NOTE: This function does nothing if the Status is ok. + void SetPayload(absl::string_view type_url, absl::Cord payload); + + // 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); + + // Status::ForEachPayload() + // + // 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 '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) + const; + + private: + friend Status CancelledError(); + + // Creates a status in the canonical error space with the specified + // code, and an empty error message. + explicit Status(absl::StatusCode code); + + static void UnrefNonInlined(uintptr_t rep); + static void Ref(uintptr_t rep); + static void Unref(uintptr_t rep); + + // REQUIRES: !ok() + // Ensures rep_ is not shared with any other Status. + void PrepareToModify(); + + const status_internal::Payloads* GetPayloads() const; + status_internal::Payloads* GetPayloads(); + + // Takes ownership of payload. + static uintptr_t NewRep(absl::StatusCode code, absl::string_view msg, + std::unique_ptr<status_internal::Payloads> payload); + static bool EqualsSlow(const absl::Status& a, const absl::Status& b); + + // MSVC 14.0 limitation requires the const. + static constexpr const char kMovedFromString[] = + "Status accessed after move."; + + static const std::string* EmptyString(); + static const std::string* MovedFromString(); + + // Returns whether rep contains an inlined representation. + // See rep_ for details. + static bool IsInlined(uintptr_t rep); + + // Indicates whether this Status was the rhs of a move operation. See rep_ + // for details. + static bool IsMovedFrom(uintptr_t rep); + static uintptr_t MovedFromRep(); + + // Convert between error::Code and the inlined uintptr_t representation used + // by rep_. See rep_ for details. + static uintptr_t CodeToInlinedRep(absl::StatusCode code); + static absl::StatusCode InlinedRepToCode(uintptr_t rep); + + // Converts between StatusRep* and the external uintptr_t representation used + // by rep_. See rep_ for details. + static uintptr_t PointerToRep(status_internal::StatusRep* r); + static status_internal::StatusRep* RepToPointer(uintptr_t r); + + // Returns string for non-ok Status. + std::string ToStringSlow() const; + + // Status supports two different representations. + // - When the low bit is off it is an inlined representation. + // It uses the canonical error space, no message or payload. + // The error code is (rep_ >> 2). + // The (rep_ & 2) bit is the "moved from" indicator, used in IsMovedFrom(). + // - When the low bit is on it is an external representation. + // In this case all the data comes from a heap allocated Rep object. + // (rep_ - 1) is a status_internal::StatusRep* pointer to that structure. + uintptr_t rep_; +}; + +// 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)) {} + +inline Status::Status(absl::StatusCode code) : rep_(CodeToInlinedRep(code)) {} + +inline Status::Status(const Status& x) : rep_(x.rep_) { Ref(rep_); } + +inline Status& Status::operator=(const Status& x) { + uintptr_t old_rep = rep_; + if (x.rep_ != old_rep) { + Ref(x.rep_); + rep_ = x.rep_; + Unref(old_rep); + } + return *this; +} + +inline Status::Status(Status&& x) noexcept : rep_(x.rep_) { + x.rep_ = MovedFromRep(); +} + +inline Status& Status::operator=(Status&& x) { + uintptr_t old_rep = rep_; + rep_ = x.rep_; + x.rep_ = MovedFromRep(); + Unref(old_rep); + return *this; +} + +inline void Status::Update(const Status& new_status) { + if (ok()) { + *this = new_status; + } +} + +inline void Status::Update(Status&& new_status) { + if (ok()) { + *this = std::move(new_status); + } +} + +inline Status::~Status() { Unref(rep_); } + +inline bool Status::ok() const { + return rep_ == CodeToInlinedRep(absl::StatusCode::kOk); +} + +inline absl::string_view Status::message() const { + return !IsInlined(rep_) + ? RepToPointer(rep_)->message + : (IsMovedFrom(rep_) ? absl::string_view(kMovedFromString) + : absl::string_view()); +} + +inline bool operator==(const Status& lhs, const Status& rhs) { + return lhs.rep_ == rhs.rep_ || Status::EqualsSlow(lhs, rhs); +} + +inline bool operator!=(const Status& lhs, const Status& rhs) { + return !(lhs == rhs); +} + +inline std::string Status::ToString() const { + return ok() ? "OK" : ToStringSlow(); +} + +inline void Status::IgnoreError() const { + // no-op +} + +inline void swap(absl::Status& a, absl::Status& b) { + using std::swap; + swap(a.rep_, b.rep_); +} + +inline const status_internal::Payloads* Status::GetPayloads() const { + return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get(); +} + +inline status_internal::Payloads* Status::GetPayloads() { + return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get(); +} + +inline bool Status::IsInlined(uintptr_t rep) { return (rep & 1) == 0; } + +inline bool Status::IsMovedFrom(uintptr_t rep) { + return IsInlined(rep) && (rep & 2) != 0; +} + +inline uintptr_t Status::MovedFromRep() { + return CodeToInlinedRep(absl::StatusCode::kInternal) | 2; +} + +inline uintptr_t Status::CodeToInlinedRep(absl::StatusCode code) { + return static_cast<uintptr_t>(code) << 2; +} + +inline absl::StatusCode Status::InlinedRepToCode(uintptr_t rep) { + assert(IsInlined(rep)); + return static_cast<absl::StatusCode>(rep >> 2); +} + +inline status_internal::StatusRep* Status::RepToPointer(uintptr_t rep) { + assert(!IsInlined(rep)); + return reinterpret_cast<status_internal::StatusRep*>(rep - 1); +} + +inline uintptr_t Status::PointerToRep(status_internal::StatusRep* rep) { + return reinterpret_cast<uintptr_t>(rep) + 1; +} + +inline void Status::Ref(uintptr_t rep) { + if (!IsInlined(rep)) { + RepToPointer(rep)->ref.fetch_add(1, std::memory_order_relaxed); + } +} + +inline void Status::Unref(uintptr_t rep) { + if (!IsInlined(rep)) { + UnrefNonInlined(rep); + } +} + +inline Status OkStatus() { return Status(); } + +// 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); } + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STATUS_STATUS_H_ diff --git a/third_party/abseil_cpp/absl/status/status_payload_printer.cc b/third_party/abseil_cpp/absl/status/status_payload_printer.cc new file mode 100644 index 000000000000..a47aea11c2d3 --- /dev/null +++ b/third_party/abseil_cpp/absl/status/status_payload_printer.cc @@ -0,0 +1,38 @@ +// 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. +#include "absl/status/status_payload_printer.h" + +#include <atomic> + +#include "absl/base/attributes.h" +#include "absl/base/internal/atomic_hook.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace status_internal { + +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES +static absl::base_internal::AtomicHook<StatusPayloadPrinter> storage; + +void SetStatusPayloadPrinter(StatusPayloadPrinter printer) { + storage.Store(printer); +} + +StatusPayloadPrinter GetStatusPayloadPrinter() { + return storage.Load(); +} + +} // namespace status_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil_cpp/absl/status/status_payload_printer.h b/third_party/abseil_cpp/absl/status/status_payload_printer.h new file mode 100644 index 000000000000..5e0937f67da8 --- /dev/null +++ b/third_party/abseil_cpp/absl/status/status_payload_printer.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_STATUS_PAYLOAD_PRINTER_H_ +#define ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_ + +#include <string> + +#include "absl/strings/cord.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace status_internal { + +// By default, `Status::ToString` and `operator<<(Status)` print a payload by +// dumping the type URL and the raw bytes. To help debugging, we provide an +// extension point, which is a global printer function that can be set by users +// to specify how to print payloads. The function takes the type URL and the +// payload as input, and should return a valid human-readable string on success +// or `absl::nullopt` on failure (in which case it falls back to the default +// approach of printing the raw bytes). +// NOTE: This is an internal API and the design is subject to change in the +// future in a non-backward-compatible way. Since it's only meant for debugging +// purpose, you should not rely on it in any critical logic. +using StatusPayloadPrinter = absl::optional<std::string> (*)(absl::string_view, + const absl::Cord&); + +// Sets the global payload printer. Only one printer should be set per process. +// If multiple printers are set, it's undefined which one will be used. +void SetStatusPayloadPrinter(StatusPayloadPrinter); + +// Returns the global payload printer if previously set, otherwise `nullptr`. +StatusPayloadPrinter GetStatusPayloadPrinter(); + +} // namespace status_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_ diff --git a/third_party/abseil_cpp/absl/status/status_test.cc b/third_party/abseil_cpp/absl/status/status_test.cc new file mode 100644 index 000000000000..ca9488ad228c --- /dev/null +++ b/third_party/abseil_cpp/absl/status/status_test.cc @@ -0,0 +1,458 @@ +// 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. + +#include "absl/status/status.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" + +namespace { + +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Optional; +using ::testing::UnorderedElementsAreArray; + +TEST(StatusCode, InsertionOperator) { + const absl::StatusCode code = absl::StatusCode::kUnknown; + std::ostringstream oss; + oss << code; + EXPECT_EQ(oss.str(), absl::StatusCodeToString(code)); +} + +// This structure holds the details for testing a single error code, +// its creator, and its classifier. +struct ErrorTest { + absl::StatusCode code; + using Creator = absl::Status (*)(absl::string_view); + using Classifier = bool (*)(const absl::Status&); + Creator creator; + Classifier classifier; +}; + +constexpr ErrorTest kErrorTests[]{ + {absl::StatusCode::kCancelled, absl::CancelledError, absl::IsCancelled}, + {absl::StatusCode::kUnknown, absl::UnknownError, absl::IsUnknown}, + {absl::StatusCode::kInvalidArgument, absl::InvalidArgumentError, + absl::IsInvalidArgument}, + {absl::StatusCode::kDeadlineExceeded, absl::DeadlineExceededError, + absl::IsDeadlineExceeded}, + {absl::StatusCode::kNotFound, absl::NotFoundError, absl::IsNotFound}, + {absl::StatusCode::kAlreadyExists, absl::AlreadyExistsError, + absl::IsAlreadyExists}, + {absl::StatusCode::kPermissionDenied, absl::PermissionDeniedError, + absl::IsPermissionDenied}, + {absl::StatusCode::kResourceExhausted, absl::ResourceExhaustedError, + absl::IsResourceExhausted}, + {absl::StatusCode::kFailedPrecondition, absl::FailedPreconditionError, + absl::IsFailedPrecondition}, + {absl::StatusCode::kAborted, absl::AbortedError, absl::IsAborted}, + {absl::StatusCode::kOutOfRange, absl::OutOfRangeError, absl::IsOutOfRange}, + {absl::StatusCode::kUnimplemented, absl::UnimplementedError, + absl::IsUnimplemented}, + {absl::StatusCode::kInternal, absl::InternalError, absl::IsInternal}, + {absl::StatusCode::kUnavailable, absl::UnavailableError, + absl::IsUnavailable}, + {absl::StatusCode::kDataLoss, absl::DataLossError, absl::IsDataLoss}, + {absl::StatusCode::kUnauthenticated, absl::UnauthenticatedError, + absl::IsUnauthenticated}, +}; + +TEST(Status, CreateAndClassify) { + for (const auto& test : kErrorTests) { + SCOPED_TRACE(absl::StatusCodeToString(test.code)); + + // Ensure that the creator does, in fact, create status objects with the + // expected error code and message. + std::string message = + absl::StrCat("error code ", test.code, " test message"); + absl::Status status = test.creator(message); + EXPECT_EQ(test.code, status.code()); + EXPECT_EQ(message, status.message()); + + // Ensure that the classifier returns true for a status produced by the + // creator. + EXPECT_TRUE(test.classifier(status)); + + // Ensure that the classifier returns false for status with a different + // code. + for (const auto& other : kErrorTests) { + if (other.code != test.code) { + EXPECT_FALSE(test.classifier(absl::Status(other.code, ""))) + << " other.code = " << other.code; + } + } + } +} + +TEST(Status, DefaultConstructor) { + absl::Status status; + EXPECT_TRUE(status.ok()); + EXPECT_EQ(absl::StatusCode::kOk, status.code()); + EXPECT_EQ("", status.message()); +} + +TEST(Status, OkStatus) { + absl::Status status = absl::OkStatus(); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(absl::StatusCode::kOk, status.code()); + EXPECT_EQ("", status.message()); +} + +TEST(Status, ConstructorWithCodeMessage) { + { + absl::Status status(absl::StatusCode::kCancelled, ""); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kCancelled, status.code()); + EXPECT_EQ("", status.message()); + } + { + absl::Status status(absl::StatusCode::kInternal, "message"); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(absl::StatusCode::kInternal, status.code()); + EXPECT_EQ("message", status.message()); + } +} + +TEST(Status, ConstructOutOfRangeCode) { + const int kRawCode = 9999; + absl::Status status(static_cast<absl::StatusCode>(kRawCode), ""); + EXPECT_EQ(absl::StatusCode::kUnknown, status.code()); + EXPECT_EQ(kRawCode, status.raw_code()); +} + +constexpr char kUrl1[] = "url.payload.1"; +constexpr char kUrl2[] = "url.payload.2"; +constexpr char kUrl3[] = "url.payload.3"; +constexpr char kUrl4[] = "url.payload.xx"; + +constexpr char kPayload1[] = "aaaaa"; +constexpr char kPayload2[] = "bbbbb"; +constexpr char kPayload3[] = "ccccc"; + +using PayloadsVec = std::vector<std::pair<std::string, absl::Cord>>; + +TEST(Status, TestGetSetPayload) { + absl::Status ok_status = absl::OkStatus(); + ok_status.SetPayload(kUrl1, absl::Cord(kPayload1)); + ok_status.SetPayload(kUrl2, absl::Cord(kPayload2)); + + EXPECT_FALSE(ok_status.GetPayload(kUrl1)); + EXPECT_FALSE(ok_status.GetPayload(kUrl2)); + + absl::Status bad_status(absl::StatusCode::kInternal, "fail"); + bad_status.SetPayload(kUrl1, absl::Cord(kPayload1)); + bad_status.SetPayload(kUrl2, absl::Cord(kPayload2)); + + EXPECT_THAT(bad_status.GetPayload(kUrl1), Optional(Eq(kPayload1))); + EXPECT_THAT(bad_status.GetPayload(kUrl2), Optional(Eq(kPayload2))); + + EXPECT_FALSE(bad_status.GetPayload(kUrl3)); + + bad_status.SetPayload(kUrl1, absl::Cord(kPayload3)); + EXPECT_THAT(bad_status.GetPayload(kUrl1), Optional(Eq(kPayload3))); + + // Testing dynamically generated type_url + bad_status.SetPayload(absl::StrCat(kUrl1, ".1"), absl::Cord(kPayload1)); + EXPECT_THAT(bad_status.GetPayload(absl::StrCat(kUrl1, ".1")), + Optional(Eq(kPayload1))); +} + +TEST(Status, TestErasePayload) { + absl::Status bad_status(absl::StatusCode::kInternal, "fail"); + bad_status.SetPayload(kUrl1, absl::Cord(kPayload1)); + bad_status.SetPayload(kUrl2, absl::Cord(kPayload2)); + bad_status.SetPayload(kUrl3, absl::Cord(kPayload3)); + + EXPECT_FALSE(bad_status.ErasePayload(kUrl4)); + + EXPECT_TRUE(bad_status.GetPayload(kUrl2)); + EXPECT_TRUE(bad_status.ErasePayload(kUrl2)); + EXPECT_FALSE(bad_status.GetPayload(kUrl2)); + EXPECT_FALSE(bad_status.ErasePayload(kUrl2)); + + EXPECT_TRUE(bad_status.ErasePayload(kUrl1)); + EXPECT_TRUE(bad_status.ErasePayload(kUrl3)); + + bad_status.SetPayload(kUrl1, absl::Cord(kPayload1)); + EXPECT_TRUE(bad_status.ErasePayload(kUrl1)); +} + +TEST(Status, TestComparePayloads) { + absl::Status bad_status1(absl::StatusCode::kInternal, "fail"); + bad_status1.SetPayload(kUrl1, absl::Cord(kPayload1)); + bad_status1.SetPayload(kUrl2, absl::Cord(kPayload2)); + bad_status1.SetPayload(kUrl3, absl::Cord(kPayload3)); + + absl::Status bad_status2(absl::StatusCode::kInternal, "fail"); + bad_status2.SetPayload(kUrl2, absl::Cord(kPayload2)); + bad_status2.SetPayload(kUrl3, absl::Cord(kPayload3)); + bad_status2.SetPayload(kUrl1, absl::Cord(kPayload1)); + + 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; + + s.ForEachPayload([&](absl::string_view type_url, const absl::Cord& payload) { + result.push_back(std::make_pair(std::string(type_url), payload)); + }); + + return result; +} + +TEST(Status, TestForEachPayload) { + absl::Status bad_status(absl::StatusCode::kInternal, "fail"); + bad_status.SetPayload(kUrl1, absl::Cord(kPayload1)); + bad_status.SetPayload(kUrl2, absl::Cord(kPayload2)); + bad_status.SetPayload(kUrl3, absl::Cord(kPayload3)); + + int count = 0; + + bad_status.ForEachPayload( + [&count](absl::string_view, const absl::Cord&) { ++count; }); + + EXPECT_EQ(count, 3); + + PayloadsVec expected_payloads = {{kUrl1, absl::Cord(kPayload1)}, + {kUrl2, absl::Cord(kPayload2)}, + {kUrl3, absl::Cord(kPayload3)}}; + + // Test that we visit all the payloads in the status. + PayloadsVec visited_payloads = AllVisitedPayloads(bad_status); + EXPECT_THAT(visited_payloads, UnorderedElementsAreArray(expected_payloads)); + + // Test that visitation order is not consistent between run. + std::vector<absl::Status> scratch; + while (true) { + scratch.emplace_back(absl::StatusCode::kInternal, "fail"); + + scratch.back().SetPayload(kUrl1, absl::Cord(kPayload1)); + scratch.back().SetPayload(kUrl2, absl::Cord(kPayload2)); + scratch.back().SetPayload(kUrl3, absl::Cord(kPayload3)); + + if (AllVisitedPayloads(scratch.back()) != visited_payloads) { + break; + } + } +} + +TEST(Status, ToString) { + absl::Status s(absl::StatusCode::kInternal, "fail"); + EXPECT_EQ("INTERNAL: fail", s.ToString()); + s.SetPayload("foo", absl::Cord("bar")); + EXPECT_EQ("INTERNAL: fail [foo='bar']", s.ToString()); + s.SetPayload("bar", absl::Cord("\377")); + EXPECT_THAT(s.ToString(), + AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"), + 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; + absl::Status copy(status); + EXPECT_EQ(copy, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + absl::Status copy(status); + EXPECT_EQ(copy, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + status.SetPayload(kUrl1, absl::Cord(kPayload1)); + absl::Status copy(status); + EXPECT_EQ(copy, status); + } +} + +TEST(Status, CopyAssignment) { + absl::Status assignee; + { + absl::Status status; + assignee = status; + EXPECT_EQ(assignee, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + assignee = status; + EXPECT_EQ(assignee, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + status.SetPayload(kUrl1, absl::Cord(kPayload1)); + assignee = status; + EXPECT_EQ(assignee, status); + } +} + +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; + absl::Status copy(absl::Status{}); + EXPECT_EQ(copy, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + absl::Status copy( + absl::Status(absl::StatusCode::kInvalidArgument, "message")); + EXPECT_EQ(copy, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + status.SetPayload(kUrl1, absl::Cord(kPayload1)); + absl::Status copy1(status); + absl::Status copy2(std::move(status)); + EXPECT_EQ(copy1, copy2); + } +} + +TEST(Status, MoveAssignment) { + absl::Status assignee; + { + absl::Status status; + assignee = absl::Status(); + EXPECT_EQ(assignee, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + assignee = absl::Status(absl::StatusCode::kInvalidArgument, "message"); + EXPECT_EQ(assignee, status); + } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + status.SetPayload(kUrl1, absl::Cord(kPayload1)); + absl::Status copy(status); + assignee = std::move(status); + EXPECT_EQ(assignee, copy); + } +} + +TEST(Status, Update) { + absl::Status s; + s.Update(absl::OkStatus()); + EXPECT_TRUE(s.ok()); + const absl::Status a(absl::StatusCode::kCancelled, "message"); + s.Update(a); + EXPECT_EQ(s, a); + const absl::Status b(absl::StatusCode::kInternal, "other message"); + s.Update(b); + EXPECT_EQ(s, a); + s.Update(absl::OkStatus()); + EXPECT_EQ(s, a); + EXPECT_FALSE(s.ok()); +} + +TEST(Status, Equality) { + absl::Status ok; + absl::Status no_payload = absl::CancelledError("no payload"); + absl::Status one_payload = absl::InvalidArgumentError("one payload"); + one_payload.SetPayload(kUrl1, absl::Cord(kPayload1)); + absl::Status two_payloads = one_payload; + two_payloads.SetPayload(kUrl2, absl::Cord(kPayload2)); + const std::array<absl::Status, 4> status_arr = {ok, no_payload, one_payload, + two_payloads}; + for (int i = 0; i < status_arr.size(); i++) { + for (int j = 0; j < status_arr.size(); j++) { + if (i == j) { + EXPECT_TRUE(status_arr[i] == status_arr[j]); + EXPECT_FALSE(status_arr[i] != status_arr[j]); + } else { + EXPECT_TRUE(status_arr[i] != status_arr[j]); + EXPECT_FALSE(status_arr[i] == status_arr[j]); + } + } + } +} + +TEST(Status, Swap) { + auto test_swap = [](const absl::Status& s1, const absl::Status& s2) { + absl::Status copy1 = s1, copy2 = s2; + swap(copy1, copy2); + EXPECT_EQ(copy1, s2); + EXPECT_EQ(copy2, s1); + }; + const absl::Status ok; + const absl::Status no_payload(absl::StatusCode::kAlreadyExists, "no payload"); + absl::Status with_payload(absl::StatusCode::kInternal, "with payload"); + with_payload.SetPayload(kUrl1, absl::Cord(kPayload1)); + test_swap(ok, no_payload); + test_swap(no_payload, ok); + test_swap(ok, with_payload); + test_swap(with_payload, ok); + test_swap(no_payload, with_payload); + test_swap(with_payload, no_payload); +} + +} // namespace diff --git a/third_party/abseil_cpp/absl/status/statusor.cc b/third_party/abseil_cpp/absl/status/statusor.cc new file mode 100644 index 000000000000..b954b45e32c7 --- /dev/null +++ b/third_party/abseil_cpp/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/third_party/abseil_cpp/absl/status/statusor.h b/third_party/abseil_cpp/absl/status/statusor.h new file mode 100644 index 000000000000..469d486fdd2e --- /dev/null +++ b/third_party/abseil_cpp/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 (i.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 if `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/third_party/abseil_cpp/absl/status/statusor_internals.h b/third_party/abseil_cpp/absl/status/statusor_internals.h new file mode 100644 index 000000000000..5366c2840c37 --- /dev/null +++ b/third_party/abseil_cpp/absl/status/statusor_internals.h @@ -0,0 +1,250 @@ +/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef ABSL_STATUS_STATUSOR_INTERNALS_H_ +#define ABSL_STATUS_STATUSOR_INTERNALS_H_ + +#include "absl/status/status.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace internal_statusor { + +class Helper { + public: + // Move type-agnostic error handling to the .cc. + static void HandleInvalidStatusCtorArg(Status*); + ABSL_ATTRIBUTE_NORETURN static void Crash(const 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(other.status_); + } + } + + template <typename U> + StatusOrData(const StatusOrData<U>& other) { + if (other.ok()) { + MakeValue(other.data_); + MakeStatus(); + } else { + MakeStatus(other.status_); + } + } + + template <typename U> + StatusOrData(StatusOrData<U>&& other) { + if (other.ok()) { + MakeValue(std::move(other.data_)); + MakeStatus(); + } else { + MakeStatus(other.status_); + } + } + + explicit StatusOrData(const T& value) : data_(value) { MakeStatus(); } + explicit StatusOrData(T&& value) : data_(std::move(value)) { MakeStatus(); } + + explicit StatusOrData(const Status& status) : status_(status) { + EnsureNotOk(); + } + explicit StatusOrData(Status&& status) : status_(std::move(status)) { + EnsureNotOk(); + } + + StatusOrData& operator=(const StatusOrData& other) { + if (this == &other) return *this; + if (other.ok()) + Assign(other.data_); + else + Assign(other.status_); + return *this; + } + + StatusOrData& operator=(StatusOrData&& other) { + if (this == &other) return *this; + if (other.ok()) + Assign(std::move(other.data_)); + else + Assign(std::move(other.status_)); + return *this; + } + + ~StatusOrData() { + if (ok()) { + status_.~Status(); + data_.~T(); + } else { + status_.~Status(); + } + } + + void Assign(const T& value) { + if (ok()) { + data_.~T(); + MakeValue(value); + } else { + MakeValue(value); + status_ = OkStatus(); + } + } + + void Assign(T&& value) { + if (ok()) { + data_.~T(); + MakeValue(std::move(value)); + } else { + MakeValue(std::move(value)); + status_ = OkStatus(); + } + } + + void Assign(const Status& status) { + Clear(); + status_ = status; + EnsureNotOk(); + } + + void Assign(Status&& status) { + Clear(); + // Note that we copy instead of moving the status here so that + // status.~StatusOrData() can call ok() without invoking UB. + status_ = status; + 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 (!ok()) Helper::Crash(status_); + } + + void EnsureNotOk() { + if (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 class to allow implicitly deleted constructors and assignment +// operations in StatusOr. +// TraitsBase will explicitly delete what it can't support and StatusOr will +// inherit that behavior implicitly. +template <bool Copy, bool Move> +struct TraitsBase { + TraitsBase() = default; + TraitsBase(const TraitsBase&) = default; + TraitsBase(TraitsBase&&) = default; + TraitsBase& operator=(const TraitsBase&) = default; + TraitsBase& operator=(TraitsBase&&) = default; +}; + +template <> +struct TraitsBase<false, true> { + TraitsBase() = default; + TraitsBase(const TraitsBase&) = delete; + TraitsBase(TraitsBase&&) = default; + TraitsBase& operator=(const TraitsBase&) = delete; + TraitsBase& operator=(TraitsBase&&) = default; +}; + +template <> +struct TraitsBase<false, false> { + TraitsBase() = default; + TraitsBase(const TraitsBase&) = delete; + TraitsBase(TraitsBase&&) = delete; + TraitsBase& operator=(const TraitsBase&) = delete; + TraitsBase& operator=(TraitsBase&&) = delete; +}; + +} // namespace internal_statusor + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STATUS_STATUSOR_INTERNALS_H_ diff --git a/third_party/abseil_cpp/absl/status/statusor_test.cc b/third_party/abseil_cpp/absl/status/statusor_test.cc new file mode 100644 index 000000000000..c2e8fb7e359e --- /dev/null +++ b/third_party/abseil_cpp/absl/status/statusor_test.cc @@ -0,0 +1,1811 @@ +// 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); +} + +TEST(StatusOr, StatusCtorForwards) { + absl::Status status(absl::StatusCode::kInternal, "Some error"); + + EXPECT_EQ(absl::StatusOr<int>(status).status().message(), "Some error"); + EXPECT_EQ(status.message(), "Some error"); + + EXPECT_EQ(absl::StatusOr<int>(std::move(status)).status().message(), + "Some error"); + EXPECT_NE(status.message(), "Some error"); +} + +// 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 |