diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 3e2d287d1eb1f..6d06771b9b8e6 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -133,6 +133,12 @@ C++ Language Changes C++2c Feature Support ^^^^^^^^^^^^^^^^^^^^^ +- Partially implemented `P2843R3 `_ Preprocessing is + never undefined. A macro expansion producing ``defined`` in a conditional + expression is now a hard error in C++26; other constructs the paper makes + ill-formed (notably embedding a directive within macro arguments) remain a + pedantic warning for now and will be promoted separately. + C++23 Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index 30efb0d90c124..2d80538efb932 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -1053,6 +1053,8 @@ def warn_defined_in_object_type_macro : Warning< def warn_defined_in_function_type_macro : Extension< "macro expansion producing 'defined' has undefined behavior">, InGroup; +def err_defined_in_macro : Error< + "macro expansion producing 'defined' is not allowed">; let CategoryName = "Nullability Issue" in { diff --git a/clang/lib/Lex/PPExpressions.cpp b/clang/lib/Lex/PPExpressions.cpp index 887fd25ac318d..46eb66a2eac7e 100644 --- a/clang/lib/Lex/PPExpressions.cpp +++ b/clang/lib/Lex/PPExpressions.cpp @@ -204,7 +204,9 @@ static bool EvaluateDefined(PPValue &Result, Token &PeekTok, DefinedTracker &DT, // in a different way, and compilers seem to agree on how to behave here. // So warn by default on object-type macros, but only warn in -pedantic // mode on function-type macros. - if (IsFunctionTypeMacro) + if (PP.getLangOpts().CPlusPlus26) + PP.Diag(beginLoc, diag::err_defined_in_macro); + else if (IsFunctionTypeMacro) PP.Diag(beginLoc, diag::warn_defined_in_function_type_macro); else PP.Diag(beginLoc, diag::warn_defined_in_object_type_macro); diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp index 8eb9ea032879c..446bb0ac76056 100644 --- a/clang/test/Lexer/cxx-features.cpp +++ b/clang/test/Lexer/cxx-features.cpp @@ -14,21 +14,25 @@ // expected-no-diagnostics -// FIXME using `defined` in a macro has undefined behavior. +// An undefined feature-test macro evaluates to 0 in an #if expression, so +// `__cpp_##macro != N` tests the feature is defined with the exact value N +// when N is nonzero, and tests the feature is not defined (or is defined to 0, +// which feature-test macros never are) when N is 0. Avoiding `defined` inside +// a macro expansion is required for conformance with [cpp.cond]. #if __cplusplus < 201103L -#define check(macro, cxx98, cxx11, cxx14, cxx17, cxx20, cxx23, cxx26) (cxx98 == 0 ? defined(__cpp_##macro) : __cpp_##macro != cxx98) +#define check(macro, cxx98, cxx11, cxx14, cxx17, cxx20, cxx23, cxx26) (__cpp_##macro != cxx98) #elif __cplusplus < 201402L -#define check(macro, cxx98, cxx11, cxx14, cxx17, cxx20, cxx23, cxx26) (cxx11 == 0 ? defined(__cpp_##macro) : __cpp_##macro != cxx11) +#define check(macro, cxx98, cxx11, cxx14, cxx17, cxx20, cxx23, cxx26) (__cpp_##macro != cxx11) #elif __cplusplus < 201703L -#define check(macro, cxx98, cxx11, cxx14, cxx17, cxx20, cxx23, cxx26) (cxx14 == 0 ? defined(__cpp_##macro) : __cpp_##macro != cxx14) +#define check(macro, cxx98, cxx11, cxx14, cxx17, cxx20, cxx23, cxx26) (__cpp_##macro != cxx14) #elif __cplusplus < 202002L -#define check(macro, cxx98, cxx11, cxx14, cxx17, cxx20, cxx23, cxx26) (cxx17 == 0 ? defined(__cpp_##macro) : __cpp_##macro != cxx17) +#define check(macro, cxx98, cxx11, cxx14, cxx17, cxx20, cxx23, cxx26) (__cpp_##macro != cxx17) #elif __cplusplus < 202302L -#define check(macro, cxx98, cxx11, cxx14, cxx17, cxx20, cxx23, cxx26) (cxx20 == 0 ? defined(__cpp_##macro) : __cpp_##macro != cxx20) +#define check(macro, cxx98, cxx11, cxx14, cxx17, cxx20, cxx23, cxx26) (__cpp_##macro != cxx20) #elif __cplusplus == 202302L -#define check(macro, cxx98, cxx11, cxx14, cxx17, cxx20, cxx23, cxx26) (cxx23 == 0 ? defined(__cpp_##macro) : __cpp_##macro != cxx23) +#define check(macro, cxx98, cxx11, cxx14, cxx17, cxx20, cxx23, cxx26) (__cpp_##macro != cxx23) #else -#define check(macro, cxx98, cxx11, cxx14, cxx17, cxx20, cxx23, cxx26) (cxx26 == 0 ? defined(__cpp_##macro) : __cpp_##macro != cxx26) +#define check(macro, cxx98, cxx11, cxx14, cxx17, cxx20, cxx23, cxx26) (__cpp_##macro != cxx26) #endif // --- C++26 features --- diff --git a/clang/test/Preprocessor/p2843r3.cpp b/clang/test/Preprocessor/p2843r3.cpp new file mode 100644 index 0000000000000..58d06cb27a22f --- /dev/null +++ b/clang/test/Preprocessor/p2843r3.cpp @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -std=c++26 -pedantic -verify=cxx26,expected -Wno-invalid-pp-token %s +// RUN: %clang_cc1 -std=c++23 -pedantic -verify=cxx23,expected -Wno-invalid-pp-token %s + +// P2843R3: Preprocessing is never undefined. The constructs this paper makes +// ill-formed were previously undefined behavior; under C++26 Clang now +// diagnoses them as errors, while retaining the pre-existing pedantic warning +// in earlier language modes for compatibility. + +// [cpp.cond] A macro expansion that produces 'defined' in a conditional +// expression. P2843R3 makes this ill-formed; promoted to a hard error in +// C++26. +#define DEFINED defined +// cxx26-error@+2 {{macro expansion producing 'defined' is not allowed}} +// cxx23-warning@+1 {{macro expansion producing 'defined' has undefined behavior}} +#if DEFINED(bar) +#endif + +// Malformed 'defined' operands are ill-formed in all modes. +#if defined() // expected-error {{macro name must be an identifier}} +#endif +#if defined(a b) // expected-error {{missing ')' after 'defined'}} expected-note {{to match this '('}} +#endif +#if defined(a, b) // expected-error {{missing ')' after 'defined'}} expected-note {{to match this '('}} +#endif + +// [cpp.replace.general] A preprocessing directive inside the arguments of a +// function-like macro invocation. Still only diagnosed as a pedantic warning; +// promoting this to a hard error is tracked separately. +#define FUNCTION_MACRO(...) +FUNCTION_MACRO( + #if 0 // expected-warning {{embedding a directive within macro arguments has undefined behavior}} + #endif +) + +// [cpp.concat] Concatenation that does not form a valid preprocessing token. +#define CONCAT(A, B) A ## B +CONCAT(=, >) // expected-error {{pasting formed '=>', an invalid preprocessing token}} +// expected-error@-1 {{expected unqualified-id}} + +// [cpp.predefined] #undef of a reserved identifier / builtin macro. +#undef defined // expected-error {{'defined' cannot be used as a macro name}} +#undef __DATE__ // expected-warning {{undefining builtin macro}} + +// [cpp.line] #line with a non-positive or out-of-range argument. +#line 0 // expected-warning {{#line directive with zero argument is a GNU extension}} +#line -1 // expected-error {{#line directive requires a positive integer argument}} +#line 2147483647 // ok, largest value required to be accepted +#line 2147483648 // expected-warning {{C requires #line number to be less than 2147483648, allowed as extension}} diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 2c834b07f9a8f..5d6babd7a95c4 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -330,7 +330,15 @@

C++2c implementation status

Preprocessing is never undefined P2843R3 - No + +
+ Partial + Clang diagnoses macro expansions producing defined in + conditional expressions in C++26, but embedded directives in macro + arguments and other constructs made ill-formed by this paper are not + yet fully implemented. +
+