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