Skip to content

Commit 3dfd88f

Browse files
Change how we decide which empty string implementation to use.
The feature macro seems to be lying on some toolchains. Instead, ask the compiler to try to run the default constructor in constexpr context which should fail if the implementation doesn't actually support it and fallback to the dynamic one. This should fix #20645 PiperOrigin-RevId: 737603382
1 parent 6aefdde commit 3dfd88f

File tree

2 files changed

+17
-11
lines changed

2 files changed

+17
-11
lines changed

src/google/protobuf/port.cc

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,9 @@ void RealDebugCounter::Register(absl::string_view name) {
109109
}
110110
}
111111

112-
#if defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L
113-
PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT const GlobalEmptyString
114-
fixed_address_empty_string{};
115-
#else
116112
PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT
117113
PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 GlobalEmptyString
118114
fixed_address_empty_string{};
119-
#endif
120115

121116
} // namespace internal
122117
} // namespace protobuf

src/google/protobuf/port.h

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -496,20 +496,27 @@ class NoopDebugCounter {
496496
// Default empty string object. Don't use this directly. Instead, call
497497
// GetEmptyString() to get the reference. This empty string is aligned with a
498498
// minimum alignment of 8 bytes to match the requirement of ArenaStringPtr.
499-
#if defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L
499+
500500
// Take advantage of C++20 constexpr support in std::string.
501-
class alignas(8) GlobalEmptyString {
501+
class alignas(8) GlobalEmptyStringConstexpr {
502502
public:
503503
const std::string& get() const { return value_; }
504504
// Nothing to init, or destroy.
505505
std::string* Init() const { return nullptr; }
506506

507+
template <typename T = std::string, bool = (T(), true)>
508+
static constexpr std::true_type HasConstexprDefaultConstructor(int) {
509+
return {};
510+
}
511+
static constexpr std::false_type HasConstexprDefaultConstructor(char) {
512+
return {};
513+
}
514+
507515
private:
508516
std::string value_;
509517
};
510-
PROTOBUF_EXPORT extern const GlobalEmptyString fixed_address_empty_string;
511-
#else
512-
class alignas(8) GlobalEmptyString {
518+
519+
class alignas(8) GlobalEmptyStringDynamicInit {
513520
public:
514521
const std::string& get() const {
515522
return *reinterpret_cast<const std::string*>(internal::Launder(buffer_));
@@ -521,8 +528,12 @@ class alignas(8) GlobalEmptyString {
521528
private:
522529
alignas(std::string) char buffer_[sizeof(std::string)];
523530
};
531+
532+
using GlobalEmptyString = std::conditional_t<
533+
GlobalEmptyStringConstexpr::HasConstexprDefaultConstructor(0),
534+
const GlobalEmptyStringConstexpr, GlobalEmptyStringDynamicInit>;
535+
524536
PROTOBUF_EXPORT extern GlobalEmptyString fixed_address_empty_string;
525-
#endif
526537

527538
enum class BoundsCheckMode { kNoEnforcement, kReturnDefault, kAbort };
528539

0 commit comments

Comments
 (0)