Branch data Line data Source code
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 : :
24 : : namespace 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.
55 : : template<class T>
56 : : class ErrorOr {
57 : : template <class OtherT> friend class ErrorOr;
58 : :
59 : : static constexpr bool isRef = std::is_reference_v<T>;
60 : :
61 : : using wrap = std::reference_wrapper<std::remove_reference_t<T>>;
62 : :
63 : : public:
64 : : using storage_type = std::conditional_t<isRef, wrap, T>;
65 : :
66 : : private:
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 : :
72 : : public:
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_v<OtherT, T>> * = 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_v<OtherT, T>> * = 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_v<OtherT, const T &>> * = nullptr) {
107 : : copyConstruct(Other);
108 : : }
109 : :
110 : : ErrorOr(ErrorOr &&Other) {
111 : : moveConstruct(std::move(Other));
112 : : }
113 : :
114 : : template <class OtherT>
115 : : ErrorOr(ErrorOr<OtherT> &&Other,
116 : : std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) {
117 : : moveConstruct(std::move(Other));
118 : : }
119 : :
120 : : // This might eventually need SFINAE but it's more complex than is_convertible
121 : : // & I'm too lazy to write it right now.
122 : : template <class OtherT>
123 : : explicit ErrorOr(
124 : : ErrorOr<OtherT> &&Other,
125 : : std::enable_if_t<!std::is_convertible_v<OtherT, T>> * = nullptr) {
126 : : moveConstruct(std::move(Other));
127 : : }
128 : :
129 : : ErrorOr &operator=(const ErrorOr &Other) {
130 : : copyAssign(Other);
131 : : return *this;
132 : : }
133 : :
134 : : ErrorOr &operator=(ErrorOr &&Other) {
135 : : moveAssign(std::move(Other));
136 : : return *this;
137 : : }
138 : :
139 : 0 : ~ErrorOr() {
140 [ # # ]: 0 : if (!HasError)
141 : 0 : getStorage()->~storage_type();
142 : 0 : }
143 : :
144 : : /// Return false if there is an error.
145 : : explicit operator bool() const {
146 : : return !HasError;
147 : : }
148 : :
149 : 0 : reference get() { return *getStorage(); }
150 : : const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); }
151 : :
152 : 0 : std::error_code getError() const {
153 [ # # ]: 0 : return HasError ? *getErrorStorage() : std::error_code();
154 : : }
155 : :
156 : : pointer operator ->() {
157 : : return toPointer(getStorage());
158 : : }
159 : :
160 : : const_pointer operator->() const { return toPointer(getStorage()); }
161 : :
162 : : reference operator *() {
163 : : return *getStorage();
164 : : }
165 : :
166 : : const_reference operator*() const { return *getStorage(); }
167 : :
168 : : private:
169 : : template <class OtherT>
170 : : void copyConstruct(const ErrorOr<OtherT> &Other) {
171 : : if (!Other.HasError) {
172 : : // Get the other value.
173 : : HasError = false;
174 : : new (getStorage()) storage_type(*Other.getStorage());
175 : : } else {
176 : : // Get other's error.
177 : : HasError = true;
178 : : new (getErrorStorage()) std::error_code(Other.getError());
179 : : }
180 : : }
181 : :
182 : : template <class T1>
183 : : static bool compareThisIfSameType(const T1 &a, const T1 &b) {
184 : : return &a == &b;
185 : : }
186 : :
187 : : template <class T1, class T2>
188 : : static bool compareThisIfSameType(const T1 &a, const T2 &b) {
189 : : return false;
190 : : }
191 : :
192 : : template <class OtherT>
193 : : void copyAssign(const ErrorOr<OtherT> &Other) {
194 : : if (compareThisIfSameType(*this, Other))
195 : : return;
196 : :
197 : : this->~ErrorOr();
198 : : new (this) ErrorOr(Other);
199 : : }
200 : :
201 : : template <class OtherT>
202 : : void moveConstruct(ErrorOr<OtherT> &&Other) {
203 : : if (!Other.HasError) {
204 : : // Get the other value.
205 : : HasError = false;
206 : : new (getStorage()) storage_type(std::move(*Other.getStorage()));
207 : : } else {
208 : : // Get other's error.
209 : : HasError = true;
210 : : new (getErrorStorage()) std::error_code(Other.getError());
211 : : }
212 : : }
213 : :
214 : : template <class OtherT>
215 : : void moveAssign(ErrorOr<OtherT> &&Other) {
216 : : if (compareThisIfSameType(*this, Other))
217 : : return;
218 : :
219 : : this->~ErrorOr();
220 : : new (this) ErrorOr(std::move(Other));
221 : : }
222 : :
223 : : pointer toPointer(pointer Val) {
224 : : return Val;
225 : : }
226 : :
227 : : const_pointer toPointer(const_pointer Val) const { return Val; }
228 : :
229 : : pointer toPointer(wrap *Val) {
230 : : return &Val->get();
231 : : }
232 : :
233 : : const_pointer toPointer(const wrap *Val) const { return &Val->get(); }
234 : :
235 : 0 : storage_type *getStorage() {
236 [ # # ]: 0 : assert(!HasError && "Cannot get value when an error exists!");
237 : 0 : return reinterpret_cast<storage_type *>(&TStorage);
238 : : }
239 : :
240 : : const storage_type *getStorage() const {
241 : : assert(!HasError && "Cannot get value when an error exists!");
242 : : return reinterpret_cast<const storage_type *>(&TStorage);
243 : : }
244 : :
245 : 0 : std::error_code *getErrorStorage() {
246 [ # # ]: 0 : assert(HasError && "Cannot get error when a value exists!");
247 : 0 : return reinterpret_cast<std::error_code *>(&ErrorStorage);
248 : : }
249 : :
250 : 0 : const std::error_code *getErrorStorage() const {
251 : 0 : return const_cast<ErrorOr<T> *>(this)->getErrorStorage();
252 : : }
253 : :
254 : : union {
255 : : AlignedCharArrayUnion<storage_type> TStorage;
256 : : AlignedCharArrayUnion<std::error_code> ErrorStorage;
257 : : };
258 : : bool HasError : 1;
259 : : };
260 : :
261 : : template <class T, class E>
262 : : std::enable_if_t<std::is_error_code_enum<E>::value ||
263 : : std::is_error_condition_enum<E>::value,
264 : : bool>
265 : : operator==(const ErrorOr<T> &Err, E Code) {
266 : : return Err.getError() == Code;
267 : : }
268 : :
269 : : } // end namespace llvm
270 : :
271 : : #endif // LLVM_SUPPORT_ERROROR_H
|