LLVM  13.0.0git
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 constexpr bool isRef = std::is_reference<T>::value;
60 
61  using wrap = std::reference_wrapper<std::remove_reference_t<T>>;
62 
63 public:
64  using storage_type = std::conditional_t<isRef, wrap, T>;
65 
66 private:
67  using reference = std::remove_reference_t<T> &;
68  using const_reference = const std::remove_reference_t<T> &;
69  using pointer = std::remove_reference_t<T> *;
70  using const_pointer = const std::remove_reference_t<T> *;
71 
72 public:
73  template <class E>
74  ErrorOr(E ErrorCode,
75  std::enable_if_t<std::is_error_code_enum<E>::value ||
76  std::is_error_condition_enum<E>::value,
77  void *> = 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  std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr)
89  : HasError(false) {
90  new (getStorage()) storage_type(std::forward<OtherT>(Val));
91  }
92 
93  ErrorOr(const ErrorOr &Other) {
94  copyConstruct(Other);
95  }
96 
97  template <class OtherT>
99  std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr) {
100  copyConstruct(Other);
101  }
102 
103  template <class OtherT>
104  explicit ErrorOr(
105  const ErrorOr<OtherT> &Other,
106  std::enable_if_t<!std::is_convertible<OtherT, const T &>::value> * =
107  nullptr) {
108  copyConstruct(Other);
109  }
110 
112  moveConstruct(std::move(Other));
113  }
114 
115  template <class OtherT>
117  std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr) {
118  moveConstruct(std::move(Other));
119  }
120 
121  // This might eventually need SFINAE but it's more complex than is_convertible
122  // & I'm too lazy to write it right now.
123  template <class OtherT>
124  explicit ErrorOr(
126  std::enable_if_t<!std::is_convertible<OtherT, T>::value> * = nullptr) {
127  moveConstruct(std::move(Other));
128  }
129 
131  copyAssign(Other);
132  return *this;
133  }
134 
136  moveAssign(std::move(Other));
137  return *this;
138  }
139 
141  if (!HasError)
142  getStorage()->~storage_type();
143  }
144 
145  /// Return false if there is an error.
146  explicit operator bool() const {
147  return !HasError;
148  }
149 
150  reference get() { return *getStorage(); }
151  const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); }
152 
153  std::error_code getError() const {
154  return HasError ? *getErrorStorage() : std::error_code();
155  }
156 
157  pointer operator ->() {
158  return toPointer(getStorage());
159  }
160 
161  const_pointer operator->() const { return toPointer(getStorage()); }
162 
163  reference operator *() {
164  return *getStorage();
165  }
166 
167  const_reference operator*() const { return *getStorage(); }
168 
169 private:
170  template <class OtherT>
171  void copyConstruct(const ErrorOr<OtherT> &Other) {
172  if (!Other.HasError) {
173  // Get the other value.
174  HasError = false;
175  new (getStorage()) storage_type(*Other.getStorage());
176  } else {
177  // Get other's error.
178  HasError = true;
179  new (getErrorStorage()) std::error_code(Other.getError());
180  }
181  }
182 
183  template <class T1>
184  static bool compareThisIfSameType(const T1 &a, const T1 &b) {
185  return &a == &b;
186  }
187 
188  template <class T1, class T2>
189  static bool compareThisIfSameType(const T1 &a, const T2 &b) {
190  return false;
191  }
192 
193  template <class OtherT>
194  void copyAssign(const ErrorOr<OtherT> &Other) {
195  if (compareThisIfSameType(*this, Other))
196  return;
197 
198  this->~ErrorOr();
199  new (this) ErrorOr(Other);
200  }
201 
202  template <class OtherT>
203  void moveConstruct(ErrorOr<OtherT> &&Other) {
204  if (!Other.HasError) {
205  // Get the other value.
206  HasError = false;
207  new (getStorage()) storage_type(std::move(*Other.getStorage()));
208  } else {
209  // Get other's error.
210  HasError = true;
211  new (getErrorStorage()) std::error_code(Other.getError());
212  }
213  }
214 
215  template <class OtherT>
216  void moveAssign(ErrorOr<OtherT> &&Other) {
217  if (compareThisIfSameType(*this, Other))
218  return;
219 
220  this->~ErrorOr();
221  new (this) ErrorOr(std::move(Other));
222  }
223 
224  pointer toPointer(pointer Val) {
225  return Val;
226  }
227 
228  const_pointer toPointer(const_pointer Val) const { return Val; }
229 
230  pointer toPointer(wrap *Val) {
231  return &Val->get();
232  }
233 
234  const_pointer toPointer(const wrap *Val) const { return &Val->get(); }
235 
236  storage_type *getStorage() {
237  assert(!HasError && "Cannot get value when an error exists!");
238  return reinterpret_cast<storage_type *>(&TStorage);
239  }
240 
241  const storage_type *getStorage() const {
242  assert(!HasError && "Cannot get value when an error exists!");
243  return reinterpret_cast<const storage_type *>(&TStorage);
244  }
245 
246  std::error_code *getErrorStorage() {
247  assert(HasError && "Cannot get error when a value exists!");
248  return reinterpret_cast<std::error_code *>(&ErrorStorage);
249  }
250 
251  const std::error_code *getErrorStorage() const {
252  return const_cast<ErrorOr<T> *>(this)->getErrorStorage();
253  }
254 
255  union {
258  };
259  bool HasError : 1;
260 };
261 
262 template <class T, class E>
263 std::enable_if_t<std::is_error_code_enum<E>::value ||
264  std::is_error_condition_enum<E>::value,
265  bool>
266 operator==(const ErrorOr<T> &Err, E Code) {
267  return Err.getError() == Code;
268 }
269 
270 } // end namespace llvm
271 
272 #endif // LLVM_SUPPORT_ERROROR_H
llvm
Definition: AllocatorList.h:23
llvm::ErrorOr::operator=
ErrorOr & operator=(const ErrorOr &Other)
Definition: ErrorOr.h:130
llvm::ErrorOr::ErrorOr
ErrorOr(const ErrorOr &Other)
Definition: ErrorOr.h:93
T1
#define T1
Definition: Mips16ISelLowering.cpp:340
AlignOf.h
llvm::AlignedCharArrayUnion< storage_type >
llvm::ErrorOr::TStorage
AlignedCharArrayUnion< storage_type > TStorage
Definition: ErrorOr.h:256
llvm::ErrorOr::operator*
reference operator*()
Definition: ErrorOr.h:163
a
=0.0 ? 0.0 :(a > 0.0 ? 1.0 :-1.0) a
Definition: README.txt:489
llvm::ErrorOr::storage_type
std::conditional_t< isRef, wrap, T > storage_type
Definition: ErrorOr.h:64
E
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
b
the resulting code requires compare and branches when and if the revised code is with conditional branches instead of More there is a byte word extend before each where there should be only and the condition codes are not remembered when the same two values are compared twice More LSR enhancements i8 and i32 load store addressing modes are identical int b
Definition: README.txt:418
false
Definition: StackSlotColoring.cpp:142
llvm::ErrorOr::getError
std::error_code getError() const
Definition: ErrorOr.h:153
llvm::ErrorOr::ErrorOr
ErrorOr(std::error_code EC)
Definition: ErrorOr.h:82
llvm::ErrorOr::ErrorOr
ErrorOr(ErrorOr &&Other)
Definition: ErrorOr.h:111
move
compiles ldr LCPI1_0 ldr ldr mov lsr tst moveq r1 ldr LCPI1_1 and r0 bx lr It would be better to do something like to fold the shift into the conditional move
Definition: README.txt:546
llvm::ErrorOr::operator->
pointer operator->()
Definition: ErrorOr.h:157
llvm::ErrorOr::operator=
ErrorOr & operator=(ErrorOr &&Other)
Definition: ErrorOr.h:135
assert
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
llvm::operator==
bool operator==(uint64_t V1, const APInt &V2)
Definition: APInt.h:2037
llvm::wrap
LLVMAttributeRef wrap(Attribute Attr)
Definition: Attributes.h:239
llvm::ErrorOr::operator*
const_reference operator*() const
Definition: ErrorOr.h:167
llvm::make_error_code
std::error_code make_error_code(BitcodeError E)
Definition: BitcodeReader.h:270
llvm::ErrorOr::ErrorOr
ErrorOr(const ErrorOr< OtherT > &Other, std::enable_if_t<!std::is_convertible< OtherT, const T & >::value > *=nullptr)
Definition: ErrorOr.h:104
llvm::ErrorOr::get
const_reference get() const
Definition: ErrorOr.h:151
llvm::ErrorOr::operator->
const_pointer operator->() const
Definition: ErrorOr.h:161
llvm::ErrorOr::ErrorOr
ErrorOr(ErrorOr< OtherT > &&Other, std::enable_if_t< std::is_convertible< OtherT, T >::value > *=nullptr)
Definition: ErrorOr.h:116
llvm::ErrorOr::ErrorOr
friend class ErrorOr
Definition: ErrorOr.h:57
llvm::ErrorOr::get
reference get()
Definition: ErrorOr.h:150
llvm::ErrorOr::ErrorOr
ErrorOr(const ErrorOr< OtherT > &Other, std::enable_if_t< std::is_convertible< OtherT, T >::value > *=nullptr)
Definition: ErrorOr.h:98
llvm::ErrorOr
Represents either an error or a value T.
Definition: ErrorOr.h:56
llvm::ErrorOr::ErrorStorage
AlignedCharArrayUnion< std::error_code > ErrorStorage
Definition: ErrorOr.h:257
llvm::ErrorOr::ErrorOr
ErrorOr(ErrorOr< OtherT > &&Other, std::enable_if_t<!std::is_convertible< OtherT, T >::value > *=nullptr)
Definition: ErrorOr.h:124
true
basic Basic Alias true
Definition: BasicAliasAnalysis.cpp:1799
llvm::ErrorOr::~ErrorOr
~ErrorOr()
Definition: ErrorOr.h:140
llvm::ErrorOr::ErrorOr
ErrorOr(E ErrorCode, std::enable_if_t< std::is_error_code_enum< E >::value||std::is_error_condition_enum< E >::value, void * >=nullptr)
Definition: ErrorOr.h:74
Other
Optional< std::vector< StOtherPiece > > Other
Definition: ELFYAML.cpp:1169
llvm::ErrorOr::ErrorOr
ErrorOr(OtherT &&Val, std::enable_if_t< std::is_convertible< OtherT, T >::value > *=nullptr)
Definition: ErrorOr.h:87