1 | //===- Optional.h - Simple variant for passing optional values --*- 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 | /// \file |
10 | /// This file provides Optional, a template class modeled in the spirit of |
11 | /// OCaml's 'opt' variant. The idea is to strongly type whether or not |
12 | /// a value can be optional. |
13 | /// |
14 | //===----------------------------------------------------------------------===// |
15 | |
16 | #ifndef LLVM_ADT_OPTIONAL_H |
17 | #define LLVM_ADT_OPTIONAL_H |
18 | |
19 | #include "llvm/ADT/Hashing.h" |
20 | #include "llvm/ADT/None.h" |
21 | #include "llvm/ADT/STLForwardCompat.h" |
22 | #include "llvm/Support/Compiler.h" |
23 | #include "llvm/Support/type_traits.h" |
24 | #include <cassert> |
25 | #include <new> |
26 | #include <utility> |
27 | |
28 | namespace llvm { |
29 | |
30 | class raw_ostream; |
31 | |
32 | namespace optional_detail { |
33 | |
34 | /// Storage for any type. |
35 | // |
36 | // The specialization condition intentionally uses |
37 | // llvm::is_trivially_{copy/move}_constructible instead of |
38 | // std::is_trivially_{copy/move}_constructible. GCC versions prior to 7.4 may |
39 | // instantiate the copy/move constructor of `T` when |
40 | // std::is_trivially_{copy/move}_constructible is instantiated. This causes |
41 | // compilation to fail if we query the trivially copy/move constructible |
42 | // property of a class which is not copy/move constructible. |
43 | // |
44 | // The current implementation of OptionalStorage insists that in order to use |
45 | // the trivial specialization, the value_type must be trivially copy |
46 | // constructible and trivially copy assignable due to =default implementations |
47 | // of the copy/move constructor/assignment. It does not follow that this is |
48 | // necessarily the case std::is_trivially_copyable is true (hence the expanded |
49 | // specialization condition). |
50 | // |
51 | // The move constructible / assignable conditions emulate the remaining behavior |
52 | // of std::is_trivially_copyable. |
53 | template <typename T, |
54 | bool = (llvm::is_trivially_copy_constructible<T>::value && |
55 | std::is_trivially_copy_assignable<T>::value && |
56 | (llvm::is_trivially_move_constructible<T>::value || |
57 | !std::is_move_constructible<T>::value) && |
58 | (std::is_trivially_move_assignable<T>::value || |
59 | !std::is_move_assignable<T>::value))> |
60 | class OptionalStorage { |
61 | union { |
62 | char empty; |
63 | T value; |
64 | }; |
65 | bool hasVal; |
66 | |
67 | public: |
68 | ~OptionalStorage() { reset(); } |
69 | |
70 | constexpr OptionalStorage() noexcept : empty(), hasVal(false) {} |
71 | |
72 | constexpr OptionalStorage(OptionalStorage const &other) : OptionalStorage() { |
73 | if (other.hasValue()) { |
74 | emplace(other.value); |
75 | } |
76 | } |
77 | constexpr OptionalStorage(OptionalStorage &&other) : OptionalStorage() { |
78 | if (other.hasValue()) { |
79 | emplace(std::move(other.value)); |
80 | } |
81 | } |
82 | |
83 | template <class... Args> |
84 | constexpr explicit OptionalStorage(in_place_t, Args &&... args) |
85 | : value(std::forward<Args>(args)...), hasVal(true) {} |
86 | |
87 | void reset() noexcept { |
88 | if (hasVal) { |
89 | value.~T(); |
90 | hasVal = false; |
91 | } |
92 | } |
93 | |
94 | constexpr bool hasValue() const noexcept { return hasVal; } |
95 | |
96 | T &getValue() LLVM_LVALUE_FUNCTION noexcept { |
97 | assert(hasVal); |
98 | return value; |
99 | } |
100 | constexpr T const &getValue() const LLVM_LVALUE_FUNCTION noexcept { |
101 | assert(hasVal); |
102 | return value; |
103 | } |
104 | #if LLVM_HAS_RVALUE_REFERENCE_THIS |
105 | T &&getValue() && noexcept { |
106 | assert(hasVal); |
107 | return std::move(value); |
108 | } |
109 | #endif |
110 | |
111 | template <class... Args> void emplace(Args &&... args) { |
112 | reset(); |
113 | ::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...); |
114 | hasVal = true; |
115 | } |
116 | |
117 | OptionalStorage &operator=(T const &y) { |
118 | if (hasValue()) { |
119 | value = y; |
120 | } else { |
121 | ::new ((void *)std::addressof(value)) T(y); |
122 | hasVal = true; |
123 | } |
124 | return *this; |
125 | } |
126 | OptionalStorage &operator=(T &&y) { |
127 | if (hasValue()) { |
128 | value = std::move(y); |
129 | } else { |
130 | ::new ((void *)std::addressof(value)) T(std::move(y)); |
131 | hasVal = true; |
132 | } |
133 | return *this; |
134 | } |
135 | |
136 | OptionalStorage &operator=(OptionalStorage const &other) { |
137 | if (other.hasValue()) { |
138 | if (hasValue()) { |
139 | value = other.value; |
140 | } else { |
141 | ::new ((void *)std::addressof(value)) T(other.value); |
142 | hasVal = true; |
143 | } |
144 | } else { |
145 | reset(); |
146 | } |
147 | return *this; |
148 | } |
149 | |
150 | OptionalStorage &operator=(OptionalStorage &&other) { |
151 | if (other.hasValue()) { |
152 | if (hasValue()) { |
153 | value = std::move(other.value); |
154 | } else { |
155 | ::new ((void *)std::addressof(value)) T(std::move(other.value)); |
156 | hasVal = true; |
157 | } |
158 | } else { |
159 | reset(); |
160 | } |
161 | return *this; |
162 | } |
163 | }; |
164 | |
165 | template <typename T> class OptionalStorage<T, true> { |
166 | union { |
167 | char empty; |
168 | T value; |
169 | }; |
170 | bool hasVal = false; |
171 | |
172 | public: |
173 | ~OptionalStorage() = default; |
174 | |
175 | constexpr OptionalStorage() noexcept : empty{} {} |
176 | |
177 | constexpr OptionalStorage(OptionalStorage const &other) = default; |
178 | constexpr OptionalStorage(OptionalStorage &&other) = default; |
179 | |
180 | OptionalStorage &operator=(OptionalStorage const &other) = default; |
181 | OptionalStorage &operator=(OptionalStorage &&other) = default; |
182 | |
183 | template <class... Args> |
184 | constexpr explicit OptionalStorage(in_place_t, Args &&... args) |
185 | : value(std::forward<Args>(args)...), hasVal(true) {} |
186 | |
187 | void reset() noexcept { |
188 | if (hasVal) { |
189 | value.~T(); |
190 | hasVal = false; |
191 | } |
192 | } |
193 | |
194 | constexpr bool hasValue() const noexcept { return hasVal; } |
195 | |
196 | T &getValue() LLVM_LVALUE_FUNCTION noexcept { |
197 | assert(hasVal); |
198 | return value; |
199 | } |
200 | constexpr T const &getValue() const LLVM_LVALUE_FUNCTION noexcept { |
201 | assert(hasVal); |
202 | return value; |
203 | } |
204 | #if LLVM_HAS_RVALUE_REFERENCE_THIS |
205 | T &&getValue() && noexcept { |
206 | assert(hasVal); |
207 | return std::move(value); |
208 | } |
209 | #endif |
210 | |
211 | template <class... Args> void emplace(Args &&... args) { |
212 | reset(); |
213 | ::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...); |
214 | hasVal = true; |
215 | } |
216 | |
217 | OptionalStorage &operator=(T const &y) { |
218 | if (hasValue()) { |
219 | value = y; |
220 | } else { |
221 | ::new ((void *)std::addressof(value)) T(y); |
222 | hasVal = true; |
223 | } |
224 | return *this; |
225 | } |
226 | OptionalStorage &operator=(T &&y) { |
227 | if (hasValue()) { |
228 | value = std::move(y); |
229 | } else { |
230 | ::new ((void *)std::addressof(value)) T(std::move(y)); |
231 | hasVal = true; |
232 | } |
233 | return *this; |
234 | } |
235 | }; |
236 | |
237 | } // namespace optional_detail |
238 | |
239 | template <typename T> class Optional { |
240 | optional_detail::OptionalStorage<T> Storage; |
241 | |
242 | public: |
243 | using value_type = T; |
244 | |
245 | constexpr Optional() = default; |
246 | constexpr Optional(NoneType) {} |
247 | |
248 | constexpr Optional(const T &y) : Storage(in_place, y) {} |
249 | constexpr Optional(const Optional &O) = default; |
250 | |
251 | constexpr Optional(T &&y) : Storage(in_place, std::move(y)) {} |
252 | constexpr Optional(Optional &&O) = default; |
253 | |
254 | template <typename... ArgTypes> |
255 | constexpr Optional(in_place_t, ArgTypes &&...Args) |
256 | : Storage(in_place, std::forward<ArgTypes>(Args)...) {} |
257 | |
258 | Optional &operator=(T &&y) { |
259 | Storage = std::move(y); |
260 | return *this; |
261 | } |
262 | Optional &operator=(Optional &&O) = default; |
263 | |
264 | /// Create a new object by constructing it in place with the given arguments. |
265 | template <typename... ArgTypes> void emplace(ArgTypes &&... Args) { |
266 | Storage.emplace(std::forward<ArgTypes>(Args)...); |
267 | } |
268 | |
269 | static constexpr Optional create(const T *y) { |
270 | return y ? Optional(*y) : Optional(); |
271 | } |
272 | |
273 | Optional &operator=(const T &y) { |
274 | Storage = y; |
275 | return *this; |
276 | } |
277 | Optional &operator=(const Optional &O) = default; |
278 | |
279 | void reset() { Storage.reset(); } |
280 | |
281 | constexpr const T *getPointer() const { return &Storage.getValue(); } |
282 | T *getPointer() { return &Storage.getValue(); } |
283 | constexpr const T &getValue() const LLVM_LVALUE_FUNCTION { |
284 | return Storage.getValue(); |
285 | } |
286 | T &getValue() LLVM_LVALUE_FUNCTION { return Storage.getValue(); } |
287 | |
288 | constexpr explicit operator bool() const { return hasValue(); } |
289 | constexpr bool hasValue() const { return Storage.hasValue(); } |
290 | constexpr const T *operator->() const { return getPointer(); } |
291 | T *operator->() { return getPointer(); } |
292 | constexpr const T &operator*() const LLVM_LVALUE_FUNCTION { |
293 | return getValue(); |
294 | } |
295 | T &operator*() LLVM_LVALUE_FUNCTION { return getValue(); } |
296 | |
297 | template <typename U> |
298 | constexpr T getValueOr(U &&value) const LLVM_LVALUE_FUNCTION { |
299 | return hasValue() ? getValue() : std::forward<U>(value); |
300 | } |
301 | |
302 | /// Apply a function to the value if present; otherwise return None. |
303 | template <class Function> |
304 | auto map(const Function &F) const LLVM_LVALUE_FUNCTION |
305 | -> Optional<decltype(F(getValue()))> { |
306 | if (*this) return F(getValue()); |
307 | return None; |
308 | } |
309 | |
310 | #if LLVM_HAS_RVALUE_REFERENCE_THIS |
311 | T &&getValue() && { return std::move(Storage.getValue()); } |
312 | T &&operator*() && { return std::move(Storage.getValue()); } |
313 | |
314 | template <typename U> |
315 | T getValueOr(U &&value) && { |
316 | return hasValue() ? std::move(getValue()) : std::forward<U>(value); |
317 | } |
318 | |
319 | /// Apply a function to the value if present; otherwise return None. |
320 | template <class Function> |
321 | auto map(const Function &F) && |
322 | -> Optional<decltype(F(std::move(*this).getValue()))> { |
323 | if (*this) return F(std::move(*this).getValue()); |
324 | return None; |
325 | } |
326 | #endif |
327 | }; |
328 | |
329 | template <class T> llvm::hash_code hash_value(const Optional<T> &O) { |
330 | return O ? hash_combine(true, *O) : hash_value(false); |
331 | } |
332 | |
333 | template <typename T, typename U> |
334 | constexpr bool operator==(const Optional<T> &X, const Optional<U> &Y) { |
335 | if (X && Y) |
336 | return *X == *Y; |
337 | return X.hasValue() == Y.hasValue(); |
338 | } |
339 | |
340 | template <typename T, typename U> |
341 | constexpr bool operator!=(const Optional<T> &X, const Optional<U> &Y) { |
342 | return !(X == Y); |
343 | } |
344 | |
345 | template <typename T, typename U> |
346 | constexpr bool operator<(const Optional<T> &X, const Optional<U> &Y) { |
347 | if (X && Y) |
348 | return *X < *Y; |
349 | return X.hasValue() < Y.hasValue(); |
350 | } |
351 | |
352 | template <typename T, typename U> |
353 | constexpr bool operator<=(const Optional<T> &X, const Optional<U> &Y) { |
354 | return !(Y < X); |
355 | } |
356 | |
357 | template <typename T, typename U> |
358 | constexpr bool operator>(const Optional<T> &X, const Optional<U> &Y) { |
359 | return Y < X; |
360 | } |
361 | |
362 | template <typename T, typename U> |
363 | constexpr bool operator>=(const Optional<T> &X, const Optional<U> &Y) { |
364 | return !(X < Y); |
365 | } |
366 | |
367 | template <typename T> |
368 | constexpr bool operator==(const Optional<T> &X, NoneType) { |
369 | return !X; |
370 | } |
371 | |
372 | template <typename T> |
373 | constexpr bool operator==(NoneType, const Optional<T> &X) { |
374 | return X == None; |
375 | } |
376 | |
377 | template <typename T> |
378 | constexpr bool operator!=(const Optional<T> &X, NoneType) { |
379 | return !(X == None); |
380 | } |
381 | |
382 | template <typename T> |
383 | constexpr bool operator!=(NoneType, const Optional<T> &X) { |
384 | return X != None; |
385 | } |
386 | |
387 | template <typename T> constexpr bool operator<(const Optional<T> &, NoneType) { |
388 | return false; |
389 | } |
390 | |
391 | template <typename T> constexpr bool operator<(NoneType, const Optional<T> &X) { |
392 | return X.hasValue(); |
393 | } |
394 | |
395 | template <typename T> |
396 | constexpr bool operator<=(const Optional<T> &X, NoneType) { |
397 | return !(None < X); |
398 | } |
399 | |
400 | template <typename T> |
401 | constexpr bool operator<=(NoneType, const Optional<T> &X) { |
402 | return !(X < None); |
403 | } |
404 | |
405 | template <typename T> constexpr bool operator>(const Optional<T> &X, NoneType) { |
406 | return None < X; |
407 | } |
408 | |
409 | template <typename T> constexpr bool operator>(NoneType, const Optional<T> &X) { |
410 | return X < None; |
411 | } |
412 | |
413 | template <typename T> |
414 | constexpr bool operator>=(const Optional<T> &X, NoneType) { |
415 | return None <= X; |
416 | } |
417 | |
418 | template <typename T> |
419 | constexpr bool operator>=(NoneType, const Optional<T> &X) { |
420 | return X <= None; |
421 | } |
422 | |
423 | template <typename T> |
424 | constexpr bool operator==(const Optional<T> &X, const T &Y) { |
425 | return X && *X == Y; |
426 | } |
427 | |
428 | template <typename T> |
429 | constexpr bool operator==(const T &X, const Optional<T> &Y) { |
430 | return Y && X == *Y; |
431 | } |
432 | |
433 | template <typename T> |
434 | constexpr bool operator!=(const Optional<T> &X, const T &Y) { |
435 | return !(X == Y); |
436 | } |
437 | |
438 | template <typename T> |
439 | constexpr bool operator!=(const T &X, const Optional<T> &Y) { |
440 | return !(X == Y); |
441 | } |
442 | |
443 | template <typename T> |
444 | constexpr bool operator<(const Optional<T> &X, const T &Y) { |
445 | return !X || *X < Y; |
446 | } |
447 | |
448 | template <typename T> |
449 | constexpr bool operator<(const T &X, const Optional<T> &Y) { |
450 | return Y && X < *Y; |
451 | } |
452 | |
453 | template <typename T> |
454 | constexpr bool operator<=(const Optional<T> &X, const T &Y) { |
455 | return !(Y < X); |
456 | } |
457 | |
458 | template <typename T> |
459 | constexpr bool operator<=(const T &X, const Optional<T> &Y) { |
460 | return !(Y < X); |
461 | } |
462 | |
463 | template <typename T> |
464 | constexpr bool operator>(const Optional<T> &X, const T &Y) { |
465 | return Y < X; |
466 | } |
467 | |
468 | template <typename T> |
469 | constexpr bool operator>(const T &X, const Optional<T> &Y) { |
470 | return Y < X; |
471 | } |
472 | |
473 | template <typename T> |
474 | constexpr bool operator>=(const Optional<T> &X, const T &Y) { |
475 | return !(X < Y); |
476 | } |
477 | |
478 | template <typename T> |
479 | constexpr bool operator>=(const T &X, const Optional<T> &Y) { |
480 | return !(X < Y); |
481 | } |
482 | |
483 | raw_ostream &operator<<(raw_ostream &OS, NoneType); |
484 | |
485 | template <typename T, typename = decltype(std::declval<raw_ostream &>() |
486 | << std::declval<const T &>())> |
487 | raw_ostream &operator<<(raw_ostream &OS, const Optional<T> &O) { |
488 | if (O) |
489 | OS << *O; |
490 | else |
491 | OS << None; |
492 | return OS; |
493 | } |
494 | |
495 | } // end namespace llvm |
496 | |
497 | #endif // LLVM_ADT_OPTIONAL_H |
498 | |