1 //===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
11 /// Provides ErrorOr<T> smart pointer.
13 //===----------------------------------------------------------------------===//
15 #ifndef LLVM_SUPPORT_ERROROR_H
16 #define LLVM_SUPPORT_ERROROR_H
18 #include "llvm/Support/AlignOf.h"
20 #include <system_error>
21 #include <type_traits>
26 /// Represents either an error or a value T.
28 /// ErrorOr<T> is a pointer-like class that represents the result of an
29 /// operation. The result is either an error, or a value of type T. This is
30 /// designed to emulate the usage of returning a pointer where nullptr indicates
31 /// failure. However instead of just knowing that the operation failed, we also
32 /// have an error_code and optional user data that describes why it failed.
34 /// It is used like the following.
36 /// ErrorOr<Buffer> getBuffer();
38 /// auto buffer = getBuffer();
39 /// if (error_code ec = buffer.getError())
41 /// buffer->write("adena");
45 /// Implicit conversion to bool returns true if there is a usable value. The
46 /// unary * and -> operators provide pointer like access to the value. Accessing
47 /// the value when there is an error has undefined behavior.
49 /// When T is a reference type the behavior is slightly different. The reference
50 /// is held in a std::reference_wrapper<std::remove_reference<T>::type>, and
51 /// there is special handling to make operator -> work as if T was not a
54 /// T cannot be a rvalue reference.
57 template <class OtherT
> friend class ErrorOr
;
59 static const bool isRef
= std::is_reference
<T
>::value
;
61 using wrap
= std::reference_wrapper
<typename
std::remove_reference
<T
>::type
>;
64 using storage_type
= typename
std::conditional
<isRef
, wrap
, T
>::type
;
67 using reference
= typename
std::remove_reference
<T
>::type
&;
68 using const_reference
= const typename
std::remove_reference
<T
>::type
&;
69 using pointer
= typename
std::remove_reference
<T
>::type
*;
70 using const_pointer
= const typename
std::remove_reference
<T
>::type
*;
75 typename
std::enable_if
<std::is_error_code_enum
<E
>::value
||
76 std::is_error_condition_enum
<E
>::value
,
77 void *>::type
= nullptr)
79 new (getErrorStorage()) std::error_code(make_error_code(ErrorCode
));
82 ErrorOr(std::error_code EC
) : HasError(true) {
83 new (getErrorStorage()) std::error_code(EC
);
86 template <class OtherT
>
88 typename
std::enable_if
<std::is_convertible
<OtherT
, T
>::value
>::type
91 new (getStorage()) storage_type(std::forward
<OtherT
>(Val
));
94 ErrorOr(const ErrorOr
&Other
) {
98 template <class OtherT
>
100 const ErrorOr
<OtherT
> &Other
,
101 typename
std::enable_if
<std::is_convertible
<OtherT
, T
>::value
>::type
* =
103 copyConstruct(Other
);
106 template <class OtherT
>
108 const ErrorOr
<OtherT
> &Other
,
109 typename
std::enable_if
<
110 !std::is_convertible
<OtherT
, const T
&>::value
>::type
* = nullptr) {
111 copyConstruct(Other
);
114 ErrorOr(ErrorOr
&&Other
) {
115 moveConstruct(std::move(Other
));
118 template <class OtherT
>
120 ErrorOr
<OtherT
> &&Other
,
121 typename
std::enable_if
<std::is_convertible
<OtherT
, T
>::value
>::type
* =
123 moveConstruct(std::move(Other
));
126 // This might eventually need SFINAE but it's more complex than is_convertible
127 // & I'm too lazy to write it right now.
128 template <class OtherT
>
130 ErrorOr
<OtherT
> &&Other
,
131 typename
std::enable_if
<!std::is_convertible
<OtherT
, T
>::value
>::type
* =
133 moveConstruct(std::move(Other
));
136 ErrorOr
&operator=(const ErrorOr
&Other
) {
141 ErrorOr
&operator=(ErrorOr
&&Other
) {
142 moveAssign(std::move(Other
));
148 getStorage()->~storage_type();
151 /// Return false if there is an error.
152 explicit operator bool() const {
156 reference
get() { return *getStorage(); }
157 const_reference
get() const { return const_cast<ErrorOr
<T
> *>(this)->get(); }
159 std::error_code
getError() const {
160 return HasError
? *getErrorStorage() : std::error_code();
163 pointer
operator ->() {
164 return toPointer(getStorage());
167 const_pointer
operator->() const { return toPointer(getStorage()); }
169 reference
operator *() {
170 return *getStorage();
173 const_reference
operator*() const { return *getStorage(); }
176 template <class OtherT
>
177 void copyConstruct(const ErrorOr
<OtherT
> &Other
) {
178 if (!Other
.HasError
) {
179 // Get the other value.
181 new (getStorage()) storage_type(*Other
.getStorage());
183 // Get other's error.
185 new (getErrorStorage()) std::error_code(Other
.getError());
190 static bool compareThisIfSameType(const T1
&a
, const T1
&b
) {
194 template <class T1
, class T2
>
195 static bool compareThisIfSameType(const T1
&a
, const T2
&b
) {
199 template <class OtherT
>
200 void copyAssign(const ErrorOr
<OtherT
> &Other
) {
201 if (compareThisIfSameType(*this, Other
))
205 new (this) ErrorOr(Other
);
208 template <class OtherT
>
209 void moveConstruct(ErrorOr
<OtherT
> &&Other
) {
210 if (!Other
.HasError
) {
211 // Get the other value.
213 new (getStorage()) storage_type(std::move(*Other
.getStorage()));
215 // Get other's error.
217 new (getErrorStorage()) std::error_code(Other
.getError());
221 template <class OtherT
>
222 void moveAssign(ErrorOr
<OtherT
> &&Other
) {
223 if (compareThisIfSameType(*this, Other
))
227 new (this) ErrorOr(std::move(Other
));
230 pointer
toPointer(pointer Val
) {
234 const_pointer
toPointer(const_pointer Val
) const { return Val
; }
236 pointer
toPointer(wrap
*Val
) {
240 const_pointer
toPointer(const wrap
*Val
) const { return &Val
->get(); }
242 storage_type
*getStorage() {
243 assert(!HasError
&& "Cannot get value when an error exists!");
244 return reinterpret_cast<storage_type
*>(TStorage
.buffer
);
247 const storage_type
*getStorage() const {
248 assert(!HasError
&& "Cannot get value when an error exists!");
249 return reinterpret_cast<const storage_type
*>(TStorage
.buffer
);
252 std::error_code
*getErrorStorage() {
253 assert(HasError
&& "Cannot get error when a value exists!");
254 return reinterpret_cast<std::error_code
*>(ErrorStorage
.buffer
);
257 const std::error_code
*getErrorStorage() const {
258 return const_cast<ErrorOr
<T
> *>(this)->getErrorStorage();
262 AlignedCharArrayUnion
<storage_type
> TStorage
;
263 AlignedCharArrayUnion
<std::error_code
> ErrorStorage
;
268 template <class T
, class E
>
269 typename
std::enable_if
<std::is_error_code_enum
<E
>::value
||
270 std::is_error_condition_enum
<E
>::value
,
272 operator==(const ErrorOr
<T
> &Err
, E Code
) {
273 return Err
.getError() == Code
;
276 } // end namespace llvm
278 #endif // LLVM_SUPPORT_ERROROR_H