• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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