Home | History | Annotate | Line # | Download | only in ADT
      1 //===- Optional.h - Simple variant for passing optional values --*- C++ -*-===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 //
      9 //  This file provides Optional, a template class modeled in the spirit of
     10 //  OCaml's 'opt' variant.  The idea is to strongly type whether or not
     11 //  a value can be optional.
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 #ifndef LLVM_ADT_OPTIONAL_H
     16 #define LLVM_ADT_OPTIONAL_H
     17 
     18 #include "llvm/ADT/Hashing.h"
     19 #include "llvm/ADT/None.h"
     20 #include "llvm/ADT/STLForwardCompat.h"
     21 #include "llvm/Support/Compiler.h"
     22 #include "llvm/Support/type_traits.h"
     23 #include <cassert>
     24 #include <memory>
     25 #include <new>
     26 #include <utility>
     27 
     28 namespace llvm {
     29 
     30 class raw_ostream;
     31 
     32 namespace optional_detail {
     33 
     34 /// Storage for any type.
     35 //
     36 // The specialization condition intentionally uses
     37 // llvm::is_trivially_copy_constructible instead of
     38 // std::is_trivially_copy_constructible.  GCC versions prior to 7.4 may
     39 // instantiate the copy constructor of `T` when
     40 // std::is_trivially_copy_constructible is instantiated.  This causes
     41 // compilation to fail if we query the trivially copy constructible property of
     42 // a class which is not copy constructible.
     43 //
     44 // The current implementation of OptionalStorage insists that in order to use
     45 // the trivial specialization, the value_type must be trivially copy
     46 // constructible and trivially copy assignable due to =default implementations
     47 // of the copy/move constructor/assignment.  It does not follow that this is
     48 // necessarily the case std::is_trivially_copyable is true (hence the expanded
     49 // specialization condition).
     50 //
     51 // The move constructible / assignable conditions emulate the remaining behavior
     52 // of std::is_trivially_copyable.
     53 template <typename T, bool = (llvm::is_trivially_copy_constructible<T>::value &&
     54                               std::is_trivially_copy_assignable<T>::value &&
     55                               (std::is_trivially_move_constructible<T>::value ||
     56                                !std::is_move_constructible<T>::value) &&
     57                               (std::is_trivially_move_assignable<T>::value ||
     58                                !std::is_move_assignable<T>::value))>
     59 class OptionalStorage {
     60   union {
     61     char empty;
     62     T value;
     63   };
     64   bool hasVal;
     65 
     66 public:
     67   ~OptionalStorage() { reset(); }
     68 
     69   constexpr OptionalStorage() noexcept : empty(), hasVal(false) {}
     70 
     71   constexpr OptionalStorage(OptionalStorage const &other) : OptionalStorage() {
     72     if (other.hasValue()) {
     73       emplace(other.value);
     74     }
     75   }
     76   constexpr OptionalStorage(OptionalStorage &&other) : OptionalStorage() {
     77     if (other.hasValue()) {
     78       emplace(std::move(other.value));
     79     }
     80   }
     81 
     82   template <class... Args>
     83   constexpr explicit OptionalStorage(in_place_t, Args &&... args)
     84       : value(std::forward<Args>(args)...), hasVal(true) {}
     85 
     86   void reset() noexcept {
     87     if (hasVal) {
     88       value.~T();
     89       hasVal = false;
     90     }
     91   }
     92 
     93   constexpr bool hasValue() const noexcept { return hasVal; }
     94 
     95   T &getValue() LLVM_LVALUE_FUNCTION noexcept {
     96     assert(hasVal);
     97     return value;
     98   }
     99   constexpr T const &getValue() const LLVM_LVALUE_FUNCTION noexcept {
    100     assert(hasVal);
    101     return value;
    102   }
    103 #if LLVM_HAS_RVALUE_REFERENCE_THIS
    104   T &&getValue() && noexcept {
    105     assert(hasVal);
    106     return std::move(value);
    107   }
    108 #endif
    109 
    110   template <class... Args> void emplace(Args &&... args) {
    111     reset();
    112     ::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...);
    113     hasVal = true;
    114   }
    115 
    116   OptionalStorage &operator=(T const &y) {
    117     if (hasValue()) {
    118       value = y;
    119     } else {
    120       ::new ((void *)std::addressof(value)) T(y);
    121       hasVal = true;
    122     }
    123     return *this;
    124   }
    125   OptionalStorage &operator=(T &&y) {
    126     if (hasValue()) {
    127       value = std::move(y);
    128     } else {
    129       ::new ((void *)std::addressof(value)) T(std::move(y));
    130       hasVal = true;
    131     }
    132     return *this;
    133   }
    134 
    135   OptionalStorage &operator=(OptionalStorage const &other) {
    136     if (other.hasValue()) {
    137       if (hasValue()) {
    138         value = other.value;
    139       } else {
    140         ::new ((void *)std::addressof(value)) T(other.value);
    141         hasVal = true;
    142       }
    143     } else {
    144       reset();
    145     }
    146     return *this;
    147   }
    148 
    149   OptionalStorage &operator=(OptionalStorage &&other) {
    150     if (other.hasValue()) {
    151       if (hasValue()) {
    152         value = std::move(other.value);
    153       } else {
    154         ::new ((void *)std::addressof(value)) T(std::move(other.value));
    155         hasVal = true;
    156       }
    157     } else {
    158       reset();
    159     }
    160     return *this;
    161   }
    162 };
    163 
    164 template <typename T> class OptionalStorage<T, true> {
    165   union {
    166     char empty;
    167     T value;
    168   };
    169   bool hasVal = false;
    170 
    171 public:
    172   ~OptionalStorage() = default;
    173 
    174   constexpr OptionalStorage() noexcept : empty{} {}
    175 
    176   constexpr OptionalStorage(OptionalStorage const &other) = default;
    177   constexpr OptionalStorage(OptionalStorage &&other) = default;
    178 
    179   OptionalStorage &operator=(OptionalStorage const &other) = default;
    180   OptionalStorage &operator=(OptionalStorage &&other) = default;
    181 
    182   template <class... Args>
    183   constexpr explicit OptionalStorage(in_place_t, Args &&... args)
    184       : value(std::forward<Args>(args)...), hasVal(true) {}
    185 
    186   void reset() noexcept {
    187     if (hasVal) {
    188       value.~T();
    189       hasVal = false;
    190     }
    191   }
    192 
    193   constexpr bool hasValue() const noexcept { return hasVal; }
    194 
    195   T &getValue() LLVM_LVALUE_FUNCTION noexcept {
    196     assert(hasVal);
    197     return value;
    198   }
    199   constexpr T const &getValue() const LLVM_LVALUE_FUNCTION noexcept {
    200     assert(hasVal);
    201     return value;
    202   }
    203 #if LLVM_HAS_RVALUE_REFERENCE_THIS
    204   T &&getValue() && noexcept {
    205     assert(hasVal);
    206     return std::move(value);
    207   }
    208 #endif
    209 
    210   template <class... Args> void emplace(Args &&... args) {
    211     reset();
    212     ::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...);
    213     hasVal = true;
    214   }
    215 
    216   OptionalStorage &operator=(T const &y) {
    217     if (hasValue()) {
    218       value = y;
    219     } else {
    220       ::new ((void *)std::addressof(value)) T(y);
    221       hasVal = true;
    222     }
    223     return *this;
    224   }
    225   OptionalStorage &operator=(T &&y) {
    226     if (hasValue()) {
    227       value = std::move(y);
    228     } else {
    229       ::new ((void *)std::addressof(value)) T(std::move(y));
    230       hasVal = true;
    231     }
    232     return *this;
    233   }
    234 };
    235 
    236 } // namespace optional_detail
    237 
    238 template <typename T> class Optional {
    239   optional_detail::OptionalStorage<T> Storage;
    240 
    241 public:
    242   using value_type = T;
    243 
    244   constexpr Optional() {}
    245   constexpr Optional(NoneType) {}
    246 
    247   constexpr Optional(const T &y) : Storage(in_place, y) {}
    248   constexpr Optional(const Optional &O) = default;
    249 
    250   constexpr Optional(T &&y) : Storage(in_place, std::move(y)) {}
    251   constexpr Optional(Optional &&O) = default;
    252 
    253   template <typename... ArgTypes>
    254   constexpr Optional(in_place_t, ArgTypes &&...Args)
    255       : Storage(in_place, std::forward<ArgTypes>(Args)...) {}
    256 
    257   Optional &operator=(T &&y) {
    258     Storage = std::move(y);
    259     return *this;
    260   }
    261   Optional &operator=(Optional &&O) = default;
    262 
    263   /// Create a new object by constructing it in place with the given arguments.
    264   template <typename... ArgTypes> void emplace(ArgTypes &&... Args) {
    265     Storage.emplace(std::forward<ArgTypes>(Args)...);
    266   }
    267 
    268   static constexpr Optional create(const T *y) {
    269     return y ? Optional(*y) : Optional();
    270   }
    271 
    272   Optional &operator=(const T &y) {
    273     Storage = y;
    274     return *this;
    275   }
    276   Optional &operator=(const Optional &O) = default;
    277 
    278   void reset() { Storage.reset(); }
    279 
    280   constexpr const T *getPointer() const { return &Storage.getValue(); }
    281   T *getPointer() { return &Storage.getValue(); }
    282   constexpr const T &getValue() const LLVM_LVALUE_FUNCTION {
    283     return Storage.getValue();
    284   }
    285   T &getValue() LLVM_LVALUE_FUNCTION { return Storage.getValue(); }
    286 
    287   constexpr explicit operator bool() const { return hasValue(); }
    288   constexpr bool hasValue() const { return Storage.hasValue(); }
    289   constexpr const T *operator->() const { return getPointer(); }
    290   T *operator->() { return getPointer(); }
    291   constexpr const T &operator*() const LLVM_LVALUE_FUNCTION {
    292     return getValue();
    293   }
    294   T &operator*() LLVM_LVALUE_FUNCTION { return getValue(); }
    295 
    296   template <typename U>
    297   constexpr T getValueOr(U &&value) const LLVM_LVALUE_FUNCTION {
    298     return hasValue() ? getValue() : std::forward<U>(value);
    299   }
    300 
    301   /// Apply a function to the value if present; otherwise return None.
    302   template <class Function>
    303   auto map(const Function &F) const LLVM_LVALUE_FUNCTION
    304       -> Optional<decltype(F(getValue()))> {
    305     if (*this) return F(getValue());
    306     return None;
    307   }
    308 
    309 #if LLVM_HAS_RVALUE_REFERENCE_THIS
    310   T &&getValue() && { return std::move(Storage.getValue()); }
    311   T &&operator*() && { return std::move(Storage.getValue()); }
    312 
    313   template <typename U>
    314   T getValueOr(U &&value) && {
    315     return hasValue() ? std::move(getValue()) : std::forward<U>(value);
    316   }
    317 
    318   /// Apply a function to the value if present; otherwise return None.
    319   template <class Function>
    320   auto map(const Function &F) &&
    321       -> Optional<decltype(F(std::move(*this).getValue()))> {
    322     if (*this) return F(std::move(*this).getValue());
    323     return None;
    324   }
    325 #endif
    326 };
    327 
    328 template <class T> llvm::hash_code hash_value(const Optional<T> &O) {
    329   return O ? hash_combine(true, *O) : hash_value(false);
    330 }
    331 
    332 template <typename T, typename U>
    333 constexpr bool operator==(const Optional<T> &X, const Optional<U> &Y) {
    334   if (X && Y)
    335     return *X == *Y;
    336   return X.hasValue() == Y.hasValue();
    337 }
    338 
    339 template <typename T, typename U>
    340 constexpr bool operator!=(const Optional<T> &X, const Optional<U> &Y) {
    341   return !(X == Y);
    342 }
    343 
    344 template <typename T, typename U>
    345 constexpr bool operator<(const Optional<T> &X, const Optional<U> &Y) {
    346   if (X && Y)
    347     return *X < *Y;
    348   return X.hasValue() < Y.hasValue();
    349 }
    350 
    351 template <typename T, typename U>
    352 constexpr bool operator<=(const Optional<T> &X, const Optional<U> &Y) {
    353   return !(Y < X);
    354 }
    355 
    356 template <typename T, typename U>
    357 constexpr bool operator>(const Optional<T> &X, const Optional<U> &Y) {
    358   return Y < X;
    359 }
    360 
    361 template <typename T, typename U>
    362 constexpr bool operator>=(const Optional<T> &X, const Optional<U> &Y) {
    363   return !(X < Y);
    364 }
    365 
    366 template <typename T>
    367 constexpr bool operator==(const Optional<T> &X, NoneType) {
    368   return !X;
    369 }
    370 
    371 template <typename T>
    372 constexpr bool operator==(NoneType, const Optional<T> &X) {
    373   return X == None;
    374 }
    375 
    376 template <typename T>
    377 constexpr bool operator!=(const Optional<T> &X, NoneType) {
    378   return !(X == None);
    379 }
    380 
    381 template <typename T>
    382 constexpr bool operator!=(NoneType, const Optional<T> &X) {
    383   return X != None;
    384 }
    385 
    386 template <typename T> constexpr bool operator<(const Optional<T> &, NoneType) {
    387   return false;
    388 }
    389 
    390 template <typename T> constexpr bool operator<(NoneType, const Optional<T> &X) {
    391   return X.hasValue();
    392 }
    393 
    394 template <typename T>
    395 constexpr bool operator<=(const Optional<T> &X, NoneType) {
    396   return !(None < X);
    397 }
    398 
    399 template <typename T>
    400 constexpr bool operator<=(NoneType, const Optional<T> &X) {
    401   return !(X < None);
    402 }
    403 
    404 template <typename T> constexpr bool operator>(const Optional<T> &X, NoneType) {
    405   return None < X;
    406 }
    407 
    408 template <typename T> constexpr bool operator>(NoneType, const Optional<T> &X) {
    409   return X < None;
    410 }
    411 
    412 template <typename T>
    413 constexpr bool operator>=(const Optional<T> &X, NoneType) {
    414   return None <= X;
    415 }
    416 
    417 template <typename T>
    418 constexpr bool operator>=(NoneType, const Optional<T> &X) {
    419   return X <= None;
    420 }
    421 
    422 template <typename T>
    423 constexpr bool operator==(const Optional<T> &X, const T &Y) {
    424   return X && *X == Y;
    425 }
    426 
    427 template <typename T>
    428 constexpr bool operator==(const T &X, const Optional<T> &Y) {
    429   return Y && X == *Y;
    430 }
    431 
    432 template <typename T>
    433 constexpr bool operator!=(const Optional<T> &X, const T &Y) {
    434   return !(X == Y);
    435 }
    436 
    437 template <typename T>
    438 constexpr bool operator!=(const T &X, const Optional<T> &Y) {
    439   return !(X == Y);
    440 }
    441 
    442 template <typename T>
    443 constexpr bool operator<(const Optional<T> &X, const T &Y) {
    444   return !X || *X < Y;
    445 }
    446 
    447 template <typename T>
    448 constexpr bool operator<(const T &X, const Optional<T> &Y) {
    449   return Y && X < *Y;
    450 }
    451 
    452 template <typename T>
    453 constexpr bool operator<=(const Optional<T> &X, const T &Y) {
    454   return !(Y < X);
    455 }
    456 
    457 template <typename T>
    458 constexpr bool operator<=(const T &X, const Optional<T> &Y) {
    459   return !(Y < X);
    460 }
    461 
    462 template <typename T>
    463 constexpr bool operator>(const Optional<T> &X, const T &Y) {
    464   return Y < X;
    465 }
    466 
    467 template <typename T>
    468 constexpr bool operator>(const T &X, const Optional<T> &Y) {
    469   return Y < X;
    470 }
    471 
    472 template <typename T>
    473 constexpr bool operator>=(const Optional<T> &X, const T &Y) {
    474   return !(X < Y);
    475 }
    476 
    477 template <typename T>
    478 constexpr bool operator>=(const T &X, const Optional<T> &Y) {
    479   return !(X < Y);
    480 }
    481 
    482 raw_ostream &operator<<(raw_ostream &OS, NoneType);
    483 
    484 template <typename T, typename = decltype(std::declval<raw_ostream &>()
    485                                           << std::declval<const T &>())>
    486 raw_ostream &operator<<(raw_ostream &OS, const Optional<T> &O) {
    487   if (O)
    488     OS << *O;
    489   else
    490     OS << None;
    491   return OS;
    492 }
    493 
    494 } // end namespace llvm
    495 
    496 #endif // LLVM_ADT_OPTIONAL_H
    497