LLVM API Documentation

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ErrorOr.h
Go to the documentation of this file.
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 
20 #include "llvm/Support/AlignOf.h"
23 
24 #include <cassert>
25 #if LLVM_HAS_CXX11_TYPETRAITS
26 #include <type_traits>
27 #endif
28 
29 namespace llvm {
30 #if LLVM_HAS_CXX11_TYPETRAITS && LLVM_HAS_RVALUE_REFERENCES
31 template<class T, class V>
32 typename 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 
38 template<class T, class V>
39 typename std::enable_if< !std::is_constructible<T, V>::value
40  , typename std::remove_reference<V>::type>::type &
42  return Val;
43 }
44 #else
45 template<class T, class V>
47  return Val;
48 }
49 #endif
50 
51 /// \brief Stores a reference that can be changed.
52 template <typename T>
54  T *Storage;
55 
56 public:
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.
93 template<class T>
94 class ErrorOr {
95  template <class OtherT> friend class ErrorOr;
96  static const bool isRef = is_reference<T>::value;
98 
99 public:
100  typedef typename
101  conditional< isRef
102  , wrap
103  , T
104  >::type storage_type;
105 
106 private:
107  typedef typename remove_reference<T>::type &reference;
108  typedef typename remove_reference<T>::type *pointer;
109 
110 public:
111  template <class E>
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>
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 
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 
187  return toPointer(get());
188  }
189 
191  return *get();
192  }
193 
194 private:
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 {
282  };
283  bool HasError : 1;
284 };
285 
286 template<class T, class E>
287 typename enable_if_c<is_error_code_enum<E>::value ||
288  is_error_condition_enum<E>::value, bool>::type
290  return error_code(Err) == Code;
291 }
292 } // end namespace llvm
293 
294 #endif
Represents either an error or a value T.
Definition: ErrorOr.h:94
ErrorOr & operator=(const ErrorOr &Other)
Definition: ErrorOr.h:136
ErrorOr(llvm::error_code EC)
Definition: ErrorOr.h:119
ErrorOr(E ErrorCode, typename enable_if_c< is_error_code_enum< E >::value||is_error_condition_enum< E >::value, void * >::type=0)
Definition: ErrorOr.h:112
void(* unspecified_bool_type)()
Definition: ErrorOr.h:174
AlignedCharArrayUnion< error_code > ErrorStorage
Definition: ErrorOr.h:281
reference operator*()
Definition: ErrorOr.h:190
pointer operator->()
Definition: ErrorOr.h:186
#define false
Definition: ConvertUTF.c:64
conditional< isRef, wrap, T >::type storage_type
Definition: ErrorOr.h:104
ErrorOr(T Val)
Definition: ErrorOr.h:123
friend class ErrorOr
Definition: ErrorOr.h:95
ErrorOr(const ErrorOr< OtherT > &Other)
Definition: ErrorOr.h:132
#define true
Definition: ConvertUTF.c:65
ReferenceStorage(T &Ref)
Definition: ErrorOr.h:57
V & moveIfMoveConstructible(V &Val)
Definition: ErrorOr.h:46
ErrorOr(const ErrorOr &Other)
Definition: ErrorOr.h:127
AlignedCharArrayUnion< storage_type > TStorage
Definition: ErrorOr.h:280
static void unspecified_bool_true()
Definition: ErrorOr.h:175
LLVMContextRef * wrap(const LLVMContext **Tys)
Definition: LLVMContext.h:123
static error_code success()
Definition: system_error.h:732
Stores a reference that can be changed.
Definition: ErrorOr.h:53
Metafunction that determines wheather the given type is a reference.
Definition: type_traits.h:149
bool operator==(uint64_t V1, const APInt &V2)
Definition: APInt.h:1684
error_code make_error_code(errc _e)
Definition: system_error.h:782
#define T1