Skip to content

Commit bbd871e

Browse files
authored
[libc++] Don't implement <stdatomic.h> before C++23 (#123130)
#95498 implemented a libc++ extension where <stdatomic.h> would forward to <atomic> even before C++23. Unfortunately, this was found to be a breaking change (with fairly widespread impact) since that changes whether _Atomic(T) is a C style atomic or std::atomic<T>. In principle, this can even be an ABI break. We generally don't implement extensions in libc++ because they cause so many problems, and that extension had been accepted because it was deemed pretty small and only a quality of life improvement. Since it has widespread impact on valid C++20 (and before) code, this patch removes the extension before we ship it in any public release.
1 parent 2dc5682 commit bbd871e

File tree

6 files changed

+97
-8
lines changed

6 files changed

+97
-8
lines changed

libcxx/include/atomic

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,16 @@ template <class T>
592592
#else
593593
# include <__config>
594594

595+
# if defined(_LIBCPP_STDATOMIC_H) || defined(kill_dependency) || defined(atomic_load)
596+
# define _LIBCPP_STDATOMIC_H_HAS_DEFINITELY_BEEN_INCLUDED 1
597+
# else
598+
# define _LIBCPP_STDATOMIC_H_HAS_DEFINITELY_BEEN_INCLUDED 0
599+
# endif
600+
601+
# if _LIBCPP_STD_VER < 23 && _LIBCPP_STDATOMIC_H_HAS_DEFINITELY_BEEN_INCLUDED
602+
# error <atomic> is incompatible with <stdatomic.h> before C++23. Please compile with -std=c++23.
603+
# endif
604+
595605
# include <__atomic/aliases.h>
596606
# include <__atomic/atomic.h>
597607
# include <__atomic/atomic_flag.h>

libcxx/include/stdatomic.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ using std::atomic_signal_fence // see below
126126
# pragma GCC system_header
127127
# endif
128128

129-
# if defined(__cplusplus)
129+
# if defined(__cplusplus) && _LIBCPP_STD_VER >= 23
130130

131131
# include <atomic>
132132
# include <version>
@@ -231,13 +231,17 @@ using std::atomic_store_explicit _LIBCPP_USING_IF_EXISTS;
231231
using std::atomic_signal_fence _LIBCPP_USING_IF_EXISTS;
232232
using std::atomic_thread_fence _LIBCPP_USING_IF_EXISTS;
233233

234-
# else
234+
# elif defined(_LIBCPP_COMPILER_CLANG_BASED)
235235

236+
// Before C++23, we include the next <stdatomic.h> on the path to avoid hijacking
237+
// the header. We do this because Clang has historically shipped a <stdatomic.h>
238+
// header that would be available in all Standard modes, and we don't want to
239+
// break that use case.
236240
# if __has_include_next(<stdatomic.h>)
237241
# include_next <stdatomic.h>
238242
# endif
239243

240-
# endif // defined(__cplusplus)
244+
# endif // defined(__cplusplus) && _LIBCPP_STD_VER >= 23
241245
#endif // defined(__cplusplus) && __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
242246

243247
#endif // _LIBCPP_STDATOMIC_H

libcxx/test/libcxx/atomics/atomics.syn/compatible_with_stdatomic.compile.pass.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@
77
//===----------------------------------------------------------------------===//
88

99
// UNSUPPORTED: no-threads
10+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
1011

1112
// XFAIL: FROZEN-CXX03-HEADERS-FIXME
1213

13-
// This test verifies that <stdatomic.h> redirects to <atomic>. As an extension,
14-
// libc++ enables this redirection even before C++23.
14+
// This test verifies that <stdatomic.h> redirects to <atomic>.
1515

16-
// Ordinarily, <stdatomic.h> can be included after <atomic>, but including it
17-
// first doesn't work because its macros break <atomic>. Verify that
18-
// <stdatomic.h> can be included first.
16+
// Before C++23, <stdatomic.h> can be included after <atomic>, but including it
17+
// first doesn't work because its macros break <atomic>. Fixing that is the point
18+
// of the C++23 change that added <stdatomic.h> to C++. Thus, this test verifies
19+
// that <stdatomic.h> can be included first.
1920
#include <stdatomic.h>
2021
#include <atomic>
2122

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: no-threads
10+
// REQUIRES: c++03 || c++11 || c++14 || c++17 || c++20
11+
12+
// This test ensures that we issue a reasonable diagnostic when including <atomic> after
13+
// <stdatomic.h> has been included. Before C++23, this otherwise leads to obscure errors
14+
// because <atomic> may try to redefine things defined by <stdatomic.h>.
15+
16+
// Ignore additional weird errors that happen when the two headers are mixed.
17+
// ADDITIONAL_COMPILE_FLAGS: -Xclang -verify-ignore-unexpected=error -Xclang -verify-ignore-unexpected=warning
18+
19+
#include <stdatomic.h>
20+
#include <atomic>
21+
22+
// expected-error@*:* {{<atomic> is incompatible with <stdatomic.h> before C++23.}}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: no-threads
10+
11+
// This test ensures that we don't hijack the <stdatomic.h> header (e.g. by providing
12+
// an empty header) even when compiling before C++23, since some users were using the
13+
// Clang or platform provided header before libc++ added its own.
14+
15+
// On GCC, the compiler-provided <stdatomic.h> is not C++ friendly, so including <stdatomic.h>
16+
// doesn't work at all if we don't use the <stdatomic.h> provided by libc++ in C++23 and above.
17+
// XFAIL: (c++11 || c++14 || c++17 || c++20) && gcc
18+
19+
#include <stdatomic.h>
20+
21+
void f() {
22+
atomic_int i; // just make sure the header isn't empty
23+
(void)i;
24+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: no-threads
10+
11+
// This test verifies that <stdatomic.h> DOES NOT redirect to <atomic> before C++23,
12+
// since doing so is a breaking change. Several things can break when that happens,
13+
// because the type of _Atomic(T) changes from _Atomic(T) to std::atomic<T>.
14+
//
15+
// For example, redeclarations can become invalid depending on whether they
16+
// have been declared with <stdatomic.h> in scope or not.
17+
18+
// REQUIRES: c++03 || c++11 || c++14 || c++17 || c++20
19+
20+
// On GCC, the compiler-provided <stdatomic.h> is not C++ friendly, so including <stdatomic.h>
21+
// doesn't work at all if we don't use the <stdatomic.h> provided by libc++ in C++23 and above.
22+
// XFAIL: (c++11 || c++14 || c++17 || c++20) && gcc
23+
24+
#include <atomic>
25+
#include <stdatomic.h>
26+
#include <type_traits>
27+
28+
static_assert(!std::is_same<_Atomic(int), std::atomic<int> >::value, "");

0 commit comments

Comments
 (0)