This is an archive of the discontinued LLVM Phabricator instance.

Mark ATOMIC_VAR_INIT and ATOMIC_FLAG_INIT as deprecated
ClosedPublic

Authored by aaron.ballman on Oct 21 2021, 7:53 AM.

Details

Summary

C17 deprecated ATOMIC_VAR_INIT with the resolution of DR 485. C++ followed suit when adopting P0883R2 for C++20, but additionally chose to deprecate ATOMIC_FLAG_INIT at the same time despite the macro still being required in C. This patch marks both macros as deprecated when appropriate to do so.

It does so by using #pragma clang deprecated and this patch presumes we don't have to guard those uses of the pragma with compiler or compiler version checks.

I believe libc++ will need some changes to address the deprecation as it seems it is using these macros. I'm not a libc++ maintainer and so I've added a few libc++ folks to the review so they can weigh in on whether libc++ should change first or can react to the changes in this patch. If libc++ needs to change first and you'd like me to drive those changes, I'd appreciate knowing what changes the maintainers would like to see there (I believe that removing the ATOMIC_VAR_INIT and using direct initialization would be appropriate, but I have no idea if the maintainers agree).

Diff Detail

Unit TestsFailed

Event Timeline

aaron.ballman requested review of this revision.Oct 21 2021, 7:53 AM
aaron.ballman created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptOct 21 2021, 7:53 AM

Rebased and hopefully fixes the CI pipeline failures. I find it very odd that Linux was failing but Windows was not...

erichkeane accepted this revision.Nov 1 2021, 5:54 AM

These look fine, I was hoping that someone else from library would take a look, but it looks right ot me.

This revision is now accepted and ready to land.Nov 1 2021, 5:54 AM

ping @ldionne or @cjdb for libc++ review

aaron.ballman added a project: Restricted Project.Nov 9 2021, 5:40 AM
aaron.ballman added a subscriber: libcxx-commits.

Subscribing the libc++ commits list in an attempt to get the attention of a libc++ reviewer.

If libc++ is using these macros, then I think it would be useful to include (the removal of) those uses in this PR.

../libcxx/include/atomic:#define ATOMIC_VAR_INIT(value) see below
../libcxx/include/atomic:#define ATOMIC_FLAG_INIT see below
../libcxx/include/atomic:#define ATOMIC_FLAG_INIT {false}
../libcxx/include/atomic:#define ATOMIC_VAR_INIT(__v) {__v}
../libcxx/src/barrier.cpp:            __atomic_base<__barrier_phase_t> __phase = ATOMIC_VAR_INIT(0);
../libcxx/src/experimental/memory_resource.cpp:        ATOMIC_VAR_INIT(&res_init.resources.new_delete_res);
../libcxx/src/ios.cpp:atomic<int> ios_base::__xindex_ = ATOMIC_VAR_INIT(0);
../libcxx/test/libcxx/atomics/atomics.flag/init_bool.pass.cpp:std::atomic_flag global = ATOMIC_FLAG_INIT;
../libcxx/test/std/atomics/atomics.flag/init.pass.cpp:// atomic_flag() = ATOMIC_FLAG_INIT;
../libcxx/test/std/atomics/atomics.flag/init.pass.cpp:    std::atomic_flag f = ATOMIC_FLAG_INIT;
../libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_var_init.pass.cpp:// #define ATOMIC_VAR_INIT(value)
../libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_var_init.pass.cpp:    std::atomic<int> v = ATOMIC_VAR_INIT(5);
../libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/ctor.pass.cpp:      constexpr Atomic a = ATOMIC_VAR_INIT(t);
../libcxx/test/std/thread/futures/futures.async/async.pass.cpp:std::atomic_bool invoked = ATOMIC_VAR_INIT(false);
clang/test/Headers/stdatomic-deprecations.c
12

This doesn't look like correct use of the ATOMIC_VAR_INIT macro. It should be initializing an atomic, yeah?
(Note for example that if you did (void)ATOMIC_FLAG_INIT(12), even with libc++'s implementation, it would just fail with a syntax error, because (void){12} is not a valid expression AFAIK.)

If libc++ is using these macros, then I think it would be useful to include (the removal of) those uses in this PR.

../libcxx/include/atomic:#define ATOMIC_VAR_INIT(value) see below
../libcxx/include/atomic:#define ATOMIC_FLAG_INIT see below
../libcxx/include/atomic:#define ATOMIC_FLAG_INIT {false}
../libcxx/include/atomic:#define ATOMIC_VAR_INIT(__v) {__v}
../libcxx/src/barrier.cpp:            __atomic_base<__barrier_phase_t> __phase = ATOMIC_VAR_INIT(0);
../libcxx/src/experimental/memory_resource.cpp:        ATOMIC_VAR_INIT(&res_init.resources.new_delete_res);
../libcxx/src/ios.cpp:atomic<int> ios_base::__xindex_ = ATOMIC_VAR_INIT(0);
../libcxx/test/libcxx/atomics/atomics.flag/init_bool.pass.cpp:std::atomic_flag global = ATOMIC_FLAG_INIT;
../libcxx/test/std/atomics/atomics.flag/init.pass.cpp:// atomic_flag() = ATOMIC_FLAG_INIT;
../libcxx/test/std/atomics/atomics.flag/init.pass.cpp:    std::atomic_flag f = ATOMIC_FLAG_INIT;
../libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_var_init.pass.cpp:// #define ATOMIC_VAR_INIT(value)
../libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_var_init.pass.cpp:    std::atomic<int> v = ATOMIC_VAR_INIT(5);
../libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/ctor.pass.cpp:      constexpr Atomic a = ATOMIC_VAR_INIT(t);
../libcxx/test/std/thread/futures/futures.async/async.pass.cpp:std::atomic_bool invoked = ATOMIC_VAR_INIT(false);

Thanks Arthur!

I don't know enough about libc++ to feel comfortable making those changes yet. For example, does libc++ need to work with other compilers than Clang (compilers which might give diagnostics if you fail to use ATOMIC_VAR_INIT in some language mode)? The deprecation is not really a DR, so should the uses be wrapped in language version checks, etc. Or are you saying I don't have to worry about any of that and I can rip this stuff out of libc++ with wild abandon?

I don't know enough about libc++ to feel comfortable making those changes yet. For example, does libc++ need to work with other compilers than Clang (compilers which might give diagnostics if you fail to use ATOMIC_VAR_INIT in some language mode)? The deprecation is not really a DR, so should the uses be wrapped in language version checks, etc. Or are you saying I don't have to worry about any of that and I can rip this stuff out of libc++ with wild abandon?

@ldionne will be the ultimate arbiter, but FWIW, I think you can rip with wild abandon — certainly from libcxx/src/.
For some background (and further links, including p0883 which you've already dug up), see
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2139r2.html#3.24

There are two kinds of places libc++ uses these macros: src/ and test/. Everywhere in src/ is compiled with C++17, so it's totally safe to use atomic<T> a{init}; instead of atomic<T> a = ATOMIC_VAR_INIT(init);.
But everywhere in test/ we need to keep working, by definition. They might have to gain a line ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS or (new and therefore worse) ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated, but that's all.

Here's the diffs in src/ AFAICT [untested code]:

diff --git a/libcxx/src/barrier.cpp b/libcxx/src/barrier.cpp
index 9ee476993b81..8e13ad088052 100644
--- a/libcxx/src/barrier.cpp
+++ b/libcxx/src/barrier.cpp
@@ -22,7 +22,7 @@ public:
     struct alignas(64) /* naturally-align the heap state */ __state_t
     {
         struct {
-            __atomic_base<__barrier_phase_t> __phase = ATOMIC_VAR_INIT(0);
+            __atomic_base<__barrier_phase_t> __phase{0};
         } __tickets[64];
     };
 
diff --git a/libcxx/src/experimental/memory_resource.cpp b/libcxx/src/experimental/memory_resource.cpp
index 5f9d3b8a4cbe..77a1d654d60f 100644
--- a/libcxx/src/experimental/memory_resource.cpp
+++ b/libcxx/src/experimental/memory_resource.cpp
@@ -97,8 +97,7 @@ static memory_resource *
 __default_memory_resource(bool set = false, memory_resource * new_res = nullptr) noexcept
 {
 #ifndef _LIBCPP_HAS_NO_ATOMIC_HEADER
-    _LIBCPP_SAFE_STATIC static atomic<memory_resource*> __res =
-        ATOMIC_VAR_INIT(&res_init.resources.new_delete_res);
+    _LIBCPP_SAFE_STATIC static atomic<memory_resource*> __res{&res_init.resources.new_delete_res};
     if (set) {
         new_res = new_res ? new_res : new_delete_resource();
         // TODO: Can a weaker ordering be used?
diff --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index a8a99015a977..338636b2220e 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -137,7 +137,7 @@ ios_base::getloc() const
 
 // xalloc
 #if defined(_LIBCPP_HAS_C_ATOMIC_IMP) && !defined(_LIBCPP_HAS_NO_THREADS)
-atomic<int> ios_base::__xindex_ = ATOMIC_VAR_INIT(0);
+atomic<int> ios_base::__xindex_{0};
 #else
 int ios_base::__xindex_ = 0;
 #endif
diff --git a/libcxx/test/std/thread/futures/futures.async/async.pass.cpp b/libcxx/test/std/thread/futures/futures.async/async.pass.cpp
index 365b895e0603..beee6f8fcfdd 100644
--- a/libcxx/test/std/thread/futures/futures.async/async.pass.cpp
+++ b/libcxx/test/std/thread/futures/futures.async/async.pass.cpp
@@ -33,7 +33,7 @@
 typedef std::chrono::high_resolution_clock Clock;
 typedef std::chrono::milliseconds ms;
 
-std::atomic_bool invoked = ATOMIC_VAR_INIT(false);
+std::atomic_bool invoked{false};
 
 int f0()
 {
clang/lib/Headers/stdatomic.h
46–52

Hmm, I do think there ought to be some way for the C++20 programmer to suppress the deprecation warning for these macros (specifically, not just via -Wno-deprecated in their whole project). For deprecations in the C++ standard library, libc++ offers an all-or-nothing flag: basically you'd do

#if (__STDC_VERSION__ >= 201710L || __cplusplus >= 202002L) && !defined(_LIBCPP_DISABLE_DEPRECATION_WARNINGS)
/* ATOMIC_VAR_INIT was deprecated in C17 and C++20. */
#pragma clang deprecated(ATOMIC_VAR_INIT)
#endif

(This also makes it easy for libcxx/test/ to suppress the deprecation warning for the purposes of testing.)

However, I'm not sure if it's appropriate to mention _LIBCPP_DISABLE_DEPRECATION_WARNINGS in this header located in clang/lib/Headers/ instead of libcxx/include/. Someone else will have to make that call.

It might be that the only way for the programmer (or libcxx/test/) to work around the warning will be for them to pass -Wno-deprecated globally; IMO that is suboptimal but quite far from disastrous.

aaron.ballman marked 2 inline comments as done.Dec 15 2021, 9:49 AM

I don't know enough about libc++ to feel comfortable making those changes yet. For example, does libc++ need to work with other compilers than Clang (compilers which might give diagnostics if you fail to use ATOMIC_VAR_INIT in some language mode)? The deprecation is not really a DR, so should the uses be wrapped in language version checks, etc. Or are you saying I don't have to worry about any of that and I can rip this stuff out of libc++ with wild abandon?

@ldionne will be the ultimate arbiter, but FWIW, I think you can rip with wild abandon — certainly from libcxx/src/.
For some background (and further links, including p0883 which you've already dug up), see
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2139r2.html#3.24

There are two kinds of places libc++ uses these macros: src/ and test/. Everywhere in src/ is compiled with C++17, so it's totally safe to use atomic<T> a{init}; instead of atomic<T> a = ATOMIC_VAR_INIT(init);.
But everywhere in test/ we need to keep working, by definition. They might have to gain a line ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS or (new and therefore worse) ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated, but that's all.

Thank you for the help here! FWIW, I'm not certain the libc++ tests need to be changed at all -- shouldn't those all be using the macro definitions obtained by libc++'s <atomic> header? If so, those are not flagged as deprecated yet: https://github.com/llvm/llvm-project/blob/main/libcxx/include/atomic#L2699

clang/lib/Headers/stdatomic.h
46–52

I think this is a reasonable idea. Microsoft has macros for a similar purpose with _CRT_SECURE_NO_WARNINGS (IIRC). I would not want to use _LIBCPP_ for this purpose, but I could imagine it's useful to add something like _CLANG_DISABLE_DEPRECATION_WARNINGS. (I'm guessing we don't want per-deprecation granularity on the macro, but if we needed something like that, I think we could add it.)

I went ahead and added _CLANG_DISABLE_CRT_DEPRECATION_WARNINGS to the next version of the patch, and documented it in the release notes and user's manual. We can quibble over the name if you have better suggestions.

clang/test/Headers/stdatomic-deprecations.c
12

This doesn't look like correct use of the ATOMIC_VAR_INIT macro. It should be initializing an atomic, yeah?

It expands to (void)(12); which is why the test works as it stands today and is valid. However, I don't mind changing it to use an actual init instead.

aaron.ballman marked 2 inline comments as done.

Updated based on review feedback from Arthur.

Thank you for the help here! FWIW, I'm not certain the libc++ tests need to be changed at all -- shouldn't those all be using the macro definitions obtained by libc++'s <atomic> header? If so, those are not flagged as deprecated yet: https://github.com/llvm/llvm-project/blob/main/libcxx/include/atomic#L2699

To be clear on this point -- I've deprecated ATOMIC_VAR_INIT when it is obtained from <stdatomic.h> to implement WG14 DR 485 (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2396.htm#dr_485) from C17, but I am not implementing the changes needed in libc++'s <atomic> for WG21 P0883R2 (https://wg21.link/p0883r2) from C++20. This makes the deprecation in <stdatomic.h> for C++ a bit awkward, but I think matches the intent of the two standards.

Sorry, I seemed to have missed this entirely.

Since this is touching only <stdatomic.h> and not libc++'s <atomic>, I think it would actually be OK not to make any changes at all inside libc++. Nothing should break. We should separately implement P0883 in libc++ and deprecate the macros that we define in <atomic>. Note that I thought P0883 was implemented -- it is definitely marked as such, so we'll have to investigate. CC @tambre who implemented P0883.

So, as far as this patch is concerned, I think we could remove all the libc++ specific stuff and this would LGTM. Thanks for the heads up -- we'll tackle the C++ equivalent of this change in libc++ separately.

tambre added a comment.EditedDec 15 2021, 11:52 AM

Sorry, I seemed to have missed this entirely.

Since this is touching only <stdatomic.h> and not libc++'s <atomic>, I think it would actually be OK not to make any changes at all inside libc++. Nothing should break. We should separately implement P0883 in libc++ and deprecate the macros that we define in <atomic>. Note that I thought P0883 was implemented -- it is definitely marked as such, so we'll have to investigate. CC @tambre who implemented P0883.

So, as far as this patch is concerned, I think we could remove all the libc++ specific stuff and this would LGTM. Thanks for the heads up -- we'll tackle the C++ equivalent of this change in libc++ separately.

I only took care of std::atomic_init() and seemingly completely missed cleaning up uses of ATOMIC_VAR_INIT.
I do remember not being able to add a deprecation notice for ATOMIC_VAR_INIT and ATOMIC_FLAG_INIT at the time as #pragma deprecated didn't exist yet. libc++ itself defines both of them.

Dropped changes to libc++ based on review feedback.

Sorry, I seemed to have missed this entirely.

No worries!

Since this is touching only <stdatomic.h> and not libc++'s <atomic>, I think it would actually be OK not to make any changes at all inside libc++. Nothing should break.

Ah, thanks for the info! I've backed the libc++ changes out here.

We should separately implement P0883 in libc++ and deprecate the macros that we define in <atomic>. Note that I thought P0883 was implemented -- it is definitely marked as such, so we'll have to investigate. CC @tambre who implemented P0883.

So, as far as this patch is concerned, I think we could remove all the libc++ specific stuff and this would LGTM. Thanks for the heads up -- we'll tackle the C++ equivalent of this change in libc++ separately.

Thanks!

I only took care of std::atomic_init() and seemingly completely missed cleaning up uses of ATOMIC_VAR_INIT.
I do remember not being able to add a deprecation notice for ATOMIC_VAR_INIT and ATOMIC_FLAG_INIT at the time as #pragma deprecated didn't exist yet. libc++ itself defines both of them.

Yeah, it's a relatively new feature that was added in 26c695b7893071d5e69afbaa70c4850ab2e468be

@tambre Any appetite for a libc++ patch? :)

Otherwise, let me know and I'll put it in my list of things to fix up.

@tambre Any appetite for a libc++ patch? :)

Otherwise, let me know and I'll put it in my list of things to fix up.

Sure, I can take it up during the weekend.

@tambre Any appetite for a libc++ patch? :)

Otherwise, let me know and I'll put it in my list of things to fix up.

Sure, I can take it up during the weekend.

Thanks a lot! Ping me and I'll review it quickly.

Ping @Quuxplusone for feedback on the _CLANG_DISABLE_CRT_DEPRECATION_WARNINGS macro that was added.

TLDR on _CLANG_DISABLE_CRT_DEPRECATION_WARNINGS: I'm neutral. :)

clang/docs/ReleaseNotes.rst
162–167

IIUC, <stdatomic.h> is not relevant to C++20; libc++'s <atomic> even #errors if you include both <stdatomic.h> and <atomic> in the same TU.
Defining _CLANG_DISABLE_CRT_DEPRECATION_WARNINGS won't affect the warnings in C++20; for those you need _LIBCPP_DISABLE_DEPRECATION_WARNINGS.
C++20 isn't relevant to this section on "C Language Changes in Clang"; arguably it could be listed under "C++ Language Changes in Clang," except that D115995 didn't change anything about Clang. So I think it's fine not to mention D115995 anywhere in this file.

clang/docs/UsersManual.rst
1128–1130

It is probably sketchy to advise people to change the setting of _CLANG_DISABLE_CRT_DEPRECATION_WARNINGS over the lifetime of a single TU. If <stdint.h> transitively includes <stdatomic.h>, this won't work. (Of course that's unlikely in this specific case, but in general, IMHO we shouldn't train people to think that this will always work.)
(In fact, <stdatomic.h> transitively includes <stdint.h>!)
We should train the programmer to think of _CLANG_DISABLE_CRT_DEPRECATION_WARNINGS as all-or-nothing: set it in your build system, or set it at the very top of the TU.

clang/lib/Headers/stdatomic.h
46–52

Re _CLANG_DISABLE_CRT_DEPRECATION_WARNINGS: This naming scheme looks novel — I don't see any other _CLANG_{DIS,EN}ABLE.* macros in clang/lib/Headers/ — but that might just be because its purpose is also novel. I'm not saying it's a good idea, but I don't object to it nor have any particularly better idea.

Btw, where above I said -Wno-deprecated, it turns out that -Wno-deprecated-pragma is slightly finer-grained. Also, I'm like 95% sure that C++ is totally covered by D115995, which means whatever you do here is relevant only to C17, which means I have no personal stake in its ergonomics. :)

aaron.ballman marked 2 inline comments as done.Jan 3 2022, 7:11 AM
aaron.ballman added inline comments.
clang/docs/ReleaseNotes.rst
162–167

The point I was trying to make is that including <stdatomic.h> in C++20 and later mode will diagnose these as deprecated, independent of <atomic>. I did this on the assumption that they're just as useless coming from <stdatomic.h> as they are coming from <atomic>, so they should also be diagnosed. I could make this more clear by mentioning <atomic> explicitly in the release note.

As for whether this should be listed under the C++ section... meh. I'm not certain it matters that much as this is about the C header file. I can mention it in the C++ section though.

clang/docs/UsersManual.rst
1128–1130

Thanks for the feedback, I've adjusted the text.

clang/lib/Headers/stdatomic.h
46–52

but that might just be because its purpose is also novel. I'm not saying it's a good idea, but I don't object to it nor have any particularly better idea.

Yes, it is novel. This is our first foray down this path.

Also, I'm like 95% sure that C++ is totally covered by D115995, which means whatever you do here is relevant only to C17, which means I have no personal stake in its ergonomics. :)

Not quite. It still matters for a C++ TU that includes <stdatomic.h> directly rather than <atomic>, which is still possible.

aaron.ballman marked 2 inline comments as done.

Updated release notes and user's manual based on review feedback.

Quuxplusone accepted this revision.Jan 11 2022, 12:35 PM

LGTM FWIW.
I'm not the right person to approve for libc, but I'm not sure any of the currently listed reviewers are any more appropriate either. So you should find and ping a new reviewer, or just ship it, at your discretion. :)

clang/docs/ReleaseNotes.rst
179

Thanks @Quuxplusone!

@rsmith, I plan to commit this (with the suggested fix to release notes applied) sometime on Fri (Jan 14) unless I hear further feedback, in case you want to weigh in.

I've commit in 0d459444e5105d689e6388612784b89a93054944, thanks all for the helpful reviews!