ErrorOr.h revision 263508
1//===- llvm/Support/ErrorOr.h - Error Smart Pointer -----------------------===//
2//
3//                             The LLVM Linker
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9///
10/// \file
11///
12/// Provides ErrorOr<T> smart pointer.
13///
14//===----------------------------------------------------------------------===//
15
16#ifndef LLVM_SUPPORT_ERROR_OR_H
17#define LLVM_SUPPORT_ERROR_OR_H
18
19#include "llvm/ADT/PointerIntPair.h"
20#include "llvm/Support/AlignOf.h"
21#include "llvm/Support/system_error.h"
22#include "llvm/Support/type_traits.h"
23
24#include <cassert>
25#if LLVM_HAS_CXX11_TYPETRAITS
26#include <type_traits>
27#endif
28
29namespace llvm {
30#if LLVM_HAS_CXX11_TYPETRAITS && LLVM_HAS_RVALUE_REFERENCES
31template<class T, class V>
32typename std::enable_if< std::is_constructible<T, V>::value
33                       , typename std::remove_reference<V>::type>::type &&
34 moveIfMoveConstructible(V &Val) {
35  return std::move(Val);
36}
37
38template<class T, class V>
39typename std::enable_if< !std::is_constructible<T, V>::value
40                       , typename std::remove_reference<V>::type>::type &
41moveIfMoveConstructible(V &Val) {
42  return Val;
43}
44#else
45template<class T, class V>
46V &moveIfMoveConstructible(V &Val) {
47  return Val;
48}
49#endif
50
51/// \brief Stores a reference that can be changed.
52template <typename T>
53class ReferenceStorage {
54  T *Storage;
55
56public:
57  ReferenceStorage(T &Ref) : Storage(&Ref) {}
58
59  operator T &() const { return *Storage; }
60  T &get() const { return *Storage; }
61};
62
63/// \brief Represents either an error or a value T.
64///
65/// ErrorOr<T> is a pointer-like class that represents the result of an
66/// operation. The result is either an error, or a value of type T. This is
67/// designed to emulate the usage of returning a pointer where nullptr indicates
68/// failure. However instead of just knowing that the operation failed, we also
69/// have an error_code and optional user data that describes why it failed.
70///
71/// It is used like the following.
72/// \code
73///   ErrorOr<Buffer> getBuffer();
74///   void handleError(error_code ec);
75///
76///   auto buffer = getBuffer();
77///   if (!buffer)
78///     handleError(buffer);
79///   buffer->write("adena");
80/// \endcode
81///
82///
83/// An implicit conversion to bool provides a way to check if there was an
84/// error. The unary * and -> operators provide pointer like access to the
85/// value. Accessing the value when there is an error has undefined behavior.
86///
87/// When T is a reference type the behaivor is slightly different. The reference
88/// is held in a std::reference_wrapper<std::remove_reference<T>::type>, and
89/// there is special handling to make operator -> work as if T was not a
90/// reference.
91///
92/// T cannot be a rvalue reference.
93template<class T>
94class ErrorOr {
95  template <class OtherT> friend class ErrorOr;
96  static const bool isRef = is_reference<T>::value;
97  typedef ReferenceStorage<typename remove_reference<T>::type> wrap;
98
99public:
100  typedef typename
101    conditional< isRef
102               , wrap
103               , T
104               >::type storage_type;
105
106private:
107  typedef typename remove_reference<T>::type &reference;
108  typedef typename remove_reference<T>::type *pointer;
109
110public:
111  template <class E>
112  ErrorOr(E ErrorCode, typename enable_if_c<is_error_code_enum<E>::value ||
113                                            is_error_condition_enum<E>::value,
114                                            void *>::type = 0)
115      : HasError(true) {
116    new (getError()) error_code(make_error_code(ErrorCode));
117  }
118
119  ErrorOr(llvm::error_code EC) : HasError(true) {
120    new (getError()) error_code(EC);
121  }
122
123  ErrorOr(T Val) : HasError(false) {
124    new (get()) storage_type(moveIfMoveConstructible<storage_type>(Val));
125  }
126
127  ErrorOr(const ErrorOr &Other) {
128    copyConstruct(Other);
129  }
130
131  template <class OtherT>
132  ErrorOr(const ErrorOr<OtherT> &Other) {
133    copyConstruct(Other);
134  }
135
136  ErrorOr &operator =(const ErrorOr &Other) {
137    copyAssign(Other);
138    return *this;
139  }
140
141  template <class OtherT>
142  ErrorOr &operator =(const ErrorOr<OtherT> &Other) {
143    copyAssign(Other);
144    return *this;
145  }
146
147#if LLVM_HAS_RVALUE_REFERENCES
148  ErrorOr(ErrorOr &&Other) {
149    moveConstruct(std::move(Other));
150  }
151
152  template <class OtherT>
153  ErrorOr(ErrorOr<OtherT> &&Other) {
154    moveConstruct(std::move(Other));
155  }
156
157  ErrorOr &operator =(ErrorOr &&Other) {
158    moveAssign(std::move(Other));
159    return *this;
160  }
161
162  template <class OtherT>
163  ErrorOr &operator =(ErrorOr<OtherT> &&Other) {
164    moveAssign(std::move(Other));
165    return *this;
166  }
167#endif
168
169  ~ErrorOr() {
170    if (!HasError)
171      get()->~storage_type();
172  }
173
174  typedef void (*unspecified_bool_type)();
175  static void unspecified_bool_true() {}
176
177  /// \brief Return false if there is an error.
178  operator unspecified_bool_type() const {
179    return HasError ? 0 : unspecified_bool_true;
180  }
181
182  operator llvm::error_code() const {
183    return HasError ? *getError() : llvm::error_code::success();
184  }
185
186  pointer operator ->() {
187    return toPointer(get());
188  }
189
190  reference operator *() {
191    return *get();
192  }
193
194private:
195  template <class OtherT>
196  void copyConstruct(const ErrorOr<OtherT> &Other) {
197    if (!Other.HasError) {
198      // Get the other value.
199      HasError = false;
200      new (get()) storage_type(*Other.get());
201    } else {
202      // Get other's error.
203      HasError = true;
204      new (getError()) error_code(Other);
205    }
206  }
207
208  template <class T1>
209  static bool compareThisIfSameType(const T1 &a, const T1 &b) {
210    return &a == &b;
211  }
212
213  template <class T1, class T2>
214  static bool compareThisIfSameType(const T1 &a, const T2 &b) {
215    return false;
216  }
217
218  template <class OtherT>
219  void copyAssign(const ErrorOr<OtherT> &Other) {
220    if (compareThisIfSameType(*this, Other))
221      return;
222
223    this->~ErrorOr();
224    new (this) ErrorOr(Other);
225  }
226
227#if LLVM_HAS_RVALUE_REFERENCES
228  template <class OtherT>
229  void moveConstruct(ErrorOr<OtherT> &&Other) {
230    if (!Other.HasError) {
231      // Get the other value.
232      HasError = false;
233      new (get()) storage_type(std::move(*Other.get()));
234    } else {
235      // Get other's error.
236      HasError = true;
237      new (getError()) error_code(Other);
238    }
239  }
240
241  template <class OtherT>
242  void moveAssign(ErrorOr<OtherT> &&Other) {
243    if (compareThisIfSameType(*this, Other))
244      return;
245
246    this->~ErrorOr();
247    new (this) ErrorOr(std::move(Other));
248  }
249#endif
250
251  pointer toPointer(pointer Val) {
252    return Val;
253  }
254
255  pointer toPointer(wrap *Val) {
256    return &Val->get();
257  }
258
259  storage_type *get() {
260    assert(!HasError && "Cannot get value when an error exists!");
261    return reinterpret_cast<storage_type*>(TStorage.buffer);
262  }
263
264  const storage_type *get() const {
265    assert(!HasError && "Cannot get value when an error exists!");
266    return reinterpret_cast<const storage_type*>(TStorage.buffer);
267  }
268
269  error_code *getError() {
270    assert(HasError && "Cannot get error when a value exists!");
271    return reinterpret_cast<error_code*>(ErrorStorage.buffer);
272  }
273
274  const error_code *getError() const {
275    return const_cast<ErrorOr<T> *>(this)->getError();
276  }
277
278
279  union {
280    AlignedCharArrayUnion<storage_type> TStorage;
281    AlignedCharArrayUnion<error_code> ErrorStorage;
282  };
283  bool HasError : 1;
284};
285
286template<class T, class E>
287typename enable_if_c<is_error_code_enum<E>::value ||
288                     is_error_condition_enum<E>::value, bool>::type
289operator ==(ErrorOr<T> &Err, E Code) {
290  return error_code(Err) == Code;
291}
292} // end namespace llvm
293
294#endif
295