summaryrefslogtreecommitdiff
path: root/absl/status/internal/statusor_internal.h
diff options
context:
space:
mode:
Diffstat (limited to 'absl/status/internal/statusor_internal.h')
-rw-r--r--absl/status/internal/statusor_internal.h399
1 files changed, 399 insertions, 0 deletions
diff --git a/absl/status/internal/statusor_internal.h b/absl/status/internal/statusor_internal.h
new file mode 100644
index 00000000..96e41da5
--- /dev/null
+++ b/absl/status/internal/statusor_internal.h
@@ -0,0 +1,399 @@
+// Copyright 2020 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
+#define ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
+
+#include <type_traits>
+#include <utility>
+
+#include "absl/meta/type_traits.h"
+#include "absl/status/status.h"
+#include "absl/utility/utility.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+template <typename T>
+class ABSL_MUST_USE_RESULT StatusOr;
+
+namespace internal_statusor {
+
+// Detects whether `U` has conversion operator to `StatusOr<T>`, i.e. `operator
+// StatusOr<T>()`.
+template <typename T, typename U, typename = void>
+struct HasConversionOperatorToStatusOr : std::false_type {};
+
+template <typename T, typename U>
+void test(char (*)[sizeof(std::declval<U>().operator absl::StatusOr<T>())]);
+
+template <typename T, typename U>
+struct HasConversionOperatorToStatusOr<T, U, decltype(test<T, U>(0))>
+ : std::true_type {};
+
+// Detects whether `T` is constructible or convertible from `StatusOr<U>`.
+template <typename T, typename U>
+using IsConstructibleOrConvertibleFromStatusOr =
+ absl::disjunction<std::is_constructible<T, StatusOr<U>&>,
+ std::is_constructible<T, const StatusOr<U>&>,
+ std::is_constructible<T, StatusOr<U>&&>,
+ std::is_constructible<T, const StatusOr<U>&&>,
+ std::is_convertible<StatusOr<U>&, T>,
+ std::is_convertible<const StatusOr<U>&, T>,
+ std::is_convertible<StatusOr<U>&&, T>,
+ std::is_convertible<const StatusOr<U>&&, T>>;
+
+// Detects whether `T` is constructible or convertible or assignable from
+// `StatusOr<U>`.
+template <typename T, typename U>
+using IsConstructibleOrConvertibleOrAssignableFromStatusOr =
+ absl::disjunction<IsConstructibleOrConvertibleFromStatusOr<T, U>,
+ std::is_assignable<T&, StatusOr<U>&>,
+ std::is_assignable<T&, const StatusOr<U>&>,
+ std::is_assignable<T&, StatusOr<U>&&>,
+ std::is_assignable<T&, const StatusOr<U>&&>>;
+
+// Detects whether direct initializing `StatusOr<T>` from `U` is ambiguous, i.e.
+// when `U` is `StatusOr<V>` and `T` is constructible or convertible from `V`.
+template <typename T, typename U>
+struct IsDirectInitializationAmbiguous
+ : public absl::conditional_t<
+ std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
+ U>::value,
+ std::false_type,
+ IsDirectInitializationAmbiguous<
+ T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {};
+
+template <typename T, typename V>
+struct IsDirectInitializationAmbiguous<T, absl::StatusOr<V>>
+ : public IsConstructibleOrConvertibleFromStatusOr<T, V> {};
+
+// Checks against the constraints of the direction initialization, i.e. when
+// `StatusOr<T>::StatusOr(U&&)` should participate in overload resolution.
+template <typename T, typename U>
+using IsDirectInitializationValid = absl::disjunction<
+ // Short circuits if T is basically U.
+ std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ absl::negation<absl::disjunction<
+ std::is_same<absl::StatusOr<T>,
+ absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ std::is_same<absl::Status,
+ absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ std::is_same<absl::in_place_t,
+ absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ IsDirectInitializationAmbiguous<T, U>>>>;
+
+// This trait detects whether `StatusOr<T>::operator=(U&&)` is ambiguous, which
+// is equivalent to whether all the following conditions are met:
+// 1. `U` is `StatusOr<V>`.
+// 2. `T` is constructible and assignable from `V`.
+// 3. `T` is constructible and assignable from `U` (i.e. `StatusOr<V>`).
+// For example, the following code is considered ambiguous:
+// (`T` is `bool`, `U` is `StatusOr<bool>`, `V` is `bool`)
+// StatusOr<bool> s1 = true; // s1.ok() && s1.ValueOrDie() == true
+// StatusOr<bool> s2 = false; // s2.ok() && s2.ValueOrDie() == false
+// s1 = s2; // ambiguous, `s1 = s2.ValueOrDie()` or `s1 = bool(s2)`?
+template <typename T, typename U>
+struct IsForwardingAssignmentAmbiguous
+ : public absl::conditional_t<
+ std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
+ U>::value,
+ std::false_type,
+ IsForwardingAssignmentAmbiguous<
+ T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {};
+
+template <typename T, typename U>
+struct IsForwardingAssignmentAmbiguous<T, absl::StatusOr<U>>
+ : public IsConstructibleOrConvertibleOrAssignableFromStatusOr<T, U> {};
+
+// Checks against the constraints of the forwarding assignment, i.e. whether
+// `StatusOr<T>::operator(U&&)` should participate in overload resolution.
+template <typename T, typename U>
+using IsForwardingAssignmentValid = absl::disjunction<
+ // Short circuits if T is basically U.
+ std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ absl::negation<absl::disjunction<
+ std::is_same<absl::StatusOr<T>,
+ absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ std::is_same<absl::Status,
+ absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ std::is_same<absl::in_place_t,
+ absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ IsForwardingAssignmentAmbiguous<T, U>>>>;
+
+class Helper {
+ public:
+ // Move type-agnostic error handling to the .cc.
+ static void HandleInvalidStatusCtorArg(Status*);
+ static void Crash(const absl::Status& status);
+};
+
+// Construct an instance of T in `p` through placement new, passing Args... to
+// the constructor.
+// This abstraction is here mostly for the gcc performance fix.
+template <typename T, typename... Args>
+void PlacementNew(void* p, Args&&... args) {
+#if defined(__GNUC__) && !defined(__clang__)
+ // Teach gcc that 'p' cannot be null, fixing code size issues.
+ if (p == nullptr) __builtin_unreachable();
+#endif
+ new (p) T(std::forward<Args>(args)...);
+}
+
+// Helper base class to hold the data and all operations.
+// We move all this to a base class to allow mixing with the appropriate
+// TraitsBase specialization.
+template <typename T>
+class StatusOrData {
+ template <typename U>
+ friend class StatusOrData;
+
+ public:
+ StatusOrData() = delete;
+
+ StatusOrData(const StatusOrData& other) {
+ if (other.ok()) {
+ MakeValue(other.data_);
+ MakeStatus();
+ } else {
+ MakeStatus(other.status_);
+ }
+ }
+
+ StatusOrData(StatusOrData&& other) noexcept {
+ if (other.ok()) {
+ MakeValue(std::move(other.data_));
+ MakeStatus();
+ } else {
+ MakeStatus(std::move(other.status_));
+ }
+ }
+
+ template <typename U>
+ explicit StatusOrData(const StatusOrData<U>& other) {
+ if (other.ok()) {
+ MakeValue(other.data_);
+ MakeStatus();
+ } else {
+ MakeStatus(other.status_);
+ }
+ }
+
+ template <typename U>
+ explicit StatusOrData(StatusOrData<U>&& other) {
+ if (other.ok()) {
+ MakeValue(std::move(other.data_));
+ MakeStatus();
+ } else {
+ MakeStatus(std::move(other.status_));
+ }
+ }
+
+ template <typename... Args>
+ explicit StatusOrData(absl::in_place_t, Args&&... args)
+ : data_(std::forward<Args>(args)...) {
+ MakeStatus();
+ }
+
+ explicit StatusOrData(const T& value) : data_(value) {
+ MakeStatus();
+ }
+ explicit StatusOrData(T&& value) : data_(std::move(value)) {
+ MakeStatus();
+ }
+
+ template <typename U,
+ absl::enable_if_t<std::is_constructible<absl::Status, U&&>::value,
+ int> = 0>
+ explicit StatusOrData(U&& v) : status_(v) {
+ EnsureNotOk();
+ }
+
+ StatusOrData& operator=(const StatusOrData& other) {
+ if (this == &other) return *this;
+ if (other.ok())
+ Assign(other.data_);
+ else
+ AssignStatus(other.status_);
+ return *this;
+ }
+
+ StatusOrData& operator=(StatusOrData&& other) {
+ if (this == &other) return *this;
+ if (other.ok())
+ Assign(std::move(other.data_));
+ else
+ AssignStatus(std::move(other.status_));
+ return *this;
+ }
+
+ ~StatusOrData() {
+ if (ok()) {
+ status_.~Status();
+ data_.~T();
+ } else {
+ status_.~Status();
+ }
+ }
+
+ template <typename U>
+ void Assign(U&& value) {
+ if (ok()) {
+ data_ = std::forward<U>(value);
+ } else {
+ MakeValue(std::forward<U>(value));
+ status_ = OkStatus();
+ }
+ }
+
+ template <typename U>
+ void AssignStatus(U&& v) {
+ Clear();
+ status_ = static_cast<absl::Status>(std::forward<U>(v));
+ EnsureNotOk();
+ }
+
+ bool ok() const { return status_.ok(); }
+
+ protected:
+ // status_ will always be active after the constructor.
+ // We make it a union to be able to initialize exactly how we need without
+ // waste.
+ // Eg. in the copy constructor we use the default constructor of Status in
+ // the ok() path to avoid an extra Ref call.
+ union {
+ Status status_;
+ };
+
+ // data_ is active iff status_.ok()==true
+ struct Dummy {};
+ union {
+ // When T is const, we need some non-const object we can cast to void* for
+ // the placement new. dummy_ is that object.
+ Dummy dummy_;
+ T data_;
+ };
+
+ void Clear() {
+ if (ok()) data_.~T();
+ }
+
+ void EnsureOk() const {
+ if (ABSL_PREDICT_FALSE(!ok())) Helper::Crash(status_);
+ }
+
+ void EnsureNotOk() {
+ if (ABSL_PREDICT_FALSE(ok())) Helper::HandleInvalidStatusCtorArg(&status_);
+ }
+
+ // Construct the value (ie. data_) through placement new with the passed
+ // argument.
+ template <typename... Arg>
+ void MakeValue(Arg&&... arg) {
+ internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg)...);
+ }
+
+ // Construct the status (ie. status_) through placement new with the passed
+ // argument.
+ template <typename... Args>
+ void MakeStatus(Args&&... args) {
+ internal_statusor::PlacementNew<Status>(&status_,
+ std::forward<Args>(args)...);
+ }
+};
+
+// Helper base classes to allow implicitly deleted constructors and assignment
+// operators in `StatusOr`. For example, `CopyCtorBase` will explicitly delete
+// the copy constructor when T is not copy constructible and `StatusOr` will
+// inherit that behavior implicitly.
+template <typename T, bool = std::is_copy_constructible<T>::value>
+struct CopyCtorBase {
+ CopyCtorBase() = default;
+ CopyCtorBase(const CopyCtorBase&) = default;
+ CopyCtorBase(CopyCtorBase&&) = default;
+ CopyCtorBase& operator=(const CopyCtorBase&) = default;
+ CopyCtorBase& operator=(CopyCtorBase&&) = default;
+};
+
+template <typename T>
+struct CopyCtorBase<T, false> {
+ CopyCtorBase() = default;
+ CopyCtorBase(const CopyCtorBase&) = delete;
+ CopyCtorBase(CopyCtorBase&&) = default;
+ CopyCtorBase& operator=(const CopyCtorBase&) = default;
+ CopyCtorBase& operator=(CopyCtorBase&&) = default;
+};
+
+template <typename T, bool = std::is_move_constructible<T>::value>
+struct MoveCtorBase {
+ MoveCtorBase() = default;
+ MoveCtorBase(const MoveCtorBase&) = default;
+ MoveCtorBase(MoveCtorBase&&) = default;
+ MoveCtorBase& operator=(const MoveCtorBase&) = default;
+ MoveCtorBase& operator=(MoveCtorBase&&) = default;
+};
+
+template <typename T>
+struct MoveCtorBase<T, false> {
+ MoveCtorBase() = default;
+ MoveCtorBase(const MoveCtorBase&) = default;
+ MoveCtorBase(MoveCtorBase&&) = delete;
+ MoveCtorBase& operator=(const MoveCtorBase&) = default;
+ MoveCtorBase& operator=(MoveCtorBase&&) = default;
+};
+
+template <typename T, bool = std::is_copy_constructible<T>::value&&
+ std::is_copy_assignable<T>::value>
+struct CopyAssignBase {
+ CopyAssignBase() = default;
+ CopyAssignBase(const CopyAssignBase&) = default;
+ CopyAssignBase(CopyAssignBase&&) = default;
+ CopyAssignBase& operator=(const CopyAssignBase&) = default;
+ CopyAssignBase& operator=(CopyAssignBase&&) = default;
+};
+
+template <typename T>
+struct CopyAssignBase<T, false> {
+ CopyAssignBase() = default;
+ CopyAssignBase(const CopyAssignBase&) = default;
+ CopyAssignBase(CopyAssignBase&&) = default;
+ CopyAssignBase& operator=(const CopyAssignBase&) = delete;
+ CopyAssignBase& operator=(CopyAssignBase&&) = default;
+};
+
+template <typename T, bool = std::is_move_constructible<T>::value&&
+ std::is_move_assignable<T>::value>
+struct MoveAssignBase {
+ MoveAssignBase() = default;
+ MoveAssignBase(const MoveAssignBase&) = default;
+ MoveAssignBase(MoveAssignBase&&) = default;
+ MoveAssignBase& operator=(const MoveAssignBase&) = default;
+ MoveAssignBase& operator=(MoveAssignBase&&) = default;
+};
+
+template <typename T>
+struct MoveAssignBase<T, false> {
+ MoveAssignBase() = default;
+ MoveAssignBase(const MoveAssignBase&) = default;
+ MoveAssignBase(MoveAssignBase&&) = default;
+ MoveAssignBase& operator=(const MoveAssignBase&) = default;
+ MoveAssignBase& operator=(MoveAssignBase&&) = delete;
+};
+
+ABSL_ATTRIBUTE_NORETURN void ThrowBadStatusOrAccess(absl::Status status);
+
+} // namespace internal_statusor
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_