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