Warning: This file is not a C or C++ file. It does not have highlighting.
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 | |
66 | FORTRAN_OPTIONAL_INLINE_WITH_ATTRS ~OptionalStorage() { reset(); } |
67 | |
68 | FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr OptionalStorage() : empty() {} |
69 | |
70 | template <typename... Args> |
71 | FORTRAN_OPTIONAL_INLINE_WITH_ATTRS constexpr explicit OptionalStorage( |
72 | std::in_place_t, Args &&...args) |
73 | : stored_value(std::forward<Args>(args)...) {} |
74 | |
75 | 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 |
Warning: This file is not a C or C++ file. It does not have highlighting.