blob: b45a74002e10c9d6b6bd7aac8456b2e3dca029bb [file] [log] [blame]
Andrew Scull5e1ddfa2018-08-14 10:06:54 +01001//===- Optional.h - Simple variant for passing optional values --*- C++ -*-===//
2//
Andrew Walbran16937d02019-10-22 13:54:20 +01003// 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
Andrew Scull5e1ddfa2018-08-14 10:06:54 +01006//
7//===----------------------------------------------------------------------===//
8//
9// This file provides Optional, a template class modeled in the spirit of
10// OCaml's 'opt' variant. The idea is to strongly type whether or not
11// a value can be optional.
12//
13//===----------------------------------------------------------------------===//
14
15#ifndef LLVM_ADT_OPTIONAL_H
16#define LLVM_ADT_OPTIONAL_H
17
18#include "llvm/ADT/None.h"
Andrew Scull5e1ddfa2018-08-14 10:06:54 +010019#include "llvm/Support/Compiler.h"
20#include "llvm/Support/type_traits.h"
Andrew Scull5e1ddfa2018-08-14 10:06:54 +010021#include <cassert>
Andrew Walbran3d2c1972020-04-07 12:24:26 +010022#include <memory>
Andrew Scull5e1ddfa2018-08-14 10:06:54 +010023#include <new>
24#include <utility>
25
26namespace llvm {
27
Andrew Walbran16937d02019-10-22 13:54:20 +010028class raw_ostream;
29
Andrew Scull5e1ddfa2018-08-14 10:06:54 +010030namespace optional_detail {
Andrew Walbran3d2c1972020-04-07 12:24:26 +010031
32struct in_place_t {};
33
Andrew Scull5e1ddfa2018-08-14 10:06:54 +010034/// Storage for any type.
Andrew Walbran3d2c1972020-04-07 12:24:26 +010035template <typename T, bool = is_trivially_copyable<T>::value>
36class OptionalStorage {
37 union {
38 char empty;
39 T value;
40 };
41 bool hasVal;
Andrew Scull5e1ddfa2018-08-14 10:06:54 +010042
Andrew Walbran3d2c1972020-04-07 12:24:26 +010043public:
Andrew Scull5e1ddfa2018-08-14 10:06:54 +010044 ~OptionalStorage() { reset(); }
45
Andrew Walbran3d2c1972020-04-07 12:24:26 +010046 OptionalStorage() noexcept : empty(), hasVal(false) {}
47
48 OptionalStorage(OptionalStorage const &other) : OptionalStorage() {
49 if (other.hasValue()) {
50 emplace(other.value);
51 }
52 }
53 OptionalStorage(OptionalStorage &&other) : OptionalStorage() {
54 if (other.hasValue()) {
55 emplace(std::move(other.value));
56 }
57 }
58
59 template <class... Args>
60 explicit OptionalStorage(in_place_t, Args &&... args)
61 : value(std::forward<Args>(args)...), hasVal(true) {}
62
63 void reset() noexcept {
Andrew Scull5e1ddfa2018-08-14 10:06:54 +010064 if (hasVal) {
Andrew Walbran3d2c1972020-04-07 12:24:26 +010065 value.~T();
Andrew Scull5e1ddfa2018-08-14 10:06:54 +010066 hasVal = false;
67 }
68 }
69
Andrew Walbran3d2c1972020-04-07 12:24:26 +010070 bool hasValue() const noexcept { return hasVal; }
71
72 T &getValue() LLVM_LVALUE_FUNCTION noexcept {
Andrew Scull5e1ddfa2018-08-14 10:06:54 +010073 assert(hasVal);
Andrew Walbran3d2c1972020-04-07 12:24:26 +010074 return value;
Andrew Scull5e1ddfa2018-08-14 10:06:54 +010075 }
Andrew Walbran3d2c1972020-04-07 12:24:26 +010076 T const &getValue() const LLVM_LVALUE_FUNCTION noexcept {
Andrew Scull5e1ddfa2018-08-14 10:06:54 +010077 assert(hasVal);
Andrew Walbran3d2c1972020-04-07 12:24:26 +010078 return value;
79 }
80#if LLVM_HAS_RVALUE_REFERENCE_THIS
81 T &&getValue() && noexcept {
82 assert(hasVal);
83 return std::move(value);
84 }
85#endif
86
87 template <class... Args> void emplace(Args &&... args) {
88 reset();
89 ::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...);
90 hasVal = true;
91 }
92
93 OptionalStorage &operator=(T const &y) {
94 if (hasValue()) {
95 value = y;
96 } else {
97 ::new ((void *)std::addressof(value)) T(y);
98 hasVal = true;
99 }
100 return *this;
101 }
102 OptionalStorage &operator=(T &&y) {
103 if (hasValue()) {
104 value = std::move(y);
105 } else {
106 ::new ((void *)std::addressof(value)) T(std::move(y));
107 hasVal = true;
108 }
109 return *this;
110 }
111
112 OptionalStorage &operator=(OptionalStorage const &other) {
113 if (other.hasValue()) {
114 if (hasValue()) {
115 value = other.value;
116 } else {
117 ::new ((void *)std::addressof(value)) T(other.value);
118 hasVal = true;
119 }
120 } else {
121 reset();
122 }
123 return *this;
124 }
125
126 OptionalStorage &operator=(OptionalStorage &&other) {
127 if (other.hasValue()) {
128 if (hasValue()) {
129 value = std::move(other.value);
130 } else {
131 ::new ((void *)std::addressof(value)) T(std::move(other.value));
132 hasVal = true;
133 }
134 } else {
135 reset();
136 }
137 return *this;
138 }
139};
140
141template <typename T> class OptionalStorage<T, true> {
142 union {
143 char empty;
144 T value;
145 };
146 bool hasVal = false;
147
148public:
149 ~OptionalStorage() = default;
150
151 OptionalStorage() noexcept : empty{} {}
152
153 OptionalStorage(OptionalStorage const &other) = default;
154 OptionalStorage(OptionalStorage &&other) = default;
155
156 OptionalStorage &operator=(OptionalStorage const &other) = default;
157 OptionalStorage &operator=(OptionalStorage &&other) = default;
158
159 template <class... Args>
160 explicit OptionalStorage(in_place_t, Args &&... args)
161 : value(std::forward<Args>(args)...), hasVal(true) {}
162
163 void reset() noexcept {
164 if (hasVal) {
165 value.~T();
166 hasVal = false;
167 }
168 }
169
170 bool hasValue() const noexcept { return hasVal; }
171
172 T &getValue() LLVM_LVALUE_FUNCTION noexcept {
173 assert(hasVal);
174 return value;
175 }
176 T const &getValue() const LLVM_LVALUE_FUNCTION noexcept {
177 assert(hasVal);
178 return value;
179 }
180#if LLVM_HAS_RVALUE_REFERENCE_THIS
181 T &&getValue() && noexcept {
182 assert(hasVal);
183 return std::move(value);
184 }
185#endif
186
187 template <class... Args> void emplace(Args &&... args) {
188 reset();
189 ::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...);
190 hasVal = true;
191 }
192
193 OptionalStorage &operator=(T const &y) {
194 if (hasValue()) {
195 value = y;
196 } else {
197 ::new ((void *)std::addressof(value)) T(y);
198 hasVal = true;
199 }
200 return *this;
201 }
202 OptionalStorage &operator=(T &&y) {
203 if (hasValue()) {
204 value = std::move(y);
205 } else {
206 ::new ((void *)std::addressof(value)) T(std::move(y));
207 hasVal = true;
208 }
209 return *this;
Andrew Scull5e1ddfa2018-08-14 10:06:54 +0100210 }
211};
212
Andrew Scull5e1ddfa2018-08-14 10:06:54 +0100213} // namespace optional_detail
214
215template <typename T> class Optional {
Andrew Walbran16937d02019-10-22 13:54:20 +0100216 optional_detail::OptionalStorage<T> Storage;
Andrew Scull5e1ddfa2018-08-14 10:06:54 +0100217
218public:
219 using value_type = T;
220
221 constexpr Optional() {}
222 constexpr Optional(NoneType) {}
223
Andrew Walbran3d2c1972020-04-07 12:24:26 +0100224 Optional(const T &y) : Storage(optional_detail::in_place_t{}, y) {}
Andrew Scull5e1ddfa2018-08-14 10:06:54 +0100225 Optional(const Optional &O) = default;
226
Andrew Walbran3d2c1972020-04-07 12:24:26 +0100227 Optional(T &&y) : Storage(optional_detail::in_place_t{}, std::move(y)) {}
Andrew Scull5e1ddfa2018-08-14 10:06:54 +0100228 Optional(Optional &&O) = default;
229
230 Optional &operator=(T &&y) {
231 Storage = std::move(y);
232 return *this;
233 }
234 Optional &operator=(Optional &&O) = default;
235
236 /// Create a new object by constructing it in place with the given arguments.
237 template <typename... ArgTypes> void emplace(ArgTypes &&... Args) {
Andrew Walbran3d2c1972020-04-07 12:24:26 +0100238 Storage.emplace(std::forward<ArgTypes>(Args)...);
Andrew Scull5e1ddfa2018-08-14 10:06:54 +0100239 }
240
241 static inline Optional create(const T *y) {
242 return y ? Optional(*y) : Optional();
243 }
244
245 Optional &operator=(const T &y) {
246 Storage = y;
247 return *this;
248 }
249 Optional &operator=(const Optional &O) = default;
250
251 void reset() { Storage.reset(); }
252
Andrew Walbran3d2c1972020-04-07 12:24:26 +0100253 const T *getPointer() const { return &Storage.getValue(); }
254 T *getPointer() { return &Storage.getValue(); }
255 const T &getValue() const LLVM_LVALUE_FUNCTION { return Storage.getValue(); }
256 T &getValue() LLVM_LVALUE_FUNCTION { return Storage.getValue(); }
Andrew Scull5e1ddfa2018-08-14 10:06:54 +0100257
Andrew Walbran3d2c1972020-04-07 12:24:26 +0100258 explicit operator bool() const { return hasValue(); }
259 bool hasValue() const { return Storage.hasValue(); }
Andrew Scull5e1ddfa2018-08-14 10:06:54 +0100260 const T *operator->() const { return getPointer(); }
261 T *operator->() { return getPointer(); }
Andrew Walbran3d2c1972020-04-07 12:24:26 +0100262 const T &operator*() const LLVM_LVALUE_FUNCTION { return getValue(); }
263 T &operator*() LLVM_LVALUE_FUNCTION { return getValue(); }
Andrew Scull5e1ddfa2018-08-14 10:06:54 +0100264
265 template <typename U>
266 constexpr T getValueOr(U &&value) const LLVM_LVALUE_FUNCTION {
267 return hasValue() ? getValue() : std::forward<U>(value);
268 }
269
270#if LLVM_HAS_RVALUE_REFERENCE_THIS
Andrew Walbran3d2c1972020-04-07 12:24:26 +0100271 T &&getValue() && { return std::move(Storage.getValue()); }
272 T &&operator*() && { return std::move(Storage.getValue()); }
Andrew Scull5e1ddfa2018-08-14 10:06:54 +0100273
274 template <typename U>
275 T getValueOr(U &&value) && {
276 return hasValue() ? std::move(getValue()) : std::forward<U>(value);
277 }
278#endif
279};
280
Andrew Scull5e1ddfa2018-08-14 10:06:54 +0100281template <typename T, typename U>
282bool operator==(const Optional<T> &X, const Optional<U> &Y) {
283 if (X && Y)
284 return *X == *Y;
285 return X.hasValue() == Y.hasValue();
286}
287
288template <typename T, typename U>
289bool operator!=(const Optional<T> &X, const Optional<U> &Y) {
290 return !(X == Y);
291}
292
293template <typename T, typename U>
294bool operator<(const Optional<T> &X, const Optional<U> &Y) {
295 if (X && Y)
296 return *X < *Y;
297 return X.hasValue() < Y.hasValue();
298}
299
300template <typename T, typename U>
301bool operator<=(const Optional<T> &X, const Optional<U> &Y) {
302 return !(Y < X);
303}
304
305template <typename T, typename U>
306bool operator>(const Optional<T> &X, const Optional<U> &Y) {
307 return Y < X;
308}
309
310template <typename T, typename U>
311bool operator>=(const Optional<T> &X, const Optional<U> &Y) {
312 return !(X < Y);
313}
314
315template<typename T>
316bool operator==(const Optional<T> &X, NoneType) {
317 return !X;
318}
319
320template<typename T>
321bool operator==(NoneType, const Optional<T> &X) {
322 return X == None;
323}
324
325template<typename T>
326bool operator!=(const Optional<T> &X, NoneType) {
327 return !(X == None);
328}
329
330template<typename T>
331bool operator!=(NoneType, const Optional<T> &X) {
332 return X != None;
333}
334
335template <typename T> bool operator<(const Optional<T> &X, NoneType) {
336 return false;
337}
338
339template <typename T> bool operator<(NoneType, const Optional<T> &X) {
340 return X.hasValue();
341}
342
343template <typename T> bool operator<=(const Optional<T> &X, NoneType) {
344 return !(None < X);
345}
346
347template <typename T> bool operator<=(NoneType, const Optional<T> &X) {
348 return !(X < None);
349}
350
351template <typename T> bool operator>(const Optional<T> &X, NoneType) {
352 return None < X;
353}
354
355template <typename T> bool operator>(NoneType, const Optional<T> &X) {
356 return X < None;
357}
358
359template <typename T> bool operator>=(const Optional<T> &X, NoneType) {
360 return None <= X;
361}
362
363template <typename T> bool operator>=(NoneType, const Optional<T> &X) {
364 return X <= None;
365}
366
367template <typename T> bool operator==(const Optional<T> &X, const T &Y) {
368 return X && *X == Y;
369}
370
371template <typename T> bool operator==(const T &X, const Optional<T> &Y) {
372 return Y && X == *Y;
373}
374
375template <typename T> bool operator!=(const Optional<T> &X, const T &Y) {
376 return !(X == Y);
377}
378
379template <typename T> bool operator!=(const T &X, const Optional<T> &Y) {
380 return !(X == Y);
381}
382
383template <typename T> bool operator<(const Optional<T> &X, const T &Y) {
384 return !X || *X < Y;
385}
386
387template <typename T> bool operator<(const T &X, const Optional<T> &Y) {
388 return Y && X < *Y;
389}
390
391template <typename T> bool operator<=(const Optional<T> &X, const T &Y) {
392 return !(Y < X);
393}
394
395template <typename T> bool operator<=(const T &X, const Optional<T> &Y) {
396 return !(Y < X);
397}
398
399template <typename T> bool operator>(const Optional<T> &X, const T &Y) {
400 return Y < X;
401}
402
403template <typename T> bool operator>(const T &X, const Optional<T> &Y) {
404 return Y < X;
405}
406
407template <typename T> bool operator>=(const Optional<T> &X, const T &Y) {
408 return !(X < Y);
409}
410
411template <typename T> bool operator>=(const T &X, const Optional<T> &Y) {
412 return !(X < Y);
413}
414
Andrew Walbran16937d02019-10-22 13:54:20 +0100415raw_ostream &operator<<(raw_ostream &OS, NoneType);
416
417template <typename T, typename = decltype(std::declval<raw_ostream &>()
418 << std::declval<const T &>())>
419raw_ostream &operator<<(raw_ostream &OS, const Optional<T> &O) {
420 if (O)
421 OS << *O;
422 else
423 OS << None;
424 return OS;
425}
426
Andrew Scull5e1ddfa2018-08-14 10:06:54 +0100427} // end namespace llvm
428
429#endif // LLVM_ADT_OPTIONAL_H