2626#define SHARE_RUNTIME_ATOMIC_HPP
2727
2828#include " cppstdlib/type_traits.hpp"
29- #include " metaprogramming/enableIf.hpp"
3029#include " metaprogramming/primitiveConversions.hpp"
3130#include " runtime/atomicAccess.hpp"
3231#include " utilities/globalDefinitions.hpp"
8988// value will be initialized as if by translating the value that would be
9089// provided by default constructing an atomic type for the value type's
9190// decayed type.
92-
93- // (3) Atomic pointers and atomic integers additionally provide
91+ //
92+ // (3) Constructors for all atomic types are constexpr, to ensure non-local
93+ // atomic variables are constant initialized (C++17 6.6.2) when initialized
94+ // with suitable arguments.
95+ //
96+ // (4) Atomic pointers and atomic integers additionally provide
9497//
9598// member functions:
9699// v.add_then_fetch(i [, o]) -> T
102105// type of i must be signed, or both must be unsigned. Atomic pointers perform
103106// element arithmetic.
104107//
105- // (4 ) Atomic integers additionally provide
108+ // (5 ) Atomic integers additionally provide
106109//
107110// member functions:
108111// v.and_then_fetch(x [, o]) -> T
112115// v.fetch_then_or(x [, o]) -> T
113116// v.fetch_then_xor(x [, o]) -> T
114117//
115- // (5 ) Atomic pointers additionally provide
118+ // (6 ) Atomic pointers additionally provide
116119//
117120// nested types:
118121// ElementType -> std::remove_pointer_t<T>
@@ -217,7 +220,7 @@ class AtomicImpl::CommonCore {
217220 T volatile _value;
218221
219222protected:
220- explicit CommonCore (T value) : _value(value) {}
223+ explicit constexpr CommonCore (T value) : _value(value) {}
221224 ~CommonCore () = default ;
222225
223226 T volatile * value_ptr () { return &_value; }
@@ -290,7 +293,7 @@ class AtomicImpl::SupportsArithmetic : public CommonCore<T> {
290293 }
291294
292295protected:
293- explicit SupportsArithmetic (T value) : CommonCore<T>(value) {}
296+ explicit constexpr SupportsArithmetic (T value) : CommonCore<T>(value) {}
294297 ~SupportsArithmetic () = default ;
295298
296299public:
@@ -333,7 +336,7 @@ class AtomicImpl::Atomic<T, AtomicImpl::Category::Integer>
333336 : public SupportsArithmetic<T>
334337{
335338public:
336- explicit Atomic (T value = 0 ) : SupportsArithmetic<T>(value) {}
339+ explicit constexpr Atomic (T value = 0 ) : SupportsArithmetic<T>(value) {}
337340
338341 NONCOPYABLE (Atomic);
339342
@@ -373,7 +376,7 @@ class AtomicImpl::Atomic<T, AtomicImpl::Category::Byte>
373376 : public CommonCore<T>
374377{
375378public:
376- explicit Atomic (T value = 0 ) : CommonCore<T>(value) {}
379+ explicit constexpr Atomic (T value = 0 ) : CommonCore<T>(value) {}
377380
378381 NONCOPYABLE (Atomic);
379382
@@ -389,7 +392,7 @@ class AtomicImpl::Atomic<T, AtomicImpl::Category::Pointer>
389392 : public SupportsArithmetic<T>
390393{
391394public:
392- explicit Atomic (T value = nullptr ) : SupportsArithmetic<T>(value) {}
395+ explicit constexpr Atomic (T value = nullptr ) : SupportsArithmetic<T>(value) {}
393396
394397 NONCOPYABLE (Atomic);
395398
@@ -410,12 +413,21 @@ class AtomicImpl::Atomic<T, AtomicImpl::Category::Translated> {
410413
411414 Atomic<Decayed> _value;
412415
413- static Decayed decay (T x) { return Translator::decay (x); }
416+ // The decay function and the constructors are constexpr so that a non-local
417+ // atomic object constructed with constant arguments will be a constant
418+ // initialization. One might ask why it's not a problem that some
419+ // specializations of these functions are not constant expressions. The
420+ // answer lies in C++17 10.1.5/6, along with us having *some* constexpr
421+ // translator decay functions, constexpr ctors for some translated types,
422+ // and constexpr ctors for some decayed types. Also, C++23 removes those
423+ // restrictions on constexpr functions and ctors.
424+
425+ static constexpr Decayed decay (T x) { return Translator::decay (x); }
414426 static T recover (Decayed x) { return Translator::recover (x); }
415427
416428 // Support for default construction via the default construction of _value.
417429 struct UseDecayedCtor {};
418- explicit Atomic (UseDecayedCtor) : _value() {}
430+ explicit constexpr Atomic (UseDecayedCtor) : _value() {}
419431 using DefaultCtorSelect =
420432 std::conditional_t <std::is_default_constructible_v<T>, T, UseDecayedCtor>;
421433
@@ -424,9 +436,9 @@ class AtomicImpl::Atomic<T, AtomicImpl::Category::Translated> {
424436
425437 // If T is default constructible, construct from a default constructed T.
426438 // Otherwise, default construct the underlying Atomic<Decayed>.
427- Atomic () : Atomic(DefaultCtorSelect()) {}
439+ constexpr Atomic () : Atomic(DefaultCtorSelect()) {}
428440
429- explicit Atomic (T value) : _value(decay(value)) {}
441+ explicit constexpr Atomic (T value) : _value(decay(value)) {}
430442
431443 NONCOPYABLE (Atomic);
432444
0 commit comments