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