Update StatusOr to support lvalue reference value types.

Some implementation notes:
 - RValue references are not supported right now. It complicates the implementation further and there doesn't seem to be a need for it. It might be done in the future.
 - Any kind of reference-to-reference conversion only allows those that do not require temporaries to be materialized. Eg `StatusOr<int&>` can convert to `StatusOr<const int&>`, but it can't convert to `StatusOr<const double&>`.
 - `operator*`/`value()`/`value_or()` always return the reference type, regardless of qualifications of the StatusOr.

PiperOrigin-RevId: 776150069
Change-Id: I8446e7f76f6227f24e4de4b9490d20a8156ee8ab
diff --git a/absl/status/internal/statusor_internal.h b/absl/status/internal/statusor_internal.h
index 029fdee..a653181 100644
--- a/absl/status/internal/statusor_internal.h
+++ b/absl/status/internal/statusor_internal.h
@@ -90,17 +90,34 @@
 struct IsDirectInitializationAmbiguous<T, absl::StatusOr<V>>
     : public IsConstructibleOrConvertibleFromStatusOr<T, V> {};
 
+// Checks whether the conversion from U to T can be done without dangling
+// temporaries.
+// REQUIRES: T and U are references.
+template <typename T, typename U>
+using IsReferenceConversionValid = absl::conjunction<  //
+    std::is_reference<T>, std::is_reference<U>,
+    // The references are convertible. This checks for
+    // lvalue/rvalue compatibility.
+    std::is_convertible<U, T>,
+    // The pointers are convertible. This checks we don't have
+    // a temporary.
+    std::is_convertible<std::remove_reference_t<U>*,
+                        std::remove_reference_t<T>*>>;
+
 // 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_cvref_t<U>>,
-    absl::negation<absl::disjunction<
-        std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>,
-        std::is_same<absl::Status, absl::remove_cvref_t<U>>,
-        std::is_same<absl::in_place_t, absl::remove_cvref_t<U>>,
-        IsDirectInitializationAmbiguous<T, U>>>>;
+    std::is_same<T, absl::remove_cvref_t<U>>,  //
+    std::conditional_t<
+        std::is_reference_v<T>,  //
+        IsReferenceConversionValid<T, U>,
+        absl::negation<absl::disjunction<
+            std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>,
+            std::is_same<absl::Status, absl::remove_cvref_t<U>>,
+            std::is_same<absl::in_place_t, absl::remove_cvref_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:
@@ -140,7 +157,9 @@
 template <bool Explicit, typename T, typename U, bool Lifetimebound>
 using IsConstructionValid = absl::conjunction<
     Equality<Lifetimebound,
-             type_traits_internal::IsLifetimeBoundAssignment<T, U>>,
+             absl::disjunction<
+                 std::is_reference<T>,
+                 type_traits_internal::IsLifetimeBoundAssignment<T, U>>>,
     IsDirectInitializationValid<T, U&&>, std::is_constructible<T, U&&>,
     Equality<!Explicit, std::is_convertible<U&&, T>>,
     absl::disjunction<
@@ -156,8 +175,13 @@
 template <typename T, typename U, bool Lifetimebound>
 using IsAssignmentValid = absl::conjunction<
     Equality<Lifetimebound,
-             type_traits_internal::IsLifetimeBoundAssignment<T, U>>,
-    std::is_constructible<T, U&&>, std::is_assignable<T&, U&&>,
+             absl::disjunction<
+                 std::is_reference<T>,
+                 type_traits_internal::IsLifetimeBoundAssignment<T, U>>>,
+    std::conditional_t<std::is_reference_v<T>,
+                       IsReferenceConversionValid<T, U&&>,
+                       absl::conjunction<std::is_constructible<T, U&&>,
+                                         std::is_assignable<T&, U&&>>>,
     absl::disjunction<
         std::is_same<T, absl::remove_cvref_t<U>>,
         absl::conjunction<
@@ -178,6 +202,9 @@
           typename UQ>
 using IsConstructionFromStatusOrValid = absl::conjunction<
     absl::negation<std::is_same<T, U>>,
+    // If `T` is a reference, then U must be a compatible one.
+    absl::disjunction<absl::negation<std::is_reference<T>>,
+                      IsReferenceConversionValid<T, U>>,
     Equality<Lifetimebound,
              type_traits_internal::IsLifetimeBoundAssignment<T, U>>,
     std::is_constructible<T, UQ>,
@@ -193,6 +220,16 @@
     absl::negation<IsConstructibleOrConvertibleOrAssignableFromStatusOr<
         T, absl::remove_cvref_t<U>>>>;
 
+template <typename T, typename U, bool Lifetimebound>
+using IsValueOrValid = absl::conjunction<
+    // If `T` is a reference, then U must be a compatible one.
+    absl::disjunction<absl::negation<std::is_reference<T>>,
+                      IsReferenceConversionValid<T, U>>,
+    Equality<Lifetimebound,
+             absl::disjunction<
+                 std::is_reference<T>,
+                 type_traits_internal::IsLifetimeBoundAssignment<T, U>>>>;
+
 class Helper {
  public:
   // Move type-agnostic error handling to the .cc.
@@ -209,6 +246,26 @@
   new (p) T(std::forward<Args>(args)...);
 }
 
+template <typename T>
+class Reference {
+ public:
+  constexpr explicit Reference(T ref ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      : payload_(std::addressof(ref)) {}
+
+  Reference(const Reference&) = default;
+  Reference& operator=(const Reference&) = default;
+  Reference& operator=(T value) {
+    payload_ = std::addressof(value);
+    return *this;
+  }
+
+  operator T() const { return static_cast<T>(*payload_); }  // NOLINT
+  T get() const { return *this; }
+
+ private:
+  std::remove_reference_t<T>* absl_nonnull payload_;
+};
+
 // 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.
@@ -217,6 +274,14 @@
   template <typename U>
   friend class StatusOrData;
 
+  decltype(auto) MaybeMoveData() {
+    if constexpr (std::is_reference_v<T>) {
+      return data_.get();
+    } else {
+      return std::move(data_);
+    }
+  }
+
  public:
   StatusOrData() = delete;
 
@@ -231,7 +296,7 @@
 
   StatusOrData(StatusOrData&& other) noexcept {
     if (other.ok()) {
-      MakeValue(std::move(other.data_));
+      MakeValue(other.MaybeMoveData());
       MakeStatus();
     } else {
       MakeStatus(std::move(other.status_));
@@ -251,7 +316,7 @@
   template <typename U>
   explicit StatusOrData(StatusOrData<U>&& other) {
     if (other.ok()) {
-      MakeValue(std::move(other.data_));
+      MakeValue(other.MaybeMoveData());
       MakeStatus();
     } else {
       MakeStatus(std::move(other.status_));
@@ -264,13 +329,6 @@
     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>
@@ -290,7 +348,7 @@
   StatusOrData& operator=(StatusOrData&& other) {
     if (this == &other) return *this;
     if (other.ok())
-      Assign(std::move(other.data_));
+      Assign(other.MaybeMoveData());
     else
       AssignStatus(std::move(other.status_));
     return *this;
@@ -299,7 +357,9 @@
   ~StatusOrData() {
     if (ok()) {
       status_.~Status();
-      data_.~T();
+      if constexpr (!std::is_trivially_destructible_v<T>) {
+        data_.~T();
+      }
     } else {
       status_.~Status();
     }
@@ -340,11 +400,13 @@
     // 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_;
+    std::conditional_t<std::is_reference_v<T>, Reference<T>, T> data_;
   };
 
   void Clear() {
-    if (ok()) data_.~T();
+    if constexpr (!std::is_trivially_destructible_v<T>) {
+      if (ok()) data_.~T();
+    }
   }
 
   void EnsureOk() const {
@@ -359,7 +421,8 @@
   // argument.
   template <typename... Arg>
   void MakeValue(Arg&&... arg) {
-    internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg)...);
+    internal_statusor::PlacementNew<decltype(data_)>(&dummy_,
+                                                     std::forward<Arg>(arg)...);
   }
 
   // Construct the status (ie. status_) through placement new with the passed
@@ -369,6 +432,22 @@
     internal_statusor::PlacementNew<Status>(&status_,
                                             std::forward<Args>(args)...);
   }
+
+  template <typename U>
+  T ValueOrImpl(U&& default_value) const& {
+    if (ok()) {
+      return data_;
+    }
+    return std::forward<U>(default_value);
+  }
+
+  template <typename U>
+  T ValueOrImpl(U&& default_value) && {
+    if (ok()) {
+      return std::move(data_);
+    }
+    return std::forward<U>(default_value);
+  }
 };
 
 // Helper base classes to allow implicitly deleted constructors and assignment
@@ -411,8 +490,9 @@
   MoveCtorBase& operator=(MoveCtorBase&&) = default;
 };
 
-template <typename T, bool = std::is_copy_constructible<T>::value&&
-                          std::is_copy_assignable<T>::value>
+template <typename T, bool = (std::is_copy_constructible<T>::value &&
+                              std::is_copy_assignable<T>::value) ||
+                             std::is_reference_v<T>>
 struct CopyAssignBase {
   CopyAssignBase() = default;
   CopyAssignBase(const CopyAssignBase&) = default;
@@ -430,8 +510,9 @@
   CopyAssignBase& operator=(CopyAssignBase&&) = default;
 };
 
-template <typename T, bool = std::is_move_constructible<T>::value&&
-                          std::is_move_assignable<T>::value>
+template <typename T, bool = (std::is_move_constructible<T>::value &&
+                              std::is_move_assignable<T>::value) ||
+                             std::is_reference_v<T>>
 struct MoveAssignBase {
   MoveAssignBase() = default;
   MoveAssignBase(const MoveAssignBase&) = default;
diff --git a/absl/status/status_matchers_test.cc b/absl/status/status_matchers_test.cc
index b8ccaa4..51a5f27 100644
--- a/absl/status/status_matchers_test.cc
+++ b/absl/status/status_matchers_test.cc
@@ -18,6 +18,7 @@
 #include "absl/status/status_matchers.h"
 
 #include <string>
+#include <vector>
 
 #include "gmock/gmock.h"
 #include "gtest/gtest-spi.h"
@@ -31,9 +32,12 @@
 using ::absl_testing::IsOk;
 using ::absl_testing::IsOkAndHolds;
 using ::absl_testing::StatusIs;
+using ::testing::ElementsAre;
 using ::testing::Eq;
 using ::testing::Gt;
 using ::testing::MatchesRegex;
+using ::testing::Not;
+using ::testing::Ref;
 
 TEST(StatusMatcherTest, StatusIsOk) { EXPECT_THAT(absl::OkStatus(), IsOk()); }
 
@@ -158,4 +162,23 @@
       "ungueltig");
 }
 
+TEST(StatusMatcherTest, ReferencesWork) {
+  int i = 17;
+  int j = 19;
+  EXPECT_THAT(absl::StatusOr<int&>(i), IsOkAndHolds(17));
+  EXPECT_THAT(absl::StatusOr<int&>(i), Not(IsOkAndHolds(19)));
+  EXPECT_THAT(absl::StatusOr<const int&>(i), IsOkAndHolds(17));
+
+  // Reference testing works as expected.
+  EXPECT_THAT(absl::StatusOr<int&>(i), IsOkAndHolds(Ref(i)));
+  EXPECT_THAT(absl::StatusOr<int&>(i), Not(IsOkAndHolds(Ref(j))));
+
+  // Try a more complex one.
+  std::vector<std::string> vec = {"A", "B", "C"};
+  EXPECT_THAT(absl::StatusOr<std::vector<std::string>&>(vec),
+              IsOkAndHolds(ElementsAre("A", "B", "C")));
+  EXPECT_THAT(absl::StatusOr<std::vector<std::string>&>(vec),
+              Not(IsOkAndHolds(ElementsAre("A", "X", "C"))));
+}
+
 }  // namespace
diff --git a/absl/status/statusor.h b/absl/status/statusor.h
index 25c6288..d2d16d5 100644
--- a/absl/status/statusor.h
+++ b/absl/status/statusor.h
@@ -194,6 +194,11 @@
                  private internal_statusor::MoveCtorBase<T>,
                  private internal_statusor::CopyAssignBase<T>,
                  private internal_statusor::MoveAssignBase<T> {
+#ifndef SWIG
+  static_assert(!std::is_rvalue_reference_v<T>,
+                "rvalue references are not yet supported.");
+#endif  // SWIG
+
   template <typename U>
   friend class StatusOr;
 
@@ -397,7 +402,7 @@
             typename std::enable_if<
                 internal_statusor::IsAssignmentValid<T, U, true>::value,
                 int>::type = 0>
-  StatusOr& operator=(U&& v ABSL_ATTRIBUTE_LIFETIME_BOUND) {
+  StatusOr& operator=(U&& v ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)) {
     this->Assign(std::forward<U>(v));
     return *this;
   }
@@ -520,8 +525,10 @@
   // REQUIRES: `this->ok() == true`, otherwise the behavior is undefined.
   //
   // Use `this->ok()` to verify that there is a current value.
-  const T* absl_nonnull operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
-  T* absl_nonnull operator->() ABSL_ATTRIBUTE_LIFETIME_BOUND;
+  std::remove_reference_t<const T>* absl_nonnull operator->() const
+      ABSL_ATTRIBUTE_LIFETIME_BOUND;
+  std::remove_reference_t<T>* absl_nonnull operator->()
+      ABSL_ATTRIBUTE_LIFETIME_BOUND;
 
   // StatusOr<T>::value_or()
   //
@@ -536,10 +543,34 @@
   //
   // 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) &&;
+  template <
+      typename U,
+      std::enable_if_t<internal_statusor::IsValueOrValid<T, U&&, false>::value,
+                       int> = 0>
+  T value_or(U&& default_value) const& {
+    return this->ValueOrImpl(std::forward<U>(default_value));
+  }
+  template <
+      typename U,
+      std::enable_if_t<internal_statusor::IsValueOrValid<T, U&&, false>::value,
+                       int> = 0>
+  T value_or(U&& default_value) && {
+    return std::move(*this).ValueOrImpl(std::forward<U>(default_value));
+  }
+  template <
+      typename U,
+      std::enable_if_t<internal_statusor::IsValueOrValid<T, U&&, true>::value,
+                       int> = 0>
+  T value_or(U&& default_value ABSL_ATTRIBUTE_LIFETIME_BOUND) const& {
+    return this->ValueOrImpl(std::forward<U>(default_value));
+  }
+  template <
+      typename U,
+      std::enable_if_t<internal_statusor::IsValueOrValid<T, U&&, true>::value,
+                       int> = 0>
+  T value_or(U&& default_value ABSL_ATTRIBUTE_LIFETIME_BOUND) && {
+    return std::move(*this).ValueOrImpl(std::forward<U>(default_value));
+  }
 
   // StatusOr<T>::IgnoreError()
   //
@@ -760,33 +791,13 @@
 }
 
 template <typename T>
-const T* absl_nonnull StatusOr<T>::operator->() const {
-  this->EnsureOk();
-  return &this->data_;
+std::remove_reference_t<const T>* absl_nonnull StatusOr<T>::operator->() const {
+  return std::addressof(**this);
 }
 
 template <typename T>
-T* absl_nonnull 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);
+std::remove_reference_t<T>* absl_nonnull StatusOr<T>::operator->() {
+  return std::addressof(**this);
 }
 
 template <typename T>
diff --git a/absl/status/statusor_test.cc b/absl/status/statusor_test.cc
index 17a3384..0004453 100644
--- a/absl/status/statusor_test.cc
+++ b/absl/status/statusor_test.cc
@@ -16,6 +16,7 @@
 
 #include <array>
 #include <cstddef>
+#include <cstdint>
 #include <initializer_list>
 #include <map>
 #include <memory>
@@ -1799,4 +1800,306 @@
   EXPECT_THAT(absl::StrCat(print_me), error_matcher);
 }
 
+TEST(StatusOr, SupportsReferenceTypes) {
+  int i = 1;
+  absl::StatusOr<int&> s = i;
+  EXPECT_EQ(&i, &*s);
+  *s = 10;
+  EXPECT_EQ(i, 10);
+}
+
+TEST(StatusOr, ReferenceFromStatus) {
+  int i = 10;
+  absl::StatusOr<int&> s = i;
+  s = absl::InternalError("foo");
+  EXPECT_EQ(s.status().message(), "foo");
+
+  absl::StatusOr<int&> s2 = absl::InternalError("foo2");
+  EXPECT_EQ(s2.status().message(), "foo2");
+}
+
+TEST(StatusOr, SupportReferenceValueConstructor) {
+  int i = 1;
+  absl::StatusOr<int&> s = i;
+  absl::StatusOr<const int&> cs = i;
+  absl::StatusOr<const int&> cs2 = std::move(i);  // `T&&` to `const T&` is ok.
+
+  EXPECT_EQ(&i, &*s);
+  EXPECT_EQ(&i, &*cs);
+
+  Derived d;
+  absl::StatusOr<const Base1&> b = d;
+  EXPECT_EQ(&d, &*b);
+
+  // We disallow constructions that cause temporaries.
+  EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<const int&>, double>));
+  EXPECT_FALSE(
+      (std::is_constructible_v<absl::StatusOr<const int&>, const double&>));
+  EXPECT_FALSE(
+      (std::is_constructible_v<absl::StatusOr<const absl::string_view&>,
+                               std::string>));
+
+  // We disallow constructions with wrong reference.
+  EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<int&>, int&&>));
+  EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<int&>, const int&>));
+}
+
+TEST(StatusOr, SupportReferenceConvertingConstructor) {
+  int i = 1;
+  absl::StatusOr<int&> s = i;
+  absl::StatusOr<const int&> cs = s;
+
+  EXPECT_EQ(&i, &*s);
+  EXPECT_EQ(&i, &*cs);
+
+  // The other direction is not allowed.
+  EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<int&>,
+                                        absl::StatusOr<const int&>>));
+
+  Derived d;
+  absl::StatusOr<const Base1&> b = absl::StatusOr<const Derived&>(d);
+  EXPECT_EQ(&d, &*b);
+
+  // The other direction is not allowed.
+  EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<const Derived&>,
+                                        absl::StatusOr<const Base1&>>));
+
+  // We disallow conversions that cause temporaries.
+  EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<const int&>,
+                                        absl::StatusOr<int>>));
+  EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<const int&>,
+                                        absl::StatusOr<double>>));
+  EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<const int&>,
+                                        absl::StatusOr<const double&>>));
+  EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<const double&>,
+                                        absl::StatusOr<const int&>>));
+  EXPECT_FALSE(
+      (std::is_constructible_v<absl::StatusOr<const absl::string_view&>,
+                               absl::StatusOr<std::string>>));
+
+  // We disallow constructions with wrong reference.
+  EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<int&>,
+                                        absl::StatusOr<const int&>>));
+}
+
+TEST(StatusOr, SupportReferenceValueAssignment) {
+  int i = 1;
+  absl::StatusOr<int&> s = i;
+  absl::StatusOr<const int&> cs;
+  cs = i;
+  absl::StatusOr<const int&> cs2;
+  cs2 = std::move(i);  // `T&&` to `const T&` is ok.
+
+  EXPECT_EQ(&i, &*s);
+  EXPECT_EQ(&i, &*cs);
+
+  Derived d;
+  absl::StatusOr<const Base1&> b;
+  b = d;
+  EXPECT_EQ(&d, &*b);
+
+  // We disallow constructions that cause temporaries.
+  EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<const int&>, double>));
+  EXPECT_FALSE(
+      (std::is_assignable_v<absl::StatusOr<const int&>, const double&>));
+  EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<const absl::string_view&>,
+                                     std::string>));
+
+  // We disallow constructions with wrong reference.
+  EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<int&>, int&&>));
+  EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<int&>, const int&>));
+}
+
+TEST(StatusOr, SupportReferenceConvertingAssignment) {
+  int i = 1;
+  absl::StatusOr<int&> s;
+  s = i;
+  absl::StatusOr<const int&> cs;
+  cs = s;
+
+  EXPECT_EQ(&i, &*s);
+  EXPECT_EQ(&i, &*cs);
+
+  // The other direction is not allowed.
+  EXPECT_FALSE(
+      (std::is_assignable_v<absl::StatusOr<int&>, absl::StatusOr<const int&>>));
+
+  Derived d;
+  absl::StatusOr<const Base1&> b;
+  b = absl::StatusOr<const Derived&>(d);
+  EXPECT_EQ(&d, &*b);
+
+  // The other direction is not allowed.
+  EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<const Derived&>,
+                                     absl::StatusOr<const Base1&>>));
+
+  // We disallow conversions that cause temporaries.
+  EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<const int&>,
+                                     absl::StatusOr<const double&>>));
+  EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<const int&>,
+                                     absl::StatusOr<double>>));
+  EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<const absl::string_view&>,
+                                     absl::StatusOr<std::string>>));
+
+  // We disallow constructions with wrong reference.
+  EXPECT_FALSE(
+      (std::is_assignable_v<absl::StatusOr<int&>, absl::StatusOr<const int&>>));
+}
+
+TEST(StatusOr, SupportReferenceToNonReferenceConversions) {
+  int i = 17;
+  absl::StatusOr<int&> si = i;
+  absl::StatusOr<float> sf = si;
+  EXPECT_THAT(sf, IsOkAndHolds(17.));
+
+  i = 20;
+  sf = si;
+  EXPECT_THAT(sf, IsOkAndHolds(20.));
+
+  EXPECT_THAT(absl::StatusOr<int64_t>(absl::StatusOr<int&>(i)),
+              IsOkAndHolds(20));
+  EXPECT_THAT(absl::StatusOr<int64_t>(absl::StatusOr<const int&>(i)),
+              IsOkAndHolds(20));
+
+  std::string str = "str";
+  absl::StatusOr<std::string> sos = absl::StatusOr<std::string&>(str);
+  EXPECT_THAT(sos, IsOkAndHolds("str"));
+  str = "str2";
+  EXPECT_THAT(sos, IsOkAndHolds("str"));
+  sos = absl::StatusOr<std::string&>(str);
+  EXPECT_THAT(sos, IsOkAndHolds("str2"));
+
+  absl::StatusOr<absl::string_view> sosv = absl::StatusOr<std::string&>(str);
+  EXPECT_THAT(sosv, IsOkAndHolds("str2"));
+  str = "str3";
+  sosv = absl::StatusOr<std::string&>(str);
+  EXPECT_THAT(sosv, IsOkAndHolds("str3"));
+
+  absl::string_view view = "view";
+  // This way it is constructible, but not convertible because
+  // string_view->string is explicit
+  EXPECT_THAT(
+      absl::StatusOr<std::string>(absl::StatusOr<absl::string_view&>(view)),
+      IsOkAndHolds("view"));
+#if defined(ABSL_USES_STD_STRING_VIEW)
+  // The assignment doesn't work with normal absl::string_view because
+  // std::string doesn't know about it.
+  sos = absl::StatusOr<absl::string_view&>(view);
+  EXPECT_THAT(sos, IsOkAndHolds("view"));
+#endif
+
+  EXPECT_FALSE((std::is_convertible_v<absl::StatusOr<absl::string_view&>,
+                                      absl::StatusOr<std::string>>));
+}
+
+TEST(StatusOr, ReferenceOperatorStarAndArrow) {
+  std::string str = "Foo";
+  absl::StatusOr<std::string&> s = str;
+  s->assign("Bar");
+  EXPECT_EQ(str, "Bar");
+
+  *s = "Baz";
+  EXPECT_EQ(str, "Baz");
+
+  const absl::StatusOr<std::string&> cs = str;
+  // Even if the StatusOr is const, the reference it gives is non-const so we
+  // can still assign.
+  *cs = "Finally";
+  EXPECT_EQ(str, "Finally");
+
+  cs->clear();
+  EXPECT_EQ(cs.value(), str);
+  EXPECT_EQ(str, "");
+}
+
+TEST(StatusOr, ReferenceValueOr) {
+  int i = 17;
+  absl::StatusOr<int&> si = i;
+
+  int other = 20;
+  EXPECT_EQ(&i, &si.value_or(other));
+
+  si = absl::UnknownError("");
+  EXPECT_EQ(&other, &si.value_or(other));
+
+  absl::StatusOr<const int&> csi = i;
+  EXPECT_EQ(&i, &csi.value_or(1));
+
+  const auto value_or_call = [](auto&& sor, auto&& v)
+      -> decltype(std::forward<decltype(sor)>(sor).value_or(
+          std::forward<decltype(v)>(v))) {};
+  using Probe = decltype(value_or_call);
+  // Just to verify that Probe works as expected in the good cases.
+  EXPECT_TRUE((std::is_invocable_v<Probe, absl::StatusOr<const int&>, int&&>));
+  // Causes temporary conversion.
+  EXPECT_FALSE(
+      (std::is_invocable_v<Probe, absl::StatusOr<const int&>, double&&>));
+  // Const invalid.
+  EXPECT_FALSE((std::is_invocable_v<Probe, absl::StatusOr<int&>, const int&>));
+}
+
+TEST(StatusOr, ReferenceAssignmentFromStatusOr) {
+  std::vector<int> v = {1, 2, 3};
+  absl::StatusOr<int&> si = v[0];
+  absl::StatusOr<int&> si2 = v[1];
+
+  EXPECT_THAT(v, ElementsAre(1, 2, 3));
+  EXPECT_THAT(si, IsOkAndHolds(1));
+  EXPECT_THAT(si2, IsOkAndHolds(2));
+
+  // This rebinds the reference.
+  si = si2;
+  EXPECT_THAT(v, ElementsAre(1, 2, 3));
+  EXPECT_THAT(si, IsOkAndHolds(2));
+  EXPECT_THAT(si2, IsOkAndHolds(2));
+  EXPECT_EQ(&*si, &*si2);
+}
+
+TEST(StatusOr, ReferenceAssignFromReference) {
+  std::vector<int> v = {1, 2, 3};
+  absl::StatusOr<int&> si = v[0];
+
+  EXPECT_THAT(v, ElementsAre(1, 2, 3));
+  EXPECT_THAT(si, IsOkAndHolds(1));
+
+  // This rebinds the reference.
+  si = v[2];
+  EXPECT_THAT(v, ElementsAre(1, 2, 3));
+  EXPECT_THAT(si, IsOkAndHolds(3));
+  EXPECT_EQ(&*si, &v[2]);
+}
+
+template <typename Expected, typename T>
+void TestReferenceDeref() {
+  static_assert(std::is_same_v<Expected, decltype(*std::declval<T>())>);
+  static_assert(std::is_same_v<Expected, decltype(std::declval<T>().value())>);
+}
+
+TEST(StatusOr, ReferenceTypeIsMaintainedOnDeref) {
+  TestReferenceDeref<int&, absl::StatusOr<int&>&>();
+  TestReferenceDeref<int&, absl::StatusOr<int&>&&>();
+  TestReferenceDeref<int&, const absl::StatusOr<int&>&>();
+  TestReferenceDeref<int&, const absl::StatusOr<int&>&&>();
+
+  TestReferenceDeref<const int&, absl::StatusOr<const int&>&>();
+  TestReferenceDeref<const int&, absl::StatusOr<const int&>&&>();
+  TestReferenceDeref<const int&, const absl::StatusOr<const int&>&>();
+  TestReferenceDeref<const int&, const absl::StatusOr<const int&>&&>();
+
+  struct Struct {
+    int value;
+  };
+  EXPECT_TRUE(
+      (std::is_same_v<
+          int&, decltype((std::declval<absl::StatusOr<Struct&>>()->value))>));
+  EXPECT_TRUE(
+      (std::is_same_v<
+          int&,
+          decltype((std::declval<const absl::StatusOr<Struct&>>()->value))>));
+  EXPECT_TRUE(
+      (std::is_same_v<
+          const int&,
+          decltype((std::declval<absl::StatusOr<const Struct&>>()->value))>));
+}
+
 }  // namespace