1 //===-- include/flang/Common/optional.h -------------------------*- 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 // Implementation of std::optional borrowed from LLVM's 10 // libc/src/__support/CPP/optional.h with modifications (e.g. value_or, emplace 11 // methods were added). 12 // 13 // The implementation defines optional in Fortran::common namespace. 14 // This standalone implementation may be used if the target 15 // does not support std::optional implementation (e.g. CUDA device env), 16 // otherwise, Fortran::common::optional is an alias for std::optional. 17 // 18 // TODO: using libcu++ is the best option for CUDA, but there is a couple 19 // of issues: 20 // * Older CUDA toolkits' libcu++ implementations do not support optional. 21 // * The include paths need to be set up such that all STD header files 22 // are taken from libcu++. 23 // * cuda:: namespace need to be forced for all std:: references. 24 // 25 //===----------------------------------------------------------------------===// 26 #ifndef FORTRAN_COMMON_OPTIONAL_H 27 #define FORTRAN_COMMON_OPTIONAL_H 28 29 #include "flang/Common/api-attrs.h" 30 #include <optional> 31 #include <type_traits> 32 33 #if !defined(STD_OPTIONAL_UNSUPPORTED) && \ 34 (defined(__CUDACC__) || defined(__CUDA__)) && defined(__CUDA_ARCH__) 35 #define STD_OPTIONAL_UNSUPPORTED 1 36 #endif 37 38 #define FORTRAN_OPTIONAL_INLINE_WITH_ATTRS inline RT_API_ATTRS 39 #define FORTRAN_OPTIONAL_INLINE inline 40 #define FORTRAN_OPTIONAL_INLINE_VAR inline 41 42 namespace Fortran::common { 43 44 #if STD_OPTIONAL_UNSUPPORTED 45 // Trivial nullopt_t struct. 46 struct nullopt_t { 47 constexpr explicit nullopt_t() = default; 48 }; 49 50 // nullopt that can be used and returned. 51 FORTRAN_OPTIONAL_INLINE_VAR constexpr nullopt_t nullopt{}; 52 53 // This is very simple implementation of the std::optional class. It makes 54 // several assumptions that the underlying type is trivially constructible, 55 // copyable, or movable. 56 template <typename T> class optional { 57 template <typename U, bool = !std::is_trivially_destructible<U>::value> 58 struct OptionalStorage { 59 union { 60 char empty; 61 U stored_value; 62 }; 63 64 bool in_use = false; 65 ~OptionalStorageOptionalStorage66 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS ~OptionalStorage() { reset(); } 67 OptionalStorageOptionalStorage68 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr OptionalStorage() : empty() {} 69 70 template <typename... Args> OptionalStorageOptionalStorage71 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr explicit OptionalStorage( 72 std::in_place_t, Args &&...args) 73 : stored_value(std::forward<Args>(args)...) {} 74 resetOptionalStorage75 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr void reset() { 76 if (in_use) 77 stored_value.~U(); 78 in_use = false; 79 } 80 }; 81 82 // The only difference is that this type U doesn't have a nontrivial 83 // destructor. 84 template <typename U> struct OptionalStorage<U, false> { 85 union { 86 char empty; 87 U stored_value; 88 }; 89 90 bool in_use = false; 91 92 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr OptionalStorage() : empty() {} 93 94 template <typename... Args> 95 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr explicit OptionalStorage( 96 std::in_place_t, Args &&...args) 97 : stored_value(std::forward<Args>(args)...) {} 98 99 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr void reset() { 100 in_use = false; 101 } 102 }; 103 104 OptionalStorage<T> storage; 105 106 public: 107 // The default methods do not use RT_API_ATTRS, which causes 108 // warnings in CUDA compilation of form: 109 // __device__/__host__ annotation is ignored on a function .* that is 110 // explicitly defaulted on its first declaration 111 FORTRAN_OPTIONAL_INLINE constexpr optional() = default; 112 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr optional(nullopt_t) {} 113 114 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr optional(const T &t) 115 : storage(std::in_place, t) { 116 storage.in_use = true; 117 } 118 FORTRAN_OPTIONAL_INLINE constexpr optional(const optional &) = default; 119 120 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr optional(T &&t) 121 : storage(std::in_place, std::move(t)) { 122 storage.in_use = true; 123 } 124 FORTRAN_OPTIONAL_INLINE constexpr optional(optional &&O) = default; 125 126 template <typename... ArgTypes> 127 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr optional( 128 std::in_place_t, ArgTypes &&...Args) 129 : storage(std::in_place, std::forward<ArgTypes>(Args)...) { 130 storage.in_use = true; 131 } 132 133 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr optional &operator=(T &&t) { 134 storage.stored_value = std::move(t); 135 storage.in_use = true; 136 return *this; 137 } 138 139 FORTRAN_OPTIONAL_INLINE constexpr optional &operator=(optional &&) = default; 140 141 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr optional &operator=(const T &t) { 142 storage.stored_value = t; 143 storage.in_use = true; 144 return *this; 145 } 146 147 FORTRAN_OPTIONAL_INLINE constexpr optional &operator=( 148 const optional &) = default; 149 150 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr void reset() { storage.reset(); } 151 152 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr const T &value() const & { 153 return storage.stored_value; 154 } 155 156 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr T &value() & { 157 return storage.stored_value; 158 } 159 160 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr explicit operator bool() const { 161 return storage.in_use; 162 } 163 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr bool has_value() const { 164 return storage.in_use; 165 } 166 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr const T *operator->() const { 167 return &storage.stored_value; 168 } 169 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr T *operator->() { 170 return &storage.stored_value; 171 } 172 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr const T &operator*() const & { 173 return storage.stored_value; 174 } 175 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr T &operator*() & { 176 return storage.stored_value; 177 } 178 179 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr T &&value() && { 180 return std::move(storage.stored_value); 181 } 182 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr T &&operator*() && { 183 return std::move(storage.stored_value); 184 } 185 186 template <typename VT> 187 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr T value_or( 188 VT &&default_value) const & { 189 return storage.in_use ? storage.stored_value 190 : static_cast<T>(std::forward<VT>(default_value)); 191 } 192 193 template <typename VT> 194 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr T value_or( 195 VT &&default_value) && { 196 return storage.in_use ? std::move(storage.stored_value) 197 : static_cast<T>(std::forward<VT>(default_value)); 198 } 199 200 template <typename... ArgTypes> 201 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS 202 std::enable_if_t<std::is_constructible_v<T, ArgTypes &&...>, T &> 203 emplace(ArgTypes &&...args) { 204 reset(); 205 new (reinterpret_cast<void *>(std::addressof(storage.stored_value))) 206 T(std::forward<ArgTypes>(args)...); 207 storage.in_use = true; 208 return value(); 209 } 210 211 template <typename U = T, 212 std::enable_if_t<(std::is_constructible_v<T, U &&> && 213 !std::is_same_v<std::decay_t<U>, std::in_place_t> && 214 !std::is_same_v<std::decay_t<U>, optional> && 215 std::is_convertible_v<U &&, T>), 216 bool> = true> 217 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr optional(U &&value) { 218 new (reinterpret_cast<void *>(std::addressof(storage.stored_value))) 219 T(std::forward<U>(value)); 220 storage.in_use = true; 221 } 222 223 template <typename U = T, 224 std::enable_if_t<(std::is_constructible_v<T, U &&> && 225 !std::is_same_v<std::decay_t<U>, std::in_place_t> && 226 !std::is_same_v<std::decay_t<U>, optional> && 227 !std::is_convertible_v<U &&, T>), 228 bool> = false> 229 FORTRAN_OPTIONAL_INLINE_WITH_ATTRS explicit constexpr optional(U &&value) { 230 new (reinterpret_cast<void *>(std::addressof(storage.stored_value))) 231 T(std::forward<U>(value)); 232 storage.in_use = true; 233 } 234 }; 235 #else // !STD_OPTIONAL_UNSUPPORTED 236 using std::nullopt; 237 using std::nullopt_t; 238 using std::optional; 239 #endif // !STD_OPTIONAL_UNSUPPORTED 240 241 } // namespace Fortran::common 242 243 #endif // FORTRAN_COMMON_OPTIONAL_H 244