Preserve non-OK status when erasing the last payload on `MakeNonOkStatusWithOkCode("")`.
When `StatusRep::ErasePayload` removes the last payload from an empty-message `StatusRep`, it transitions the representation back to inlined. However, an out-of-line `StatusRep` with `StatusCode::kOk` originates from `Status::MakeNonOkStatusWithOkCode` (`!ok()`), and converting it to `CodeToInlinedRep(StatusCode::kOk)` collapses it into `OkStatus()` and drops any attached `SourceLocation` entries. Guard the inlining transition with `code_ != absl::StatusCode::kOk` so such statuses remain out-of-line and non-OK.
PiperOrigin-RevId: 1006018850
diff --git a/absl/status/internal/status_internal.cc b/absl/status/internal/status_internal.cc
index 77f3ff7..9ea01ab 100644
--- a/absl/status/internal/status_internal.cc
+++ b/absl/status/internal/status_internal.cc
@@ -101,9 +101,12 @@
status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
if (!index.has_value()) return {false, Status::PointerToRep(this)};
payloads_->erase(payloads_->begin() + index.value());
- if (payloads_->empty() && message_.empty()) {
+ if (payloads_->empty() && message_.empty() &&
+ code_ != absl::StatusCode::kOk) {
// Special case: If this can be represented inlined, it MUST be inlined
- // (== depends on this behavior).
+ // (== depends on this behavior). An out-of-line StatusRep with
+ // StatusCode::kOk originates from Status::MakeNonOkStatusWithOkCode and
+ // must stay out-of-line so it does not become OkStatus().
EraseResult result = {true, Status::CodeToInlinedRep(code_)};
Unref();
return result;
diff --git a/absl/status/status_test.cc b/absl/status/status_test.cc
index c4679d6..ee0e8a1 100644
--- a/absl/status/status_test.cc
+++ b/absl/status/status_test.cc
@@ -31,6 +31,13 @@
#include "absl/strings/string_view.h"
#include "absl/types/source_location.h"
+class absl::status_internal::StatusPrivateAccessor {
+ public:
+ static absl::Status MakeNonOkStatusWithOkCode(absl::string_view message) {
+ return absl::Status::MakeNonOkStatusWithOkCode(message);
+ }
+};
+
namespace {
using ::testing::Eq;
@@ -340,6 +347,30 @@
EXPECT_EQ(payload_status, empty_status);
}
+TEST(Status, ErasePayloadOnNonOkStatusWithOkCodeAndEmptyMessage) {
+ const absl::SourceLocation loc = absl::SourceLocation::current();
+ absl::Status status =
+ absl::status_internal::StatusPrivateAccessor::MakeNonOkStatusWithOkCode(
+ "");
+ status.AddSourceLocation(loc);
+ status.SetPayload(kUrl1, absl::Cord(kPayload1));
+
+ EXPECT_TRUE(status.ErasePayload(kUrl1));
+
+ EXPECT_FALSE(status.ok());
+ EXPECT_EQ(status.code(), absl::StatusCode::kOk);
+ EXPECT_EQ(status.message(), "");
+ EXPECT_FALSE(status.GetPayload(kUrl1).has_value());
+ EXPECT_EQ(
+ status,
+ absl::status_internal::StatusPrivateAccessor::MakeNonOkStatusWithOkCode(
+ ""));
+ ASSERT_EQ(status.GetSourceLocations().size(), 1);
+ EXPECT_EQ(absl::string_view(status.GetSourceLocations()[0].file_name()),
+ absl::string_view(loc.file_name()));
+ EXPECT_EQ(status.GetSourceLocations()[0].line(), loc.line());
+}
+
PayloadsVec AllVisitedPayloads(const absl::Status& s) {
PayloadsVec result;