xref: /llvm-project/flang/include/flang/Common/optional.h (revision 8ebf741136c66f51053315bf4f0ef828c6f66094)
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