xref: /aosp_15_r20/external/pigweed/third_party/fuchsia/repo/sdk/lib/stdcompat/include/lib/stdcompat/internal/storage.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2019 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef LIB_STDCOMPAT_INTERNAL_STORAGE_H_
6 #define LIB_STDCOMPAT_INTERNAL_STORAGE_H_
7 
8 #include <cstddef>
9 #include <cstdint>
10 #include <limits>
11 #include <new>
12 #include <type_traits>
13 
14 #include "utility.h"
15 
16 namespace cpp17 {
17 namespace internal {
18 
19 // Type tag to select overloads based on type T.
20 template <typename T>
21 struct type_tag {
22   using type = T;
23 };
24 
25 // Type tag to select overloads based on index Index.
26 template <std::size_t Index>
27 struct index_tag {
28   static constexpr std::size_t index = Index;
29 };
30 
31 // Type tag to select trivial initialization.
32 enum trivial_init_t { trivial_init_v };
33 
34 // Type tag to select default initialization.
35 enum default_init_t { default_init_v };
36 
37 // Type tag to select conditional initialization.
38 enum maybe_init_t { maybe_init_v };
39 
40 // Represents the pair (T, Index) in the type system.
41 template <typename T, std::size_t Index>
42 struct type_index {};
43 
44 // Represents whether a type is trivially/non-trivially destructible.
45 enum class destructor_class {
46   trivial,
47   non_trivial,
48 };
49 
50 // Represents whether a type is trivially/non-trivially copyable.
51 enum class copy_class {
52   trivial,
53   non_trivial,
54 };
55 
56 // Represents whether a type is trivially/non-trivially movable.
57 enum class move_class {
58   trivial,
59   non_trivial,
60 };
61 
62 // Represents the full complement of move/copy/destruct classes for a type.
63 template <destructor_class DestructorClass, copy_class CopyClass, move_class MoveClass>
64 struct storage_class {};
65 
66 template <typename... Ts>
67 using make_storage_class =
68     storage_class<is_trivially_destructible_v<Ts...> ? destructor_class::trivial
69                                                      : destructor_class::non_trivial,
70                   is_trivially_copyable_v<Ts...> ? copy_class::trivial : copy_class::non_trivial,
71                   is_trivially_movable_v<Ts...> ? move_class::trivial : move_class::non_trivial>;
72 
73 // A trivial type for the empty alternative of union-based storage.
74 struct empty_type {};
75 
76 // Index type used to track the active variant. Tracking uses zero-based
77 // indices. Empty is denoted by the maximum representable value.
78 using index_type = std::size_t;
79 
80 // Index denoting that no user-specified variant is active. Take care not to
81 // ODR-use this value.
82 constexpr index_type empty_index = std::numeric_limits<index_type>::max();
83 
84 #ifdef NDEBUG
85 #define LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT __builtin_unreachable
86 #else
87 #define LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT __builtin_abort
88 #endif
89 
90 // Base type for lazy-initialized union storage types. This type implements a
91 // recursive union of the element types in Ts. Specializations handle the
92 // recursive and terminal cases, and the different storage requirements for
93 // trivially/non-trivially destructible types.
94 template <destructor_class, typename...>
95 union storage_base;
96 
97 // Non-trivial terminal case.
98 template <>
99 union storage_base<destructor_class::non_trivial, type_index<empty_type, empty_index>> {
100   storage_base() : empty{} {}
101 
102   template <typename... Args>
103   storage_base(type_tag<empty_type>, Args&&...) : empty{} {}
104   template <typename... Args>
105   storage_base(index_tag<empty_index>, Args&&...) : empty{} {}
106 
107   // Non-trivial destructor.
108   ~storage_base() {}
109 
110   storage_base(const storage_base&) = default;
111   storage_base(storage_base&&) = default;
112   storage_base& operator=(const storage_base&) = default;
113   storage_base& operator=(storage_base&&) = default;
114 
115   void construct_at(std::size_t index, const storage_base&) {
116     if (index == empty_index) {
117       new (&empty) empty_type{};
118     } else {
119       LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT();
120     }
121   }
122   void construct_at(std::size_t index, storage_base&&) {
123     if (index == empty_index) {
124       new (&empty) empty_type{};
125     } else {
126       LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT();
127     }
128   }
129 
130   void assign_at(std::size_t index, const storage_base& other) {
131     if (index == empty_index) {
132       empty = other.empty;
133     } else {
134       LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT();
135     }
136   }
137   void assign_at(std::size_t index, storage_base&& other) {
138     if (index == empty_index) {
139       empty = std::move(other.empty);
140     } else {
141       LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT();
142     }
143   }
144 
145   void swap_at(std::size_t index, storage_base& other) {
146     if (index == empty_index) {
147       using std::swap;
148       swap(empty, other.empty);
149     } else {
150       LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT();
151     }
152   }
153 
154   template <typename... Args>
155   std::size_t construct(type_tag<empty_type>, Args&&...) {
156     new (&empty) empty_type{};
157     return empty_index;
158   }
159   template <typename... Args>
160   std::size_t construct(index_tag<empty_index>, Args&&...) {
161     new (&empty) empty_type{};
162     return empty_index;
163   }
164 
165   void reset(std::size_t index) {
166     if (index == empty_index) {
167       empty.empty_type::~empty_type();
168     } else {
169       LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT();
170     }
171   }
172 
173   empty_type& get(type_tag<empty_type>) { return empty; }
174   const empty_type& get(type_tag<empty_type>) const { return empty; }
175   empty_type& get(index_tag<empty_index>) { return empty; }
176   const empty_type& get(index_tag<empty_index>) const { return empty; }
177 
178   std::size_t index(type_tag<empty_type>) const { return empty_index; }
179 
180   template <typename V>
181   bool visit(std::size_t, V&&) {
182     return false;
183   }
184   template <typename V>
185   bool visit(std::size_t, V&&) const {
186     return false;
187   }
188 
189   empty_type empty;
190 };
191 
192 // Trivial terminal case.
193 template <>
194 union storage_base<destructor_class::trivial, type_index<empty_type, empty_index>> {
195   constexpr storage_base() : empty{} {}
196 
197   template <typename... Args>
198   constexpr storage_base(type_tag<empty_type>, Args&&...) : empty{} {}
199   template <typename... Args>
200   constexpr storage_base(index_tag<empty_index>, Args&&...) : empty{} {}
201 
202   // Trivial destructor.
203   ~storage_base() = default;
204 
205   constexpr storage_base(const storage_base&) = default;
206   constexpr storage_base(storage_base&&) = default;
207   constexpr storage_base& operator=(const storage_base&) = default;
208   constexpr storage_base& operator=(storage_base&&) = default;
209 
210   constexpr void construct_at(std::size_t index, const storage_base&) {
211     if (index == empty_index) {
212       new (&empty) empty_type{};
213     } else {
214       LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT();
215     }
216   }
217   constexpr void construct_at(std::size_t index, storage_base&&) {
218     if (index == empty_index) {
219       new (&empty) empty_type{};
220     } else {
221       LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT();
222     }
223   }
224 
225   constexpr void assign_at(std::size_t index, const storage_base& other) {
226     if (index == empty_index) {
227       empty = other.empty;
228     } else {
229       LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT();
230     }
231   }
232   constexpr void assign_at(std::size_t index, storage_base&& other) {
233     if (index == empty_index) {
234       empty = std::move(other.empty);
235     } else {
236       LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT();
237     }
238   }
239 
240   constexpr void swap_at(std::size_t index, storage_base& other) {
241     if (index == empty_index) {
242       using std::swap;
243       swap(empty, other.empty);
244     } else {
245       LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT();
246     }
247   }
248 
249   template <typename... Args>
250   constexpr std::size_t construct(type_tag<empty_type>, Args&&...) {
251     new (&empty) empty_type{};
252     return empty_index;
253   }
254   template <typename... Args>
255   constexpr std::size_t construct(index_tag<empty_index>, Args&&...) {
256     new (&empty) empty_type{};
257     return empty_index;
258   }
259 
260   constexpr void reset(std::size_t index) {
261     if (index == empty_index) {
262       empty.empty_type::~empty_type();
263     } else {
264       LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT();
265     }
266   }
267 
268   constexpr empty_type& get(type_tag<empty_type>) { return empty; }
269   constexpr const empty_type& get(type_tag<empty_type>) const { return empty; }
270   constexpr empty_type& get(index_tag<empty_index>) { return empty; }
271   constexpr const empty_type& get(index_tag<empty_index>) const { return empty; }
272 
273   constexpr std::size_t index(type_tag<empty_type>) const { return empty_index; }
274 
275   template <typename V>
276   constexpr bool visit(std::size_t, V&&) {
277     return false;
278   }
279   template <typename V>
280   constexpr bool visit(std::size_t, V&&) const {
281     return false;
282   }
283 
284   empty_type empty;
285 };
286 
287 template <typename T, std::size_t Index, typename... Ts, std::size_t... Is>
288 union storage_base<destructor_class::non_trivial, type_index<T, Index>, type_index<Ts, Is>...> {
289   storage_base() : empty{} {}
290 
291   template <typename... Args>
292   storage_base(type_tag<T>, Args&&... args) : value(std::forward<Args>(args)...) {}
293   template <typename... Args>
294   storage_base(index_tag<Index>, Args&&... args) : value(std::forward<Args>(args)...) {}
295 
296   template <typename U, typename... Args>
297   storage_base(type_tag<U>, Args&&... args) : rest(type_tag<U>{}, std::forward<Args>(args)...) {}
298   template <std::size_t OtherIndex, typename... Args>
299   storage_base(index_tag<OtherIndex>, Args&&... args)
300       : rest(index_tag<OtherIndex>{}, std::forward<Args>(args)...) {}
301 
302   // Non-trivial destructor.
303   ~storage_base() {}
304 
305   // Trival copy/move construction and assignment.
306   storage_base(const storage_base&) = default;
307   storage_base(storage_base&&) = default;
308   storage_base& operator=(const storage_base&) = default;
309   storage_base& operator=(storage_base&&) = default;
310 
311   void construct_at(std::size_t index, const storage_base& other) {
312     if (index == Index) {
313       new (&value) T{other.value};
314     } else {
315       rest.construct_at(index, other.rest);
316     }
317   }
318   void construct_at(std::size_t index, storage_base&& other) {
319     if (index == Index) {
320       new (&value) T{std::move(other.value)};
321     } else {
322       rest.construct_at(index, std::move(other.rest));
323     }
324   }
325 
326   void assign_at(std::size_t index, const storage_base& other) {
327     if (index == Index) {
328       value = other.value;
329     } else {
330       rest.assign_at(index, other.rest);
331     }
332   }
333   void assign_at(std::size_t index, storage_base&& other) {
334     if (index == Index) {
335       value = std::move(other.value);
336     } else {
337       rest.assign_at(index, std::move(other.rest));
338     }
339   }
340 
341   void swap_at(std::size_t index, storage_base& other) {
342     if (index == Index) {
343       using std::swap;
344       swap(value, other.value);
345     } else {
346       rest.swap_at(index, other.rest);
347     }
348   }
349 
350   template <typename... Args>
351   std::size_t construct(type_tag<T>, Args&&... args) {
352     new (&value) T(std::forward<Args>(args)...);
353     return Index;
354   }
355   template <typename U, typename... Args>
356   std::size_t construct(type_tag<U>, Args&&... args) {
357     return rest.construct(type_tag<U>{}, std::forward<Args>(args)...);
358   }
359   template <typename... Args>
360   std::size_t construct(index_tag<Index>, Args&&... args) {
361     new (&value) T(std::forward<Args>(args)...);
362     return Index;
363   }
364   template <std::size_t OtherIndex, typename... Args>
365   std::size_t construct(index_tag<OtherIndex>, Args&&... args) {
366     return rest.construct(index_tag<OtherIndex>{}, std::forward<Args>(args)...);
367   }
368 
369   void reset(std::size_t index) {
370     if (index == Index) {
371       value.~T();
372     } else {
373       rest.reset(index);
374     }
375   }
376 
377   T& get(type_tag<T>) { return value; }
378   const T& get(type_tag<T>) const { return value; }
379   template <typename U>
380   U& get(type_tag<U>) {
381     return rest.get(type_tag<U>{});
382   }
383   template <typename U>
384   const U& get(type_tag<U>) const {
385     return rest.get(type_tag<U>{});
386   }
387   T& get(index_tag<Index>) { return value; }
388   const T& get(index_tag<Index>) const { return value; }
389   template <std::size_t OtherIndex>
390   auto& get(index_tag<OtherIndex>) {
391     return rest.get(index_tag<OtherIndex>{});
392   }
393   template <std::size_t OtherIndex>
394   const auto& get(index_tag<OtherIndex>) const {
395     return rest.get(index_tag<OtherIndex>{});
396   }
397 
398   std::size_t index(type_tag<T>) const { return Index; }
399   template <typename U>
400   std::size_t index(type_tag<U>) const {
401     return rest.index(type_tag<U>{});
402   }
403 
404   template <typename V>
405   bool visit(std::size_t index, V&& visitor) {
406     if (index == Index) {
407       std::forward<V>(visitor)(type_tag<T>{}, index_tag<Index>{}, this);
408       return true;
409     } else {
410       return rest.visit(index, std::forward<V>(visitor));
411     }
412   }
413   template <typename V>
414   bool visit(std::size_t index, V&& visitor) const {
415     if (index == Index) {
416       std::forward<V>(visitor)(type_tag<T>{}, index_tag<Index>{}, this);
417       return true;
418     } else {
419       return rest.visit(index, std::forward<V>(visitor));
420     }
421   }
422 
423   empty_type empty;
424   T value;
425   storage_base<destructor_class::non_trivial, type_index<Ts, Is>...> rest;
426 };
427 
428 template <typename T, std::size_t Index, typename... Ts, std::size_t... Is>
429 union storage_base<destructor_class::trivial, type_index<T, Index>, type_index<Ts, Is>...> {
430   constexpr storage_base() : empty{} {}
431 
432   template <typename... Args>
433   constexpr storage_base(type_tag<T>, Args&&... args) : value(std::forward<Args>(args)...) {}
434   template <typename... Args>
435   constexpr storage_base(index_tag<Index>, Args&&... args) : value(std::forward<Args>(args)...) {}
436 
437   template <typename U, typename... Args>
438   constexpr storage_base(type_tag<U>, Args&&... args)
439       : rest(type_tag<U>{}, std::forward<Args>(args)...) {}
440   template <std::size_t OtherIndex, typename... Args>
441   constexpr storage_base(index_tag<OtherIndex>, Args&&... args)
442       : rest(index_tag<OtherIndex>{}, std::forward<Args>(args)...) {}
443 
444   // Trivial destructor.
445   ~storage_base() = default;
446 
447   // Trival copy/move construction and assignment.
448   constexpr storage_base(const storage_base&) = default;
449   constexpr storage_base(storage_base&&) = default;
450   constexpr storage_base& operator=(const storage_base&) = default;
451   constexpr storage_base& operator=(storage_base&&) = default;
452 
453   constexpr void construct_at(std::size_t index, const storage_base& other) {
454     if (index == Index) {
455       new (&value) T{other.value};
456     } else {
457       rest.construct_at(index, other.rest);
458     }
459   }
460   constexpr void construct_at(std::size_t index, storage_base&& other) {
461     if (index == Index) {
462       new (&value) T{std::move(other.value)};
463     } else {
464       rest.construct_at(index, std::move(other.rest));
465     }
466   }
467 
468   constexpr void assign_at(std::size_t index, const storage_base& other) {
469     if (index == Index) {
470       value = other.value;
471     } else {
472       rest.assign_at(index, other.rest);
473     }
474   }
475   constexpr void assign_at(std::size_t index, storage_base&& other) {
476     if (index == Index) {
477       value = std::move(other.value);
478     } else {
479       rest.assign_at(index, std::move(other.rest));
480     }
481   }
482 
483   constexpr void swap_at(std::size_t index, storage_base& other) {
484     if (index == Index) {
485       using std::swap;
486       swap(value, other.value);
487     } else {
488       rest.swap_at(index, other.rest);
489     }
490   }
491 
492   template <typename... Args>
493   constexpr std::size_t construct(type_tag<T>, Args&&... args) {
494     new (&value) T(std::forward<Args>(args)...);
495     return Index;
496   }
497   template <typename U, typename... Args>
498   constexpr std::size_t construct(type_tag<U>, Args&&... args) {
499     return rest.construct(type_tag<U>{}, std::forward<Args>(args)...);
500   }
501   template <typename... Args>
502   constexpr std::size_t construct(index_tag<Index>, Args&&... args) {
503     new (&value) T(std::forward<Args>(args)...);
504     return Index;
505   }
506   template <std::size_t OtherIndex, typename... Args>
507   constexpr std::size_t construct(index_tag<OtherIndex>, Args&&... args) {
508     return rest.construct(index_tag<OtherIndex>{}, std::forward<Args>(args)...);
509   }
510 
511   constexpr void reset(std::size_t) {}
512 
513   constexpr T& get(type_tag<T>) { return value; }
514   constexpr const T& get(type_tag<T>) const { return value; }
515   template <typename U>
516   constexpr U& get(type_tag<U>) {
517     return rest.get(type_tag<U>{});
518   }
519   template <typename U>
520   constexpr const U& get(type_tag<U>) const {
521     return rest.get(type_tag<U>{});
522   }
523   constexpr T& get(index_tag<Index>) { return value; }
524   constexpr const T& get(index_tag<Index>) const { return value; }
525   template <std::size_t OtherIndex>
526   constexpr auto& get(index_tag<OtherIndex>) {
527     return rest.get(index_tag<OtherIndex>{});
528   }
529   template <std::size_t OtherIndex>
530   constexpr const auto& get(index_tag<OtherIndex>) const {
531     return rest.get(index_tag<OtherIndex>{});
532   }
533 
534   constexpr std::size_t index(type_tag<T>) const { return Index; }
535   template <typename U>
536   constexpr std::size_t index(type_tag<U>) const {
537     return rest.index(type_tag<U>{});
538   }
539 
540   template <typename V>
541   constexpr bool visit(std::size_t index, V&& visitor) {
542     if (index == Index) {
543       std::forward<V>(visitor)(type_tag<T>{}, index_tag<Index>{}, this);
544       return true;
545     } else {
546       return rest.visit(index, std::forward<V>(visitor));
547     }
548   }
549   template <typename V>
550   constexpr bool visit(std::size_t index, V&& visitor) const {
551     if (index == Index) {
552       std::forward<V>(visitor)(type_tag<T>{}, index_tag<Index>{}, this);
553       return true;
554     } else {
555       return rest.visit(index, std::forward<V>(visitor));
556     }
557   }
558 
559   empty_type empty;
560   T value;
561   storage_base<destructor_class::trivial, type_index<Ts, Is>...> rest;
562 };
563 
564 // Lazy-initialized union storage type that tracks the index of the active
565 // variant.
566 template <destructor_class, typename...>
567 class indexed_storage;
568 
569 template <destructor_class DestructorClass, typename... Ts, std::size_t... Is>
570 class indexed_storage<DestructorClass, type_index<Ts, Is>...> {
571  private:
572   using base_type =
573       storage_base<DestructorClass, type_index<Ts, Is>..., type_index<empty_type, empty_index>>;
574 
575  public:
576   static constexpr bool nothrow_default_constructible =
577       std::is_nothrow_default_constructible<first_t<Ts...>>::value;
578   static constexpr bool nothrow_move_constructible =
579       conjunction_v<std::is_nothrow_move_constructible<Ts>...>;
580   static constexpr bool nothrow_move_assignable =
581       conjunction_v<std::is_nothrow_move_assignable<Ts>...>;
582 
583   constexpr indexed_storage() = default;
584 
585   constexpr indexed_storage(trivial_init_t) : indexed_storage{} {}
586 
587   constexpr indexed_storage(default_init_t) : index_{0}, base_{index_tag<0>{}} {}
588 
589   // Only used by trivial copy/move types.
590   constexpr indexed_storage(const indexed_storage& other) = default;
591   constexpr indexed_storage& operator=(const indexed_storage& other) = default;
592   constexpr indexed_storage(indexed_storage&& other) = default;
593   constexpr indexed_storage& operator=(indexed_storage&& other) = default;
594 
595   template <typename T, typename... Args>
596   constexpr indexed_storage(type_tag<T>, Args&&... args)
597       : base_(type_tag<T>{}, std::forward<Args>(args)...) {
598     index_ = base_.index(type_tag<T>{});
599   }
600   template <std::size_t Index, typename... Args>
601   constexpr indexed_storage(index_tag<Index>, Args&&... args)
602       : index_{Index}, base_(index_tag<Index>{}, std::forward<Args>(args)...) {}
603 
604   constexpr indexed_storage(maybe_init_t, const indexed_storage& other)
605       : index_{other.index()}, base_{} {
606     base_.construct_at(other.index(), other.base_);
607   }
608   constexpr indexed_storage(maybe_init_t, indexed_storage&& other)
609       : index_{other.index()}, base_{} {
610     base_.construct_at(other.index(), std::move(other.base_));
611   }
612 
613   ~indexed_storage() = default;
614 
615   constexpr index_type index() const { return index_; }
616   constexpr bool is_empty() const { return index() == empty_index; }
617   template <typename T>
618   constexpr bool has_value(type_tag<T>) const {
619     return index() == base_.index(type_tag<T>{});
620   }
621   template <std::size_t Index>
622   constexpr bool has_value(index_tag<Index>) const {
623     return index() == Index;
624   }
625 
626   template <typename T>
627   constexpr auto& get(type_tag<T>) {
628     return base_.get(type_tag<T>{});
629   }
630   template <typename T>
631   constexpr const auto& get(type_tag<T>) const {
632     return base_.get(type_tag<T>{});
633   }
634   template <std::size_t Index>
635   constexpr auto& get(index_tag<Index>) {
636     return base_.get(index_tag<Index>{});
637   }
638   template <std::size_t Index>
639   constexpr const auto& get(index_tag<Index>) const {
640     return base_.get(index_tag<Index>{});
641   }
642 
643   template <typename T, typename... Args>
644   constexpr void construct(type_tag<T>, Args&&... args) {
645     index_ = base_.construct(type_tag<T>{}, std::forward<Args>(args)...);
646   }
647   template <std::size_t Index, typename... Args>
648   constexpr void construct(index_tag<Index>, Args&&... args) {
649     index_ = base_.construct(index_tag<Index>{}, std::forward<Args>(args)...);
650   }
651 
652   constexpr void assign(const indexed_storage& other) {
653     if (index() == other.index()) {
654       base_.assign_at(index_, other.base_);
655     } else {
656       reset();
657       base_.construct_at(other.index_, other.base_);
658       index_ = other.index_;
659     }
660   }
661   constexpr void assign(indexed_storage&& other) {
662     if (index() == other.index()) {
663       base_.assign_at(index_, std::move(other.base_));
664     } else {
665       reset();
666       base_.construct_at(other.index_, std::move(other.base_));
667       index_ = other.index_;
668     }
669   }
670 
671   template <typename V>
672   constexpr bool visit(V&& visitor) {
673     return base_.visit(index_, std::forward<V>(visitor));
674   }
675   template <typename V>
676   constexpr bool visit(V&& visitor) const {
677     return base_.visit(index_, std::forward<V>(visitor));
678   }
679 
680   constexpr void swap(indexed_storage& other) {
681     if (index() == other.index()) {
682       // Swap directly when the variants are the same, including empty.
683       base_.swap_at(index_, other.base_);
684     } else {
685       // Swap when the variants are different, including one being empty.
686       // This approach avoids GCC -Wmaybe-uninitialized warnings by
687       // initializing and accessing |temp| unconditionally within a
688       // conditional scope. The alternative, using the maybe_init_t
689       // constructor confuses GCC because it doesn't understand that the
690       // index checks prevent uninitialized access.
691       auto do_swap = [](indexed_storage& a, indexed_storage& b) {
692         return a.base_.visit(a.index_, [&a, &b](auto, auto index_tag_v, auto* element) {
693           indexed_storage temp{index_tag_v, std::move(element->value)};
694           a.reset();
695 
696           a.base_.construct_at(b.index_, std::move(b.base_));
697           a.index_ = b.index_;
698           b.reset();
699 
700           b.base_.construct_at(temp.index_, std::move(temp.base_));
701           b.index_ = temp.index_;
702           temp.reset();
703         });
704       };
705 
706       // The visitor above returns false when the first argument is empty
707       // and no action is taken. In that case, the other order is tried to
708       // complete the half-empty swap.
709       do_swap(*this, other) || do_swap(other, *this);
710     }
711   }
712 
713   // Destroys the active variant. Does nothing when already empty.
714   constexpr void reset() {
715     base_.reset(index_);
716     index_ = empty_index;
717   }
718 
719  private:
720   index_type index_{empty_index};
721   base_type base_;
722 };
723 
724 // Internal variant storage type used by cpp17::optional and cpp17::variant.
725 // Specializations of this type select trivial vs. non-trivial copy/move
726 // construction, assignment operators, and destructor based on the storage class
727 // of the types in Ts.
728 template <typename StorageClass, typename... Ts>
729 struct storage;
730 
731 template <typename... Ts, std::size_t... Is>
732 struct storage<storage_class<destructor_class::trivial, copy_class::trivial, move_class::trivial>,
733                type_index<Ts, Is>...>
734     : indexed_storage<destructor_class::trivial, type_index<Ts, Is>...> {
735   using base_type = indexed_storage<destructor_class::trivial, type_index<Ts, Is>...>;
736   using base_type::base_type;
737   constexpr storage() = default;
738 };
739 
740 template <typename... Ts, std::size_t... Is>
741 struct storage<
742     storage_class<destructor_class::trivial, copy_class::non_trivial, move_class::trivial>,
743     type_index<Ts, Is>...> : indexed_storage<destructor_class::trivial, type_index<Ts, Is>...> {
744   using base_type = indexed_storage<destructor_class::trivial, type_index<Ts, Is>...>;
745   using base_type::base_type;
746 
747   ~storage() = default;
748   constexpr storage() = default;
749 
750   constexpr storage(const storage& other) : base_type{maybe_init_v, other} {}
751 
752   constexpr storage& operator=(const storage& other) {
753     this->assign(other);
754     return *this;
755   }
756 
757   constexpr storage(storage&&) = default;
758   constexpr storage& operator=(storage&&) = default;
759 };
760 
761 template <typename... Ts, std::size_t... Is>
762 struct storage<
763     storage_class<destructor_class::trivial, copy_class::trivial, move_class::non_trivial>,
764     type_index<Ts, Is>...> : indexed_storage<destructor_class::trivial, type_index<Ts, Is>...> {
765   using base_type = indexed_storage<destructor_class::trivial, type_index<Ts, Is>...>;
766   using base_type::base_type;
767 
768   ~storage() = default;
769   constexpr storage() = default;
770   constexpr storage(const storage&) = default;
771   constexpr storage& operator=(const storage&) = default;
772 
773   constexpr storage(storage&& other) noexcept(base_type::nothrow_move_constructible)
774       : base_type{maybe_init_v, std::move(other)} {}
775 
776   constexpr storage& operator=(storage&& other) noexcept(base_type::nothrow_move_assignable) {
777     this->assign(std::move(other));
778     return *this;
779   }
780 };
781 
782 template <typename... Ts, std::size_t... Is>
783 struct storage<
784     storage_class<destructor_class::trivial, copy_class::non_trivial, move_class::non_trivial>,
785     type_index<Ts, Is>...> : indexed_storage<destructor_class::trivial, type_index<Ts, Is>...> {
786   using base_type = indexed_storage<destructor_class::trivial, type_index<Ts, Is>...>;
787   using base_type::base_type;
788 
789   ~storage() = default;
790   constexpr storage() = default;
791 
792   constexpr storage(const storage& other) : base_type{maybe_init_v, other} {}
793 
794   constexpr storage& operator=(const storage& other) {
795     this->assign(other);
796     return *this;
797   }
798 
799   constexpr storage(storage&& other) noexcept(base_type::nothrow_move_constructible)
800       : base_type{maybe_init_v, std::move(other)} {}
801 
802   constexpr storage& operator=(storage&& other) noexcept(base_type::nothrow_move_assignable) {
803     this->assign(std::move(other));
804     return *this;
805   }
806 };
807 
808 // Specialization for non-trivially movable/copyable types. Types with a non-
809 // trivial destructor are always non-trivially movable/copyable.
810 template <copy_class CopyClass, move_class MoveClass, typename... Ts, std::size_t... Is>
811 struct storage<storage_class<destructor_class::non_trivial, CopyClass, MoveClass>,
812                type_index<Ts, Is>...>
813     : indexed_storage<destructor_class::non_trivial, type_index<Ts, Is>...> {
814   using base_type = indexed_storage<destructor_class::non_trivial, type_index<Ts, Is>...>;
815   using base_type::base_type;
816 
817   ~storage() { this->reset(); }
818 
819   constexpr storage() = default;
820 
821   constexpr storage(const storage& other) : base_type{maybe_init_v, other} {}
822 
823   constexpr storage& operator=(const storage& other) {
824     this->assign(other);
825     return *this;
826   }
827 
828   constexpr storage(storage&& other) noexcept(base_type::nothrow_move_constructible)
829       : base_type{maybe_init_v, std::move(other)} {}
830 
831   constexpr storage& operator=(storage&& other) noexcept(base_type::nothrow_move_assignable) {
832     this->assign(std::move(other));
833     return *this;
834   }
835 };
836 
837 template <typename... Ts, std::size_t... Is>
838 constexpr auto make_storage(std::index_sequence<Is...>) {
839   return storage<make_storage_class<Ts...>, type_index<Ts, Is>...>{};
840 }
841 
842 template <typename... Ts>
843 using storage_type = decltype(make_storage<Ts...>(std::index_sequence_for<Ts...>{}));
844 
845 }  // namespace internal
846 }  // namespace cpp17
847 
848 #endif  // LIB_STDCOMPAT_INTERNAL_STORAGE_H_
849