[Clang] Implement P2843R3 - Preprocessing is never undefined (#192073)
This PR marks [P2843R3 - Preprocessing is never undefined](https://wg21.link/P2843) as implemented and add tests. Fixes https://github.com/llvm/llvm-project/issues/145658 --------- Signed-off-by: yronglin <yronglin777@gmail.com>
This commit is contained in:
parent
2b3965adc7
commit
42e0cdf2fc
6 changed files with 80 additions and 10 deletions
|
|
@ -133,6 +133,12 @@ C++ Language Changes
|
|||
C++2c Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Partially implemented `P2843R3 <https://wg21.link/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
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
|
|||
|
|
@ -1060,6 +1060,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<ExpansionToDefined>;
|
||||
def err_defined_in_macro : Error<
|
||||
"macro expansion producing 'defined' is not allowed">;
|
||||
|
||||
let CategoryName = "Nullability Issue" in {
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 ---
|
||||
|
|
|
|||
48
clang/test/Preprocessor/p2843r3.cpp
Normal file
48
clang/test/Preprocessor/p2843r3.cpp
Normal file
|
|
@ -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}}
|
||||
|
|
@ -330,7 +330,15 @@ C++23, informally referred to as C++26.</p>
|
|||
<tr>
|
||||
<td>Preprocessing is never undefined</td>
|
||||
<td><a href="https://wg21.link/P2843">P2843R3</a></td>
|
||||
<td class="none" align="center">No</td>
|
||||
<td class="partial" align="center">
|
||||
<details>
|
||||
<summary>Partial</summary>
|
||||
Clang diagnoses macro expansions producing <tt>defined</tt> 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.
|
||||
</details>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Kona, Fall 2025-->
|
||||
<tr>
|
||||
|
|
|
|||
Loading…
Reference in a new issue