llvm.org GIT mirror llvm / 01812be
[Support] Port ErrorOr<T> from lld to C++03. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@172991 91177308-0d34-0410-b5e6-96231b3b80d8 Michael J. Spencer 7 years ago
5 changed file(s) with 452 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
3939 #define LLVM_HAS_RVALUE_REFERENCE_THIS 1
4040 #else
4141 #define LLVM_HAS_RVALUE_REFERENCE_THIS 0
42 #endif
43
44 /// \macro LLVM_HAS_CXX11_TYPETRAITS
45 /// \brief Does the compiler have the C++11 type traits.
46 ///
47 /// #include
48 ///
49 /// * enable_if
50 /// * {true,false}_type
51 /// * is_constructible
52 /// * etc...
53 #if defined(__GXX_EXPERIMENTAL_CXX0X__) \
54 || (defined(_MSC_VER) && _MSC_VER >= 1600)
55 #define LLVM_HAS_CXX11_TYPETRAITS 1
56 #else
57 #define LLVM_HAS_CXX11_TYPETRAITS 0
58 #endif
59
60 /// \macro LLVM_HAS_CXX11_STDLIB
61 /// \brief Does the compiler have the C++11 standard library.
62 ///
63 /// Implies LLVM_HAS_RVALUE_REFERENCES, LLVM_HAS_CXX11_TYPETRAITS
64 #if defined(__GXX_EXPERIMENTAL_CXX0X__) \
65 || (defined(_MSC_VER) && _MSC_VER >= 1600)
66 #define LLVM_HAS_CXX11_STDLIB 1
67 #else
68 #define LLVM_HAS_CXX11_STDLIB 0
4269 #endif
4370
4471 /// llvm_move - Expands to ::std::move if the compiler supports
0 //===- llvm/Support/ErrorOr.h - Error Smart Pointer -----------------------===//
1 //
2 // The LLVM Linker
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 ///
11 /// Provides ErrorOr smart pointer.
12 ///
13 //===----------------------------------------------------------------------===//
14
15 #ifndef LLVM_SUPPORT_ERROR_OR_H
16 #define LLVM_SUPPORT_ERROR_OR_H
17
18 #include "llvm/Support/AlignOf.h"
19 #include "llvm/Support/system_error.h"
20 #include "llvm/Support/type_traits.h"
21
22 #include
23 #if LLVM_HAS_CXX11_TYPETRAITS
24 #include
25 #endif
26
27 namespace llvm {
28 struct ErrorHolderBase {
29 error_code Error;
30 uint16_t RefCount;
31 bool HasUserData;
32
33 ErrorHolderBase() : RefCount(1) {}
34
35 void aquire() {
36 ++RefCount;
37 }
38
39 void release() {
40 if (--RefCount == 0)
41 delete this;
42 }
43
44 protected:
45 virtual ~ErrorHolderBase() {}
46 };
47
48 template
49 struct ErrorHolder : ErrorHolderBase {
50 #if LLVM_HAS_RVALUE_REFERENCES
51 ErrorHolder(T &&UD) : UserData(llvm_move(UD)) {}
52 #else
53 ErrorHolder(T &UD) : UserData(UD) {}
54 #endif
55 T UserData;
56 };
57
58 template struct ErrorOrUserDataTraits : llvm::false_type {};
59
60 #if LLVM_HAS_CXX11_TYPETRAITS && LLVM_HAS_RVALUE_REFERENCES
61 template
62 typename std::enable_if< std::is_constructible::value
63 , typename std::remove_reference::type>::type &&
64 moveIfMoveConstructible(V &Val) {
65 return std::move(Val);
66 }
67
68 template
69 typename std::enable_if< !std::is_constructible::value
70 , typename std::remove_reference::type>::type &
71 moveIfMoveConstructible(V &Val) {
72 return Val;
73 }
74 #else
75 template
76 V &moveIfMoveConstructible(V &Val) {
77 return Val;
78 }
79 #endif
80
81 /// \brief Stores a reference that can be changed.
82 template
83 class ReferenceStorage {
84 T *Storage;
85
86 public:
87 ReferenceStorage(T &Ref) : Storage(&Ref) {}
88
89 operator T &() const { return *Storage; }
90 T &get() const { return *Storage; }
91 };
92
93 /// \brief Represents either an error or a value T.
94 ///
95 /// ErrorOr is a pointer-like class that represents the result of an
96 /// operation. The result is either an error, or a value of type T. This is
97 /// designed to emulate the usage of returning a pointer where nullptr indicates
98 /// failure. However instead of just knowing that the operation failed, we also
99 /// have an error_code and optional user data that describes why it failed.
100 ///
101 /// It is used like the following.
102 /// \code
103 /// ErrorOr getBuffer();
104 /// void handleError(error_code ec);
105 ///
106 /// auto buffer = getBuffer();
107 /// if (!buffer)
108 /// handleError(buffer);
109 /// buffer->write("adena");
110 /// \endcode
111 ///
112 /// ErrorOr also supports user defined data for specific error_codes. To use
113 /// this feature you must first add a template specialization of
114 /// ErrorOrUserDataTraits derived from std::true_type for your type in the lld
115 /// namespace. This specialization must have a static error_code error()
116 /// function that returns the error_code this data is used with.
117 ///
118 /// getError() may be called to get either the stored user data, or
119 /// a default constructed UserData if none was stored.
120 ///
121 /// Example:
122 /// \code
123 /// struct InvalidArgError {
124 /// InvalidArgError() {}
125 /// InvalidArgError(std::string S) : ArgName(S) {}
126 /// std::string ArgName;
127 /// };
128 ///
129 /// namespace llvm {
130 /// template<>
131 /// struct ErrorOrUserDataTraits : std::true_type {
132 /// static error_code error() {
133 /// return make_error_code(errc::invalid_argument);
134 /// }
135 /// };
136 /// } // end namespace llvm
137 ///
138 /// using namespace llvm;
139 ///
140 /// ErrorOr foo() {
141 /// return InvalidArgError("adena");
142 /// }
143 ///
144 /// int main() {
145 /// auto a = foo();
146 /// if (!a && error_code(a) == errc::invalid_argument)
147 /// llvm::errs() << a.getError().ArgName << "\n";
148 /// }
149 /// \endcode
150 ///
151 /// An implicit conversion to bool provides a way to check if there was an
152 /// error. The unary * and -> operators provide pointer like access to the
153 /// value. Accessing the value when there is an error has undefined behavior.
154 ///
155 /// When T is a reference type the behaivor is slightly different. The reference
156 /// is held in a std::reference_wrapper::type>, and
157 /// there is special handling to make operator -> work as if T was not a
158 /// reference.
159 ///
160 /// T cannot be a rvalue reference.
161 template
162 class ErrorOr {
163 static const bool isRef = is_reference::value;
164 typedef ReferenceStorage::type> wrap;
165
166 public:
167 typedef typename
168 conditional< isRef
169 , wrap
170 , T
171 >::type storage_type;
172
173 private:
174 typedef T &reference;
175 typedef typename remove_reference::type *pointer;
176
177 public:
178 ErrorOr() : IsValid(false) {}
179
180 ErrorOr(llvm::error_code EC) : HasError(true), IsValid(true) {
181 Error = new ErrorHolderBase;
182 Error->Error = EC;
183 Error->HasUserData = false;
184 }
185
186 template
187 ErrorOr(UserDataT UD, typename
188 enable_if_c::value>::type* = 0)
189 : HasError(true), IsValid(true) {
190 Error = new ErrorHolder(llvm_move(UD));
191 Error->Error = ErrorOrUserDataTraits::error();
192 Error->HasUserData = true;
193 }
194
195 ErrorOr(T Val) : HasError(false), IsValid(true) {
196 new (get()) storage_type(moveIfMoveConstructible(Val));
197 }
198
199 ErrorOr(const ErrorOr &Other) : IsValid(false) {
200 // Construct an invalid ErrorOr if other is invalid.
201 if (!Other.IsValid)
202 return;
203 if (!Other.HasError) {
204 // Get the other value.
205 new (get()) storage_type(*Other.get());
206 HasError = false;
207 } else {
208 // Get other's error.
209 Error = Other.Error;
210 HasError = true;
211 Error->aquire();
212 }
213
214 IsValid = true;
215 }
216
217 ErrorOr &operator =(const ErrorOr &Other) {
218 if (this == &Other)
219 return *this;
220
221 this->~ErrorOr();
222 new (this) ErrorOr(Other);
223
224 return *this;
225 }
226
227 #if LLVM_HAS_RVALUE_REFERENCES
228 ErrorOr(ErrorOr &&Other) : IsValid(false) {
229 // Construct an invalid ErrorOr if other is invalid.
230 if (!Other.IsValid)
231 return;
232 if (!Other.HasError) {
233 // Get the other value.
234 IsValid = true;
235 new (get()) storage_type(std::move(*Other.get()));
236 HasError = false;
237 // Tell other not to do any destruction.
238 Other.IsValid = false;
239 } else {
240 // Get other's error.
241 Error = Other.Error;
242 HasError = true;
243 // Tell other not to do any destruction.
244 Other.IsValid = false;
245 }
246
247 IsValid = true;
248 }
249
250 ErrorOr &operator =(ErrorOr &&Other) {
251 if (this == &Other)
252 return *this;
253
254 this->~ErrorOr();
255 new (this) ErrorOr(std::move(Other));
256
257 return *this;
258 }
259
260 ~ErrorOr() {
261 if (!IsValid)
262 return;
263 if (HasError)
264 Error->release();
265 else
266 get()->~storage_type();
267 }
268 #endif
269
270 template
271 ET getError() const {
272 assert(IsValid && "Cannot get the error of a default constructed ErrorOr!");
273 assert(HasError && "Cannot get an error if none exists!");
274 assert(ErrorOrUserDataTraits::error() == Error->Error &&
275 "Incorrect user error data type for error!");
276 if (!Error->HasUserData)
277 return ET();
278 return reinterpret_cast*>(Error)->UserData;
279 }
280
281 typedef void (*unspecified_bool_type)();
282 static void unspecified_bool_true() {}
283
284 /// \brief Return false if there is an error.
285 operator unspecified_bool_type() const {
286 assert(IsValid && "Can't do anything on a default constructed ErrorOr!");
287 return HasError ? 0 : unspecified_bool_true;
288 }
289
290 operator llvm::error_code() const {
291 assert(IsValid && "Can't do anything on a default constructed ErrorOr!");
292 return HasError ? Error->Error : llvm::error_code::success();
293 }
294
295 pointer operator ->() {
296 return toPointer(get());
297 }
298
299 reference operator *() {
300 return *get();
301 }
302
303 private:
304 pointer toPointer(pointer Val) {
305 return Val;
306 }
307
308 pointer toPointer(wrap *Val) {
309 return &Val->get();
310 }
311
312 protected:
313 storage_type *get() {
314 assert(IsValid && "Can't do anything on a default constructed ErrorOr!");
315 assert(!HasError && "Cannot get value when an error exists!");
316 return reinterpret_cast(TStorage.buffer);
317 }
318
319 const storage_type *get() const {
320 assert(IsValid && "Can't do anything on a default constructed ErrorOr!");
321 assert(!HasError && "Cannot get value when an error exists!");
322 return reinterpret_cast(TStorage.buffer);
323 }
324
325 union {
326 AlignedCharArrayUnion TStorage;
327 ErrorHolderBase *Error;
328 };
329 bool HasError : 1;
330 bool IsValid : 1;
331 };
332
333 template
334 typename enable_if_c::value ||
335 is_error_condition_enum::value, bool>::type
336 operator ==(ErrorOr &Err, E Code) {
337 return error_code(Err) == Code;
338 }
339 } // end namespace llvm
340
341 #endif
144144 template struct is_pointer : true_type {};
145145 template struct is_pointer : true_type {};
146146
147 /// \brief Metafunction that determines wheather the given type is a reference.
148 template struct is_reference : false_type {};
149 template struct is_reference : true_type {};
150
147151 /// \brief Metafunction that determines whether the given type is either an
148152 /// integral type or an enumeration type.
149153 ///
1212 ConstantRangeTest.cpp
1313 DataExtractorTest.cpp
1414 EndianTest.cpp
15 ErrorOrTest.cpp
1516 FileOutputBufferTest.cpp
1617 IntegersSubsetTest.cpp
1718 LeakDetectorTest.cpp
0 //===- unittests/ErrorOrTest.cpp - ErrorOr.h tests ------------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "llvm/Support/ErrorOr.h"
10
11 #include "gtest/gtest.h"
12
13 #include
14
15 using namespace llvm;
16
17 namespace {
18
19 ErrorOr t1() {return 1;}
20 ErrorOr t2() {return make_error_code(errc::invalid_argument);}
21
22 TEST(ErrorOr, SimpleValue) {
23 ErrorOr a = t1();
24 EXPECT_TRUE(a);
25 EXPECT_EQ(1, *a);
26
27 a = t2();
28 EXPECT_FALSE(a);
29 EXPECT_EQ(errc::invalid_argument, a);
30 EXPECT_DEBUG_DEATH(*a, "Cannot get value when an error exists");
31 }
32
33 #if LLVM_HAS_CXX11_STDLIB
34 ErrorOr > t3() {
35 return std::unique_ptr(new int(3));
36 }
37 #endif
38
39 TEST(ErrorOr, Types) {
40 int x;
41 ErrorOr a(x);
42 *a = 42;
43 EXPECT_EQ(42, x);
44
45 #if LLVM_HAS_CXX11_STDLIB
46 // Move only types.
47 EXPECT_EQ(3, **t3());
48 #endif
49 }
50 } // end anon namespace
51
52 struct InvalidArgError {
53 InvalidArgError() {}
54 InvalidArgError(std::string S) : ArgName(S) {}
55 std::string ArgName;
56 };
57
58 namespace llvm {
59 template<>
60 struct ErrorOrUserDataTraits : std::true_type {
61 static error_code error() {
62 return make_error_code(errc::invalid_argument);
63 }
64 };
65 } // end namespace lld
66
67 ErrorOr t4() {
68 return InvalidArgError("adena");
69 }
70
71 namespace {
72 TEST(ErrorOr, UserErrorData) {
73 ErrorOr a = t4();
74 EXPECT_EQ(errc::invalid_argument, a);
75 EXPECT_EQ("adena", t4().getError().ArgName);
76 }
77 } // end anon namespace