Bug 1642744 [wpt PR 23920] - [ScrollTimeline] Update compositor timeline from blink...
[gecko.git] / mfbt / Result.h
blobbb33270f1d4b60b066a6acf766d3ed4649aa2715
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/Variant.h"
19 namespace mozilla {
21 /**
22 * Empty struct, indicating success for operations that have no return value.
23 * For example, if you declare another empty struct `struct OutOfMemory {};`,
24 * then `Result<Ok, OutOfMemory>` represents either success or OOM.
26 struct Ok {};
28 template <typename E>
29 class GenericErrorResult;
30 template <typename V, typename E>
31 class Result;
33 namespace detail {
35 enum class PackingStrategy {
36 Variant,
37 NullIsOk,
38 LowBitTagIsError,
39 PackedVariant,
42 template <typename V, typename E, PackingStrategy Strategy>
43 class ResultImplementation;
45 template <typename V, typename E>
46 class ResultImplementation<V, E, PackingStrategy::Variant> {
47 mozilla::Variant<V, E> mStorage;
49 public:
50 ResultImplementation(ResultImplementation&&) = default;
51 ResultImplementation(const ResultImplementation&) = default;
52 ResultImplementation& operator=(const ResultImplementation&) = default;
53 ResultImplementation& operator=(ResultImplementation&&) = default;
55 explicit ResultImplementation(V&& aValue)
56 : mStorage(std::forward<V>(aValue)) {}
57 explicit ResultImplementation(const V& aValue) : mStorage(aValue) {}
58 explicit ResultImplementation(const E& aErrorValue) : mStorage(aErrorValue) {}
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 =
188 std::conditional_t<sizeof(VEbool) <= sizeof(EVbool), VEbool, EVbool>;
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 (std::is_empty_v<V> && UnusedZero<E>::value)
271 ? PackingStrategy::NullIsOk
272 : (detail::HasFreeLSB<V>::value && detail::HasFreeLSB<E>::value)
273 ? PackingStrategy::LowBitTagIsError
274 : (std::is_default_constructible_v<V> &&
275 std::is_default_constructible_v<E> &&
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 : std::false_type {};
286 template <typename V, typename E>
287 struct IsResult<Result<V, E>> : std::true_type {};
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 using ok_type = V;
331 using err_type = E;
333 /** Create a success result. */
334 MOZ_IMPLICIT Result(V&& aValue) : mImpl(std::forward<V>(aValue)) {
335 MOZ_ASSERT(isOk());
338 /** Create a success result. */
339 MOZ_IMPLICIT Result(const V& aValue) : mImpl(aValue) { MOZ_ASSERT(isOk()); }
341 /** Create an error result. */
342 explicit Result(E aErrorValue) : mImpl(std::forward<E>(aErrorValue)) {
343 MOZ_ASSERT(isErr());
347 * Implementation detail of MOZ_TRY().
348 * Create an error result from another error result.
350 template <typename E2>
351 MOZ_IMPLICIT Result(GenericErrorResult<E2>&& aErrorResult)
352 : mImpl(std::forward<E2>(aErrorResult.mErrorValue)) {
353 static_assert(std::is_convertible_v<E2, E>, "E2 must be convertible to E");
354 MOZ_ASSERT(isErr());
358 * Implementation detail of MOZ_TRY().
359 * Create an error result from another error result.
361 template <typename E2>
362 MOZ_IMPLICIT Result(const GenericErrorResult<E2>& aErrorResult)
363 : mImpl(aErrorResult.mErrorValue) {
364 static_assert(std::is_convertible_v<E2, E>, "E2 must be convertible to E");
365 MOZ_ASSERT(isErr());
368 Result(const Result&) = default;
369 Result(Result&&) = default;
370 Result& operator=(const Result&) = default;
371 Result& operator=(Result&&) = default;
373 /** True if this Result is a success result. */
374 bool isOk() const { return mImpl.isOk(); }
376 /** True if this Result is an error result. */
377 bool isErr() const { return !mImpl.isOk(); }
379 /** Take the success value from this Result, which must be a success result.
381 V unwrap() {
382 MOZ_ASSERT(isOk());
383 return mImpl.unwrap();
387 * Take the success value from this Result, which must be a success result.
388 * If it is an error result, then return the aValue.
390 V unwrapOr(V aValue) {
391 return MOZ_LIKELY(isOk()) ? mImpl.unwrap() : std::move(aValue);
394 /** Take the error value from this Result, which must be an error result. */
395 E unwrapErr() {
396 MOZ_ASSERT(isErr());
397 return mImpl.unwrapErr();
400 /** See the success value from this Result, which must be a success result. */
401 const V& inspect() const { return mImpl.inspect(); }
403 /** See the error value from this Result, which must be an error result. */
404 const E& inspectErr() const {
405 MOZ_ASSERT(isErr());
406 return mImpl.inspectErr();
409 /** Propagate the error value from this Result, which must be an error result.
411 * This can be used to propagate an error from a function call to the caller
412 * with a different value type, but the same error type:
414 * Result<T1, E> Func1() {
415 * Result<T2, E> res = Func2();
416 * if (res.isErr()) { return res.propagateErr(); }
419 GenericErrorResult<E> propagateErr() {
420 MOZ_ASSERT(isErr());
421 return GenericErrorResult<E>{mImpl.unwrapErr()};
425 * Map a function V -> W over this result's success variant. If this result is
426 * an error, do not invoke the function and propagate the error.
428 * Mapping over success values invokes the function to produce a new success
429 * value:
431 * // Map Result<int, E> to another Result<int, E>
432 * Result<int, E> res(5);
433 * Result<int, E> res2 = res.map([](int x) { return x * x; });
434 * MOZ_ASSERT(res2.unwrap() == 25);
436 * // Map Result<const char*, E> to Result<size_t, E>
437 * Result<const char*, E> res("hello, map!");
438 * Result<size_t, E> res2 = res.map(strlen);
439 * MOZ_ASSERT(res2.unwrap() == 11);
441 * Mapping over an error does not invoke the function and propagates the
442 * error:
444 * Result<V, int> res(5);
445 * MOZ_ASSERT(res.isErr());
446 * Result<W, int> res2 = res.map([](V v) { ... });
447 * MOZ_ASSERT(res2.isErr());
448 * MOZ_ASSERT(res2.unwrapErr() == 5);
450 template <typename F>
451 auto map(F f) -> Result<decltype(f(*((V*)nullptr))), E> {
452 using RetResult = Result<decltype(f(*((V*)nullptr))), E>;
453 return MOZ_LIKELY(isOk()) ? RetResult(f(unwrap())) : RetResult(unwrapErr());
457 * Map a function V -> W over this result's error variant. If this result is
458 * a success, do not invoke the function and move the success over.
460 * Mapping over error values invokes the function to produce a new error
461 * value:
463 * // Map Result<V, int> to another Result<V, int>
464 * Result<V, int> res(5);
465 * Result<V, int> res2 = res.mapErr([](int x) { return x * x; });
466 * MOZ_ASSERT(res2.unwrapErr() == 25);
468 * // Map Result<V, const char*> to Result<V, size_t>
469 * Result<V, const char*> res("hello, map!");
470 * Result<size_t, E> res2 = res.mapErr(strlen);
471 * MOZ_ASSERT(res2.unwrapErr() == 11);
473 * Mapping over a success does not invoke the function and copies the error:
475 * Result<int, V> res(5);
476 * MOZ_ASSERT(res.isOk());
477 * Result<int, W> res2 = res.mapErr([](V v) { ... });
478 * MOZ_ASSERT(res2.isOk());
479 * MOZ_ASSERT(res2.unwrap() == 5);
481 template <typename F>
482 auto mapErr(F f) -> Result<V, std::result_of_t<F(E)>> {
483 using RetResult = Result<V, std::result_of_t<F(E)>>;
484 return isOk() ? RetResult(unwrap()) : RetResult(f(unwrapErr()));
488 * Given a function V -> Result<W, E>, apply it to this result's success value
489 * and return its result. If this result is an error value, it is propagated.
491 * This is sometimes called "flatMap" or ">>=" in other contexts.
493 * `andThen`ing over success values invokes the function to produce a new
494 * result:
496 * Result<const char*, Error> res("hello, andThen!");
497 * Result<HtmlFreeString, Error> res2 = res.andThen([](const char* s) {
498 * return containsHtmlTag(s)
499 * ? Result<HtmlFreeString, Error>(Error("Invalid: contains HTML"))
500 * : Result<HtmlFreeString, Error>(HtmlFreeString(s));
502 * });
503 * MOZ_ASSERT(res2.isOk());
504 * MOZ_ASSERT(res2.unwrap() == HtmlFreeString("hello, andThen!");
506 * `andThen`ing over error results does not invoke the function, and just
507 * propagates the error result:
509 * Result<int, const char*> res("some error");
510 * auto res2 = res.andThen([](int x) { ... });
511 * MOZ_ASSERT(res2.isErr());
512 * MOZ_ASSERT(res.unwrapErr() == res2.unwrapErr());
514 template <typename F, typename = std::enable_if_t<detail::IsResult<
515 decltype((*((F*)nullptr))(*((V*)nullptr)))>::value>>
516 auto andThen(F f) -> decltype(f(*((V*)nullptr))) {
517 return MOZ_LIKELY(isOk()) ? f(unwrap()) : propagateErr();
522 * A type that auto-converts to an error Result. This is like a Result without
523 * a success type. It's the best return type for functions that always return
524 * an error--functions designed to build and populate error objects. It's also
525 * useful in error-handling macros; see MOZ_TRY for an example.
527 template <typename E>
528 class MOZ_MUST_USE_TYPE GenericErrorResult {
529 E mErrorValue;
531 template <typename V, typename E2>
532 friend class Result;
534 public:
535 explicit GenericErrorResult(E&& aErrorValue)
536 : mErrorValue(std::forward<E>(aErrorValue)) {}
539 template <typename E>
540 inline GenericErrorResult<E> Err(E&& aErrorValue) {
541 return GenericErrorResult<E>(std::forward<E>(aErrorValue));
544 } // namespace mozilla
547 * MOZ_TRY(expr) is the C++ equivalent of Rust's `try!(expr);`. First, it
548 * evaluates expr, which must produce a Result value. On success, it
549 * discards the result altogether. On error, it immediately returns an error
550 * Result from the enclosing function.
552 #define MOZ_TRY(expr) \
553 do { \
554 auto mozTryTempResult_ = ::mozilla::ToResult(expr); \
555 if (MOZ_UNLIKELY(mozTryTempResult_.isErr())) { \
556 return mozTryTempResult_.propagateErr(); \
558 } while (0)
561 * MOZ_TRY_VAR(target, expr) is the C++ equivalent of Rust's `target =
562 * try!(expr);`. First, it evaluates expr, which must produce a Result value. On
563 * success, the result's success value is assigned to target. On error,
564 * immediately returns the error result. |target| must evaluate to a reference
565 * without any side effects.
567 #define MOZ_TRY_VAR(target, expr) \
568 do { \
569 auto mozTryVarTempResult_ = (expr); \
570 if (MOZ_UNLIKELY(mozTryVarTempResult_.isErr())) { \
571 return mozTryVarTempResult_.propagateErr(); \
573 (target) = mozTryVarTempResult_.unwrap(); \
574 } while (0)
576 #endif // mozilla_Result_h