LLVM  9.0.0svn
ErrorOr.h
Go to the documentation of this file.
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 
24 namespace 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.
55 template<class T>
56 class 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 
63 public:
64  using storage_type = typename std::conditional<isRef, wrap, T>::type;
65 
66 private:
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 
72 public:
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>
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 
115  moveConstruct(std::move(Other));
116  }
117 
118  template <class OtherT>
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(
131  typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * =
132  nullptr) {
133  moveConstruct(std::move(Other));
134  }
135 
137  copyAssign(Other);
138  return *this;
139  }
140 
142  moveAssign(std::move(Other));
143  return *this;
144  }
145 
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 
175 private:
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 {
264  };
265  bool HasError : 1;
266 };
267 
268 template <class T, class E>
269 typename std::enable_if<std::is_error_code_enum<E>::value ||
270  std::is_error_condition_enum<E>::value,
271  bool>::type
272 operator==(const ErrorOr<T> &Err, E Code) {
273  return Err.getError() == Code;
274 }
275 
276 } // end namespace llvm
277 
278 #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:99
Represents either an error or a value T.
Definition: ErrorOr.h:56
ErrorOr & operator=(const ErrorOr &Other)
Definition: ErrorOr.h:136
This class represents lattice values for constants.
Definition: AllocatorList.h:23
ErrorOr(OtherT &&Val, typename std::enable_if< std::is_convertible< OtherT, T >::value >::type *=nullptr)
Definition: ErrorOr.h:87
typename std::conditional< isRef, wrap, T >::type storage_type
Definition: ErrorOr.h:64
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:107
reference operator*()
Definition: ErrorOr.h:169
pointer operator->()
Definition: ErrorOr.h:163
std::error_code make_error_code(BitcodeError E)
ELFYAML::ELF_STO Other
Definition: ELFYAML.cpp:871
friend class ErrorOr
Definition: ErrorOr.h:57
const_pointer operator->() const
Definition: ErrorOr.h:167
AlignedCharArrayUnion< std::error_code > ErrorStorage
Definition: ErrorOr.h:263
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
ErrorOr(const ErrorOr &Other)
Definition: ErrorOr.h:94
std::error_code getError() const
Definition: ErrorOr.h:159
AlignedCharArrayUnion< storage_type > TStorage
Definition: ErrorOr.h:262
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:74
char buffer[Size]
Definition: AlignOf.h:36
ErrorOr(ErrorOr< OtherT > &&Other, typename std::enable_if<!std::is_convertible< OtherT, T >::value >::type *=nullptr)
Definition: ErrorOr.h:129
ErrorOr(std::error_code EC)
Definition: ErrorOr.h:82
ErrorOr(ErrorOr< OtherT > &&Other, typename std::enable_if< std::is_convertible< OtherT, T >::value >::type *=nullptr)
Definition: ErrorOr.h:119
const_reference operator*() const
Definition: ErrorOr.h:173
ErrorOr(ErrorOr &&Other)
Definition: ErrorOr.h:114
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
ErrorOr & operator=(ErrorOr &&Other)
Definition: ErrorOr.h:141
bool operator==(uint64_t V1, const APInt &V2)
Definition: APInt.h:1966
#define T1