[clang] [clang] Finalize #embed support for C++26 (P1967R14) (PR #233285)
Nikolai Fedotenko via cfe-commits
cfe-commits at lists.llvm.org
Sun Oct 18 12:44:33 PDT 2026
https://github.com/nfedotenko created https://github.com/llvm/llvm-project/pull/233285
`#embed` was adopted into C++26, but Clang still diagnosed it as "a Clang extension" in every C++ mode and did not define the feature-test macro.
- Move the `#embed` extension/compatibility diagnostics to the C23Compat/CXX26Compat framework: no diagnostic in C++26 mode, "#embed is a C++2c extension" (`-Wc++26-extensions`) in earlier C++ modes, and a `-Wpre-c++26-compat` compatibility warning in C++26 mode. C mode diagnostics are unchanged.
- Define `__cpp_pp_embed` to `202502L` in all C++ modes, like the other C++26 features that Clang supports as extensions in earlier modes.
- Update the status page to Partial: the remaining known issues are the braced-list initialization bugs around multi-byte `#embed` (#222796 and friends), which are being fixed separately (#218262, #231938).
Part of #127610.
LLM tools were used for this contribution. I've reviewed, built, and tested the change myself before pushing to GitHub.
>From 1167b9bb2147dc1e353b7fd4732012ad91a3a2d0 Mon Sep 17 00:00:00 2001
From: Nikolay Fedotenko <kalkolay at protonmail.com>
Date: Sun, 18 Oct 2026 23:38:19 +0400
Subject: [PATCH] [clang] Finalize #embed support for C++26 (P1967R14)
#embed was adopted into C++26, but Clang still diagnosed it as "a Clang
extension" in every C++ mode and did not define the feature-test macro.
- Move the #embed extension/compatibility diagnostics to the
C23Compat/CXX26Compat framework: no diagnostic in C++26 mode,
"#embed is a C++2c extension" (-Wc++26-extensions) in earlier C++
modes, and a -Wpre-c++26-compat compatibility warning in C++26 mode.
C mode diagnostics are unchanged.
- Define __cpp_pp_embed to 202502L in all C++ modes, like the other
C++26 features that Clang supports as extensions in earlier modes.
- Update the status page to Partial: the remaining known issues are the
braced-list initialization bugs around multi-byte #embed (#222796 and
friends), which are being fixed separately (#218262, #231938).
Part of #127610.
---
clang/docs/ReleaseNotes.md | 7 +++++++
clang/include/clang/Basic/DiagnosticLexKinds.td | 8 ++------
clang/lib/Frontend/InitPreprocessor.cpp | 1 +
clang/lib/Lex/PPDirectives.cpp | 8 +++-----
clang/test/Lexer/cxx-features.cpp | 4 ++++
clang/test/Preprocessor/cxx_oper_comma.cpp | 4 ++--
clang/test/Preprocessor/embed_constexpr.cpp | 4 ++--
clang/test/Preprocessor/embed_ext_compat_diags.c | 8 +++++---
clang/test/Preprocessor/embed_feature_test.cpp | 10 ++++++++++
clang/test/Preprocessor/embed_weird.cpp | 4 ++--
clang/test/Sema/embed_compound_literal.c | 2 +-
clang/test/Sema/excess-embed-data.c | 2 +-
clang/test/SemaCXX/GH128985.cpp | 2 +-
clang/test/SemaCXX/embed-init-list.cpp | 2 +-
clang/www/cxx_status.html | 10 +++++++++-
15 files changed, 51 insertions(+), 25 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 7d6347414cc5c8..85ecc5c2722d34 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -191,6 +191,13 @@ features cannot lower the translation-unit ABI level;
#### C++2c Feature Support
+- `#embed` ([P1967R14](https://wg21.link/P1967R14)) is no longer diagnosed as
+ an extension in C++26 mode, and the `__cpp_pp_embed` feature-test macro is
+ now defined. In earlier C++ language modes, the directive is now diagnosed
+ as a C++26 extension under `-Wc++26-extensions` (previously it was diagnosed
+ as a Clang extension under `-Wc23-extensions`), and a new
+ `-Wpre-c++26-compat` warning is available in C++26 mode.
+
- Added `__builtin_type_order` for compatibility with GCC as part of the
implementation of [P2830R10](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2830r10.html) (Constexpr Type Ordering).
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 85f4dc6eab88a5..ee3fb0e74d2209 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -14,6 +14,7 @@ let Component = "Lex", CategoryName = "Lexical or Preprocessor Issue" in {
// C23 compatibility with C17 and earlier.
defm c23_pp_directive : C23Compat<
"use of a '#%select{<BUG IF SEEN>|elifdef|elifndef}0' directive is", /*ext_warn*/true>;
+defm c23_pp_embed_directive : C23Compat<"#embed is", /*ext_warn*/true>;
// C++11 compatibility with C++98.
defm empty_fnmacro_arg : CXX11Compat<
@@ -30,6 +31,7 @@ defm cxx23_pp_directive : CXX23Compat<
// C++26 compatibility with C++23 and earlier.
defm raw_string_literal_character_set : CXX26Compat<
"'%0' in a raw string literal delimiter is", /*ext_warn*/false>;
+defm cxx26_pp_embed_directive : CXX26Compat<"#embed is">;
def null_in_char_or_string : Warning<
"null character(s) preserved in %select{char|string}0 literal">,
@@ -485,12 +487,6 @@ def warn_cxx23_compat_warning_directive : Warning<
def warn_c23_compat_warning_directive : Warning<
"#warning is incompatible with C standards before C23">,
InGroup<CPre23Compat>, DefaultIgnore;
-def ext_pp_embed_directive : ExtWarn<
- "#embed is a %select{C23|Clang}0 extension">,
- InGroup<C23>;
-def warn_compat_pp_embed_directive : Warning<
- "#embed is incompatible with C standards before C23">,
- InGroup<CPre23Compat>, DefaultIgnore;
def err_pp_embed_dup_params : Error<
"cannot specify parameter '%0' twice in the same '#embed' directive">;
def err_pp_embed_device_file : Error<
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 784ff9951a25ad..4f2a2a1bd28b79 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -765,6 +765,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
Builder.defineMacro("__cpp_deleted_function", "202403L");
Builder.defineMacro("__cpp_variadic_friend", "202403L");
Builder.defineMacro("__cpp_trivial_relocatability", "202502L");
+ Builder.defineMacro("__cpp_pp_embed", "202502L");
if (LangOpts.Char8)
Builder.defineMacro("__cpp_char8_t", "202207L");
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 3d991c411378e0..5d8adf36e755cf 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -4086,11 +4086,9 @@ void Preprocessor::HandleEmbedDirectiveImpl(
void Preprocessor::HandleEmbedDirective(SourceLocation HashLoc,
Token &EmbedTok) {
// Give the usual extension/compatibility warnings.
- if (LangOpts.C23)
- Diag(EmbedTok, diag::warn_compat_pp_embed_directive);
- else
- Diag(EmbedTok, diag::ext_pp_embed_directive)
- << (LangOpts.CPlusPlus ? /*Clang*/ 1 : /*C23*/ 0);
+ DiagCompat(EmbedTok, LangOpts.CPlusPlus
+ ? diag_compat::cxx26_pp_embed_directive
+ : diag_compat::c23_pp_embed_directive);
// Parse the filename header
Token FilenameTok;
diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index b505f18dff14fe..f920ad64ba056b 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/test/Lexer/cxx-features.cpp
@@ -54,6 +54,10 @@
#error "wrong value for __cpp_placeholder_variables"
#endif
+#if check(pp_embed, 202502, 202502, 202502, 202502, 202502, 202502, 202502)
+#error "wrong value for __cpp_pp_embed"
+#endif
+
#if check(trivial_relocatability, 202502, 202502, 202502, 202502, 202502, 202502, 202502)
#error "wrong value for __cpp_trivial_relocatability"
#endif
diff --git a/clang/test/Preprocessor/cxx_oper_comma.cpp b/clang/test/Preprocessor/cxx_oper_comma.cpp
index 5db803ae41f328..cef2a0d905b20d 100644
--- a/clang/test/Preprocessor/cxx_oper_comma.cpp
+++ b/clang/test/Preprocessor/cxx_oper_comma.cpp
@@ -3,8 +3,8 @@
// RUN: %clang_cc1 -E -pedantic-errors %s -verify -std=c++14
// RUN: %clang_cc1 -E -pedantic-errors %s -verify -std=c++17
// RUN: %clang_cc1 -E -pedantic-errors %s -verify -std=c++20
-// RUN: %clang_cc1 -E -pedantic-errors %s -verify=expected,since-cxx23 -std=c++23 -Wno-c23-extensions
-// RUN: %clang_cc1 -E -pedantic-errors %s -verify=expected,since-cxx23 -std=c++2c -Wno-c23-extensions
+// RUN: %clang_cc1 -E -pedantic-errors %s -verify=expected,since-cxx23 -std=c++23 -Wno-c++26-extensions
+// RUN: %clang_cc1 -E -pedantic-errors %s -verify=expected,since-cxx23 -std=c++2c -Wno-c++26-extensions
// Test 1: Top-level comma
// expected-error at +1 {{expected end of line in preprocessor expression}}
diff --git a/clang/test/Preprocessor/embed_constexpr.cpp b/clang/test/Preprocessor/embed_constexpr.cpp
index c51c02def7dfb5..d82c194d7d5f42 100644
--- a/clang/test/Preprocessor/embed_constexpr.cpp
+++ b/clang/test/Preprocessor/embed_constexpr.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 %s -fsyntax-only --embed-dir=%S/Inputs -verify -Wno-c23-extensions
-// RUN: %clang_cc1 %s -fsyntax-only --embed-dir=%S/Inputs -verify -fexperimental-new-constant-interpreter -Wno-c23-extensions
+// RUN: %clang_cc1 %s -fsyntax-only --embed-dir=%S/Inputs -verify -Wno-c++26-extensions
+// RUN: %clang_cc1 %s -fsyntax-only --embed-dir=%S/Inputs -verify -fexperimental-new-constant-interpreter -Wno-c++26-extensions
// expected-no-diagnostics
constexpr int value(int a, int b) {
diff --git a/clang/test/Preprocessor/embed_ext_compat_diags.c b/clang/test/Preprocessor/embed_ext_compat_diags.c
index 74f24176d9ccab..4195014ea39b34 100644
--- a/clang/test/Preprocessor/embed_ext_compat_diags.c
+++ b/clang/test/Preprocessor/embed_ext_compat_diags.c
@@ -1,7 +1,9 @@
// RUN: %clang_cc1 -std=c23 %s -fsyntax-only --embed-dir=%S/Inputs -verify=none -pedantic
// RUN: %clang_cc1 -std=c23 %s -fsyntax-only --embed-dir=%S/Inputs -verify=compat -Wpre-c23-compat
// RUN: %clang_cc1 -std=c17 %s -fsyntax-only --embed-dir=%S/Inputs -verify=ext -pedantic
-// RUN: %clang_cc1 -x c++ %s -fsyntax-only --embed-dir=%S/Inputs -verify=cxx -pedantic
+// RUN: %clang_cc1 -x c++ -std=c++26 %s -fsyntax-only --embed-dir=%S/Inputs -verify=none -pedantic
+// RUN: %clang_cc1 -x c++ -std=c++26 %s -fsyntax-only --embed-dir=%S/Inputs -verify=cxx-compat -Wpre-c++26-compat
+// RUN: %clang_cc1 -x c++ -std=c++23 %s -fsyntax-only --embed-dir=%S/Inputs -verify=cxx-ext -pedantic
// none-no-diagnostics
#if __has_embed("jk.txt")
@@ -9,8 +11,8 @@
const char buffer[] = {
#embed "jk.txt" /* compat-warning {{#embed is incompatible with C standards before C23}}
ext-warning {{#embed is a C23 extension}}
- cxx-warning {{#embed is a Clang extension}}
+ cxx-compat-warning {{#embed is incompatible with C++ standards before C++2c}}
+ cxx-ext-warning {{#embed is a C++2c extension}}
*/
};
#endif
-
diff --git a/clang/test/Preprocessor/embed_feature_test.cpp b/clang/test/Preprocessor/embed_feature_test.cpp
index 2648804132599b..2b44586012a168 100644
--- a/clang/test/Preprocessor/embed_feature_test.cpp
+++ b/clang/test/Preprocessor/embed_feature_test.cpp
@@ -5,3 +5,13 @@
#if !defined(__has_embed)
#error 1
#endif
+
+#ifdef __cplusplus
+#if __cpp_pp_embed != 202502L
+#error 2
+#endif
+#else
+#ifdef __cpp_pp_embed
+#error 3
+#endif
+#endif
diff --git a/clang/test/Preprocessor/embed_weird.cpp b/clang/test/Preprocessor/embed_weird.cpp
index e44aff55c59e22..fd7cc921891a9a 100644
--- a/clang/test/Preprocessor/embed_weird.cpp
+++ b/clang/test/Preprocessor/embed_weird.cpp
@@ -2,9 +2,9 @@
// RUN: cp %S/Inputs/single_byte.txt %S/Inputs/jk.txt %S/Inputs/numbers.txt %t/
// RUN: cp %S/Inputs/media/empty %t/media/
// RUN: printf "\0" > %t/null_byte.bin
-// RUN: %clang_cc1 %s -fsyntax-only --embed-dir=%t -verify=expected,cxx -Wno-c23-extensions
+// RUN: %clang_cc1 %s -fsyntax-only --embed-dir=%t -verify=expected,cxx -Wno-c++26-extensions
// RUN: %clang_cc1 -x c -std=c23 %s -fsyntax-only --embed-dir=%t -verify=expected,c
-// RUN: %clang_cc1 %s -fsyntax-only -fexperimental-new-constant-interpreter --embed-dir=%t -verify=expected,cxx -Wno-c23-extensions
+// RUN: %clang_cc1 %s -fsyntax-only -fexperimental-new-constant-interpreter --embed-dir=%t -verify=expected,cxx -Wno-c++26-extensions
// RUN: %clang_cc1 -x c -std=c23 %s -fsyntax-only -fexperimental-new-constant-interpreter --embed-dir=%t -verify=expected,c
#embed <media/empty>
;
diff --git a/clang/test/Sema/embed_compound_literal.c b/clang/test/Sema/embed_compound_literal.c
index 7ba6fd8e649683..895ef8869b21d0 100644
--- a/clang/test/Sema/embed_compound_literal.c
+++ b/clang/test/Sema/embed_compound_literal.c
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -std=c23 -fsyntax-only -verify %s
-// RUN: %clang_cc1 -fsyntax-only -verify -x c++ -Wno-c23-extensions %s
+// RUN: %clang_cc1 -fsyntax-only -verify -x c++ -Wno-c++26-extensions %s
// expected-no-diagnostics
char *p1 = (char[]){
diff --git a/clang/test/Sema/excess-embed-data.c b/clang/test/Sema/excess-embed-data.c
index d5b84921abc894..7bc9c4faf3f151 100644
--- a/clang/test/Sema/excess-embed-data.c
+++ b/clang/test/Sema/excess-embed-data.c
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -std=c23 -fsyntax-only -verify=c %s
-// RUN: %clang_cc1 -fsyntax-only -verify=cpp -x c++ -Wno-c23-extensions %s
+// RUN: %clang_cc1 -fsyntax-only -verify=cpp -x c++ -Wno-c++26-extensions %s
struct S {
diff --git a/clang/test/SemaCXX/GH128985.cpp b/clang/test/SemaCXX/GH128985.cpp
index 0d51e3e53582f6..9a6d24d773f458 100644
--- a/clang/test/SemaCXX/GH128985.cpp
+++ b/clang/test/SemaCXX/GH128985.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c23-extensions %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c++26-extensions %s
struct S {
int a, b;
diff --git a/clang/test/SemaCXX/embed-init-list.cpp b/clang/test/SemaCXX/embed-init-list.cpp
index c511ca707a537d..3e596475a2d938 100644
--- a/clang/test/SemaCXX/embed-init-list.cpp
+++ b/clang/test/SemaCXX/embed-init-list.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c23-extensions %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c++26-extensions %s
// expected-no-diagnostics
namespace std {
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 3f105f831f9b76..780009948672f5 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -400,7 +400,15 @@ <h2 id="cxx26">C++2c implementation status</h2>
<tr>
<td><pre>#embed</pre></td>
<td><a href="https://wg21.link/P1967">P1967R14</a></td>
- <td class="none" align="center">No</td>
+ <td class="partial" align="center">
+ <details>
+ <summary>Clang 24 (Partial)</summary>
+ The directive and its parameters are supported since Clang 19, and the
+ <tt>__cpp_pp_embed</tt> feature-test macro since Clang 24. A multi-byte
+ <tt>#embed</tt> in a braced initializer list that initializes a
+ non-aggregate object is not yet handled correctly in some contexts.
+ </details>
+ </td>
</tr>
<!-- Bulgaria, Spring 2025 -->
<tr>
More information about the cfe-commits
mailing list