Hi,
During the implementation of C++26 P2843R3(https://wg21.link/P2843R3), we encountered several issues; specifically, a significant amount of existing code may rely on the undefined behaviors described in this paper. We are therefore seeking further input to ensure that, while adhering to the relevant standards, we can maintain the highest possible level of compatibility within Clang. Furthermore, we wish to use this discussion to alert Clang users to this situation, thereby providing them with sufficient time to smoothly address any potential issues in their code.
Background
P2843R3 (adopted for C++26) makes a number of previously-undefined preprocessor constructs IFNDR. WG21’s position, expressed on the reflectors, is essentially that because the constructs are IFNDR, implementations are free to do whatever they want – including nothing.
Clang has historically diagnosed most of these constructs under -pedantic. PR #192073 marked the feature implemented; #196989 re-landed it and, in addition, promoted most of those -pedantic warnings to default-on so that users in any language mode would catch real bugs (the constructs are also UB pre-C++26).
#196989 has now been reverted twice because of breakage in widely-used third-party code:
1. Windows SDK `WinBase.h`:
#define M (_WIN32_WINNT >= 0x0502 || !defined(_WINBASE_))
#if M ...
-- macro expansion producing `defined`.
2. llvm-libc `__llvm-libc-common.h`, compiled in C++17:
#undef _Noreturn
#undef _Alignas
#undef _Static_assert
#undef _Alignof
3. libjpeg-turbo, compiled in C11:
#undef inline
These are not bugs in Clang – the constructs really are UB / IFNDR – but they are pervasive in shipping headers and toolchains, and -Werror turns the warning into a hard build break.
We would like community feedback before re-landing.
Questions for the community
Q1. Default-on vs. -pedantic?
For each of the diagnostics promoted by P2843R3:
-
(a)
definedproduced by macro expansion in a conditional – already split into object-type (default-on, existing behavior) and function-type (-pedantic, existing behavior). #196989 promoted function-type to default-on. nico@ points out the function-type case is noisy and often unfixable in third-party code, which is why it was gated under-pedanticin the first place. Should we keep the function-type case under-pedantic? -
(b) Preprocessing directive embedded in a function-like macro argument. Promote to default-on, or keep under
-pedantic? -
(c)
#define/#undefof a name that is lexically identical to a keyword, special identifier, or standard attribute token. In particular,#undef keyword: this is dubious to diagnose because the preceding#define keyword ...is what created the UB, and the#undefonly restores the keyword. Should the#undefcase be default-off, or-pedantic, even when#defineis default-on? -
(d)
#line 0,#line Nfor N > 2^31. P4136R2 (adopted as a DR against P2843R3) walks these back, so #196989 has already reverted the#linechanges. We should land the P4136R2 status update separately. -
(e) Reserved attribute identifiers. #196989 removes
DefaultIgnoreonwarn_pp_macro_is_reserved_attribute_id. Should this be default-on or stay-pedantic?
Q2. Language-mode gating?
The IFNDR rule lives in C++26 wording, but the underlying UB has always existed (in C and earlier C++). Should the default-on policy be:
- (a) on in all language modes (current PR direction), or
- (b) default-on only in C++26 and later,
-pedanticelsewhere, - (c) default-on in C++26, and downgraded to
-pedanticonly for constructs we have evidence are pervasive in real code (i.e., (a)-function-type and (c)-#undef)?
Q3. Warning group structure?
Today -Wkeyword-macro covers both #define keyword (which hides the keyword) and #undef keyword. Aaron and nico both suggested separating these so users can suppress one without losing the other. Concretely we propose:
-Wkeyword-macro (umbrella, default-on)
-Wkeyword-macro-define (#define hiding a keyword)
-Wkeyword-macro-undef (#undef of keyword/attr-token)
and analogously for the embedded-directive and defined-from-macro cases.
Q4. System headers.
The Windows SDK case is in a system header; Clang already suppresses these diagnostics there, so once libc / libjpeg-turbo are updated, the remaining default-on noise should be largely confined to user code. Is the SystemHeaders default sufficient, or do we want a -W...-in-system-headers re-enable for users who actually want to clean up vendor headers?
Proposed plan
Pending feedback…
Prior discussion
- PR #192073 (initial implementation, reverted)
https://github.com/llvm/llvm-project/pull/192073
- PR #196989 (reapply, reverted)
https://github.com/llvm/llvm-project/pull/196989
- WG21 P2843R3: https://wg21.link/P2843
- WG21 P4136R2: https://wg21.link/P4136R2 (DR against P2843R3)
- CWG 2575: https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2575
Thanks!