LLVM  4.0.0
ErrorOr.h
Go to the documentation of this file.
1 //===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- C++ -*-===//
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_ERROROR_H
17 #define LLVM_SUPPORT_ERROROR_H
18 
20 #include "llvm/Support/AlignOf.h"
21 #include <cassert>
22 #include <system_error>
23 #include <type_traits>
24 
25 namespace llvm {
26 /// \brief Stores a reference that can be changed.
27 template <typename T>
29  T *Storage;
30 
31 public:
32  ReferenceStorage(T &Ref) : Storage(&Ref) {}
33 
34  operator T &() const { return *Storage; }
35  T &get() const { return *Storage; }
36 };
37 
38 /// \brief Represents either an error or a value T.
39 ///
40 /// ErrorOr<T> is a pointer-like class that represents the result of an
41 /// operation. The result is either an error, or a value of type T. This is
42 /// designed to emulate the usage of returning a pointer where nullptr indicates
43 /// failure. However instead of just knowing that the operation failed, we also
44 /// have an error_code and optional user data that describes why it failed.
45 ///
46 /// It is used like the following.
47 /// \code
48 /// ErrorOr<Buffer> getBuffer();
49 ///
50 /// auto buffer = getBuffer();
51 /// if (error_code ec = buffer.getError())
52 /// return ec;
53 /// buffer->write("adena");
54 /// \endcode
55 ///
56 ///
57 /// Implicit conversion to bool returns true if there is a usable value. The
58 /// unary * and -> operators provide pointer like access to the value. Accessing
59 /// the value when there is an error has undefined behavior.
60 ///
61 /// When T is a reference type the behavior is slightly different. The reference
62 /// is held in a std::reference_wrapper<std::remove_reference<T>::type>, and
63 /// there is special handling to make operator -> work as if T was not a
64 /// reference.
65 ///
66 /// T cannot be a rvalue reference.
67 template<class T>
68 class ErrorOr {
69  template <class OtherT> friend class ErrorOr;
70  static const bool isRef = std::is_reference<T>::value;
72 
73 public:
74  typedef typename std::conditional<isRef, wrap, T>::type storage_type;
75 
76 private:
77  typedef typename std::remove_reference<T>::type &reference;
78  typedef const typename std::remove_reference<T>::type &const_reference;
79  typedef typename std::remove_reference<T>::type *pointer;
80  typedef const typename std::remove_reference<T>::type *const_pointer;
81 
82 public:
83  template <class E>
84  ErrorOr(E ErrorCode,
85  typename std::enable_if<std::is_error_code_enum<E>::value ||
86  std::is_error_condition_enum<E>::value,
87  void *>::type = nullptr)
88  : HasError(true) {
89  new (getErrorStorage()) std::error_code(make_error_code(ErrorCode));
90  }
91 
92  ErrorOr(std::error_code EC) : HasError(true) {
93  new (getErrorStorage()) std::error_code(EC);
94  }
95 
96  template <class OtherT>
97  ErrorOr(OtherT &&Val,
98  typename std::enable_if<std::is_convertible<OtherT, T>::value>::type
99  * = nullptr)
100  : HasError(false) {
101  new (getStorage()) storage_type(std::forward<OtherT>(Val));
102  }
103 
105  copyConstruct(Other);
106  }
107 
108  template <class OtherT>
110  const ErrorOr<OtherT> &Other,
111  typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
112  nullptr) {
113  copyConstruct(Other);
114  }
115 
116  template <class OtherT>
117  explicit ErrorOr(
118  const ErrorOr<OtherT> &Other,
119  typename std::enable_if<
120  !std::is_convertible<OtherT, const T &>::value>::type * = nullptr) {
121  copyConstruct(Other);
122  }
123 
125  moveConstruct(std::move(Other));
126  }
127 
128  template <class OtherT>
131  typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
132  nullptr) {
133  moveConstruct(std::move(Other));
134  }
135 
136  // This might eventually need SFINAE but it's more complex than is_convertible
137  // & I'm too lazy to write it right now.
138  template <class OtherT>
139  explicit ErrorOr(
141  typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * =
142  nullptr) {
143  moveConstruct(std::move(Other));
144  }
145 
147  copyAssign(Other);
148  return *this;
149  }
150 
152  moveAssign(std::move(Other));
153  return *this;
154  }
155 
157  if (!HasError)
158  getStorage()->~storage_type();
159  }
160 
161  /// \brief Return false if there is an error.
162  explicit operator bool() const {
163  return !HasError;
164  }
165 
166  reference get() { return *getStorage(); }
167  const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); }
168 
169  std::error_code getError() const {
170  return HasError ? *getErrorStorage() : std::error_code();
171  }
172 
173  pointer operator ->() {
174  return toPointer(getStorage());
175  }
176 
177  const_pointer operator->() const { return toPointer(getStorage()); }
178 
179  reference operator *() {
180  return *getStorage();
181  }
182 
183  const_reference operator*() const { return *getStorage(); }
184 
185 private:
186  template <class OtherT>
187  void copyConstruct(const ErrorOr<OtherT> &Other) {
188  if (!Other.HasError) {
189  // Get the other value.
190  HasError = false;
191  new (getStorage()) storage_type(*Other.getStorage());
192  } else {
193  // Get other's error.
194  HasError = true;
195  new (getErrorStorage()) std::error_code(Other.getError());
196  }
197  }
198 
199  template <class T1>
200  static bool compareThisIfSameType(const T1 &a, const T1 &b) {
201  return &a == &b;
202  }
203 
204  template <class T1, class T2>
205  static bool compareThisIfSameType(const T1 &a, const T2 &b) {
206  return false;
207  }
208 
209  template <class OtherT>
210  void copyAssign(const ErrorOr<OtherT> &Other) {
211  if (compareThisIfSameType(*this, Other))
212  return;
213 
214  this->~ErrorOr();
215  new (this) ErrorOr(Other);
216  }
217 
218  template <class OtherT>
219  void moveConstruct(ErrorOr<OtherT> &&Other) {
220  if (!Other.HasError) {
221  // Get the other value.
222  HasError = false;
223  new (getStorage()) storage_type(std::move(*Other.getStorage()));
224  } else {
225  // Get other's error.
226  HasError = true;
227  new (getErrorStorage()) std::error_code(Other.getError());
228  }
229  }
230 
231  template <class OtherT>
232  void moveAssign(ErrorOr<OtherT> &&Other) {
233  if (compareThisIfSameType(*this, Other))
234  return;
235 
236  this->~ErrorOr();
237  new (this) ErrorOr(std::move(Other));
238  }
239 
240  pointer toPointer(pointer Val) {
241  return Val;
242  }
243 
244  const_pointer toPointer(const_pointer Val) const { return Val; }
245 
246  pointer toPointer(wrap *Val) {
247  return &Val->get();
248  }
249 
250  const_pointer toPointer(const wrap *Val) const { return &Val->get(); }
251 
252  storage_type *getStorage() {
253  assert(!HasError && "Cannot get value when an error exists!");
254  return reinterpret_cast<storage_type*>(TStorage.buffer);
255  }
256 
257  const storage_type *getStorage() const {
258  assert(!HasError && "Cannot get value when an error exists!");
259  return reinterpret_cast<const storage_type*>(TStorage.buffer);
260  }
261 
262  std::error_code *getErrorStorage() {
263  assert(HasError && "Cannot get error when a value exists!");
264  return reinterpret_cast<std::error_code *>(ErrorStorage.buffer);
265  }
266 
267  const std::error_code *getErrorStorage() const {
268  return const_cast<ErrorOr<T> *>(this)->getErrorStorage();
269  }
270 
271  union {
274  };
275  bool HasError : 1;
276 };
277 
278 template <class T, class E>
279 typename std::enable_if<std::is_error_code_enum<E>::value ||
280  std::is_error_condition_enum<E>::value,
281  bool>::type
282 operator==(const ErrorOr<T> &Err, E Code) {
283  return Err.getError() == Code;
284 }
285 } // end namespace llvm
286 
287 #endif // LLVM_SUPPORT_ERROROR_H
ErrorOr(const ErrorOr< OtherT > &Other, typename std::enable_if< std::is_convertible< OtherT, T >::value >::type *=nullptr)
Definition: ErrorOr.h:109
std::error_code getError() const
Definition: ErrorOr.h:169
Represents either an error or a value T.
Definition: ErrorOr.h:68
ErrorOr & operator=(const ErrorOr &Other)
Definition: ErrorOr.h:146
ErrorOr(OtherT &&Val, typename std::enable_if< std::is_convertible< OtherT, T >::value >::type *=nullptr)
Definition: ErrorOr.h:97
const_pointer operator->() const
Definition: ErrorOr.h:177
reference operator*()
Definition: ErrorOr.h:179
pointer operator->()
Definition: ErrorOr.h:173
std::error_code make_error_code(BitcodeError E)
ELFYAML::ELF_STO Other
Definition: ELFYAML.cpp:662
Function Alias Analysis false
friend class ErrorOr
Definition: ErrorOr.h:69
static GCRegistry::Add< CoreCLRGC > E("coreclr","CoreCLR-compatible GC")
ErrorOr(E ErrorCode, typename std::enable_if< std::is_error_code_enum< E >::value||std::is_error_condition_enum< E >::value, void * >::type=nullptr)
Definition: ErrorOr.h:84
AlignedCharArrayUnion< std::error_code > ErrorStorage
Definition: ErrorOr.h:273
ReferenceStorage(T &Ref)
Definition: ErrorOr.h:32
ErrorOr(const ErrorOr &Other)
Definition: ErrorOr.h:104
AlignedCharArrayUnion< storage_type > TStorage
Definition: ErrorOr.h:272
ErrorOr(ErrorOr< OtherT > &&Other, typename std::enable_if<!std::is_convertible< OtherT, T >::value >::type *=nullptr)
Definition: ErrorOr.h:139
ErrorOr(std::error_code EC)
Definition: ErrorOr.h:92
ErrorOr(ErrorOr< OtherT > &&Other, typename std::enable_if< std::is_convertible< OtherT, T >::value >::type *=nullptr)
Definition: ErrorOr.h:129
std::conditional< isRef, wrap, T >::type storage_type
Definition: ErrorOr.h:74
Basic Alias true
LLVMAttributeRef wrap(Attribute Attr)
Definition: Attributes.h:186
ErrorOr(const ErrorOr< OtherT > &Other, typename std::enable_if< !std::is_convertible< OtherT, const T & >::value >::type *=nullptr)
Definition: ErrorOr.h:117
ErrorOr(ErrorOr &&Other)
Definition: ErrorOr.h:124
Stores a reference that can be changed.
Definition: ErrorOr.h:28
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
ErrorOr & operator=(ErrorOr &&Other)
Definition: ErrorOr.h:151
const_reference operator*() const
Definition: ErrorOr.h:183
bool operator==(uint64_t V1, const APInt &V2)
Definition: APInt.h:1722
#define T1