Skip to content

fix: disable PROTOBUF_CONSTINIT when building with libc++#26783

Open
linzhp wants to merge 1 commit intoprotocolbuffers:mainfrom
linzhp:constinit
Open

fix: disable PROTOBUF_CONSTINIT when building with libc++#26783
linzhp wants to merge 1 commit intoprotocolbuffers:mainfrom
linzhp:constinit

Conversation

@linzhp
Copy link
Copy Markdown

@linzhp linzhp commented Apr 8, 2026

Problem

Building protobuf with Clang + libc++ fails to compile port.cc:

error: variable does not have a constant initializer
PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT
PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 GlobalEmptyString
fixed_address_empty_string{};
note: pointer to subobject of heap-allocated object is not a constant expression

PROTOBUF_CONSTINIT expands to constinit (or [[clang::require_constant_initialization]])
on Clang. This requires GlobalEmptyString's default constructor — which wraps std::string
to be evaluable at compile time. However, libc++'s std::string default constructor performs a
heap allocation even for empty strings and is therefore not constinit-compatible. libstdc++ and
MSVC implement the Small String Optimization such that an empty std::string requires no
allocation, so constinit works there.

This was previously reported in #22065 ("Unable to build v31.1 with zig-cc"), where a maintainer
noted:

"if there's a small update to PROTOBUF_CONSTINIT to avoid this issue on zigcc we would likely
accept the patch."

The root cause is not zig-cc specifically but libc++, which zig-cc uses by default. The bug is
present on the current main branch.

Fix

Add !defined(_LIBCPP_VERSION) to both Clang branches in port_def.inc that enable
PROTOBUF_CONSTINIT. _LIBCPP_VERSION is the canonical macro defined by all libc++ versions.
When using libc++, both branches are skipped and PROTOBUF_CONSTINIT falls through to the
existing #else, which already defines it as empty — so no new code path is introduced.

fixed_address_empty_string remains safely initialized ahead of other globals via
PROTOBUF_ATTRIBUTE_INIT_PRIORITY1, preserving the static initialization order guarantee without
constinit.

Testing

Verified by building protobuf on main with Clang 18 + libc++ via the Zig toolchain targeting
x86_64-unknown-linux-gnu.

References

Fixes #22065

@linzhp
Copy link
Copy Markdown
Author

linzhp commented Apr 8, 2026

@esrauchg can you take a look? The test failed because the CI considered my fork as unsafe

@esrauchg esrauchg added the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Apr 8, 2026
@esrauchg esrauchg requested a review from sbenzaquen April 8, 2026 16:37
@github-actions github-actions bot removed the 🅰️ safe for tests Mark a commit as safe to run presubmits over label Apr 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unable to build v31.1 with zig-cc

2 participants