Bug 1837620 - Part 2: Remove baseline ICs that guard objects when the objects becomes...
[gecko.git] / mfbt / InitializedOnce.h
blobaac152df35a0f1d3bb8de453d110b843bd0f4c33
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // Class template for objects that can only be initialized once.
9 #ifndef mozilla_mfbt_initializedonce_h__
10 #define mozilla_mfbt_initializedonce_h__
12 #include "mozilla/Assertions.h"
13 #include "mozilla/Maybe.h"
15 #include <type_traits>
17 namespace mozilla {
19 namespace detail {
21 enum struct InitWhen { InConstructorOnly, LazyAllowed };
22 enum struct DestroyWhen { EarlyAllowed, InDestructorOnly };
24 namespace ValueCheckPolicies {
25 template <typename T>
26 struct AllowAnyValue {
27 constexpr static bool Check(const T& /*aValue*/) { return true; }
30 template <typename T>
31 struct ConvertsToTrue {
32 constexpr static bool Check(const T& aValue) {
33 return static_cast<bool>(aValue);
36 } // namespace ValueCheckPolicies
38 // A kind of mozilla::Maybe that can only be initialized and cleared once. It
39 // cannot be re-initialized. This is a more stateful than a const Maybe<T> in
40 // that it can be cleared, but much less stateful than a non-const Maybe<T>
41 // which could be reinitialized multiple times. Can only be used with const T
42 // to ensure that the contents cannot be modified either.
43 // TODO: Make constructors constexpr when Maybe's constructors are constexpr
44 // (Bug 1601336).
45 template <typename T, InitWhen InitWhenVal, DestroyWhen DestroyWhenVal,
46 template <typename> class ValueCheckPolicy =
47 ValueCheckPolicies::AllowAnyValue>
48 class InitializedOnce final {
49 static_assert(std::is_const_v<T>);
50 using MaybeType = Maybe<std::remove_const_t<T>>;
52 public:
53 using ValueType = T;
55 template <typename Dummy = void>
56 explicit constexpr InitializedOnce(
57 std::enable_if_t<InitWhenVal == InitWhen::LazyAllowed, Dummy>* =
58 nullptr) {}
60 // note: aArg0 is named separately here to disallow calling this with no
61 // arguments. The default constructor should only be available conditionally
62 // and is declared above.
63 template <typename Arg0, typename... Args>
64 explicit constexpr InitializedOnce(Arg0&& aArg0, Args&&... aArgs)
65 : mMaybe{Some(std::remove_const_t<T>{std::forward<Arg0>(aArg0),
66 std::forward<Args>(aArgs)...})} {
67 MOZ_ASSERT(ValueCheckPolicy<T>::Check(*mMaybe));
70 InitializedOnce(const InitializedOnce&) = delete;
71 InitializedOnce(InitializedOnce&& aOther) : mMaybe{std::move(aOther.mMaybe)} {
72 static_assert(DestroyWhenVal == DestroyWhen::EarlyAllowed);
73 #ifdef DEBUG
74 aOther.mWasReset = true;
75 #endif
77 InitializedOnce& operator=(const InitializedOnce&) = delete;
78 InitializedOnce& operator=(InitializedOnce&& aOther) {
79 static_assert(InitWhenVal == InitWhen::LazyAllowed &&
80 DestroyWhenVal == DestroyWhen::EarlyAllowed);
81 MOZ_ASSERT(!mWasReset);
82 MOZ_ASSERT(!mMaybe);
83 mMaybe.~MaybeType();
84 new (&mMaybe) MaybeType{std::move(aOther.mMaybe)};
85 #ifdef DEBUG
86 aOther.mWasReset = true;
87 #endif
88 return *this;
91 template <typename... Args, typename Dummy = void>
92 constexpr std::enable_if_t<InitWhenVal == InitWhen::LazyAllowed, Dummy> init(
93 Args&&... aArgs) {
94 MOZ_ASSERT(mMaybe.isNothing());
95 MOZ_ASSERT(!mWasReset);
96 mMaybe.emplace(std::remove_const_t<T>{std::forward<Args>(aArgs)...});
97 MOZ_ASSERT(ValueCheckPolicy<T>::Check(*mMaybe));
100 constexpr explicit operator bool() const { return isSome(); }
101 constexpr bool isSome() const { return mMaybe.isSome(); }
102 constexpr bool isNothing() const { return mMaybe.isNothing(); }
104 constexpr T& operator*() const { return *mMaybe; }
105 constexpr T* operator->() const { return mMaybe.operator->(); }
107 constexpr T& ref() const { return mMaybe.ref(); }
109 template <typename Dummy = void>
110 std::enable_if_t<DestroyWhenVal == DestroyWhen::EarlyAllowed, Dummy>
111 destroy() {
112 MOZ_ASSERT(mMaybe.isSome());
113 maybeDestroy();
116 template <typename Dummy = void>
117 std::enable_if_t<DestroyWhenVal == DestroyWhen::EarlyAllowed, Dummy>
118 maybeDestroy() {
119 mMaybe.reset();
120 #ifdef DEBUG
121 mWasReset = true;
122 #endif
125 template <typename Dummy = T>
126 std::enable_if_t<DestroyWhenVal == DestroyWhen::EarlyAllowed, Dummy>
127 release() {
128 MOZ_ASSERT(mMaybe.isSome());
129 auto res = std::move(mMaybe.ref());
130 destroy();
131 return res;
134 private:
135 MaybeType mMaybe;
136 #ifdef DEBUG
137 bool mWasReset = false;
138 #endif
141 template <typename T, InitWhen InitWhenVal, DestroyWhen DestroyWhenVal,
142 template <typename> class ValueCheckPolicy>
143 class LazyInitializer {
144 public:
145 explicit LazyInitializer(InitializedOnce<T, InitWhenVal, DestroyWhenVal,
146 ValueCheckPolicy>& aLazyInitialized)
147 : mLazyInitialized{aLazyInitialized} {}
149 template <typename U>
150 LazyInitializer& operator=(U&& aValue) {
151 mLazyInitialized.init(std::forward<U>(aValue));
152 return *this;
155 LazyInitializer(const LazyInitializer&) = delete;
156 LazyInitializer& operator=(const LazyInitializer&) = delete;
158 private:
159 InitializedOnce<T, InitWhenVal, DestroyWhenVal, ValueCheckPolicy>&
160 mLazyInitialized;
163 } // namespace detail
165 // The following *InitializedOnce* template aliases allow to declare class
166 // member variables that can only be initialized once, but maybe destroyed
167 // earlier explicitly than in the containing classes destructor.
168 // The intention is to restrict the possible state transitions for member
169 // variables that can almost be const, but not quite. This may be particularly
170 // useful for classes with a lot of members. Uses in other contexts, e.g. as
171 // local variables, are possible, but probably seldom useful. They can only be
172 // instantiated with a const element type. Any misuses that cannot be detected
173 // at compile time trigger a MOZ_ASSERT at runtime. Individually spelled out
174 // assertions for these aspects are not necessary, which may improve the
175 // readability of the code without impairing safety.
177 // The base variant InitializedOnce requires initialization in the constructor,
178 // but allows early destruction using destroy(), and allow move construction. It
179 // is similar to Maybe<const T> in some sense, but a Maybe<const T> could be
180 // reinitialized arbitrarily. InitializedOnce expresses the intent not to do
181 // this, and prohibits reinitialization.
183 // The Lazy* variants allow default construction, and can be initialized lazily
184 // using init() in that case, but it cannot be reinitialized either. They do not
185 // allow early destruction.
187 // The Lazy*EarlyDestructible variants allow lazy initialization, early
188 // destruction, move construction and move assignment. This should be used only
189 // when really required.
191 // The *NotNull variants only allow initialization with values that convert to
192 // bool as true. They are named NotNull because the typical use case is with
193 // (smart) pointer types, but any other type convertible to bool will also work
194 // analogously.
196 // There is no variant combining detail::DestroyWhen::InConstructorOnly with
197 // detail::DestroyWhen::InDestructorOnly because this would be equivalent to a
198 // const member.
200 // For special cases, e.g. requiring custom value check policies,
201 // detail::InitializedOnce might be instantiated directly, but be mindful when
202 // doing this.
204 template <typename T>
205 using InitializedOnce =
206 detail::InitializedOnce<T, detail::InitWhen::InConstructorOnly,
207 detail::DestroyWhen::EarlyAllowed>;
209 template <typename T>
210 using InitializedOnceNotNull =
211 detail::InitializedOnce<T, detail::InitWhen::InConstructorOnly,
212 detail::DestroyWhen::EarlyAllowed,
213 detail::ValueCheckPolicies::ConvertsToTrue>;
215 template <typename T>
216 using LazyInitializedOnce =
217 detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
218 detail::DestroyWhen::InDestructorOnly>;
220 template <typename T>
221 using LazyInitializedOnceNotNull =
222 detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
223 detail::DestroyWhen::InDestructorOnly,
224 detail::ValueCheckPolicies::ConvertsToTrue>;
226 template <typename T>
227 using LazyInitializedOnceEarlyDestructible =
228 detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
229 detail::DestroyWhen::EarlyAllowed>;
231 template <typename T>
232 using LazyInitializedOnceNotNullEarlyDestructible =
233 detail::InitializedOnce<T, detail::InitWhen::LazyAllowed,
234 detail::DestroyWhen::EarlyAllowed,
235 detail::ValueCheckPolicies::ConvertsToTrue>;
237 template <typename T, detail::InitWhen InitWhenVal,
238 detail::DestroyWhen DestroyWhenVal,
239 template <typename> class ValueCheckPolicy>
240 auto do_Init(detail::InitializedOnce<T, InitWhenVal, DestroyWhenVal,
241 ValueCheckPolicy>& aLazyInitialized) {
242 return detail::LazyInitializer(aLazyInitialized);
245 } // namespace mozilla
247 #endif