[clang] [Clang][Lex] Implement CWG3013: Error if #embed parameters were defined as macros (PR #224769)
Ian Li via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 21 22:19:04 PDT 2026
https://github.com/ianayl updated https://github.com/llvm/llvm-project/pull/224769
>From fd0ec1d6dd50bdd02a1e32ba49c7146dcf8bad73 Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Fri, 18 Sep 2026 15:34:24 -0700
Subject: [PATCH 1/9] Implement CWG3013 in clang, but only for C++
---
clang/docs/ReleaseNotes.md | 18 ++++++
clang/include/clang/Basic/DiagnosticGroups.td | 4 +-
.../include/clang/Basic/DiagnosticLexKinds.td | 8 +++
clang/include/clang/Lex/Preprocessor.h | 4 ++
clang/lib/Lex/PPDirectives.cpp | 4 ++
clang/lib/Lex/Preprocessor.cpp | 24 ++++++++
clang/test/CXX/drs/cwg3013.cpp | 58 +++++++++++++++++++
clang/test/CXX/drs/inputs/media/art.txt | 13 +++++
clang/test/CXX/drs/inputs/media/empty | 0
9 files changed, 132 insertions(+), 1 deletion(-)
create mode 100644 clang/test/CXX/drs/cwg3013.cpp
create mode 100644 clang/test/CXX/drs/inputs/media/art.txt
create mode 100644 clang/test/CXX/drs/inputs/media/empty
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index f4a34a37aff52..da925b260ee1c 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -54,6 +54,13 @@ in a future version of Clang.
mode, as it was removed from the standard by
[P3475R2](https://wg21.link/P3475R2).
+- An error diagnostic is now issued if an `#embed` directive or a `__has_embed`
+ expression contains a parameter (i.e. `limit`, `prefix`, `suffix`, `if_empty`)
+ that has previously been defined as a macro, as per
+ [CWG3013](https://wg21.link/cwg3013). Previously macros that shared names with
+ `#embed` parameter names were expanded regardless, similar to its behavior in
+ C: Note that this expansion behavior is still present in C since there are no
+ rule analogue to CWG3013 in C.
### Objective-C Specific Potentially Breaking Changes
@@ -194,6 +201,11 @@ features cannot lower the translation-unit ABI level;
them to an enumeration type with a fixed `bool` underlying type. This
resolves [CWG1094](https://wg21.link/cwg1094).
+- Clang now diagnoses an error if an `#embed` directive or `__has_embed`
+ statement uses a parameter name (i.e. `limit`, `prefix`, `suffix`, `if_empty`)
+ that has previously been defined as a macro. This resolves
+ [CWG3013](https://wg21.link/cwg3013), which marks such code as ill-formed.
+
### C Language Changes
#### C2y Feature Support
@@ -518,6 +530,12 @@ features cannot lower the translation-unit ABI level;
- Improve Clang diagnoses when unary `__imag` operator with non-complex type operand is used as lvalue. (GH222383)
+- Added `-Wembed-parameter-is-macro`, which warns in C if an `#embed` directive
+ or a `__has_embed` expression uses a parameter (i.e. `limit`, `prefix`,
+ `suffix`, `if_empty`) that has also been defined as a macro. C expands the
+ macro, but the same code is ill-formed in C++, so `-Wembed-parameter-is-macro`
+ is also part of `-Wc++-compat`; this warning is disabled by default otherwise.
+
### Improvements to Clang's time-trace
### Improvements to Coverage Mapping
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 1da7698944b24..b3c36c76d490b 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -187,6 +187,7 @@ def C23Compat : DiagGroup<"c23-compat">;
def : DiagGroup<"c2x-compat", [C23Compat]>;
def CppKeywordInC : DiagGroup<"c++-keyword">;
+def EmbedParameterIsMacro : DiagGroup<"embed-parameter-is-macro">;
def DuplicateDeclSpecifier : DiagGroup<"duplicate-decl-specifier">;
def InitStringTooLongMissingNonString :
DiagGroup<"unterminated-string-initialization">;
@@ -212,7 +213,8 @@ def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast, DefaultConstInit,
ImplicitIntToEnumCast, HiddenCppDecl,
InitStringTooLongForCpp, CppKeywordInC,
TentativeDefnCompat, JumpBypassesInit,
- DuplicateDeclSpecifier]>;
+ DuplicateDeclSpecifier,
+ EmbedParameterIsMacro]>;
def ExternCCompat : DiagGroup<"extern-c-compat">;
def KeywordCompat : DiagGroup<"keyword-compat">;
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index ff51485a1810b..006da9a53e96a 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -492,6 +492,14 @@ def warn_compat_pp_embed_directive : Warning<
InGroup<CPre23Compat>, DefaultIgnore;
def err_pp_embed_dup_params : Error<
"cannot specify parameter '%0' twice in the same '#embed' directive">;
+def err_pp_embed_parameter_is_macro : Error<
+ "cannot use %0 as %select{an '#embed'|a '__has_embed'}1 parameter if also"
+ " defined as a macro">;
+def warn_c_pp_embed_parameter_is_macro : Warning<
+ "%0 is defined as a macro and gets expanded when used as a "
+ "%select{an '#embed'|a '__has_embed'}1 parameter in C; this is "
+ "ill-formed in C++">,
+ InGroup<EmbedParameterIsMacro>, DefaultIgnore;
def err_pp_embed_device_file : Error<
"device files are not yet supported by '#embed' directive">;
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 31b68a0fd0670..4711728b1b764 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -302,6 +302,10 @@ class Preprocessor {
/// True if we are currently preprocessing a #if or #elif directive
bool ParsingIfOrElifDirective;
+ /// True if we are preprocessing the parameters of an #embed directive or a
+ /// __has_embed expression.
+ bool ParsingEmbedParameters;
+
/// True if we are pre-expanding macro arguments.
bool InMacroArgPreExpansion;
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index f1e9aaa72ff04..0b7c727d2ce89 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -3771,6 +3771,10 @@ void Preprocessor::HandleElifFamilyDirective(Token &ElifToken,
std::optional<LexEmbedParametersResult>
Preprocessor::LexEmbedParameters(Token &CurTok, bool ForHasEmbed) {
LexEmbedParametersResult Result{};
+ if (ForHasEmbed)
+ assert(isParsingIfOrElifDirective() &&
+ "__has_embed outside of #if or #elif directive?");
+ llvm::SaveAndRestore InEmbedParams(ParsingEmbedParameters, true);
tok::TokenKind EndTokenKind = ForHasEmbed ? tok::r_paren : tok::eod;
auto DiagMismatchedBracesAndSkipToEOD =
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 84907adc5d744..9bab63818007c 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -119,6 +119,7 @@ Preprocessor::Preprocessor(const PreprocessorOptions &PPOpts,
NumCachedTokenLexers = 0;
PragmasEnabled = true;
ParsingIfOrElifDirective = false;
+ ParsingEmbedParameters = false;
PreprocessedOutput = false;
// We haven't read anything from the external source.
@@ -879,6 +880,11 @@ void Preprocessor::updateOutOfDateIdentifier(const IdentifierInfo &II) const {
getExternalSource()->updateOutOfDateIdentifier(II);
}
+static bool isProtectedEmbedParameterName(const IdentifierInfo *II) {
+ return II->isStr("limit") || II->isStr("prefix") || II->isStr("suffix") ||
+ II->isStr("if_empty");
+}
+
/// HandleIdentifier - This callback is invoked when the lexer reads an
/// identifier. This callback looks up the identifier in the map and/or
/// potentially macro expands it or turns it into a named token (like 'for').
@@ -922,6 +928,24 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
if (const MacroDefinition MD = getMacroDefinition(&II)) {
const auto *MI = MD.getMacroInfo();
assert(MI && "macro definition with no macro info?");
+ // C++ [cpp.pre]/p4, [cpp.cond]/p9: if one of the pp-tokens of a #embed
+ // directive (or a has-embed-expression) is the identifier limit, prefix,
+ // suffix, or if_empty and that identifier is defined as a macro, the
+ // program is ill-formed.
+ //
+ // Thus, do not continue processing if compiling for C++. C doesn't have
+ // this restriction however, so only issue a warning for C if -Wc++-compat
+ // is enabled.
+ if (ParsingEmbedParameters && isProtectedEmbedParameterName(&II)) {
+ Diag(Identifier, getLangOpts().CPlusPlus
+ ? diag::err_pp_embed_parameter_is_macro
+ : diag::warn_c_pp_embed_parameter_is_macro)
+ << &II << isParsingIfOrElifDirective();
+ Diag(MI->getDefinitionLoc(), diag::note_macro_here) << &II;
+ if (getLangOpts().CPlusPlus)
+ return true;
+ }
+
if (!DisableMacroExpansion) {
if (!Identifier.isExpandDisabled() && MI->isEnabled()) {
// C99 6.10.3p10: If the preprocessing token immediately after the
diff --git a/clang/test/CXX/drs/cwg3013.cpp b/clang/test/CXX/drs/cwg3013.cpp
new file mode 100644
index 0000000000000..95f4f6fc6f9e2
--- /dev/null
+++ b/clang/test/CXX/drs/cwg3013.cpp
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 --embed-dir=%S/inputs -Wno-c23-extensions -fsyntax-only -verify=cxx,common -x c++ %s
+// RUN: %clang_cc1 --embed-dir=%S/inputs -fsyntax-only -verify=c -std=c23 -x c %s
+// RUN: %clang_cc1 --embed-dir=%S/inputs -fsyntax-only -verify=c-compat,common -std=c23 -Wc++-compat -x c %s
+//
+// Test -Wembed-parameter-is-macro:
+// RUN: %clang_cc1 --embed-dir=%S/inputs -fsyntax-only -verify=c-compat,common -std=c23 -Wembed-parameter-is-macro -x c %s
+// RUN: %clang_cc1 --embed-dir=%S/inputs -fsyntax-only -verify=c -std=c23 -Wc++-compat -Wno-embed-parameter-is-macro -x c %s
+
+// CWG3013: if one of the pp-tokens of a #embed directive (or a
+// has-embed-expression) is the identifier limit, prefix, suffix, or if_empty
+// and that identifier is defined as a macro, the program is ill-formed.
+// (C++ [cpp.pre]/p4, [cpp.cond]/p9)
+//
+// However, C doesn't have this restriction, so we should only issue a warning
+// for C if -Wc++-compat/-Wembed-parameter-is-macro is enabled.
+
+// c-no-diagnostics
+
+#define limit limit
+// common-note at -1 2 {{macro 'limit' defined here}}
+const int a[] = {
+#embed <media/art.txt> limit(2)
+// cxx-error at -1 {{cannot use 'limit' as an '#embed' parameter if also defined as a macro}}
+// c-compat-warning at -2 {{'limit' is defined as a macro and gets expanded when used as an '#embed' parameter in C; this is ill-formed in C++}}
+};
+
+#define prefix prefix
+// common-note at -1 {{macro 'prefix' defined here}}
+const int b[] = {
+#embed <media/art.txt> prefix(0,)
+// cxx-error at -1 {{cannot use 'prefix' as an '#embed' parameter if also defined as a macro}}
+// c-compat-warning at -2 {{'prefix' is defined as a macro and gets expanded when used as an '#embed' parameter in C; this is ill-formed in C++}}
+};
+
+#define suffix suffix
+// common-note at -1 2 {{macro 'suffix' defined here}}
+const int c[] = {
+#embed <media/art.txt> suffix(,0)
+// cxx-error at -1 {{cannot use 'suffix' as an '#embed' parameter if also defined as a macro}}
+// c-compat-warning at -2 {{'suffix' is defined as a macro and gets expanded when used as an '#embed' parameter in C; this is ill-formed in C++}}
+};
+
+#define if_empty if_empty
+// common-note at -1 {{macro 'if_empty' defined here}}
+const int d[] = {
+#embed <media/empty> if_empty(0)
+// cxx-error at -1 {{cannot use 'if_empty' as an '#embed' parameter if also defined as a macro}}
+// c-compat-warning at -2 {{'if_empty' is defined as a macro and gets expanded when used as an '#embed' parameter in C; this is ill-formed in C++}}
+};
+
+// The prohibition also covers the __has_embed argument.
+#if __has_embed(<media/art.txt> limit(1) suffix(0))
+// cxx-error at -1 {{cannot use 'limit' as a '__has_embed' parameter if also defined as a macro}}
+// cxx-error at -2 {{cannot use 'suffix' as a '__has_embed' parameter if also defined as a macro}}
+// c-compat-warning at -3 {{'limit' is defined as a macro and gets expanded when used as a '__has_embed' parameter in C; this is ill-formed in C++}}
+// c-compat-warning at -4 {{'suffix' is defined as a macro and gets expanded when used as a '__has_embed' parameter in C; this is ill-formed in C++}}
+int e;
+#endif
diff --git a/clang/test/CXX/drs/inputs/media/art.txt b/clang/test/CXX/drs/inputs/media/art.txt
new file mode 100644
index 0000000000000..f4536f39af349
--- /dev/null
+++ b/clang/test/CXX/drs/inputs/media/art.txt
@@ -0,0 +1,13 @@
+
+ -------------------------------------------
+ . .
+ . _ .
+ . _ _ >(. ) _ .
+ . >(. )__ >(- )__ //___ >(. )__ .
+ . ~(____/ -~(_(=-/-~~(_(__/-~~(____/~ .
+ . ~. -~~~ . -~. ~~- .
+ . ~- .
+ . .
+ -------------------------------------------
+
+ O Pato
\ No newline at end of file
diff --git a/clang/test/CXX/drs/inputs/media/empty b/clang/test/CXX/drs/inputs/media/empty
new file mode 100644
index 0000000000000..e69de29bb2d1d
>From f3284b6d51d229603ef0ac32a22d02f495ed2b9d Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Fri, 18 Sep 2026 16:14:30 -0700
Subject: [PATCH 2/9] typo
---
clang/include/clang/Basic/DiagnosticLexKinds.td | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 006da9a53e96a..fbe9fc1461f09 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -496,7 +496,7 @@ def err_pp_embed_parameter_is_macro : Error<
"cannot use %0 as %select{an '#embed'|a '__has_embed'}1 parameter if also"
" defined as a macro">;
def warn_c_pp_embed_parameter_is_macro : Warning<
- "%0 is defined as a macro and gets expanded when used as a "
+ "%0 is defined as a macro and gets expanded when used as "
"%select{an '#embed'|a '__has_embed'}1 parameter in C; this is "
"ill-formed in C++">,
InGroup<EmbedParameterIsMacro>, DefaultIgnore;
>From f32f333e7872597bbdfb8fbdaf16cff61ad6ad1c Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Mon, 21 Sep 2026 20:39:07 -0700
Subject: [PATCH 3/9] split c/c++ tests and make cwg test conform to other dr
tests
---
clang/test/CXX/drs/cwg3013.cpp | 75 +++++++++-------------
clang/test/CXX/drs/cwg30xx.cpp | 2 +
clang/test/Preprocessor/embed_cxx_compat.c | 51 +++++++++++++++
3 files changed, 84 insertions(+), 44 deletions(-)
create mode 100644 clang/test/Preprocessor/embed_cxx_compat.c
diff --git a/clang/test/CXX/drs/cwg3013.cpp b/clang/test/CXX/drs/cwg3013.cpp
index 95f4f6fc6f9e2..965ac8d180369 100644
--- a/clang/test/CXX/drs/cwg3013.cpp
+++ b/clang/test/CXX/drs/cwg3013.cpp
@@ -1,58 +1,45 @@
-// RUN: %clang_cc1 --embed-dir=%S/inputs -Wno-c23-extensions -fsyntax-only -verify=cxx,common -x c++ %s
-// RUN: %clang_cc1 --embed-dir=%S/inputs -fsyntax-only -verify=c -std=c23 -x c %s
-// RUN: %clang_cc1 --embed-dir=%S/inputs -fsyntax-only -verify=c-compat,common -std=c23 -Wc++-compat -x c %s
-//
-// Test -Wembed-parameter-is-macro:
-// RUN: %clang_cc1 --embed-dir=%S/inputs -fsyntax-only -verify=c-compat,common -std=c23 -Wembed-parameter-is-macro -x c %s
-// RUN: %clang_cc1 --embed-dir=%S/inputs -fsyntax-only -verify=c -std=c23 -Wc++-compat -Wno-embed-parameter-is-macro -x c %s
-
-// CWG3013: if one of the pp-tokens of a #embed directive (or a
-// has-embed-expression) is the identifier limit, prefix, suffix, or if_empty
-// and that identifier is defined as a macro, the program is ill-formed.
-// (C++ [cpp.pre]/p4, [cpp.cond]/p9)
-//
-// However, C doesn't have this restriction, so we should only issue a warning
-// for C if -Wc++-compat/-Wembed-parameter-is-macro is enabled.
-
-// c-no-diagnostics
-
-#define limit limit
-// common-note at -1 2 {{macro 'limit' defined here}}
+// RUN: %clang_cc1 -std=c++2c -fexceptions -fcxx-exceptions -pedantic-errors -verify-directives -Wno-c23-extensions -verify %s
+
+// C doesn't have an analogue to CWG3013: There is a corresponding C test at
+// clang/test/Preprocessor/embed_cxx_compat.c testing an opt-in CXX-compat
+// diagnostic.
+
+namespace cwg3013 { // cwg3013: 24
+
+#define limit limit // #cwg3013-limit
const int a[] = {
-#embed <media/art.txt> limit(2)
-// cxx-error at -1 {{cannot use 'limit' as an '#embed' parameter if also defined as a macro}}
-// c-compat-warning at -2 {{'limit' is defined as a macro and gets expanded when used as an '#embed' parameter in C; this is ill-formed in C++}}
+#embed __FILE__ limit(2)
+// expected-error at -1 {{cannot use 'limit' as an '#embed' parameter if also defined as a macro}}
+// expected-note@#cwg3013-limit {{macro 'limit' defined here}}
};
-#define prefix prefix
-// common-note at -1 {{macro 'prefix' defined here}}
+#define prefix prefix // #cwg3013-prefix
const int b[] = {
-#embed <media/art.txt> prefix(0,)
-// cxx-error at -1 {{cannot use 'prefix' as an '#embed' parameter if also defined as a macro}}
-// c-compat-warning at -2 {{'prefix' is defined as a macro and gets expanded when used as an '#embed' parameter in C; this is ill-formed in C++}}
+#embed __FILE__ prefix(0,)
+// expected-error at -1 {{cannot use 'prefix' as an '#embed' parameter if also defined as a macro}}
+// expected-note@#cwg3013-prefix {{macro 'prefix' defined here}}
};
-#define suffix suffix
-// common-note at -1 2 {{macro 'suffix' defined here}}
+#define suffix suffix // #cwg3013-suffix
const int c[] = {
-#embed <media/art.txt> suffix(,0)
-// cxx-error at -1 {{cannot use 'suffix' as an '#embed' parameter if also defined as a macro}}
-// c-compat-warning at -2 {{'suffix' is defined as a macro and gets expanded when used as an '#embed' parameter in C; this is ill-formed in C++}}
+#embed __FILE__ suffix(,0)
+// expected-error at -1 {{cannot use 'suffix' as an '#embed' parameter if also defined as a macro}}
+// expected-note@#cwg3013-suffix {{macro 'suffix' defined here}}
};
-#define if_empty if_empty
-// common-note at -1 {{macro 'if_empty' defined here}}
+#define if_empty if_empty // #cwg3013-if_empty
const int d[] = {
-#embed <media/empty> if_empty(0)
-// cxx-error at -1 {{cannot use 'if_empty' as an '#embed' parameter if also defined as a macro}}
-// c-compat-warning at -2 {{'if_empty' is defined as a macro and gets expanded when used as an '#embed' parameter in C; this is ill-formed in C++}}
+#embed __FILE__ if_empty(0)
+// expected-error at -1 {{cannot use 'if_empty' as an '#embed' parameter if also defined as a macro}}
+// expected-note@#cwg3013-if_empty {{macro 'if_empty' defined here}}
};
-// The prohibition also covers the __has_embed argument.
-#if __has_embed(<media/art.txt> limit(1) suffix(0))
-// cxx-error at -1 {{cannot use 'limit' as a '__has_embed' parameter if also defined as a macro}}
-// cxx-error at -2 {{cannot use 'suffix' as a '__has_embed' parameter if also defined as a macro}}
-// c-compat-warning at -3 {{'limit' is defined as a macro and gets expanded when used as a '__has_embed' parameter in C; this is ill-formed in C++}}
-// c-compat-warning at -4 {{'suffix' is defined as a macro and gets expanded when used as a '__has_embed' parameter in C; this is ill-formed in C++}}
+#if __has_embed(__FILE__ limit(1) suffix(0))
+// expected-error at -1 {{cannot use 'limit' as a '__has_embed' parameter if also defined as a macro}}
+// expected-note@#cwg3013-limit {{macro 'limit' defined here}}
+// expected-error at -3 {{cannot use 'suffix' as a '__has_embed' parameter if also defined as a macro}}
+// expected-note@#cwg3013-suffix {{macro 'suffix' defined here}}
int e;
#endif
+
+} // namespace cwg3013
\ No newline at end of file
diff --git a/clang/test/CXX/drs/cwg30xx.cpp b/clang/test/CXX/drs/cwg30xx.cpp
index a664fe6b3f073..a4053b55cd751 100644
--- a/clang/test/CXX/drs/cwg30xx.cpp
+++ b/clang/test/CXX/drs/cwg30xx.cpp
@@ -22,6 +22,8 @@ void f(
} // namespace cwg3005
+// cwg3013 is in cwg3013.cpp
+
namespace cwg3035 { // cwg3035: no
#if __cplusplus >= 201103L
static union {
diff --git a/clang/test/Preprocessor/embed_cxx_compat.c b/clang/test/Preprocessor/embed_cxx_compat.c
new file mode 100644
index 0000000000000..96c7c19682cfd
--- /dev/null
+++ b/clang/test/Preprocessor/embed_cxx_compat.c
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -std=c23 --embed-dir=%S/Inputs -fsyntax-only -verify=silent %s
+// RUN: %clang_cc1 -std=c23 --embed-dir=%S/Inputs -fsyntax-only -verify=compat -Wc++-compat %s
+//
+// Test -Wembed-parameter-is-macro:
+// RUN: %clang_cc1 -std=c23 --embed-dir=%S/Inputs -fsyntax-only -verify=compat -Wembed-parameter-is-macro %s
+// RUN: %clang_cc1 -std=c23 --embed-dir=%S/Inputs -fsyntax-only -verify=silent -Wc++-compat -Wno-embed-parameter-is-macro %s
+
+// C++ has CWG3013: if one of the pp-tokens of a #embed directive (or a
+// has-embed-expression) is the identifier limit, prefix, suffix, or if_empty
+// and that identifier is defined as a macro, the program is ill-formed.
+// (C++ [cpp.pre]/p4, [cpp.cond]/p9)
+//
+// ... But C doesn't seem to have this restriction, so we allow macro-expansion
+// and only warn if -Wc++-compat or -Wembed-parameter-is-macro is enabled. C++
+// conformance with CWG3013 is tested in clang/test/CXX/drs/cwg3013.cpp.
+
+// silent-no-diagnostics
+
+#define limit limit
+// compat-note at -1 2 {{macro 'limit' defined here}}
+const int a[] = {
+#embed __FILE__ limit(2)
+// compat-warning at -1 {{'limit' is defined as a macro and gets expanded when used as an '#embed' parameter in C; this is ill-formed in C++}}
+};
+
+#define prefix prefix
+// compat-note at -1 {{macro 'prefix' defined here}}
+const int b[] = {
+#embed __FILE__ prefix(0,)
+// compat-warning at -1 {{'prefix' is defined as a macro and gets expanded when used as an '#embed' parameter in C; this is ill-formed in C++}}
+};
+
+#define suffix suffix
+// compat-note at -1 2 {{macro 'suffix' defined here}}
+const int c[] = {
+#embed __FILE__ suffix(,0)
+// compat-warning at -1 {{'suffix' is defined as a macro and gets expanded when used as an '#embed' parameter in C; this is ill-formed in C++}}
+};
+
+#define if_empty if_empty
+// compat-note at -1 {{macro 'if_empty' defined here}}
+const int d[] = {
+#embed __FILE__ if_empty(0)
+// compat-warning at -1 {{'if_empty' is defined as a macro and gets expanded when used as an '#embed' parameter in C; this is ill-formed in C++}}
+};
+
+#if __has_embed(__FILE__ limit(1) suffix(0))
+// compat-warning at -1 {{'limit' is defined as a macro and gets expanded when used as a '__has_embed' parameter in C; this is ill-formed in C++}}
+// compat-warning at -2 {{'suffix' is defined as a macro and gets expanded when used as a '__has_embed' parameter in C; this is ill-formed in C++}}
+int e;
+#endif
>From 667e78bf7a6e94576c8758b6f8b1c2e3b11b7474 Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Mon, 21 Sep 2026 20:39:50 -0700
Subject: [PATCH 4/9] rerun clang/www/make_cxx_dr_status
---
clang/www/cxx_dr_status.html | 128 +++++++++++++++++++++++++++++------
1 file changed, 106 insertions(+), 22 deletions(-)
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index e7679da30d5c2..b1e31e20b7078 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -1413,12 +1413,12 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td>Converting between function and object pointers</td>
<td class="full" align="center">Clang 2.7</td>
</tr>
- <tr class="open" id="196">
+ <tr id="196">
<td><a href="https://cplusplus.github.io/CWG/issues/196.html">196</a></td>
<td>[<a href="https://wg21.link/expr.delete">expr.delete</a>]</td>
- <td>open</td>
+ <td>C++17</td>
<td>Arguments to deallocation functions</td>
- <td align="center">Not resolved</td>
+ <td class="unknown" align="center">Unknown</td>
</tr>
<tr id="197">
<td><a href="https://cplusplus.github.io/CWG/issues/197.html">197</a></td>
@@ -1910,12 +1910,12 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td>No grammar sentence symbol</td>
<td class="na" align="center">N/A</td>
</tr>
- <tr class="open" id="267">
+ <tr id="267">
<td><a href="https://cplusplus.github.io/CWG/issues/267.html">267</a></td>
<td>[<a href="https://wg21.link/expr.new">expr.new</a>]</td>
- <td>open</td>
+ <td>NAD</td>
<td>Alignment requirement for <I>new-expression</I>s</td>
- <td align="center">Not resolved</td>
+ <td class="unknown" align="center">Unknown</td>
</tr>
<tr class="open" id="268">
<td><a href="https://cplusplus.github.io/CWG/issues/268.html">268</a></td>
@@ -10017,12 +10017,12 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td>Deduction failure vs “ill-formed, no diagnostic required”</td>
<td class="unknown" align="center">Unknown</td>
</tr>
- <tr class="open" id="1463">
+ <tr id="1463">
<td><a href="https://cplusplus.github.io/CWG/issues/1463.html">1463</a></td>
<td>[<a href="https://wg21.link/temp.pre">temp.pre</a>]</td>
- <td>drafting</td>
+ <td>DR</td>
<td><TT>extern "C"</TT> alias templates</td>
- <td align="center">Not resolved</td>
+ <td class="unknown" align="center">Unknown</td>
</tr>
<tr id="1464">
<td><a href="https://cplusplus.github.io/CWG/issues/1464.html">1464</a></td>
@@ -11179,7 +11179,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<tr class="open" id="1628">
<td><a href="https://cplusplus.github.io/CWG/issues/1628.html">1628</a></td>
<td>[<a href="https://wg21.link/expr.new">expr.new</a>]</td>
- <td>open</td>
+ <td>review</td>
<td>Deallocation function templates</td>
<td align="center">Not resolved</td>
</tr>
@@ -13255,7 +13255,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<tr class="open" id="1924">
<td><a href="https://cplusplus.github.io/CWG/issues/1924.html">1924</a></td>
<td>[<a href="https://wg21.link/lex.literal">lex.literal</a>]</td>
- <td>review</td>
+ <td>open</td>
<td>Definition of “literal” and kinds of literals</td>
<td align="center">Not resolved</td>
</tr>
@@ -20908,7 +20908,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td>[<a href="https://wg21.link/cpp.embed.gen">cpp.embed.gen</a>]</td>
<td>CD7</td>
<td>Disallowing macros for <TT>#embed</TT> parameters</td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="unreleased" align="center">Clang 24</td>
</tr>
<tr id="3014">
<td><a href="https://cplusplus.github.io/CWG/issues/3014.html">3014</a></td>
@@ -20969,7 +20969,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<tr class="open" id="3022">
<td><a href="https://cplusplus.github.io/CWG/issues/3022.html">3022</a></td>
<td>[<a href="https://wg21.link/class.dtor">class.dtor</a>]</td>
- <td>review</td>
+ <td>tentatively ready</td>
<td>Redundant specification of explicit destructor calls</td>
<td align="center">Not resolved</td>
</tr>
@@ -22194,7 +22194,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<tr class="open" id="3197">
<td><a href="https://cplusplus.github.io/CWG/issues/3197.html">3197</a></td>
<td>[<a href="https://wg21.link/diff.basic">diff.basic</a>]</td>
- <td>review</td>
+ <td>tentatively ready</td>
<td>Relaxed requirements for integer representations</td>
<td align="center">Not resolved</td>
</tr>
@@ -22271,7 +22271,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<tr class="open" id="3208">
<td><a href="https://cplusplus.github.io/CWG/issues/3208.html">3208</a></td>
<td>[<a href="https://wg21.link/basic.life">basic.life</a>]</td>
- <td>open</td>
+ <td>tentatively ready</td>
<td>Base classes of virtual base classes</td>
<td align="center">Not resolved</td>
</tr>
@@ -22285,42 +22285,42 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<tr class="open" id="3210">
<td><a href="https://cplusplus.github.io/CWG/issues/3210.html">3210</a></td>
<td>[<a href="https://wg21.link/stmt.expand">stmt.expand</a>]</td>
- <td>open</td>
+ <td>tentatively ready</td>
<td>constexpr for empty destructuring expansion statements</td>
<td align="center">Not resolved</td>
</tr>
<tr class="open" id="3211">
<td><a href="https://cplusplus.github.io/CWG/issues/3211.html">3211</a></td>
<td>[<a href="https://wg21.link/basic.def.odr">basic.def.odr</a>]</td>
- <td>open</td>
+ <td>tentatively ready</td>
<td>Explicitly captured variable is not odr-usable in lambda contract</td>
<td align="center">Not resolved</td>
</tr>
<tr class="open" id="3212">
<td><a href="https://cplusplus.github.io/CWG/issues/3212.html">3212</a></td>
<td>[<a href="https://wg21.link/stmt.if">stmt.if</a>]</td>
- <td>open</td>
+ <td>drafting</td>
<td>Misleading disambiguation rule for nested <TT>if</TT></td>
<td align="center">Not resolved</td>
</tr>
<tr class="open" id="3213">
<td><a href="https://cplusplus.github.io/CWG/issues/3213.html">3213</a></td>
<td>[<a href="https://wg21.link/temp.param">temp.param</a>]</td>
- <td>open</td>
+ <td>tentatively ready</td>
<td>Restrictions on the <I>template-head</I> of a concept definition</td>
<td align="center">Not resolved</td>
</tr>
<tr class="open" id="3214">
<td><a href="https://cplusplus.github.io/CWG/issues/3214.html">3214</a></td>
<td>[<a href="https://wg21.link/basic.link">basic.link</a>]</td>
- <td>open</td>
+ <td>tentatively ready</td>
<td>Redeclaration of types and namespaces and their aliases</td>
<td align="center">Not resolved</td>
</tr>
<tr class="open" id="3215">
<td><a href="https://cplusplus.github.io/CWG/issues/3215.html">3215</a></td>
<td>[<a href="https://wg21.link/expr.const.core">expr.const.core</a>]</td>
- <td>open</td>
+ <td>tentatively ready</td>
<td><TT>this</TT> in a default member initializer during constant evaluation</td>
<td align="center">Not resolved</td>
</tr>
@@ -22341,7 +22341,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<tr class="open" id="3218">
<td><a href="https://cplusplus.github.io/CWG/issues/3218.html">3218</a></td>
<td>[<a href="https://wg21.link/temp.variadic">temp.variadic</a>]</td>
- <td>open</td>
+ <td>tentatively ready</td>
<td>Expanding packs created within a pack expansion</td>
<td align="center">Not resolved</td>
</tr>
@@ -22351,6 +22351,90 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td>open</td>
<td>Acquiring value representations and indeterminate or erroneous values</td>
<td align="center">Not resolved</td>
+ </tr>
+ <tr class="open" id="3220">
+ <td><a href="https://cplusplus.github.io/CWG/issues/3220.html">3220</a></td>
+ <td>[<a href="https://wg21.link/basic.link">basic.link</a>]</td>
+ <td>open</td>
+ <td>Regression in module attachment rules introduced by CWG3171</td>
+ <td align="center">Not resolved</td>
+ </tr>
+ <tr class="open" id="3221">
+ <td><a href="https://cplusplus.github.io/CWG/issues/3221.html">3221</a></td>
+ <td>[<a href="https://wg21.link/basic.scope.block">basic.scope.block</a>]</td>
+ <td>tentatively ready</td>
+ <td>Missing Annex C entry for conflicting declarations in for-loops</td>
+ <td align="center">Not resolved</td>
+ </tr>
+ <tr class="open" id="3222">
+ <td><a href="https://cplusplus.github.io/CWG/issues/3222.html">3222</a></td>
+ <td>[<a href="https://wg21.link/cpp.module">cpp.module</a>]</td>
+ <td>tentatively ready</td>
+ <td>Deleted module directives</td>
+ <td align="center">Not resolved</td>
+ </tr>
+ <tr class="open" id="3223">
+ <td><a href="https://cplusplus.github.io/CWG/issues/3223.html">3223</a></td>
+ <td>[<a href="https://wg21.link/class.base.init">class.base.init</a>]</td>
+ <td>tentatively ready</td>
+ <td>Undefined behavior for constructor preconditions and destructor postconditions</td>
+ <td align="center">Not resolved</td>
+ </tr>
+ <tr class="open" id="3224">
+ <td><a href="https://cplusplus.github.io/CWG/issues/3224.html">3224</a></td>
+ <td>[<a href="https://wg21.link/cpp.replace.general">cpp.replace.general</a>]</td>
+ <td>tentatively ready</td>
+ <td>Directives in multi-line macro arguments</td>
+ <td align="center">Not resolved</td>
+ </tr>
+ <tr class="open" id="3225">
+ <td><a href="https://cplusplus.github.io/CWG/issues/3225.html">3225</a></td>
+ <td>[<a href="https://wg21.link/conv.integral">conv.integral</a>]</td>
+ <td>tentatively ready</td>
+ <td>Conversion of <TT>bool</TT> to signed integer of width 1</td>
+ <td align="center">Not resolved</td>
+ </tr>
+ <tr class="open" id="3226">
+ <td><a href="https://cplusplus.github.io/CWG/issues/3226.html">3226</a></td>
+ <td>[<a href="https://wg21.link/temp.res.general">temp.res.general</a>]</td>
+ <td>tentatively ready</td>
+ <td>Conflict of templated entity vs. enclosing template</td>
+ <td align="center">Not resolved</td>
+ </tr>
+ <tr class="open" id="3227">
+ <td><a href="https://cplusplus.github.io/CWG/issues/3227.html">3227</a></td>
+ <td>[<a href="https://wg21.link/temp.res.general">temp.res.general</a>]</td>
+ <td>tentatively ready</td>
+ <td>IFNDR for always-empty packs</td>
+ <td align="center">Not resolved</td>
+ </tr>
+ <tr class="open" id="3228">
+ <td><a href="https://cplusplus.github.io/CWG/issues/3228.html">3228</a></td>
+ <td>[<a href="https://wg21.link/dcl.init">dcl.init</a>]</td>
+ <td>tentatively ready</td>
+ <td>Empty initializers vs. empty packs</td>
+ <td align="center">Not resolved</td>
+ </tr>
+ <tr class="open" id="3229">
+ <td><a href="https://cplusplus.github.io/CWG/issues/3229.html">3229</a></td>
+ <td>[<a href="https://wg21.link/basic.def.odr">basic.def.odr</a>]</td>
+ <td>tentatively ready</td>
+ <td>Incomplete restrictions on same meaning for same tokens</td>
+ <td align="center">Not resolved</td>
+ </tr>
+ <tr class="open" id="3230">
+ <td><a href="https://cplusplus.github.io/CWG/issues/3230.html">3230</a></td>
+ <td>[<a href="https://wg21.link/basic.pre">basic.pre</a>]</td>
+ <td>open</td>
+ <td>Are non-static data members "variables"?</td>
+ <td align="center">Not resolved</td>
+ </tr>
+ <tr class="open" id="3231">
+ <td><a href="https://cplusplus.github.io/CWG/issues/3231.html">3231</a></td>
+ <td>[<a href="https://wg21.link/temp.concept">temp.concept</a>]</td>
+ <td>open</td>
+ <td>Default template argument for the prototype parameter of a concept</td>
+ <td align="center">Not resolved</td>
</tr></table>
</div>
>From 0fd92492049832121df4989c3095b1cab063cc3d Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Mon, 21 Sep 2026 20:46:01 -0700
Subject: [PATCH 5/9] delete duplicate point in release notes
---
clang/docs/ReleaseNotes.md | 5 -----
1 file changed, 5 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index da925b260ee1c..dc5c765cb8418 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -201,11 +201,6 @@ features cannot lower the translation-unit ABI level;
them to an enumeration type with a fixed `bool` underlying type. This
resolves [CWG1094](https://wg21.link/cwg1094).
-- Clang now diagnoses an error if an `#embed` directive or `__has_embed`
- statement uses a parameter name (i.e. `limit`, `prefix`, `suffix`, `if_empty`)
- that has previously been defined as a macro. This resolves
- [CWG3013](https://wg21.link/cwg3013), which marks such code as ill-formed.
-
### C Language Changes
#### C2y Feature Support
>From cf667c5993041b9d77ff557fb998588e16aedd3c Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Mon, 21 Sep 2026 22:02:30 -0700
Subject: [PATCH 6/9] ate logo o pato
---
clang/test/CXX/drs/inputs/media/art.txt | 13 -------------
clang/test/CXX/drs/inputs/media/empty | 0
2 files changed, 13 deletions(-)
delete mode 100644 clang/test/CXX/drs/inputs/media/art.txt
delete mode 100644 clang/test/CXX/drs/inputs/media/empty
diff --git a/clang/test/CXX/drs/inputs/media/art.txt b/clang/test/CXX/drs/inputs/media/art.txt
deleted file mode 100644
index f4536f39af349..0000000000000
--- a/clang/test/CXX/drs/inputs/media/art.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-
- -------------------------------------------
- . .
- . _ .
- . _ _ >(. ) _ .
- . >(. )__ >(- )__ //___ >(. )__ .
- . ~(____/ -~(_(=-/-~~(_(__/-~~(____/~ .
- . ~. -~~~ . -~. ~~- .
- . ~- .
- . .
- -------------------------------------------
-
- O Pato
\ No newline at end of file
diff --git a/clang/test/CXX/drs/inputs/media/empty b/clang/test/CXX/drs/inputs/media/empty
deleted file mode 100644
index e69de29bb2d1d..0000000000000
>From b2986ddf69c821872eee12aac2301164686f7cc4 Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Mon, 21 Sep 2026 22:02:54 -0700
Subject: [PATCH 7/9] use ExtWarn for warn_c_pp_embed_parameter_is_macro
---
clang/include/clang/Basic/DiagnosticLexKinds.td | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index fbe9fc1461f09..fdb9ffba01613 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -495,7 +495,7 @@ def err_pp_embed_dup_params : Error<
def err_pp_embed_parameter_is_macro : Error<
"cannot use %0 as %select{an '#embed'|a '__has_embed'}1 parameter if also"
" defined as a macro">;
-def warn_c_pp_embed_parameter_is_macro : Warning<
+def warn_c_pp_embed_parameter_is_macro : ExtWarn<
"%0 is defined as a macro and gets expanded when used as "
"%select{an '#embed'|a '__has_embed'}1 parameter in C; this is "
"ill-formed in C++">,
>From ea6b88cefbfc58af8e8797e33e59a048f59804eb Mon Sep 17 00:00:00 2001
From: Ian Li <ianayl.work at gmail.com>
Date: Tue, 22 Sep 2026 01:11:50 -0400
Subject: [PATCH 8/9] Apply suggestion from @Fznamznon
Co-authored-by: Mariya Podchishchaeva <mariya.podchishchaeva at intel.com>
---
clang/lib/Lex/PPDirectives.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 0b7c727d2ce89..bcf6a554b77f6 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -3771,8 +3771,7 @@ void Preprocessor::HandleElifFamilyDirective(Token &ElifToken,
std::optional<LexEmbedParametersResult>
Preprocessor::LexEmbedParameters(Token &CurTok, bool ForHasEmbed) {
LexEmbedParametersResult Result{};
- if (ForHasEmbed)
- assert(isParsingIfOrElifDirective() &&
+ assert(!ForHasEmbed || isParsingIfOrElifDirective() &&
"__has_embed outside of #if or #elif directive?");
llvm::SaveAndRestore InEmbedParams(ParsingEmbedParameters, true);
tok::TokenKind EndTokenKind = ForHasEmbed ? tok::r_paren : tok::eod;
>From d371eae8b8dc38cfa145cca04bf2930c2dd61bc8 Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Mon, 21 Sep 2026 22:18:39 -0700
Subject: [PATCH 9/9] clang-format
---
clang/lib/Lex/PPDirectives.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index bcf6a554b77f6..35697284e682b 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -3771,8 +3771,8 @@ void Preprocessor::HandleElifFamilyDirective(Token &ElifToken,
std::optional<LexEmbedParametersResult>
Preprocessor::LexEmbedParameters(Token &CurTok, bool ForHasEmbed) {
LexEmbedParametersResult Result{};
- assert(!ForHasEmbed || isParsingIfOrElifDirective() &&
- "__has_embed outside of #if or #elif directive?");
+ assert(!ForHasEmbed || isParsingIfOrElifDirective() &&
+ "__has_embed outside of #if or #elif directive?");
llvm::SaveAndRestore InEmbedParams(ParsingEmbedParameters, true);
tok::TokenKind EndTokenKind = ForHasEmbed ? tok::r_paren : tok::eod;
More information about the cfe-commits
mailing list