diff options
Diffstat (limited to 'include/llvm/ADT/Optional.h')
-rw-r--r-- | include/llvm/ADT/Optional.h | 255 |
1 files changed, 178 insertions, 77 deletions
diff --git a/include/llvm/ADT/Optional.h b/include/llvm/ADT/Optional.h index 76937d632ae1..b45a74002e10 100644 --- a/include/llvm/ADT/Optional.h +++ b/include/llvm/ADT/Optional.h @@ -1,9 +1,8 @@ //===- Optional.h - Simple variant for passing optional values --*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -17,94 +16,197 @@ #define LLVM_ADT_OPTIONAL_H #include "llvm/ADT/None.h" -#include "llvm/Support/AlignOf.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/type_traits.h" -#include <algorithm> #include <cassert> +#include <memory> #include <new> #include <utility> namespace llvm { +class raw_ostream; + namespace optional_detail { + +struct in_place_t {}; + /// Storage for any type. -template <typename T, bool = isPodLike<T>::value> struct OptionalStorage { - AlignedCharArrayUnion<T> storage; - bool hasVal = false; +template <typename T, bool = is_trivially_copyable<T>::value> +class OptionalStorage { + union { + char empty; + T value; + }; + bool hasVal; - OptionalStorage() = default; +public: + ~OptionalStorage() { reset(); } - OptionalStorage(const T &y) : hasVal(true) { new (storage.buffer) T(y); } - OptionalStorage(const OptionalStorage &O) : hasVal(O.hasVal) { - if (hasVal) - new (storage.buffer) T(*O.getPointer()); + OptionalStorage() noexcept : empty(), hasVal(false) {} + + OptionalStorage(OptionalStorage const &other) : OptionalStorage() { + if (other.hasValue()) { + emplace(other.value); + } } - OptionalStorage(T &&y) : hasVal(true) { - new (storage.buffer) T(std::forward<T>(y)); + OptionalStorage(OptionalStorage &&other) : OptionalStorage() { + if (other.hasValue()) { + emplace(std::move(other.value)); + } } - OptionalStorage(OptionalStorage &&O) : hasVal(O.hasVal) { - if (O.hasVal) { - new (storage.buffer) T(std::move(*O.getPointer())); + + template <class... Args> + explicit OptionalStorage(in_place_t, Args &&... args) + : value(std::forward<Args>(args)...), hasVal(true) {} + + void reset() noexcept { + if (hasVal) { + value.~T(); + hasVal = false; } } - OptionalStorage &operator=(T &&y) { - if (hasVal) - *getPointer() = std::move(y); - else { - new (storage.buffer) T(std::move(y)); + bool hasValue() const noexcept { return hasVal; } + + T &getValue() LLVM_LVALUE_FUNCTION noexcept { + assert(hasVal); + return value; + } + T const &getValue() const LLVM_LVALUE_FUNCTION noexcept { + assert(hasVal); + return value; + } +#if LLVM_HAS_RVALUE_REFERENCE_THIS + T &&getValue() && noexcept { + assert(hasVal); + return std::move(value); + } +#endif + + template <class... Args> void emplace(Args &&... args) { + reset(); + ::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...); + hasVal = true; + } + + OptionalStorage &operator=(T const &y) { + if (hasValue()) { + value = y; + } else { + ::new ((void *)std::addressof(value)) T(y); hasVal = true; } return *this; } - OptionalStorage &operator=(OptionalStorage &&O) { - if (!O.hasVal) - reset(); - else { - *this = std::move(*O.getPointer()); + OptionalStorage &operator=(T &&y) { + if (hasValue()) { + value = std::move(y); + } else { + ::new ((void *)std::addressof(value)) T(std::move(y)); + hasVal = true; } return *this; } - // FIXME: these assignments (& the equivalent const T&/const Optional& ctors) - // could be made more efficient by passing by value, possibly unifying them - // with the rvalue versions above - but this could place a different set of - // requirements (notably: the existence of a default ctor) when implemented - // in that way. Careful SFINAE to avoid such pitfalls would be required. - OptionalStorage &operator=(const T &y) { - if (hasVal) - *getPointer() = y; - else { - new (storage.buffer) T(y); - hasVal = true; + OptionalStorage &operator=(OptionalStorage const &other) { + if (other.hasValue()) { + if (hasValue()) { + value = other.value; + } else { + ::new ((void *)std::addressof(value)) T(other.value); + hasVal = true; + } + } else { + reset(); } return *this; } - OptionalStorage &operator=(const OptionalStorage &O) { - if (!O.hasVal) + + OptionalStorage &operator=(OptionalStorage &&other) { + if (other.hasValue()) { + if (hasValue()) { + value = std::move(other.value); + } else { + ::new ((void *)std::addressof(value)) T(std::move(other.value)); + hasVal = true; + } + } else { reset(); - else - *this = *O.getPointer(); + } return *this; } +}; - ~OptionalStorage() { reset(); } +template <typename T> class OptionalStorage<T, true> { + union { + char empty; + T value; + }; + bool hasVal = false; + +public: + ~OptionalStorage() = default; + + OptionalStorage() noexcept : empty{} {} + + OptionalStorage(OptionalStorage const &other) = default; + OptionalStorage(OptionalStorage &&other) = default; + + OptionalStorage &operator=(OptionalStorage const &other) = default; + OptionalStorage &operator=(OptionalStorage &&other) = default; + + template <class... Args> + explicit OptionalStorage(in_place_t, Args &&... args) + : value(std::forward<Args>(args)...), hasVal(true) {} - void reset() { + void reset() noexcept { if (hasVal) { - (*getPointer()).~T(); + value.~T(); hasVal = false; } } - T *getPointer() { + bool hasValue() const noexcept { return hasVal; } + + T &getValue() LLVM_LVALUE_FUNCTION noexcept { assert(hasVal); - return reinterpret_cast<T *>(storage.buffer); + return value; } - const T *getPointer() const { + T const &getValue() const LLVM_LVALUE_FUNCTION noexcept { assert(hasVal); - return reinterpret_cast<const T *>(storage.buffer); + return value; + } +#if LLVM_HAS_RVALUE_REFERENCE_THIS + T &&getValue() && noexcept { + assert(hasVal); + return std::move(value); + } +#endif + + template <class... Args> void emplace(Args &&... args) { + reset(); + ::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...); + hasVal = true; + } + + OptionalStorage &operator=(T const &y) { + if (hasValue()) { + value = y; + } else { + ::new ((void *)std::addressof(value)) T(y); + hasVal = true; + } + return *this; + } + OptionalStorage &operator=(T &&y) { + if (hasValue()) { + value = std::move(y); + } else { + ::new ((void *)std::addressof(value)) T(std::move(y)); + hasVal = true; + } + return *this; } }; @@ -119,10 +221,10 @@ public: constexpr Optional() {} constexpr Optional(NoneType) {} - Optional(const T &y) : Storage(y) {} + Optional(const T &y) : Storage(optional_detail::in_place_t{}, y) {} Optional(const Optional &O) = default; - Optional(T &&y) : Storage(std::forward<T>(y)) {} + Optional(T &&y) : Storage(optional_detail::in_place_t{}, std::move(y)) {} Optional(Optional &&O) = default; Optional &operator=(T &&y) { @@ -133,9 +235,7 @@ public: /// Create a new object by constructing it in place with the given arguments. template <typename... ArgTypes> void emplace(ArgTypes &&... Args) { - reset(); - Storage.hasVal = true; - new (getPointer()) T(std::forward<ArgTypes>(Args)...); + Storage.emplace(std::forward<ArgTypes>(Args)...); } static inline Optional create(const T *y) { @@ -150,23 +250,17 @@ public: void reset() { Storage.reset(); } - const T *getPointer() const { - assert(Storage.hasVal); - return reinterpret_cast<const T *>(Storage.storage.buffer); - } - T *getPointer() { - assert(Storage.hasVal); - return reinterpret_cast<T *>(Storage.storage.buffer); - } - const T &getValue() const LLVM_LVALUE_FUNCTION { return *getPointer(); } - T &getValue() LLVM_LVALUE_FUNCTION { return *getPointer(); } + const T *getPointer() const { return &Storage.getValue(); } + T *getPointer() { return &Storage.getValue(); } + const T &getValue() const LLVM_LVALUE_FUNCTION { return Storage.getValue(); } + T &getValue() LLVM_LVALUE_FUNCTION { return Storage.getValue(); } - explicit operator bool() const { return Storage.hasVal; } - bool hasValue() const { return Storage.hasVal; } + explicit operator bool() const { return hasValue(); } + bool hasValue() const { return Storage.hasValue(); } const T *operator->() const { return getPointer(); } T *operator->() { return getPointer(); } - const T &operator*() const LLVM_LVALUE_FUNCTION { return *getPointer(); } - T &operator*() LLVM_LVALUE_FUNCTION { return *getPointer(); } + const T &operator*() const LLVM_LVALUE_FUNCTION { return getValue(); } + T &operator*() LLVM_LVALUE_FUNCTION { return getValue(); } template <typename U> constexpr T getValueOr(U &&value) const LLVM_LVALUE_FUNCTION { @@ -174,8 +268,8 @@ public: } #if LLVM_HAS_RVALUE_REFERENCE_THIS - T &&getValue() && { return std::move(*getPointer()); } - T &&operator*() && { return std::move(*getPointer()); } + T &&getValue() && { return std::move(Storage.getValue()); } + T &&operator*() && { return std::move(Storage.getValue()); } template <typename U> T getValueOr(U &&value) && { @@ -184,11 +278,6 @@ public: #endif }; -template <typename T> struct isPodLike<Optional<T>> { - // An Optional<T> is pod-like if T is. - static const bool value = isPodLike<T>::value; -}; - template <typename T, typename U> bool operator==(const Optional<T> &X, const Optional<U> &Y) { if (X && Y) @@ -323,6 +412,18 @@ template <typename T> bool operator>=(const T &X, const Optional<T> &Y) { return !(X < Y); } +raw_ostream &operator<<(raw_ostream &OS, NoneType); + +template <typename T, typename = decltype(std::declval<raw_ostream &>() + << std::declval<const T &>())> +raw_ostream &operator<<(raw_ostream &OS, const Optional<T> &O) { + if (O) + OS << *O; + else + OS << None; + return OS; +} + } // end namespace llvm #endif // LLVM_ADT_OPTIONAL_H |