Result.h (30774B)
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/. */ 6 7 /* A type suitable for returning either a value or an error from a function. */ 8 9 #ifndef mozilla_Result_h 10 #define mozilla_Result_h 11 12 #include <algorithm> 13 #include <cstdint> 14 #include <cstring> 15 #include <type_traits> 16 #include "mozilla/Assertions.h" 17 #include "mozilla/Attributes.h" 18 #include "mozilla/CompactPair.h" 19 #include "mozilla/MaybeStorageBase.h" 20 21 namespace mozilla { 22 23 /** 24 * Empty struct, indicating success for operations that have no return value. 25 * For example, if you declare another empty struct `struct OutOfMemory {};`, 26 * then `Result<Ok, OutOfMemory>` represents either success or OOM. 27 */ 28 struct Ok {}; 29 30 /** 31 * A tag used to differentiate between GenericErrorResult created by the Err 32 * function (completely new error) and GenericErrorResult created by the 33 * Result::propagateErr function (propagated error). This can be used to track 34 * error propagation and eventually produce error stacks for logging/debugging 35 * purposes. 36 */ 37 struct ErrorPropagationTag {}; 38 39 template <typename E> 40 class GenericErrorResult; 41 template <typename V, typename E> 42 class Result; 43 44 namespace detail { 45 46 enum class PackingStrategy { 47 Variant, 48 NullIsOk, 49 LowBitTagIsError, 50 PackedVariant, 51 ZeroIsEmptyError, 52 }; 53 54 template <typename T> 55 struct UnusedZero; 56 57 template <typename V, typename E, PackingStrategy Strategy> 58 class ResultImplementation; 59 60 template <typename V> 61 struct EmptyWrapper : V { 62 constexpr EmptyWrapper() = default; 63 explicit constexpr EmptyWrapper(const V&) {} 64 explicit constexpr EmptyWrapper(std::in_place_t) {} 65 66 constexpr V* addr() { return this; } 67 constexpr const V* addr() const { return this; } 68 }; 69 70 // The purpose of AlignedStorageOrEmpty is to make an empty class look like 71 // std::aligned_storage_t for the purposes of the PackingStrategy::NullIsOk 72 // specializations of ResultImplementation below. We can't use 73 // std::aligned_storage_t itself with an empty class, since it would no longer 74 // be empty. 75 template <typename V> 76 using AlignedStorageOrEmpty = 77 std::conditional_t<std::is_empty_v<V>, EmptyWrapper<V>, 78 MaybeStorageBase<V>>; 79 80 template <typename V, typename E> 81 class ResultImplementationNullIsOkBase { 82 protected: 83 using ErrorStorageType = typename UnusedZero<E>::StorageType; 84 85 static constexpr auto kNullValue = UnusedZero<E>::nullValue; 86 87 static_assert(std::is_trivially_copyable_v<ErrorStorageType>); 88 89 // XXX This can't be statically asserted in general, if ErrorStorageType is 90 // not a basic type. With C++20 bit_cast, we could probably re-add such as 91 // assertion. static_assert(kNullValue == decltype(kNullValue)(0)); 92 93 CompactPair<AlignedStorageOrEmpty<V>, ErrorStorageType> mValue; 94 95 public: 96 explicit constexpr ResultImplementationNullIsOkBase(const V& aSuccessValue) 97 : mValue(aSuccessValue, kNullValue) {} 98 explicit constexpr ResultImplementationNullIsOkBase(V&& aSuccessValue) 99 : mValue(std::move(aSuccessValue), kNullValue) {} 100 template <typename... Args> 101 explicit constexpr ResultImplementationNullIsOkBase(std::in_place_t, 102 Args&&... aArgs) 103 : mValue(std::piecewise_construct, 104 std::tuple(std::in_place, std::forward<Args>(aArgs)...), 105 std::tuple(kNullValue)) {} 106 explicit constexpr ResultImplementationNullIsOkBase(E aErrorValue) 107 : mValue(std::piecewise_construct, std::tuple<>(), 108 std::tuple(UnusedZero<E>::Store(std::move(aErrorValue)))) { 109 MOZ_ASSERT(mValue.second() != kNullValue); 110 } 111 112 constexpr ResultImplementationNullIsOkBase( 113 ResultImplementationNullIsOkBase&& aOther) 114 : mValue(std::piecewise_construct, std::tuple<>(), 115 std::tuple(aOther.mValue.second())) { 116 if constexpr (!std::is_empty_v<V>) { 117 if (isOk()) { 118 new (mValue.first().addr()) V(std::move(*aOther.mValue.first().addr())); 119 } 120 } 121 } 122 ResultImplementationNullIsOkBase& operator=( 123 ResultImplementationNullIsOkBase&& aOther) { 124 if constexpr (!std::is_empty_v<V>) { 125 if (isOk()) { 126 mValue.first().addr()->~V(); 127 } 128 } 129 mValue.second() = std::move(aOther.mValue.second()); 130 if constexpr (!std::is_empty_v<V>) { 131 if (isOk()) { 132 new (mValue.first().addr()) V(std::move(*aOther.mValue.first().addr())); 133 } 134 } 135 return *this; 136 } 137 138 constexpr bool isOk() const { return mValue.second() == kNullValue; } 139 140 constexpr const V& inspect() const { return *mValue.first().addr(); } 141 constexpr V unwrap() { return std::move(*mValue.first().addr()); } 142 constexpr void updateAfterTracing(V&& aValue) { 143 MOZ_ASSERT(isOk()); 144 if (!std::is_empty_v<V>) { 145 mValue.first().addr()->~V(); 146 new (mValue.first().addr()) V(std::move(aValue)); 147 } 148 } 149 150 constexpr decltype(auto) inspectErr() const { 151 return UnusedZero<E>::Inspect(mValue.second()); 152 } 153 constexpr E unwrapErr() { return UnusedZero<E>::Unwrap(mValue.second()); } 154 constexpr void updateErrorAfterTracing(E&& aErrorValue) { 155 mValue.second() = UnusedZero<E>::Store(std::move(aErrorValue)); 156 } 157 }; 158 159 template <typename V, typename E, 160 bool IsVTriviallyDestructible = std::is_trivially_destructible_v<V>> 161 class ResultImplementationNullIsOk; 162 163 template <typename V, typename E> 164 class ResultImplementationNullIsOk<V, E, true> 165 : public ResultImplementationNullIsOkBase<V, E> { 166 public: 167 using ResultImplementationNullIsOkBase<V, 168 E>::ResultImplementationNullIsOkBase; 169 }; 170 171 template <typename V, typename E> 172 class ResultImplementationNullIsOk<V, E, false> 173 : public ResultImplementationNullIsOkBase<V, E> { 174 public: 175 using ResultImplementationNullIsOkBase<V, 176 E>::ResultImplementationNullIsOkBase; 177 178 ResultImplementationNullIsOk(ResultImplementationNullIsOk&&) = default; 179 ResultImplementationNullIsOk& operator=(ResultImplementationNullIsOk&&) = 180 default; 181 182 ~ResultImplementationNullIsOk() { 183 if (this->isOk()) { 184 this->mValue.first().addr()->~V(); 185 } 186 } 187 }; 188 189 /** 190 * Specialization for when the success type is one of integral, pointer, or 191 * enum, where 0 is unused, and the error type is an empty struct. 192 */ 193 template <typename V, typename E> 194 class ResultImplementation<V, E, PackingStrategy::ZeroIsEmptyError> { 195 static_assert(std::is_integral_v<V> || std::is_pointer_v<V> || 196 std::is_enum_v<V>); 197 static_assert(std::is_empty_v<E>); 198 199 V mValue; 200 201 public: 202 static constexpr PackingStrategy Strategy = PackingStrategy::ZeroIsEmptyError; 203 204 explicit constexpr ResultImplementation(V aValue) : mValue(aValue) {} 205 explicit constexpr ResultImplementation(E aErrorValue) : mValue(V(0)) {} 206 207 constexpr bool isOk() const { return mValue != V(0); } 208 209 constexpr V inspect() const { return mValue; } 210 constexpr V unwrap() { return inspect(); } 211 212 constexpr E inspectErr() const { return E(); } 213 constexpr E unwrapErr() { return inspectErr(); } 214 215 constexpr void updateAfterTracing(V&& aValue) { 216 this->~ResultImplementation(); 217 new (this) ResultImplementation(std::move(aValue)); 218 } 219 constexpr void updateErrorAfterTracing(E&& aErrorValue) { 220 this->~ResultImplementation(); 221 new (this) ResultImplementation(std::move(aErrorValue)); 222 } 223 }; 224 225 /** 226 * Specialization for when the success type is default-constructible and the 227 * error type is a value type which can never have the value 0 (as determined by 228 * UnusedZero<>). 229 */ 230 template <typename V, typename E> 231 class ResultImplementation<V, E, PackingStrategy::NullIsOk> 232 : public ResultImplementationNullIsOk<V, E> { 233 public: 234 static constexpr PackingStrategy Strategy = PackingStrategy::NullIsOk; 235 using ResultImplementationNullIsOk<V, E>::ResultImplementationNullIsOk; 236 }; 237 238 template <size_t S> 239 using UnsignedIntType = std::conditional_t< 240 S == 1, std::uint8_t, 241 std::conditional_t< 242 S == 2, std::uint16_t, 243 std::conditional_t<S == 3 || S == 4, std::uint32_t, 244 std::conditional_t<S <= 8, std::uint64_t, void>>>>; 245 246 /** 247 * Specialization for when alignment permits using the least significant bit 248 * as a tag bit. 249 */ 250 template <typename V, typename E> 251 class ResultImplementation<V, E, PackingStrategy::LowBitTagIsError> { 252 static_assert(std::is_trivially_copyable_v<V> && 253 std::is_trivially_destructible_v<V>); 254 static_assert(std::is_trivially_copyable_v<E> && 255 std::is_trivially_destructible_v<E>); 256 257 static constexpr size_t kRequiredSize = std::max(sizeof(V), sizeof(E)); 258 259 using StorageType = UnsignedIntType<kRequiredSize>; 260 261 #if defined(__clang__) 262 alignas(std::max(alignof(V), alignof(E))) StorageType mBits; 263 #else 264 // Some gcc versions choke on using std::max with alignas, see 265 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94929 (and this seems to have 266 // regressed in some gcc 9.x version before being fixed again) Keeping the 267 // code above since we would eventually drop this when we no longer support 268 // gcc versions with the bug. 269 alignas(alignof(V) > alignof(E) ? alignof(V) : alignof(E)) StorageType mBits; 270 #endif 271 272 public: 273 static constexpr PackingStrategy Strategy = PackingStrategy::LowBitTagIsError; 274 275 explicit constexpr ResultImplementation(V aValue) : mBits(0) { 276 if constexpr (!std::is_empty_v<V>) { 277 std::memcpy(&mBits, &aValue, sizeof(V)); 278 MOZ_ASSERT((mBits & 1) == 0); 279 } else { 280 (void)aValue; 281 } 282 } 283 explicit constexpr ResultImplementation(E aErrorValue) : mBits(1) { 284 if constexpr (!std::is_empty_v<E>) { 285 std::memcpy(&mBits, &aErrorValue, sizeof(E)); 286 MOZ_ASSERT((mBits & 1) == 0); 287 mBits |= 1; 288 } else { 289 (void)aErrorValue; 290 } 291 } 292 293 constexpr bool isOk() const { return (mBits & 1) == 0; } 294 295 constexpr V inspect() const { 296 V res; 297 std::memcpy(&res, &mBits, sizeof(V)); 298 return res; 299 } 300 constexpr V unwrap() { return inspect(); } 301 302 constexpr E inspectErr() const { 303 const auto bits = mBits ^ 1; 304 E res; 305 std::memcpy(&res, &bits, sizeof(E)); 306 return res; 307 } 308 constexpr E unwrapErr() { return inspectErr(); } 309 310 constexpr void updateAfterTracing(V&& aValue) { 311 this->~ResultImplementation(); 312 new (this) ResultImplementation(std::move(aValue)); 313 } 314 constexpr void updateErrorAfterTracing(E&& aErrorValue) { 315 this->~ResultImplementation(); 316 new (this) ResultImplementation(std::move(aErrorValue)); 317 } 318 }; 319 320 // Return true if any of the struct can fit in a word. 321 template <typename V, typename E> 322 struct IsPackableVariant { 323 struct VEbool { 324 explicit constexpr VEbool(V&& aValue) : v(std::move(aValue)), ok(true) {} 325 explicit constexpr VEbool(E&& aErrorValue) 326 : e(std::move(aErrorValue)), ok(false) {} 327 V v; 328 E e; 329 bool ok; 330 }; 331 struct EVbool { 332 explicit constexpr EVbool(V&& aValue) : v(std::move(aValue)), ok(true) {} 333 explicit constexpr EVbool(E&& aErrorValue) 334 : e(std::move(aErrorValue)), ok(false) {} 335 E e; 336 V v; 337 bool ok; 338 }; 339 340 using Impl = 341 std::conditional_t<sizeof(VEbool) <= sizeof(EVbool), VEbool, EVbool>; 342 343 static const bool value = sizeof(Impl) <= sizeof(uintptr_t); 344 }; 345 346 /** 347 * Specialization for when both type are not using all the bytes, in order to 348 * use one byte as a tag. 349 */ 350 template <typename V, typename E> 351 class ResultImplementation<V, E, PackingStrategy::PackedVariant> { 352 using Impl = typename IsPackableVariant<V, E>::Impl; 353 Impl data; 354 355 public: 356 static constexpr PackingStrategy Strategy = PackingStrategy::PackedVariant; 357 358 explicit constexpr ResultImplementation(V aValue) : data(std::move(aValue)) {} 359 explicit constexpr ResultImplementation(E aErrorValue) 360 : data(std::move(aErrorValue)) {} 361 362 constexpr bool isOk() const { return data.ok; } 363 364 constexpr const V& inspect() const { return data.v; } 365 constexpr V unwrap() { return std::move(data.v); } 366 367 constexpr const E& inspectErr() const { return data.e; } 368 constexpr E unwrapErr() { return std::move(data.e); } 369 370 constexpr void updateAfterTracing(V&& aValue) { 371 MOZ_ASSERT(data.ok); 372 this->~ResultImplementation(); 373 new (this) ResultImplementation(std::move(aValue)); 374 } 375 constexpr void updateErrorAfterTracing(E&& aErrorValue) { 376 MOZ_ASSERT(!data.ok); 377 this->~ResultImplementation(); 378 new (this) ResultImplementation(std::move(aErrorValue)); 379 } 380 }; 381 382 // To use nullptr as a special value, we need the counter part to exclude zero 383 // from its range of valid representations. 384 // 385 // By default assume that zero can be represented. 386 template <typename T> 387 struct UnusedZero { 388 static const bool value = false; 389 }; 390 391 // This template can be used as a helper for specializing UnusedZero for scoped 392 // enum types which never use 0 as an error value, e.g. 393 // 394 // namespace mozilla::detail { 395 // 396 // template <> 397 // struct UnusedZero<MyEnumType> : UnusedZeroEnum<MyEnumType> {}; 398 // 399 // } // namespace mozilla::detail 400 // 401 template <typename T> 402 struct UnusedZeroEnum { 403 using StorageType = std::underlying_type_t<T>; 404 405 static constexpr bool value = true; 406 static constexpr StorageType nullValue = 0; 407 408 static constexpr T Inspect(const StorageType& aValue) { 409 return static_cast<T>(aValue); 410 } 411 static constexpr T Unwrap(StorageType aValue) { 412 return static_cast<T>(aValue); 413 } 414 static constexpr StorageType Store(T aValue) { 415 return static_cast<StorageType>(aValue); 416 } 417 }; 418 419 // A bit of help figuring out which of the above specializations to use. 420 // 421 // We begin by safely assuming types don't have a spare bit, unless they are 422 // empty. 423 template <typename T> 424 struct HasFreeLSB { 425 static const bool value = std::is_empty_v<T>; 426 }; 427 428 // As an incomplete type, void* does not have a spare bit. 429 template <> 430 struct HasFreeLSB<void*> { 431 static const bool value = false; 432 }; 433 434 // The lowest bit of a properly-aligned pointer is always zero if the pointee 435 // type is greater than byte-aligned. That bit is free to use if it's masked 436 // out of such pointers before they're dereferenced. 437 template <typename T> 438 struct HasFreeLSB<T*> { 439 static const bool value = (alignof(T) & 1) == 0; 440 }; 441 442 // Select one of the previous result implementation based on the properties of 443 // the V and E types. 444 template <typename V, typename E> 445 struct SelectResultImpl { 446 static const PackingStrategy value = 447 (UnusedZero<V>::value && std::is_empty_v<E>) 448 ? PackingStrategy::ZeroIsEmptyError 449 : (HasFreeLSB<V>::value && HasFreeLSB<E>::value) 450 ? PackingStrategy::LowBitTagIsError 451 : (UnusedZero<E>::value && sizeof(E) <= sizeof(uintptr_t)) 452 ? PackingStrategy::NullIsOk 453 : (std::is_default_constructible_v<V> && 454 std::is_default_constructible_v<E> && IsPackableVariant<V, E>::value) 455 ? PackingStrategy::PackedVariant 456 : PackingStrategy::Variant; 457 458 using Type = ResultImplementation<V, E, value>; 459 }; 460 461 template <typename T> 462 struct IsResult : std::false_type {}; 463 464 template <typename V, typename E> 465 struct IsResult<Result<V, E>> : std::true_type {}; 466 467 } // namespace detail 468 469 template <typename V, typename E> 470 constexpr auto ToResult(Result<V, E>&& aValue) 471 -> decltype(std::forward<Result<V, E>>(aValue)) { 472 return std::forward<Result<V, E>>(aValue); 473 } 474 475 /** 476 * Result<V, E> represents the outcome of an operation that can either succeed 477 * or fail. It contains either a success value of type V or an error value of 478 * type E. 479 * 480 * All Result methods are const, so results are basically immutable. 481 * This is just like Variant<V, E> but with a slightly different API, and the 482 * following cases are optimized so Result can be stored more efficiently: 483 * 484 * - If both the success and error types do not use their least significant bit, 485 * are trivially copyable and destructible, Result<V, E> is guaranteed to be as 486 * large as the larger type. This is determined via the HasFreeLSB trait. By 487 * default, empty classes (in particular Ok) and aligned pointer types are 488 * assumed to have a free LSB, but you can specialize this trait for other 489 * types. If the success type is empty, the representation is guaranteed to be 490 * all zero bits on success. Do not change this representation! There is JIT 491 * code that depends on it. (Implementation note: The lowest bit is used as a 492 * tag bit: 0 to indicate the Result's bits are a success value, 1 to indicate 493 * the Result's bits (with the 1 masked out) encode an error value) 494 * 495 * - Else, if the error type can't have a all-zero bits representation and is 496 * not larger than a pointer, a CompactPair is used to represent this rather 497 * than a Variant. This has shown to be better optimizable, and the template 498 * code is much simpler than that of Variant, so it should also compile faster. 499 * Whether an error type can't be all-zero bits, is determined via the 500 * UnusedZero trait. MFBT doesn't declare any public type UnusedZero, but 501 * nsresult is declared UnusedZero in XPCOM. 502 * 503 * The purpose of Result is to reduce the screwups caused by using `false` or 504 * `nullptr` to indicate errors. 505 * What screwups? See <https://bugzilla.mozilla.org/show_bug.cgi?id=912928> for 506 * a partial list. 507 * 508 * Result<const V, E> or Result<V, const E> are not meaningful. The success or 509 * error values in a Result instance are non-modifiable in-place anyway. This 510 * guarantee must also be maintained when evolving Result. They can be 511 * unwrap()ped, but this loses const qualification. However, Result<const V, E> 512 * or Result<V, const E> may be misleading and prevent movability. Just use 513 * Result<V, E>. (Result<const V*, E> may make sense though, just Result<const 514 * V* const, E> is not possible.) 515 */ 516 template <typename V, typename E> 517 class [[nodiscard]] Result final { 518 // See class comment on Result<const V, E> and Result<V, const E>. 519 static_assert(!std::is_const_v<V>); 520 static_assert(!std::is_const_v<E>); 521 static_assert(!std::is_reference_v<V>); 522 static_assert(!std::is_reference_v<E>); 523 524 using Impl = typename detail::SelectResultImpl<V, E>::Type; 525 526 Impl mImpl; 527 // Are you getting this error? 528 // > error: implicit instantiation of undefined template 529 // > 'mozilla::detail::ResultImplementation<$V,$E, 530 // > mozilla::detail::PackingStrategy::Variant>' 531 // You need to include "ResultVariant.h"! 532 533 public: 534 static constexpr detail::PackingStrategy Strategy = Impl::Strategy; 535 using ok_type = V; 536 using err_type = E; 537 538 /** Create a success result. */ 539 MOZ_IMPLICIT constexpr Result(V&& aValue) : mImpl(std::move(aValue)) { 540 MOZ_ASSERT(isOk()); 541 } 542 543 /** Create a success result. */ 544 MOZ_IMPLICIT constexpr Result(const V& aValue) : mImpl(aValue) { 545 MOZ_ASSERT(isOk()); 546 } 547 548 /** Create a success result in-place. */ 549 template <typename... Args> 550 explicit constexpr Result(std::in_place_t, Args&&... aArgs) 551 : mImpl(std::in_place, std::forward<Args>(aArgs)...) { 552 MOZ_ASSERT(isOk()); 553 } 554 555 /** Create an error result. */ 556 explicit constexpr Result(const E& aErrorValue) : mImpl(aErrorValue) { 557 MOZ_ASSERT(isErr()); 558 } 559 explicit constexpr Result(E&& aErrorValue) : mImpl(std::move(aErrorValue)) { 560 MOZ_ASSERT(isErr()); 561 } 562 563 /** 564 * Create a (success/error) result from another (success/error) result with 565 * different but convertible value and error types. 566 */ 567 template <typename V2, typename E2, 568 typename = std::enable_if_t<std::is_convertible_v<V2, V> && 569 std::is_convertible_v<E2, E>>> 570 MOZ_IMPLICIT constexpr Result(Result<V2, E2>&& aOther) 571 : mImpl(aOther.isOk() ? Impl{aOther.unwrap()} 572 : Impl{aOther.unwrapErr()}) {} 573 574 /** 575 * Implementation detail of MOZ_TRY(). 576 * Create an error result from another error result. 577 */ 578 template <typename E2> 579 MOZ_IMPLICIT constexpr Result(GenericErrorResult<E2>&& aErrorResult) 580 : mImpl(std::move(aErrorResult.mErrorValue)) { 581 static_assert(std::is_convertible_v<E2, E>, "E2 must be convertible to E"); 582 MOZ_ASSERT(isErr()); 583 } 584 585 /** 586 * Implementation detail of MOZ_TRY(). 587 * Create an error result from another error result. 588 */ 589 template <typename E2> 590 MOZ_IMPLICIT constexpr Result(const GenericErrorResult<E2>& aErrorResult) 591 : mImpl(aErrorResult.mErrorValue) { 592 static_assert(std::is_convertible_v<E2, E>, "E2 must be convertible to E"); 593 MOZ_ASSERT(isErr()); 594 } 595 596 Result(const Result&) = delete; 597 Result(Result&&) = default; 598 Result& operator=(const Result&) = delete; 599 Result& operator=(Result&&) = default; 600 601 /** True if this Result is a success result. */ 602 constexpr bool isOk() const { return mImpl.isOk(); } 603 604 /** True if this Result is an error result. */ 605 constexpr bool isErr() const { return !mImpl.isOk(); } 606 607 /** Take the success value from this Result, which must be a success result. 608 */ 609 constexpr V unwrap() { 610 MOZ_ASSERT(isOk()); 611 return mImpl.unwrap(); 612 } 613 614 /** 615 * Take the success value from this Result, which must be a success result. 616 * If it is an error result, then return the aValue. 617 */ 618 constexpr V unwrapOr(V aValue) { 619 return MOZ_LIKELY(isOk()) ? mImpl.unwrap() : std::move(aValue); 620 } 621 622 /** Take the error value from this Result, which must be an error result. */ 623 constexpr E unwrapErr() { 624 MOZ_ASSERT(isErr()); 625 return mImpl.unwrapErr(); 626 } 627 628 /** Used only for GC tracing. If used in Rooted<Result<...>>, V must have a 629 * GCPolicy for tracing it. */ 630 constexpr void updateAfterTracing(V&& aValue) { 631 mImpl.updateAfterTracing(std::move(aValue)); 632 } 633 634 /** Used only for GC tracing. If used in Rooted<Result<...>>, E must have a 635 * GCPolicy for tracing it. */ 636 constexpr void updateErrorAfterTracing(E&& aErrorValue) { 637 mImpl.updateErrorAfterTracing(std::move(aErrorValue)); 638 } 639 640 /** See the success value from this Result, which must be a success result. */ 641 constexpr decltype(auto) inspect() const { 642 static_assert(!std::is_reference_v< 643 std::invoke_result_t<decltype(&Impl::inspect), Impl>> || 644 std::is_const_v<std::remove_reference_t< 645 std::invoke_result_t<decltype(&Impl::inspect), Impl>>>); 646 MOZ_ASSERT(isOk()); 647 return mImpl.inspect(); 648 } 649 650 /** See the error value from this Result, which must be an error result. */ 651 constexpr decltype(auto) inspectErr() const { 652 static_assert( 653 !std::is_reference_v< 654 std::invoke_result_t<decltype(&Impl::inspectErr), Impl>> || 655 std::is_const_v<std::remove_reference_t< 656 std::invoke_result_t<decltype(&Impl::inspectErr), Impl>>>); 657 MOZ_ASSERT(isErr()); 658 return mImpl.inspectErr(); 659 } 660 661 /** Propagate the error value from this Result, which must be an error result. 662 * 663 * This can be used to propagate an error from a function call to the caller 664 * with a different value type, but the same error type: 665 * 666 * Result<T1, E> Func1() { 667 * Result<T2, E> res = Func2(); 668 * if (res.isErr()) { return res.propagateErr(); } 669 * } 670 */ 671 constexpr GenericErrorResult<E> propagateErr() { 672 MOZ_ASSERT(isErr()); 673 return GenericErrorResult<E>{mImpl.unwrapErr(), ErrorPropagationTag{}}; 674 } 675 676 /** 677 * Map a function V -> V2 over this result's success variant. If this result 678 * is an error, do not invoke the function and propagate the error. 679 * 680 * Mapping over success values invokes the function to produce a new success 681 * value: 682 * 683 * // Map Result<int, E> to another Result<int, E> 684 * Result<int, E> res(5); 685 * Result<int, E> res2 = res.map([](int x) { return x * x; }); 686 * MOZ_ASSERT(res.isOk()); 687 * MOZ_ASSERT(res2.unwrap() == 25); 688 * 689 * // Map Result<const char*, E> to Result<size_t, E> 690 * Result<const char*, E> res("hello, map!"); 691 * Result<size_t, E> res2 = res.map(strlen); 692 * MOZ_ASSERT(res.isOk()); 693 * MOZ_ASSERT(res2.unwrap() == 11); 694 * 695 * Mapping over an error does not invoke the function and propagates the 696 * error: 697 * 698 * Result<V, int> res(5); 699 * MOZ_ASSERT(res.isErr()); 700 * Result<V2, int> res2 = res.map([](V v) { ... }); 701 * MOZ_ASSERT(res2.isErr()); 702 * MOZ_ASSERT(res2.unwrapErr() == 5); 703 */ 704 template <typename F> 705 constexpr auto map(F f) -> Result<std::invoke_result_t<F, V>, E> { 706 using RetResult = Result<std::invoke_result_t<F, V>, E>; 707 return MOZ_LIKELY(isOk()) ? RetResult(f(unwrap())) : RetResult(unwrapErr()); 708 } 709 710 /** 711 * Map a function E -> E2 over this result's error variant. If this result is 712 * a success, do not invoke the function and move the success over. 713 * 714 * Mapping over error values invokes the function to produce a new error 715 * value: 716 * 717 * // Map Result<V, int> to another Result<V, int> 718 * Result<V, int> res(5); 719 * Result<V, int> res2 = res.mapErr([](int x) { return x * x; }); 720 * MOZ_ASSERT(res2.isErr()); 721 * MOZ_ASSERT(res2.unwrapErr() == 25); 722 * 723 * // Map Result<V, const char*> to Result<V, size_t> 724 * Result<V, const char*> res("hello, mapErr!"); 725 * Result<V, size_t> res2 = res.mapErr(strlen); 726 * MOZ_ASSERT(res2.isErr()); 727 * MOZ_ASSERT(res2.unwrapErr() == 14); 728 * 729 * Mapping over a success does not invoke the function and moves the success: 730 * 731 * Result<int, E> res(5); 732 * MOZ_ASSERT(res.isOk()); 733 * Result<int, E2> res2 = res.mapErr([](E e) { ... }); 734 * MOZ_ASSERT(res2.isOk()); 735 * MOZ_ASSERT(res2.unwrap() == 5); 736 */ 737 template <typename F> 738 constexpr auto mapErr(F f) { 739 using RetResult = Result<V, std::invoke_result_t<F, E>>; 740 return MOZ_UNLIKELY(isErr()) ? RetResult(f(unwrapErr())) 741 : RetResult(unwrap()); 742 } 743 744 /** 745 * Map a function E -> Result<V, E2> over this result's error variant. If 746 * this result is a success, do not invoke the function and move the success 747 * over. 748 * 749 * `orElse`ing over error values invokes the function to produce a new 750 * result: 751 * 752 * // `orElse` Result<V, int> error variant to another Result<V, int> 753 * // error variant or Result<V, int> success variant 754 * auto orElse = [](int x) -> Result<V, int> { 755 * if (x != 6) { 756 * return Err(x * x); 757 * } 758 * return V(...); 759 * }; 760 * 761 * Result<V, int> res(5); 762 * auto res2 = res.orElse(orElse); 763 * MOZ_ASSERT(res2.isErr()); 764 * MOZ_ASSERT(res2.unwrapErr() == 25); 765 * 766 * Result<V, int> res3(6); 767 * auto res4 = res3.orElse(orElse); 768 * MOZ_ASSERT(res4.isOk()); 769 * MOZ_ASSERT(res4.unwrap() == ...); 770 * 771 * // `orElse` Result<V, const char*> error variant to Result<V, size_t> 772 * // error variant or Result<V, size_t> success variant 773 * auto orElse = [](const char* s) -> Result<V, size_t> { 774 * if (strcmp(s, "foo")) { 775 * return Err(strlen(s)); 776 * } 777 * return V(...); 778 * }; 779 * 780 * Result<V, const char*> res("hello, orElse!"); 781 * auto res2 = res.orElse(orElse); 782 * MOZ_ASSERT(res2.isErr()); 783 * MOZ_ASSERT(res2.unwrapErr() == 14); 784 * 785 * Result<V, const char*> res3("foo"); 786 * auto res4 = ress.orElse(orElse); 787 * MOZ_ASSERT(res4.isOk()); 788 * MOZ_ASSERT(res4.unwrap() == ...); 789 * 790 * `orElse`ing over a success does not invoke the function and moves the 791 * success: 792 * 793 * Result<int, E> res(5); 794 * MOZ_ASSERT(res.isOk()); 795 * Result<int, E2> res2 = res.orElse([](E e) { ... }); 796 * MOZ_ASSERT(res2.isOk()); 797 * MOZ_ASSERT(res2.unwrap() == 5); 798 */ 799 template <typename F> 800 auto orElse(F f) -> Result<V, typename std::invoke_result_t<F, E>::err_type> { 801 return MOZ_UNLIKELY(isErr()) ? f(unwrapErr()) : unwrap(); 802 } 803 804 /** 805 * Given a function V -> Result<V2, E>, apply it to this result's success 806 * value and return its result. If this result is an error value, it is 807 * propagated. 808 * 809 * This is sometimes called "flatMap" or ">>=" in other contexts. 810 * 811 * `andThen`ing over success values invokes the function to produce a new 812 * result: 813 * 814 * Result<const char*, Error> res("hello, andThen!"); 815 * Result<HtmlFreeString, Error> res2 = res.andThen([](const char* s) { 816 * return containsHtmlTag(s) 817 * ? Result<HtmlFreeString, Error>(Error("Invalid: contains HTML")) 818 * : Result<HtmlFreeString, Error>(HtmlFreeString(s)); 819 * } 820 * }); 821 * MOZ_ASSERT(res2.isOk()); 822 * MOZ_ASSERT(res2.unwrap() == HtmlFreeString("hello, andThen!"); 823 * 824 * `andThen`ing over error results does not invoke the function, and just 825 * propagates the error result: 826 * 827 * Result<int, const char*> res("some error"); 828 * auto res2 = res.andThen([](int x) { ... }); 829 * MOZ_ASSERT(res2.isErr()); 830 * MOZ_ASSERT(res.unwrapErr() == res2.unwrapErr()); 831 */ 832 template <typename F, typename = std::enable_if_t<detail::IsResult< 833 std::invoke_result_t<F, V&&>>::value>> 834 constexpr auto andThen(F f) -> std::invoke_result_t<F, V&&> { 835 return MOZ_LIKELY(isOk()) ? f(unwrap()) : propagateErr(); 836 } 837 838 bool operator==(const Result<V, E>& aOther) const { 839 return (isOk() && aOther.isOk() && inspect() == aOther.inspect()) || 840 (isErr() && aOther.isErr() && inspectErr() == aOther.inspectErr()); 841 } 842 843 bool operator!=(const Result<V, E>& aOther) const { 844 return !(*this == aOther); 845 } 846 }; 847 848 /** 849 * A type that auto-converts to an error Result. This is like a Result without 850 * a success type. It's the best return type for functions that always return 851 * an error--functions designed to build and populate error objects. It's also 852 * useful in error-handling macros; see MOZ_TRY for an example. 853 */ 854 template <typename E> 855 class [[nodiscard]] GenericErrorResult { 856 E mErrorValue; 857 858 template <typename V, typename E2> 859 friend class Result; 860 861 public: 862 explicit constexpr GenericErrorResult(const E& aErrorValue) 863 : mErrorValue(aErrorValue) {} 864 865 explicit constexpr GenericErrorResult(E&& aErrorValue) 866 : mErrorValue(std::move(aErrorValue)) {} 867 868 constexpr GenericErrorResult(const E& aErrorValue, const ErrorPropagationTag&) 869 : GenericErrorResult(aErrorValue) {} 870 871 constexpr GenericErrorResult(E&& aErrorValue, const ErrorPropagationTag&) 872 : GenericErrorResult(std::move(aErrorValue)) {} 873 }; 874 875 template <typename E> 876 inline constexpr auto Err(E&& aErrorValue) { 877 return GenericErrorResult<std::decay_t<E>>(std::forward<E>(aErrorValue)); 878 } 879 880 } // namespace mozilla 881 882 #endif // mozilla_Result_h