1//===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- 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///
11/// Provides ErrorOr<T> smart pointer.
12///
13//===----------------------------------------------------------------------===//
14
15#ifndef LLVM_SUPPORT_ERROROR_H
16#define LLVM_SUPPORT_ERROROR_H
17
18#include "llvm/Support/AlignOf.h"
19#include <cassert>
20#include <system_error>
21#include <type_traits>
22#include <utility>
23
24namespace llvm {
25
26/// Represents either an error or a value T.
27///
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.
33///
34/// It is used like the following.
35/// \code
36/// ErrorOr<Buffer> getBuffer();
37///
38/// auto buffer = getBuffer();
39/// if (error_code ec = buffer.getError())
40/// return ec;
41/// buffer->write("adena");
42/// \endcode
43///
44///
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.
48///
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
52/// reference.
53///
54/// T cannot be a rvalue reference.
55template<class T>
56class ErrorOr {
57 template <class OtherT> friend class ErrorOr;
58
59 static constexpr bool isRef = std::is_reference<T>::value;
60
61 using wrap = std::reference_wrapper<std::remove_reference_t<T>>;
62
63public:
64 using storage_type = std::conditional_t<isRef, wrap, T>;
65
66private:
67 using reference = std::remove_reference_t<T> &;
68 using const_reference = const std::remove_reference_t<T> &;
69 using pointer = std::remove_reference_t<T> *;
70 using const_pointer = const std::remove_reference_t<T> *;
71
72public:
73 template <class E>
74 ErrorOr(E ErrorCode,
75 std::enable_if_t<std::is_error_code_enum<E>::value ||
76 std::is_error_condition_enum<E>::value,
77 void *> = nullptr)
78 : HasError(true) {
79 new (getErrorStorage()) std::error_code(make_error_code(ErrorCode));
80 }
81
82 ErrorOr(std::error_code EC) : HasError(true) {
83 new (getErrorStorage()) std::error_code(EC);
84 }
85
86 template <class OtherT>
87 ErrorOr(OtherT &&Val,
88 std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr)
89 : HasError(false) {
90 new (getStorage()) storage_type(std::forward<OtherT>(Val));
91 }
92
93 ErrorOr(const ErrorOr &Other) {
94 copyConstruct(Other);
95 }
96
97 template <class OtherT>
98 ErrorOr(const ErrorOr<OtherT> &Other,
99 std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr) {
100 copyConstruct(Other);
101 }
102
103 template <class OtherT>
104 explicit ErrorOr(
105 const ErrorOr<OtherT> &Other,
106 std::enable_if_t<!std::is_convertible<OtherT, const T &>::value> * =
107 nullptr) {
108 copyConstruct(Other);
109 }
110
111 ErrorOr(ErrorOr &&Other) {
112 moveConstruct(std::move(Other));
113 }
114
115 template <class OtherT>
116 ErrorOr(ErrorOr<OtherT> &&Other,
117 std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr) {
118 moveConstruct(std::move(Other));
119 }
120
121 // This might eventually need SFINAE but it's more complex than is_convertible
122 // & I'm too lazy to write it right now.
123 template <class OtherT>
124 explicit ErrorOr(
125 ErrorOr<OtherT> &&Other,
126 std::enable_if_t<!std::is_convertible<OtherT, T>::value> * = nullptr) {
127 moveConstruct(std::move(Other));
128 }
129
130 ErrorOr &operator=(const ErrorOr &Other) {
131 copyAssign(Other);
132 return *this;
133 }
134
135 ErrorOr &operator=(ErrorOr &&Other) {
136 moveAssign(std::move(Other));
137 return *this;
138 }
139
140 ~ErrorOr() {
141 if (!HasError)
142 getStorage()->~storage_type();
143 }
144
145 /// Return false if there is an error.
146 explicit operator bool() const {
147 return !HasError;
148 }
149
150 reference get() { return *getStorage(); }
151 const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); }
152
153 std::error_code getError() const {
154 return HasError ? *getErrorStorage() : std::error_code();
155 }
156
157 pointer operator ->() {
158 return toPointer(getStorage());
159 }
160
161 const_pointer operator->() const { return toPointer(getStorage()); }
162
163 reference operator *() {
164 return *getStorage();
165 }
166
167 const_reference operator*() const { return *getStorage(); }
168
169private:
170 template <class OtherT>
171 void copyConstruct(const ErrorOr<OtherT> &Other) {
172 if (!Other.HasError) {
173 // Get the other value.
174 HasError = false;
175 new (getStorage()) storage_type(*Other.getStorage());
176 } else {
177 // Get other's error.
178 HasError = true;
179 new (getErrorStorage()) std::error_code(Other.getError());
180 }
181 }
182
183 template <class T1>
184 static bool compareThisIfSameType(const T1 &a, const T1 &b) {
185 return &a == &b;
186 }
187
188 template <class T1, class T2>
189 static bool compareThisIfSameType(const T1 &a, const T2 &b) {
190 return false;
191 }
192
193 template <class OtherT>
194 void copyAssign(const ErrorOr<OtherT> &Other) {
195 if (compareThisIfSameType(*this, Other))
196 return;
197
198 this->~ErrorOr();
199 new (this) ErrorOr(Other);
200 }
201
202 template <class OtherT>
203 void moveConstruct(ErrorOr<OtherT> &&Other) {
204 if (!Other.HasError) {
205 // Get the other value.
206 HasError = false;
207 new (getStorage()) storage_type(std::move(*Other.getStorage()));
208 } else {
209 // Get other's error.
210 HasError = true;
211 new (getErrorStorage()) std::error_code(Other.getError());
212 }
213 }
214
215 template <class OtherT>
216 void moveAssign(ErrorOr<OtherT> &&Other) {
217 if (compareThisIfSameType(*this, Other))
218 return;
219
220 this->~ErrorOr();
221 new (this) ErrorOr(std::move(Other));
222 }
223
224 pointer toPointer(pointer Val) {
225 return Val;
226 }
227
228 const_pointer toPointer(const_pointer Val) const { return Val; }
229
230 pointer toPointer(wrap *Val) {
231 return &Val->get();
232 }
233
234 const_pointer toPointer(const wrap *Val) const { return &Val->get(); }
235
236 storage_type *getStorage() {
237 assert(!HasError && "Cannot get value when an error exists!");
238 return reinterpret_cast<storage_type *>(&TStorage);
239 }
240
241 const storage_type *getStorage() const {
242 assert(!HasError && "Cannot get value when an error exists!");
243 return reinterpret_cast<const storage_type *>(&TStorage);
244 }
245
246 std::error_code *getErrorStorage() {
247 assert(HasError && "Cannot get error when a value exists!");
248 return reinterpret_cast<std::error_code *>(&ErrorStorage);
249 }
250
251 const std::error_code *getErrorStorage() const {
252 return const_cast<ErrorOr<T> *>(this)->getErrorStorage();
253 }
254
255 union {
256 AlignedCharArrayUnion<storage_type> TStorage;
257 AlignedCharArrayUnion<std::error_code> ErrorStorage;
258 };
259 bool HasError : 1;
260};
261
262template <class T, class E>
263std::enable_if_t<std::is_error_code_enum<E>::value ||
264 std::is_error_condition_enum<E>::value,
265 bool>
266operator==(const ErrorOr<T> &Err, E Code) {
267 return Err.getError() == Code;
268}
269
270} // end namespace llvm
271
272#endif // LLVM_SUPPORT_ERROROR_H
273