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 const bool isRef = std::is_reference<T>::value;
60
61  using wrap = std::reference_wrapper<typename std::remove_reference<T>::type>;
62
63public:
64  using storage_type = typename std::conditional<isRef, wrap, T>::type;
65
66private:
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 *;
71
72public:
73  template <class E>
74  ErrorOr(E ErrorCode,
75          typename std::enable_if<std::is_error_code_enum<E>::value ||
76                                      std::is_error_condition_enum<E>::value,
77                                  void *>::type = 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          typename std::enable_if<std::is_convertible<OtherT, T>::value>::type
89              * = nullptr)
90      : HasError(false) {
91    new (getStorage()) storage_type(std::forward<OtherT>(Val));
92  }
93
94  ErrorOr(const ErrorOr &Other) {
95    copyConstruct(Other);
96  }
97
98  template <class OtherT>
99  ErrorOr(
100      const ErrorOr<OtherT> &Other,
101      typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
102          nullptr) {
103    copyConstruct(Other);
104  }
105
106  template <class OtherT>
107  explicit ErrorOr(
108      const ErrorOr<OtherT> &Other,
109      typename std::enable_if<
110          !std::is_convertible<OtherT, const T &>::value>::type * = nullptr) {
111    copyConstruct(Other);
112  }
113
114  ErrorOr(ErrorOr &&Other) {
115    moveConstruct(std::move(Other));
116  }
117
118  template <class OtherT>
119  ErrorOr(
120      ErrorOr<OtherT> &&Other,
121      typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
122          nullptr) {
123    moveConstruct(std::move(Other));
124  }
125
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>
129  explicit ErrorOr(
130      ErrorOr<OtherT> &&Other,
131      typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * =
132          nullptr) {
133    moveConstruct(std::move(Other));
134  }
135
136  ErrorOr &operator=(const ErrorOr &Other) {
137    copyAssign(Other);
138    return *this;
139  }
140
141  ErrorOr &operator=(ErrorOr &&Other) {
142    moveAssign(std::move(Other));
143    return *this;
144  }
145
146  ~ErrorOr() {
147    if (!HasError)
148      getStorage()->~storage_type();
149  }
150
151  /// Return false if there is an error.
152  explicit operator bool() const {
153    return !HasError;
154  }
155
156  reference get() { return *getStorage(); }
157  const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); }
158
159  std::error_code getError() const {
160    return HasError ? *getErrorStorage() : std::error_code();
161  }
162
163  pointer operator ->() {
164    return toPointer(getStorage());
165  }
166
167  const_pointer operator->() const { return toPointer(getStorage()); }
168
169  reference operator *() {
170    return *getStorage();
171  }
172
173  const_reference operator*() const { return *getStorage(); }
174
175private:
176  template <class OtherT>
177  void copyConstruct(const ErrorOr<OtherT> &Other) {
178    if (!Other.HasError) {
179      // Get the other value.
180      HasError = false;
181      new (getStorage()) storage_type(*Other.getStorage());
182    } else {
183      // Get other's error.
184      HasError = true;
185      new (getErrorStorage()) std::error_code(Other.getError());
186    }
187  }
188
189  template <class T1>
190  static bool compareThisIfSameType(const T1 &a, const T1 &b) {
191    return &a == &b;
192  }
193
194  template <class T1, class T2>
195  static bool compareThisIfSameType(const T1 &a, const T2 &b) {
196    return false;
197  }
198
199  template <class OtherT>
200  void copyAssign(const ErrorOr<OtherT> &Other) {
201    if (compareThisIfSameType(*this, Other))
202      return;
203
204    this->~ErrorOr();
205    new (this) ErrorOr(Other);
206  }
207
208  template <class OtherT>
209  void moveConstruct(ErrorOr<OtherT> &&Other) {
210    if (!Other.HasError) {
211      // Get the other value.
212      HasError = false;
213      new (getStorage()) storage_type(std::move(*Other.getStorage()));
214    } else {
215      // Get other's error.
216      HasError = true;
217      new (getErrorStorage()) std::error_code(Other.getError());
218    }
219  }
220
221  template <class OtherT>
222  void moveAssign(ErrorOr<OtherT> &&Other) {
223    if (compareThisIfSameType(*this, Other))
224      return;
225
226    this->~ErrorOr();
227    new (this) ErrorOr(std::move(Other));
228  }
229
230  pointer toPointer(pointer Val) {
231    return Val;
232  }
233
234  const_pointer toPointer(const_pointer Val) const { return Val; }
235
236  pointer toPointer(wrap *Val) {
237    return &Val->get();
238  }
239
240  const_pointer toPointer(const wrap *Val) const { return &Val->get(); }
241
242  storage_type *getStorage() {
243    assert(!HasError && "Cannot get value when an error exists!");
244    return reinterpret_cast<storage_type*>(TStorage.buffer);
245  }
246
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);
250  }
251
252  std::error_code *getErrorStorage() {
253    assert(HasError && "Cannot get error when a value exists!");
254    return reinterpret_cast<std::error_code *>(ErrorStorage.buffer);
255  }
256
257  const std::error_code *getErrorStorage() const {
258    return const_cast<ErrorOr<T> *>(this)->getErrorStorage();
259  }
260
261  union {
262    AlignedCharArrayUnion<storage_type> TStorage;
263    AlignedCharArrayUnion<std::error_code> ErrorStorage;
264  };
265  bool HasError : 1;
266};
267
268template <class T, class E>
269typename std::enable_if<std::is_error_code_enum<E>::value ||
270                            std::is_error_condition_enum<E>::value,
271                        bool>::type
272operator==(const ErrorOr<T> &Err, E Code) {
273  return Err.getError() == Code;
274}
275
276} // end namespace llvm
277
278#endif // LLVM_SUPPORT_ERROROR_H
279