Bug 1601859 - Vendor cubeb-pulse-rs. r=kinetik
[gecko.git] / mfbt / Result.h
blob9f61220752958a1adc1a1c4c40ad739107ca3f75
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* A type suitable for returning either a value or an error from a function. */
9 #ifndef mozilla_Result_h
10 #define mozilla_Result_h
12 #include <type_traits>
13 #include "mozilla/Alignment.h"
14 #include "mozilla/Assertions.h"
15 #include "mozilla/Attributes.h"
16 #include "mozilla/Types.h"
17 #include "mozilla/TypeTraits.h"
18 #include "mozilla/Variant.h"
20 namespace mozilla {
22 /**
23 * Empty struct, indicating success for operations that have no return value.
24 * For example, if you declare another empty struct `struct OutOfMemory {};`,
25 * then `Result<Ok, OutOfMemory>` represents either success or OOM.
27 struct Ok {};
29 template <typename E>
30 class GenericErrorResult;
31 template <typename V, typename E>
32 class Result;
34 namespace detail {
36 enum class PackingStrategy {
37 Variant,
38 NullIsOk,
39 LowBitTagIsError,
40 PackedVariant,
43 template <typename V, typename E, PackingStrategy Strategy>
44 class ResultImplementation;
46 template <typename V, typename E>
47 class ResultImplementation<V, E, PackingStrategy::Variant> {
48 mozilla::Variant<V, E> mStorage;
50 public:
51 ResultImplementation(ResultImplementation&&) = default;
52 ResultImplementation(const ResultImplementation&) = default;
53 ResultImplementation& operator=(const ResultImplementation&) = default;
54 ResultImplementation& operator=(ResultImplementation&&) = default;
56 explicit ResultImplementation(V&& aValue)
57 : mStorage(std::forward<V>(aValue)) {}
58 explicit ResultImplementation(const V& aValue) : mStorage(aValue) {}
59 explicit ResultImplementation(E aErrorValue)
60 : mStorage(std::forward<E>(aErrorValue)) {}
62 bool isOk() const { return mStorage.template is<V>(); }
64 // The callers of these functions will assert isOk() has the proper value, so
65 // these functions (in all ResultImplementation specializations) don't need
66 // to do so.
67 V unwrap() { return std::move(mStorage.template as<V>()); }
68 const V& inspect() const { return mStorage.template as<V>(); }
70 E unwrapErr() { return std::move(mStorage.template as<E>()); }
71 const E& inspectErr() const { return mStorage.template as<E>(); }
74 /**
75 * mozilla::Variant doesn't like storing a reference. This is a specialization
76 * to store E as pointer if it's a reference.
78 template <typename V, typename E>
79 class ResultImplementation<V, E&, PackingStrategy::Variant> {
80 mozilla::Variant<V, E*> mStorage;
82 public:
83 explicit ResultImplementation(V&& aValue)
84 : mStorage(std::forward<V>(aValue)) {}
85 explicit ResultImplementation(const V& aValue) : mStorage(aValue) {}
86 explicit ResultImplementation(E& aErrorValue) : mStorage(&aErrorValue) {}
88 bool isOk() const { return mStorage.template is<V>(); }
90 const V& inspect() const { return mStorage.template as<V>(); }
91 V unwrap() { return std::move(mStorage.template as<V>()); }
93 E& unwrapErr() { return *mStorage.template as<E*>(); }
94 const E& inspectErr() const { return *mStorage.template as<E*>(); }
97 /**
98 * Specialization for when the success type is Ok (or another empty class) and
99 * the error type is a reference.
101 template <typename V, typename E>
102 class ResultImplementation<V, E&, PackingStrategy::NullIsOk> {
103 E* mErrorValue;
105 public:
106 explicit ResultImplementation(V) : mErrorValue(nullptr) {}
107 explicit ResultImplementation(E& aErrorValue) : mErrorValue(&aErrorValue) {}
109 bool isOk() const { return mErrorValue == nullptr; }
111 const V& inspect() const = delete;
112 V unwrap() { return V(); }
114 const E& inspectErr() const { return *mErrorValue; }
115 E& unwrapErr() { return *mErrorValue; }
119 * Specialization for when the success type is Ok (or another empty class) and
120 * the error type is a value type which can never have the value 0 (as
121 * determined by UnusedZero<>).
123 template <typename V, typename E>
124 class ResultImplementation<V, E, PackingStrategy::NullIsOk> {
125 static constexpr E NullValue = E(0);
127 E mErrorValue;
129 public:
130 explicit ResultImplementation(V) : mErrorValue(NullValue) {}
131 explicit ResultImplementation(E aErrorValue) : mErrorValue(aErrorValue) {
132 MOZ_ASSERT(aErrorValue != NullValue);
135 bool isOk() const { return mErrorValue == NullValue; }
137 const V& inspect() const = delete;
138 V unwrap() { return V(); }
140 const E& inspectErr() const { return mErrorValue; }
141 E unwrapErr() { return std::move(mErrorValue); }
145 * Specialization for when alignment permits using the least significant bit as
146 * a tag bit.
148 template <typename V, typename E>
149 class ResultImplementation<V*, E&, PackingStrategy::LowBitTagIsError> {
150 uintptr_t mBits;
152 public:
153 explicit ResultImplementation(V* aValue)
154 : mBits(reinterpret_cast<uintptr_t>(aValue)) {
155 MOZ_ASSERT((uintptr_t(aValue) % MOZ_ALIGNOF(V)) == 0,
156 "Result value pointers must not be misaligned");
158 explicit ResultImplementation(E& aErrorValue)
159 : mBits(reinterpret_cast<uintptr_t>(&aErrorValue) | 1) {
160 MOZ_ASSERT((uintptr_t(&aErrorValue) % MOZ_ALIGNOF(E)) == 0,
161 "Result errors must not be misaligned");
164 bool isOk() const { return (mBits & 1) == 0; }
166 V* inspect() const { return reinterpret_cast<V*>(mBits); }
167 V* unwrap() { return inspect(); }
169 E& inspectErr() const { return *reinterpret_cast<E*>(mBits ^ 1); }
170 E& unwrapErr() { return inspectErr(); }
173 // Return true if any of the struct can fit in a word.
174 template <typename V, typename E>
175 struct IsPackableVariant {
176 struct VEbool {
177 V v;
178 E e;
179 bool ok;
181 struct EVbool {
182 E e;
183 V v;
184 bool ok;
187 using Impl = typename Conditional<sizeof(VEbool) <= sizeof(EVbool), VEbool,
188 EVbool>::Type;
190 static const bool value = sizeof(Impl) <= sizeof(uintptr_t);
194 * Specialization for when both type are not using all the bytes, in order to
195 * use one byte as a tag.
197 template <typename V, typename E>
198 class ResultImplementation<V, E, PackingStrategy::PackedVariant> {
199 using Impl = typename IsPackableVariant<V, E>::Impl;
200 Impl data;
202 public:
203 explicit ResultImplementation(V aValue) {
204 data.v = std::move(aValue);
205 data.ok = true;
207 explicit ResultImplementation(E aErrorValue) {
208 data.e = std::move(aErrorValue);
209 data.ok = false;
212 bool isOk() const { return data.ok; }
214 const V& inspect() const { return data.v; }
215 V unwrap() { return std::move(data.v); }
217 const E& inspectErr() const { return data.e; }
218 E unwrapErr() { return std::move(data.e); }
221 // To use nullptr as a special value, we need the counter part to exclude zero
222 // from its range of valid representations.
224 // By default assume that zero can be represented.
225 template <typename T>
226 struct UnusedZero {
227 static const bool value = false;
230 // References can't be null.
231 template <typename T>
232 struct UnusedZero<T&> {
233 static const bool value = true;
236 // A bit of help figuring out which of the above specializations to use.
238 // We begin by safely assuming types don't have a spare bit.
239 template <typename T>
240 struct HasFreeLSB {
241 static const bool value = false;
244 // As an incomplete type, void* does not have a spare bit.
245 template <>
246 struct HasFreeLSB<void*> {
247 static const bool value = false;
250 // The lowest bit of a properly-aligned pointer is always zero if the pointee
251 // type is greater than byte-aligned. That bit is free to use if it's masked
252 // out of such pointers before they're dereferenced.
253 template <typename T>
254 struct HasFreeLSB<T*> {
255 static const bool value = (alignof(T) & 1) == 0;
258 // We store references as pointers, so they have a free bit if a pointer would
259 // have one.
260 template <typename T>
261 struct HasFreeLSB<T&> {
262 static const bool value = HasFreeLSB<T*>::value;
265 // Select one of the previous result implementation based on the properties of
266 // the V and E types.
267 template <typename V, typename E>
268 struct SelectResultImpl {
269 static const PackingStrategy value =
270 (IsEmpty<V>::value && UnusedZero<E>::value)
271 ? PackingStrategy::NullIsOk
272 : (detail::HasFreeLSB<V>::value && detail::HasFreeLSB<E>::value)
273 ? PackingStrategy::LowBitTagIsError
274 : (IsDefaultConstructible<V>::value &&
275 IsDefaultConstructible<E>::value &&
276 IsPackableVariant<V, E>::value)
277 ? PackingStrategy::PackedVariant
278 : PackingStrategy::Variant;
280 using Type = detail::ResultImplementation<V, E, value>;
283 template <typename T>
284 struct IsResult : FalseType {};
286 template <typename V, typename E>
287 struct IsResult<Result<V, E>> : TrueType {};
289 } // namespace detail
291 template <typename V, typename E>
292 auto ToResult(Result<V, E>&& aValue)
293 -> decltype(std::forward<Result<V, E>>(aValue)) {
294 return std::forward<Result<V, E>>(aValue);
298 * Result<V, E> represents the outcome of an operation that can either succeed
299 * or fail. It contains either a success value of type V or an error value of
300 * type E.
302 * All Result methods are const, so results are basically immutable.
303 * This is just like Variant<V, E> but with a slightly different API, and the
304 * following cases are optimized so Result can be stored more efficiently:
306 * - If the success type is Ok (or another empty class) and the error type is a
307 * reference, Result<V, E&> is guaranteed to be pointer-sized and all zero
308 * bits on success. Do not change this representation! There is JIT code that
309 * depends on it.
311 * - If the success type is a pointer type and the error type is a reference
312 * type, and the least significant bit is unused for both types when stored
313 * as a pointer (due to alignment rules), Result<V*, E&> is guaranteed to be
314 * pointer-sized. In this case, we use the lowest bit as tag bit: 0 to
315 * indicate the Result's bits are a V, 1 to indicate the Result's bits (with
316 * the 1 masked out) encode an E*.
318 * The purpose of Result is to reduce the screwups caused by using `false` or
319 * `nullptr` to indicate errors.
320 * What screwups? See <https://bugzilla.mozilla.org/show_bug.cgi?id=912928> for
321 * a partial list.
323 template <typename V, typename E>
324 class MOZ_MUST_USE_TYPE Result final {
325 using Impl = typename detail::SelectResultImpl<V, E>::Type;
327 Impl mImpl;
329 public:
330 /** Create a success result. */
331 MOZ_IMPLICIT Result(V&& aValue) : mImpl(std::forward<V>(aValue)) {
332 MOZ_ASSERT(isOk());
335 /** Create a success result. */
336 MOZ_IMPLICIT Result(const V& aValue) : mImpl(aValue) { MOZ_ASSERT(isOk()); }
338 /** Create an error result. */
339 explicit Result(E aErrorValue) : mImpl(std::forward<E>(aErrorValue)) {
340 MOZ_ASSERT(isErr());
344 * Implementation detail of MOZ_TRY().
345 * Create an error result from another error result.
347 template <typename E2>
348 MOZ_IMPLICIT Result(GenericErrorResult<E2>&& aErrorResult)
349 : mImpl(std::forward<E2>(aErrorResult.mErrorValue)) {
350 static_assert(mozilla::IsConvertible<E2, E>::value,
351 "E2 must be convertible to E");
352 MOZ_ASSERT(isErr());
356 * Implementation detail of MOZ_TRY().
357 * Create an error result from another error result.
359 template <typename E2>
360 MOZ_IMPLICIT Result(const GenericErrorResult<E2>& aErrorResult)
361 : mImpl(aErrorResult.mErrorValue) {
362 static_assert(mozilla::IsConvertible<E2, E>::value,
363 "E2 must be convertible to E");
364 MOZ_ASSERT(isErr());
367 Result(const Result&) = default;
368 Result(Result&&) = default;
369 Result& operator=(const Result&) = default;
370 Result& operator=(Result&&) = default;
372 /** True if this Result is a success result. */
373 bool isOk() const { return mImpl.isOk(); }
375 /** True if this Result is an error result. */
376 bool isErr() const { return !mImpl.isOk(); }
378 /** Take the success value from this Result, which must be a success result.
380 V unwrap() {
381 MOZ_ASSERT(isOk());
382 return mImpl.unwrap();
386 * Take the success value from this Result, which must be a success result.
387 * If it is an error result, then return the aValue.
389 V unwrapOr(V aValue) {
390 return MOZ_LIKELY(isOk()) ? mImpl.unwrap() : std::move(aValue);
393 /** Take the error value from this Result, which must be an error result. */
394 E unwrapErr() {
395 MOZ_ASSERT(isErr());
396 return mImpl.unwrapErr();
399 /** See the success value from this Result, which must be a success result. */
400 const V& inspect() const { return mImpl.inspect(); }
402 /** See the error value from this Result, which must be an error result. */
403 const E& inspectErr() const {
404 MOZ_ASSERT(isErr());
405 return mImpl.inspectErr();
409 * Map a function V -> W over this result's success variant. If this result is
410 * an error, do not invoke the function and return a copy of the error.
412 * Mapping over success values invokes the function to produce a new success
413 * value:
415 * // Map Result<int, E> to another Result<int, E>
416 * Result<int, E> res(5);
417 * Result<int, E> res2 = res.map([](int x) { return x * x; });
418 * MOZ_ASSERT(res2.unwrap() == 25);
420 * // Map Result<const char*, E> to Result<size_t, E>
421 * Result<const char*, E> res("hello, map!");
422 * Result<size_t, E> res2 = res.map(strlen);
423 * MOZ_ASSERT(res2.unwrap() == 11);
425 * Mapping over an error does not invoke the function and copies the error:
427 * Result<V, int> res(5);
428 * MOZ_ASSERT(res.isErr());
429 * Result<W, int> res2 = res.map([](V v) { ... });
430 * MOZ_ASSERT(res2.isErr());
431 * MOZ_ASSERT(res2.unwrapErr() == 5);
433 template <typename F>
434 auto map(F f) -> Result<decltype(f(*((V*)nullptr))), E> {
435 using RetResult = Result<decltype(f(*((V*)nullptr))), E>;
436 return MOZ_LIKELY(isOk()) ? RetResult(f(unwrap())) : RetResult(unwrapErr());
440 * Map a function V -> W over this result's error variant. If this result is
441 * a success, do not invoke the function and move the success over.
443 * Mapping over error values invokes the function to produce a new error
444 * value:
446 * // Map Result<V, int> to another Result<V, int>
447 * Result<V, int> res(5);
448 * Result<V, int> res2 = res.mapErr([](int x) { return x * x; });
449 * MOZ_ASSERT(res2.unwrapErr() == 25);
451 * // Map Result<V, const char*> to Result<V, size_t>
452 * Result<V, const char*> res("hello, map!");
453 * Result<size_t, E> res2 = res.mapErr(strlen);
454 * MOZ_ASSERT(res2.unwrapErr() == 11);
456 * Mapping over a success does not invoke the function and copies the error:
458 * Result<int, V> res(5);
459 * MOZ_ASSERT(res.isOk());
460 * Result<int, W> res2 = res.mapErr([](V v) { ... });
461 * MOZ_ASSERT(res2.isOk());
462 * MOZ_ASSERT(res2.unwrap() == 5);
464 template <typename F>
465 auto mapErr(F f) -> Result<V, std::result_of_t<F(E)>> {
466 using RetResult = Result<V, std::result_of_t<F(E)>>;
467 return isOk() ? RetResult(unwrap()) : RetResult(f(unwrapErr()));
471 * Given a function V -> Result<W, E>, apply it to this result's success value
472 * and return its result. If this result is an error value, then return a
473 * copy.
475 * This is sometimes called "flatMap" or ">>=" in other contexts.
477 * `andThen`ing over success values invokes the function to produce a new
478 * result:
480 * Result<const char*, Error> res("hello, andThen!");
481 * Result<HtmlFreeString, Error> res2 = res.andThen([](const char* s) {
482 * return containsHtmlTag(s)
483 * ? Result<HtmlFreeString, Error>(Error("Invalid: contains HTML"))
484 * : Result<HtmlFreeString, Error>(HtmlFreeString(s));
486 * });
487 * MOZ_ASSERT(res2.isOk());
488 * MOZ_ASSERT(res2.unwrap() == HtmlFreeString("hello, andThen!");
490 * `andThen`ing over error results does not invoke the function, and just
491 * produces a new copy of the error result:
493 * Result<int, const char*> res("some error");
494 * auto res2 = res.andThen([](int x) { ... });
495 * MOZ_ASSERT(res2.isErr());
496 * MOZ_ASSERT(res.unwrapErr() == res2.unwrapErr());
498 template <typename F, typename = typename EnableIf<detail::IsResult<decltype(
499 (*((F*)nullptr))(*((V*)nullptr)))>::value>::Type>
500 auto andThen(F f) -> decltype(f(*((V*)nullptr))) {
501 return MOZ_LIKELY(isOk()) ? f(unwrap())
502 : GenericErrorResult<E>(unwrapErr());
507 * A type that auto-converts to an error Result. This is like a Result without
508 * a success type. It's the best return type for functions that always return
509 * an error--functions designed to build and populate error objects. It's also
510 * useful in error-handling macros; see MOZ_TRY for an example.
512 template <typename E>
513 class MOZ_MUST_USE_TYPE GenericErrorResult {
514 E mErrorValue;
516 template <typename V, typename E2>
517 friend class Result;
519 public:
520 explicit GenericErrorResult(E aErrorValue)
521 : mErrorValue(std::forward<E>(aErrorValue)) {}
524 template <typename E>
525 inline GenericErrorResult<E> Err(E&& aErrorValue) {
526 return GenericErrorResult<E>(std::forward<E>(aErrorValue));
529 } // namespace mozilla
532 * MOZ_TRY(expr) is the C++ equivalent of Rust's `try!(expr);`. First, it
533 * evaluates expr, which must produce a Result value. On success, it
534 * discards the result altogether. On error, it immediately returns an error
535 * Result from the enclosing function.
537 #define MOZ_TRY(expr) \
538 do { \
539 auto mozTryTempResult_ = ::mozilla::ToResult(expr); \
540 if (MOZ_UNLIKELY(mozTryTempResult_.isErr())) { \
541 return ::mozilla::Err(mozTryTempResult_.unwrapErr()); \
543 } while (0)
546 * MOZ_TRY_VAR(target, expr) is the C++ equivalent of Rust's `target =
547 * try!(expr);`. First, it evaluates expr, which must produce a Result value. On
548 * success, the result's success value is assigned to target. On error,
549 * immediately returns the error result. |target| must evaluate to a reference
550 * without any side effects.
552 #define MOZ_TRY_VAR(target, expr) \
553 do { \
554 auto mozTryVarTempResult_ = (expr); \
555 if (MOZ_UNLIKELY(mozTryVarTempResult_.isErr())) { \
556 return ::mozilla::Err(mozTryVarTempResult_.unwrapErr()); \
558 (target) = mozTryVarTempResult_.unwrap(); \
559 } while (0)
561 #endif // mozilla_Result_h