1 // Copyright 2018 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef COMMON_RESULT_H_
16 #define COMMON_RESULT_H_
17
18 #include "common/Assert.h"
19 #include "common/Compiler.h"
20
21 #include <cstddef>
22 #include <cstdint>
23 #include <memory>
24 #include <type_traits>
25 #include <utility>
26
27 // Result<T, E> is the following sum type (Haskell notation):
28 //
29 // data Result T E = Success T | Error E | Empty
30 //
31 // It is meant to be used as the return type of functions that might fail. The reason for the Empty
32 // case is that a Result should never be discarded, only destructured (its error or success moved
33 // out) or moved into a different Result. The Empty case tags Results that have been moved out and
34 // Result's destructor should ASSERT on it being Empty.
35 //
36 // Since C++ doesn't have efficient sum types for the special cases we care about, we provide
37 // template specializations for them.
38
39 template <typename T, typename E>
40 class Result;
41
42 // The interface of Result<T, E> should look like the following.
43 // public:
44 // Result(T&& success);
45 // Result(std::unique_ptr<E> error);
46 //
47 // Result(Result<T, E>&& other);
48 // Result<T, E>& operator=(Result<T, E>&& other);
49 //
50 // ~Result();
51 //
52 // bool IsError() const;
53 // bool IsSuccess() const;
54 //
55 // T&& AcquireSuccess();
56 // std::unique_ptr<E> AcquireError();
57
58 // Specialization of Result for returning errors only via pointers. It is basically a pointer
59 // where nullptr is both Success and Empty.
60 template <typename E>
61 class DAWN_NO_DISCARD Result<void, E> {
62 public:
63 Result();
64 Result(std::unique_ptr<E> error);
65
66 Result(Result<void, E>&& other);
67 Result<void, E>& operator=(Result<void, E>&& other);
68
69 ~Result();
70
71 bool IsError() const;
72 bool IsSuccess() const;
73
74 void AcquireSuccess();
75 std::unique_ptr<E> AcquireError();
76
77 private:
78 std::unique_ptr<E> mError;
79 };
80
81 // Uses SFINAE to try to get alignof(T) but fallback to Default if T isn't defined.
82 template <typename T, size_t Default, typename = size_t>
83 constexpr size_t alignof_if_defined_else_default = Default;
84
85 template <typename T, size_t Default>
86 constexpr size_t alignof_if_defined_else_default<T, Default, decltype(alignof(T))> = alignof(T);
87
88 // Specialization of Result when both the error an success are pointers. It is implemented as a
89 // tagged pointer. The tag for Success is 0 so that returning the value is fastest.
90
91 namespace detail {
92 // Utility functions to manipulate the tagged pointer. Some of them don't need to be templated
93 // but we really want them inlined so we keep them in the headers
94 enum PayloadType {
95 Success = 0,
96 Error = 1,
97 Empty = 2,
98 };
99
100 intptr_t MakePayload(const void* pointer, PayloadType type);
101 PayloadType GetPayloadType(intptr_t payload);
102
103 template <typename T>
104 static T* GetSuccessFromPayload(intptr_t payload);
105 template <typename E>
106 static E* GetErrorFromPayload(intptr_t payload);
107
108 constexpr static intptr_t kEmptyPayload = Empty;
109 } // namespace detail
110
111 template <typename T, typename E>
112 class DAWN_NO_DISCARD Result<T*, E> {
113 public:
114 static_assert(alignof_if_defined_else_default<T, 4> >= 4,
115 "Result<T*, E*> reserves two bits for tagging pointers");
116 static_assert(alignof_if_defined_else_default<E, 4> >= 4,
117 "Result<T*, E*> reserves two bits for tagging pointers");
118
119 Result(T* success);
120 Result(std::unique_ptr<E> error);
121
122 // Support returning a Result<T*, E*> from a Result<TChild*, E*>
123 template <typename TChild>
124 Result(Result<TChild*, E>&& other);
125 template <typename TChild>
126 Result<T*, E>& operator=(Result<TChild*, E>&& other);
127
128 ~Result();
129
130 bool IsError() const;
131 bool IsSuccess() const;
132
133 T* AcquireSuccess();
134 std::unique_ptr<E> AcquireError();
135
136 private:
137 template <typename T2, typename E2>
138 friend class Result;
139
140 intptr_t mPayload = detail::kEmptyPayload;
141 };
142
143 template <typename T, typename E>
144 class DAWN_NO_DISCARD Result<const T*, E> {
145 public:
146 static_assert(alignof_if_defined_else_default<T, 4> >= 4,
147 "Result<T*, E*> reserves two bits for tagging pointers");
148 static_assert(alignof_if_defined_else_default<E, 4> >= 4,
149 "Result<T*, E*> reserves two bits for tagging pointers");
150
151 Result(const T* success);
152 Result(std::unique_ptr<E> error);
153
154 Result(Result<const T*, E>&& other);
155 Result<const T*, E>& operator=(Result<const T*, E>&& other);
156
157 ~Result();
158
159 bool IsError() const;
160 bool IsSuccess() const;
161
162 const T* AcquireSuccess();
163 std::unique_ptr<E> AcquireError();
164
165 private:
166 intptr_t mPayload = detail::kEmptyPayload;
167 };
168
169 template <typename T>
170 class Ref;
171
172 template <typename T, typename E>
173 class DAWN_NO_DISCARD Result<Ref<T>, E> {
174 public:
175 static_assert(alignof_if_defined_else_default<T, 4> >= 4,
176 "Result<Ref<T>, E> reserves two bits for tagging pointers");
177 static_assert(alignof_if_defined_else_default<E, 4> >= 4,
178 "Result<Ref<T>, E> reserves two bits for tagging pointers");
179
180 template <typename U>
181 Result(Ref<U>&& success);
182 Result(std::unique_ptr<E> error);
183
184 template <typename U>
185 Result(Result<Ref<U>, E>&& other);
186 template <typename U>
187 Result<Ref<U>, E>& operator=(Result<Ref<U>, E>&& other);
188
189 ~Result();
190
191 bool IsError() const;
192 bool IsSuccess() const;
193
194 Ref<T> AcquireSuccess();
195 std::unique_ptr<E> AcquireError();
196
197 private:
198 template <typename T2, typename E2>
199 friend class Result;
200
201 intptr_t mPayload = detail::kEmptyPayload;
202 };
203
204 // Catchall definition of Result<T, E> implemented as a tagged struct. It could be improved to use
205 // a tagged union instead if it turns out to be a hotspot. T and E must be movable and default
206 // constructible.
207 template <typename T, typename E>
208 class DAWN_NO_DISCARD Result {
209 public:
210 Result(T&& success);
211 Result(std::unique_ptr<E> error);
212
213 Result(Result<T, E>&& other);
214 Result<T, E>& operator=(Result<T, E>&& other);
215
216 ~Result();
217
218 bool IsError() const;
219 bool IsSuccess() const;
220
221 T&& AcquireSuccess();
222 std::unique_ptr<E> AcquireError();
223
224 private:
225 enum PayloadType {
226 Success = 0,
227 Error = 1,
228 Acquired = 2,
229 };
230 PayloadType mType;
231
232 std::unique_ptr<E> mError;
233 T mSuccess;
234 };
235
236 // Implementation of Result<void, E>
237 template <typename E>
Result()238 Result<void, E>::Result() {
239 }
240
241 template <typename E>
Result(std::unique_ptr<E> error)242 Result<void, E>::Result(std::unique_ptr<E> error) : mError(std::move(error)) {
243 }
244
245 template <typename E>
Result(Result<void,E> && other)246 Result<void, E>::Result(Result<void, E>&& other) : mError(std::move(other.mError)) {
247 }
248
249 template <typename E>
250 Result<void, E>& Result<void, E>::operator=(Result<void, E>&& other) {
251 ASSERT(mError == nullptr);
252 mError = std::move(other.mError);
253 return *this;
254 }
255
256 template <typename E>
~Result()257 Result<void, E>::~Result() {
258 ASSERT(mError == nullptr);
259 }
260
261 template <typename E>
IsError()262 bool Result<void, E>::IsError() const {
263 return mError != nullptr;
264 }
265
266 template <typename E>
IsSuccess()267 bool Result<void, E>::IsSuccess() const {
268 return mError == nullptr;
269 }
270
271 template <typename E>
AcquireSuccess()272 void Result<void, E>::AcquireSuccess() {
273 }
274
275 template <typename E>
AcquireError()276 std::unique_ptr<E> Result<void, E>::AcquireError() {
277 return std::move(mError);
278 }
279
280 // Implementation details of the tagged pointer Results
281 namespace detail {
282
283 template <typename T>
GetSuccessFromPayload(intptr_t payload)284 T* GetSuccessFromPayload(intptr_t payload) {
285 ASSERT(GetPayloadType(payload) == Success);
286 return reinterpret_cast<T*>(payload);
287 }
288
289 template <typename E>
GetErrorFromPayload(intptr_t payload)290 E* GetErrorFromPayload(intptr_t payload) {
291 ASSERT(GetPayloadType(payload) == Error);
292 return reinterpret_cast<E*>(payload ^ 1);
293 }
294
295 } // namespace detail
296
297 // Implementation of Result<T*, E>
298 template <typename T, typename E>
Result(T * success)299 Result<T*, E>::Result(T* success) : mPayload(detail::MakePayload(success, detail::Success)) {
300 }
301
302 template <typename T, typename E>
Result(std::unique_ptr<E> error)303 Result<T*, E>::Result(std::unique_ptr<E> error)
304 : mPayload(detail::MakePayload(error.release(), detail::Error)) {
305 }
306
307 template <typename T, typename E>
308 template <typename TChild>
Result(Result<TChild *,E> && other)309 Result<T*, E>::Result(Result<TChild*, E>&& other) : mPayload(other.mPayload) {
310 other.mPayload = detail::kEmptyPayload;
311 static_assert(std::is_same<T, TChild>::value || std::is_base_of<T, TChild>::value, "");
312 }
313
314 template <typename T, typename E>
315 template <typename TChild>
316 Result<T*, E>& Result<T*, E>::operator=(Result<TChild*, E>&& other) {
317 ASSERT(mPayload == detail::kEmptyPayload);
318 static_assert(std::is_same<T, TChild>::value || std::is_base_of<T, TChild>::value, "");
319 mPayload = other.mPayload;
320 other.mPayload = detail::kEmptyPayload;
321 return *this;
322 }
323
324 template <typename T, typename E>
~Result()325 Result<T*, E>::~Result() {
326 ASSERT(mPayload == detail::kEmptyPayload);
327 }
328
329 template <typename T, typename E>
IsError()330 bool Result<T*, E>::IsError() const {
331 return detail::GetPayloadType(mPayload) == detail::Error;
332 }
333
334 template <typename T, typename E>
IsSuccess()335 bool Result<T*, E>::IsSuccess() const {
336 return detail::GetPayloadType(mPayload) == detail::Success;
337 }
338
339 template <typename T, typename E>
AcquireSuccess()340 T* Result<T*, E>::AcquireSuccess() {
341 T* success = detail::GetSuccessFromPayload<T>(mPayload);
342 mPayload = detail::kEmptyPayload;
343 return success;
344 }
345
346 template <typename T, typename E>
AcquireError()347 std::unique_ptr<E> Result<T*, E>::AcquireError() {
348 std::unique_ptr<E> error(detail::GetErrorFromPayload<E>(mPayload));
349 mPayload = detail::kEmptyPayload;
350 return std::move(error);
351 }
352
353 // Implementation of Result<const T*, E*>
354 template <typename T, typename E>
Result(const T * success)355 Result<const T*, E>::Result(const T* success)
356 : mPayload(detail::MakePayload(success, detail::Success)) {
357 }
358
359 template <typename T, typename E>
Result(std::unique_ptr<E> error)360 Result<const T*, E>::Result(std::unique_ptr<E> error)
361 : mPayload(detail::MakePayload(error.release(), detail::Error)) {
362 }
363
364 template <typename T, typename E>
Result(Result<const T *,E> && other)365 Result<const T*, E>::Result(Result<const T*, E>&& other) : mPayload(other.mPayload) {
366 other.mPayload = detail::kEmptyPayload;
367 }
368
369 template <typename T, typename E>
370 Result<const T*, E>& Result<const T*, E>::operator=(Result<const T*, E>&& other) {
371 ASSERT(mPayload == detail::kEmptyPayload);
372 mPayload = other.mPayload;
373 other.mPayload = detail::kEmptyPayload;
374 return *this;
375 }
376
377 template <typename T, typename E>
~Result()378 Result<const T*, E>::~Result() {
379 ASSERT(mPayload == detail::kEmptyPayload);
380 }
381
382 template <typename T, typename E>
IsError()383 bool Result<const T*, E>::IsError() const {
384 return detail::GetPayloadType(mPayload) == detail::Error;
385 }
386
387 template <typename T, typename E>
IsSuccess()388 bool Result<const T*, E>::IsSuccess() const {
389 return detail::GetPayloadType(mPayload) == detail::Success;
390 }
391
392 template <typename T, typename E>
AcquireSuccess()393 const T* Result<const T*, E>::AcquireSuccess() {
394 T* success = detail::GetSuccessFromPayload<T>(mPayload);
395 mPayload = detail::kEmptyPayload;
396 return success;
397 }
398
399 template <typename T, typename E>
AcquireError()400 std::unique_ptr<E> Result<const T*, E>::AcquireError() {
401 std::unique_ptr<E> error(detail::GetErrorFromPayload<E>(mPayload));
402 mPayload = detail::kEmptyPayload;
403 return std::move(error);
404 }
405
406 // Implementation of Result<Ref<T>, E>
407 template <typename T, typename E>
408 template <typename U>
Result(Ref<U> && success)409 Result<Ref<T>, E>::Result(Ref<U>&& success)
410 : mPayload(detail::MakePayload(success.Detach(), detail::Success)) {
411 static_assert(std::is_convertible<U*, T*>::value, "");
412 }
413
414 template <typename T, typename E>
Result(std::unique_ptr<E> error)415 Result<Ref<T>, E>::Result(std::unique_ptr<E> error)
416 : mPayload(detail::MakePayload(error.release(), detail::Error)) {
417 }
418
419 template <typename T, typename E>
420 template <typename U>
Result(Result<Ref<U>,E> && other)421 Result<Ref<T>, E>::Result(Result<Ref<U>, E>&& other) : mPayload(other.mPayload) {
422 static_assert(std::is_convertible<U*, T*>::value, "");
423 other.mPayload = detail::kEmptyPayload;
424 }
425
426 template <typename T, typename E>
427 template <typename U>
428 Result<Ref<U>, E>& Result<Ref<T>, E>::operator=(Result<Ref<U>, E>&& other) {
429 static_assert(std::is_convertible<U*, T*>::value, "");
430 ASSERT(mPayload == detail::kEmptyPayload);
431 mPayload = other.mPayload;
432 other.mPayload = detail::kEmptyPayload;
433 return *this;
434 }
435
436 template <typename T, typename E>
~Result()437 Result<Ref<T>, E>::~Result() {
438 ASSERT(mPayload == detail::kEmptyPayload);
439 }
440
441 template <typename T, typename E>
IsError()442 bool Result<Ref<T>, E>::IsError() const {
443 return detail::GetPayloadType(mPayload) == detail::Error;
444 }
445
446 template <typename T, typename E>
IsSuccess()447 bool Result<Ref<T>, E>::IsSuccess() const {
448 return detail::GetPayloadType(mPayload) == detail::Success;
449 }
450
451 template <typename T, typename E>
AcquireSuccess()452 Ref<T> Result<Ref<T>, E>::AcquireSuccess() {
453 ASSERT(IsSuccess());
454 Ref<T> success = AcquireRef(detail::GetSuccessFromPayload<T>(mPayload));
455 mPayload = detail::kEmptyPayload;
456 return success;
457 }
458
459 template <typename T, typename E>
AcquireError()460 std::unique_ptr<E> Result<Ref<T>, E>::AcquireError() {
461 ASSERT(IsError());
462 std::unique_ptr<E> error(detail::GetErrorFromPayload<E>(mPayload));
463 mPayload = detail::kEmptyPayload;
464 return std::move(error);
465 }
466
467 // Implementation of Result<T, E>
468 template <typename T, typename E>
Result(T && success)469 Result<T, E>::Result(T&& success) : mType(Success), mSuccess(std::move(success)) {
470 }
471
472 template <typename T, typename E>
Result(std::unique_ptr<E> error)473 Result<T, E>::Result(std::unique_ptr<E> error) : mType(Error), mError(std::move(error)) {
474 }
475
476 template <typename T, typename E>
~Result()477 Result<T, E>::~Result() {
478 ASSERT(mType == Acquired);
479 }
480
481 template <typename T, typename E>
Result(Result<T,E> && other)482 Result<T, E>::Result(Result<T, E>&& other)
483 : mType(other.mType), mError(std::move(other.mError)), mSuccess(std::move(other.mSuccess)) {
484 other.mType = Acquired;
485 }
486 template <typename T, typename E>
487 Result<T, E>& Result<T, E>::operator=(Result<T, E>&& other) {
488 mType = other.mType;
489 mError = std::move(other.mError);
490 mSuccess = std::move(other.mSuccess);
491 other.mType = Acquired;
492 return *this;
493 }
494
495 template <typename T, typename E>
IsError()496 bool Result<T, E>::IsError() const {
497 return mType == Error;
498 }
499
500 template <typename T, typename E>
IsSuccess()501 bool Result<T, E>::IsSuccess() const {
502 return mType == Success;
503 }
504
505 template <typename T, typename E>
AcquireSuccess()506 T&& Result<T, E>::AcquireSuccess() {
507 ASSERT(mType == Success);
508 mType = Acquired;
509 return std::move(mSuccess);
510 }
511
512 template <typename T, typename E>
AcquireError()513 std::unique_ptr<E> Result<T, E>::AcquireError() {
514 ASSERT(mType == Error);
515 mType = Acquired;
516 return std::move(mError);
517 }
518
519 #endif // COMMON_RESULT_H_
520