8373208: Make Atomic class template constant initializable

Reviewed-by: stefank, dholmes
This commit is contained in:
Kim Barrett 2025-12-19 00:01:55 +00:00
parent f88cbfb8c6
commit 623164651c

View File

@ -26,7 +26,6 @@
#define SHARE_RUNTIME_ATOMIC_HPP
#include "cppstdlib/type_traits.hpp"
#include "metaprogramming/enableIf.hpp"
#include "metaprogramming/primitiveConversions.hpp"
#include "runtime/atomicAccess.hpp"
#include "utilities/globalDefinitions.hpp"
@ -89,8 +88,12 @@
// value will be initialized as if by translating the value that would be
// provided by default constructing an atomic type for the value type's
// decayed type.
// (3) Atomic pointers and atomic integers additionally provide
//
// (3) Constructors for all atomic types are constexpr, to ensure non-local
// atomic variables are constant initialized (C++17 6.6.2) when initialized
// with suitable arguments.
//
// (4) Atomic pointers and atomic integers additionally provide
//
// member functions:
// v.add_then_fetch(i [, o]) -> T
@ -102,7 +105,7 @@
// type of i must be signed, or both must be unsigned. Atomic pointers perform
// element arithmetic.
//
// (4) Atomic integers additionally provide
// (5) Atomic integers additionally provide
//
// member functions:
// v.and_then_fetch(x [, o]) -> T
@ -112,7 +115,7 @@
// v.fetch_then_or(x [, o]) -> T
// v.fetch_then_xor(x [, o]) -> T
//
// (5) Atomic pointers additionally provide
// (6) Atomic pointers additionally provide
//
// nested types:
// ElementType -> std::remove_pointer_t<T>
@ -217,7 +220,7 @@ class AtomicImpl::CommonCore {
T volatile _value;
protected:
explicit CommonCore(T value) : _value(value) {}
explicit constexpr CommonCore(T value) : _value(value) {}
~CommonCore() = default;
T volatile* value_ptr() { return &_value; }
@ -290,7 +293,7 @@ class AtomicImpl::SupportsArithmetic : public CommonCore<T> {
}
protected:
explicit SupportsArithmetic(T value) : CommonCore<T>(value) {}
explicit constexpr SupportsArithmetic(T value) : CommonCore<T>(value) {}
~SupportsArithmetic() = default;
public:
@ -333,7 +336,7 @@ class AtomicImpl::Atomic<T, AtomicImpl::Category::Integer>
: public SupportsArithmetic<T>
{
public:
explicit Atomic(T value = 0) : SupportsArithmetic<T>(value) {}
explicit constexpr Atomic(T value = 0) : SupportsArithmetic<T>(value) {}
NONCOPYABLE(Atomic);
@ -373,7 +376,7 @@ class AtomicImpl::Atomic<T, AtomicImpl::Category::Byte>
: public CommonCore<T>
{
public:
explicit Atomic(T value = 0) : CommonCore<T>(value) {}
explicit constexpr Atomic(T value = 0) : CommonCore<T>(value) {}
NONCOPYABLE(Atomic);
@ -389,7 +392,7 @@ class AtomicImpl::Atomic<T, AtomicImpl::Category::Pointer>
: public SupportsArithmetic<T>
{
public:
explicit Atomic(T value = nullptr) : SupportsArithmetic<T>(value) {}
explicit constexpr Atomic(T value = nullptr) : SupportsArithmetic<T>(value) {}
NONCOPYABLE(Atomic);
@ -410,12 +413,21 @@ class AtomicImpl::Atomic<T, AtomicImpl::Category::Translated> {
Atomic<Decayed> _value;
static Decayed decay(T x) { return Translator::decay(x); }
// The decay function and the constructors are constexpr so that a non-local
// atomic object constructed with constant arguments will be a constant
// initialization. One might ask why it's not a problem that some
// specializations of these functions are not constant expressions. The
// answer lies in C++17 10.1.5/6, along with us having *some* constexpr
// translator decay functions, constexpr ctors for some translated types,
// and constexpr ctors for some decayed types. Also, C++23 removes those
// restrictions on constexpr functions and ctors.
static constexpr Decayed decay(T x) { return Translator::decay(x); }
static T recover(Decayed x) { return Translator::recover(x); }
// Support for default construction via the default construction of _value.
struct UseDecayedCtor {};
explicit Atomic(UseDecayedCtor) : _value() {}
explicit constexpr Atomic(UseDecayedCtor) : _value() {}
using DefaultCtorSelect =
std::conditional_t<std::is_default_constructible_v<T>, T, UseDecayedCtor>;
@ -424,9 +436,9 @@ public:
// If T is default constructible, construct from a default constructed T.
// Otherwise, default construct the underlying Atomic<Decayed>.
Atomic() : Atomic(DefaultCtorSelect()) {}
constexpr Atomic() : Atomic(DefaultCtorSelect()) {}
explicit Atomic(T value) : _value(decay(value)) {}
explicit constexpr Atomic(T value) : _value(decay(value)) {}
NONCOPYABLE(Atomic);