Allow efficient construction of `absl::Status` from `std::string&&`.

PiperOrigin-RevId: 944002601
Change-Id: Iadb5434511a7c00ed5ffd8d3313ca1c9cd27d206
diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h
index 2428069..d2892c5 100644
--- a/absl/status/internal/status_internal.h
+++ b/absl/status/internal/status_internal.h
@@ -21,6 +21,7 @@
 #include <memory>
 #include <optional>
 #include <string>
+#include <type_traits>
 #include <utility>
 #include <vector>
 
@@ -86,6 +87,15 @@
         message_(message_arg),
         payloads_(std::move(payloads_arg)) {}
 
+  template <typename String,
+            typename = std::enable_if_t<std::is_same_v<String, std::string>>>
+  StatusRep(absl::StatusCode code_arg, String&& message_arg,
+            std::unique_ptr<status_internal::Payloads> payloads_arg)
+      : ref_(int32_t{1}),
+        code_(code_arg),
+        message_(std::forward<String>(message_arg)),
+        payloads_(std::move(payloads_arg)) {}
+
   absl::StatusCode code() const { return code_; }
   const std::string& message() const { return message_; }
 
diff --git a/absl/status/status.cc b/absl/status/status.cc
index 3ef4a27..18adf51 100644
--- a/absl/status/status.cc
+++ b/absl/status/status.cc
@@ -22,6 +22,8 @@
 #include <memory>
 #include <ostream>
 #include <string>
+#include <type_traits>
+#include <utility>
 
 #include "absl/base/attributes.h"
 #include "absl/base/config.h"
@@ -112,20 +114,36 @@
           absl::StatusCode::kOk, message, nullptr)));
 }
 
-uintptr_t Status::MakeRep(uintptr_t inlined_rep, absl::string_view msg,
-                          absl::SourceLocation loc) {
-  bool ok = inlined_rep == CodeToInlinedRep(absl::StatusCode::kOk);
+template <typename StringOrView>
+uintptr_t MakeStatusRepImpl(uintptr_t inlined_rep, StringOrView msg,
+                            absl::SourceLocation loc) {
+  static_assert(std::is_same_v<StringOrView, absl::string_view> ||
+                std::is_same_v<StringOrView, std::string&&>);
+  bool ok = inlined_rep == Status::CodeToInlinedRep(absl::StatusCode::kOk);
   if (ok) return inlined_rep;
   if (msg.empty()
   ) {
     return inlined_rep;
   }
-  auto* rep = new status_internal::StatusRep(InlinedRepToCode(inlined_rep), msg,
-                                             nullptr);
+  auto* rep =
+      new status_internal::StatusRep(Status::InlinedRepToCode(inlined_rep),
+                                     std::forward<StringOrView>(msg), nullptr);
   if (loc.file_name()[0] != '\0') {
     rep->AddSourceLocation(loc);
   }
-  return PointerToRep(rep);
+  return Status::PointerToRep(rep);
+}
+
+uintptr_t Status::MakeRepFromStringView(uintptr_t inlined_rep,
+                                        absl::string_view msg,
+                                        absl::SourceLocation loc) {
+  return MakeStatusRepImpl<absl::string_view>(inlined_rep, msg, loc);
+}
+
+uintptr_t Status::MakeRepFromStringRvalue(uintptr_t inlined_rep,
+                                          std::string&& msg,
+                                          absl::SourceLocation loc) {
+  return MakeStatusRepImpl<std::string&&>(inlined_rep, std::move(msg), loc);
 }
 
 uintptr_t Status::AddSourceLocationImpl(uintptr_t rep,
diff --git a/absl/status/status.h b/absl/status/status.h
index 55802cd..faed05e 100644
--- a/absl/status/status.h
+++ b/absl/status/status.h
@@ -56,6 +56,7 @@
 #include <optional>
 #include <ostream>
 #include <string>
+#include <type_traits>
 #include <utility>
 
 #include "absl/base/attributes.h"
@@ -459,6 +460,14 @@
   Status(absl::StatusCode code, absl::string_view msg,
          absl::SourceLocation loc = SourceLocation::current());
 
+  // Same as above but for rvalue string.
+  // Note: using a template to disambiguate the case of matching string_view and
+  // string&& (e.g. char*) as a template lowers the priority of the overload.
+  template <typename String,
+            typename = std::enable_if_t<std::is_same_v<String, std::string>>>
+  Status(absl::StatusCode code, String&& msg,
+         absl::SourceLocation loc = SourceLocation::current());
+
   // Create a status from a `base_status` and a `loc`. The `loc` will be
   // appended to the location chain of the new status, iff the `base_status` is
   // not ok and has non-empty msg.
@@ -707,8 +716,18 @@
 
   // Delegate factory in header that ensures CodeToInlinedRep is inlined
   // where possible.
-  static uintptr_t MakeRep(uintptr_t inlined_rep, absl::string_view msg,
-                           absl::SourceLocation loc);
+  static uintptr_t MakeRepFromStringView(uintptr_t inlined_rep,
+                                         absl::string_view msg,
+                                         absl::SourceLocation loc);
+
+  // Same as above but for rvalue string.
+  static uintptr_t MakeRepFromStringRvalue(uintptr_t inlined_rep,
+                                           std::string&& msg,
+                                           absl::SourceLocation loc);
+
+  template <typename StringOrView>
+  friend uintptr_t MakeStatusRepImpl(uintptr_t inlined_rep, StringOrView msg,
+                                     absl::SourceLocation loc);
 
   // Underlying constructor for status from a rep_.
   explicit Status(uintptr_t rep) : rep_(rep) {}
@@ -897,7 +916,13 @@
 
 inline Status::Status(absl::StatusCode code, absl::string_view msg,
                       absl::SourceLocation loc)
-    : Status(MakeRep(CodeToInlinedRep(code), msg, loc)) {}
+    : Status(MakeRepFromStringView(CodeToInlinedRep(code), msg, loc)) {}
+
+template <typename String, typename>
+inline Status::Status(absl::StatusCode code, String&& msg,
+                      absl::SourceLocation loc)
+    : Status(MakeRepFromStringRvalue(CodeToInlinedRep(code),
+                                     std::forward<String>(msg), loc)) {}
 
 inline Status::Status(const Status& x) : Status(x.rep_) { Ref(rep_); }
 
diff --git a/absl/status/status_benchmark.cc b/absl/status/status_benchmark.cc
index 539b783..0404db1 100644
--- a/absl/status/status_benchmark.cc
+++ b/absl/status/status_benchmark.cc
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <string>
 #include <utility>
 
 #include "absl/status/status.h"
@@ -46,4 +47,14 @@
 }
 BENCHMARK(BM_AppendSourceLocation);
 
+void BM_LongMessageRValue(benchmark::State& state) {
+  for (auto _ : state) {
+    std::string msg(100, 'X');
+    benchmark::DoNotOptimize(msg);
+    absl::Status s(absl::StatusCode::kInvalidArgument, std::move(msg));
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_LongMessageRValue);
+
 }  // namespace