tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestResult.cpp (25755B)


      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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include <stdint.h>
      8 #include <string.h>
      9 #include "mozilla/Casting.h"
     10 #include "mozilla/ResultVariant.h"
     11 #include "mozilla/Try.h"
     12 #include "mozilla/UniquePtr.h"
     13 
     14 using mozilla::Err;
     15 using mozilla::GenericErrorResult;
     16 using mozilla::Ok;
     17 using mozilla::Result;
     18 using mozilla::UniquePtr;
     19 
     20 #define MOZ_STATIC_AND_RELEASE_ASSERT(expr) \
     21  static_assert(expr);                      \
     22  MOZ_RELEASE_ASSERT(expr)
     23 
     24 enum struct TestUnusedZeroEnum : int16_t { Ok = 0, NotOk = 1 };
     25 
     26 namespace mozilla::detail {
     27 template <>
     28 struct UnusedZero<TestUnusedZeroEnum> : UnusedZeroEnum<TestUnusedZeroEnum> {};
     29 }  // namespace mozilla::detail
     30 
     31 struct Failed {};
     32 
     33 namespace mozilla::detail {
     34 template <>
     35 struct UnusedZero<Failed> {
     36  using StorageType = uintptr_t;
     37 
     38  static constexpr bool value = true;
     39  static constexpr StorageType nullValue = 0;
     40  static constexpr StorageType GetDefaultValue() { return 2; }
     41 
     42  static constexpr void AssertValid(StorageType aValue) {}
     43  static constexpr Failed Inspect(const StorageType& aValue) {
     44    return Failed{};
     45  }
     46  static constexpr Failed Unwrap(StorageType aValue) { return Failed{}; }
     47  static constexpr StorageType Store(Failed aValue) {
     48    return GetDefaultValue();
     49  }
     50 };
     51 
     52 }  // namespace mozilla::detail
     53 
     54 // V is trivially default-constructible, and E has UnusedZero<E>::value == true,
     55 // for a reference type and for a non-reference type
     56 static_assert(mozilla::detail::SelectResultImpl<uintptr_t, Failed>::value ==
     57              mozilla::detail::PackingStrategy::NullIsOk);
     58 static_assert(
     59    mozilla::detail::SelectResultImpl<Ok, TestUnusedZeroEnum>::value ==
     60    mozilla::detail::PackingStrategy::NullIsOk);
     61 static_assert(mozilla::detail::SelectResultImpl<Ok, Failed>::value ==
     62              mozilla::detail::PackingStrategy::LowBitTagIsError);
     63 
     64 static_assert(std::is_trivially_destructible_v<Result<uintptr_t, Failed>>);
     65 static_assert(std::is_trivially_destructible_v<Result<Ok, TestUnusedZeroEnum>>);
     66 static_assert(std::is_trivially_destructible_v<Result<Ok, Failed>>);
     67 
     68 static_assert(
     69    sizeof(Result<bool, TestUnusedZeroEnum>) <= sizeof(uintptr_t),
     70    "Result with bool value type should not be larger than pointer-sized");
     71 static_assert(sizeof(Result<Ok, Failed>) == sizeof(uint8_t),
     72              "Result with empty value type should be size 1");
     73 static_assert(sizeof(Result<int*, Failed>) == sizeof(uintptr_t),
     74              "Result with two aligned pointer types should be pointer-sized");
     75 static_assert(
     76    sizeof(Result<char*, Failed*>) > sizeof(char*),
     77    "Result with unaligned success type `char*` must not be pointer-sized");
     78 static_assert(
     79    sizeof(Result<int*, char*>) > sizeof(char*),
     80    "Result with unaligned error type `char*` must not be pointer-sized");
     81 
     82 enum Foo8 : uint8_t {};
     83 enum Foo16 : uint16_t {};
     84 enum Foo32 : uint32_t {};
     85 static_assert(sizeof(Result<Ok, Foo8>) <= sizeof(uintptr_t),
     86              "Result with small types should be pointer-sized");
     87 static_assert(sizeof(Result<Ok, Foo16>) <= sizeof(uintptr_t),
     88              "Result with small types should be pointer-sized");
     89 static_assert(sizeof(Foo32) >= sizeof(uintptr_t) ||
     90                  sizeof(Result<Ok, Foo32>) <= sizeof(uintptr_t),
     91              "Result with small types should be pointer-sized");
     92 
     93 static_assert(sizeof(Result<Foo16, Foo8>) <= sizeof(uintptr_t),
     94              "Result with small types should be pointer-sized");
     95 static_assert(sizeof(Result<Foo8, Foo16>) <= sizeof(uintptr_t),
     96              "Result with small types should be pointer-sized");
     97 static_assert(sizeof(Foo32) >= sizeof(uintptr_t) ||
     98                  sizeof(Result<Foo32, Foo16>) <= sizeof(uintptr_t),
     99              "Result with small types should be pointer-sized");
    100 static_assert(sizeof(Foo32) >= sizeof(uintptr_t) ||
    101                  sizeof(Result<Foo16, Foo32>) <= sizeof(uintptr_t),
    102              "Result with small types should be pointer-sized");
    103 
    104 #if __cplusplus < 202002L
    105 static_assert(std::is_literal_type_v<Result<int*, Failed>>);
    106 static_assert(std::is_literal_type_v<Result<Ok, Failed>>);
    107 static_assert(std::is_literal_type_v<Result<Ok, Foo8>>);
    108 static_assert(std::is_literal_type_v<Result<Foo8, Foo16>>);
    109 static_assert(!std::is_literal_type_v<Result<Ok, UniquePtr<int>>>);
    110 #endif
    111 
    112 static constexpr GenericErrorResult<Failed> Fail() { return Err(Failed{}); }
    113 
    114 static constexpr GenericErrorResult<TestUnusedZeroEnum>
    115 FailTestUnusedZeroEnum() {
    116  return Err(TestUnusedZeroEnum::NotOk);
    117 }
    118 
    119 static constexpr Result<Ok, Failed> Task1(bool pass) {
    120  if (!pass) {
    121    return Fail();  // implicit conversion from GenericErrorResult to Result
    122  }
    123  return Ok();
    124 }
    125 
    126 static constexpr Result<Ok, TestUnusedZeroEnum> Task1UnusedZeroEnumErr(
    127    bool pass) {
    128  if (!pass) {
    129    return FailTestUnusedZeroEnum();  // implicit conversion from
    130                                      // GenericErrorResult to Result
    131  }
    132  return Ok();
    133 }
    134 
    135 static constexpr Result<int, Failed> Task2(bool pass, int value) {
    136  MOZ_TRY(
    137      Task1(pass));  // converts one type of result to another in the error case
    138  return value;      // implicit conversion from T to Result<T, E>
    139 }
    140 
    141 static constexpr Result<int, TestUnusedZeroEnum> Task2UnusedZeroEnumErr(
    142    bool pass, int value) {
    143  MOZ_TRY(Task1UnusedZeroEnumErr(
    144      pass));    // converts one type of result to another in the error case
    145  return value;  // implicit conversion from T to Result<T, E>
    146 }
    147 
    148 static Result<int, Failed> Task3(bool pass1, bool pass2, int value) {
    149  auto x = MOZ_TRY(Task2(pass1, value));
    150  auto y = MOZ_TRY(Task2(pass2, value));
    151  return x + y;
    152 }
    153 
    154 static void BasicTests() {
    155  MOZ_STATIC_AND_RELEASE_ASSERT(Task1(true).isOk());
    156  MOZ_STATIC_AND_RELEASE_ASSERT(!Task1(true).isErr());
    157  MOZ_STATIC_AND_RELEASE_ASSERT(!Task1(false).isOk());
    158  MOZ_STATIC_AND_RELEASE_ASSERT(Task1(false).isErr());
    159 
    160  MOZ_STATIC_AND_RELEASE_ASSERT(Task1UnusedZeroEnumErr(true).isOk());
    161  MOZ_STATIC_AND_RELEASE_ASSERT(!Task1UnusedZeroEnumErr(true).isErr());
    162  MOZ_STATIC_AND_RELEASE_ASSERT(!Task1UnusedZeroEnumErr(false).isOk());
    163  MOZ_STATIC_AND_RELEASE_ASSERT(Task1UnusedZeroEnumErr(false).isErr());
    164  MOZ_STATIC_AND_RELEASE_ASSERT(TestUnusedZeroEnum::NotOk ==
    165                                Task1UnusedZeroEnumErr(false).inspectErr());
    166  MOZ_STATIC_AND_RELEASE_ASSERT(TestUnusedZeroEnum::NotOk ==
    167                                Task1UnusedZeroEnumErr(false).unwrapErr());
    168 
    169  // MOZ_TRY works.
    170  MOZ_RELEASE_ASSERT(Task2(true, 3).isOk());
    171  MOZ_RELEASE_ASSERT(Task2(true, 3).unwrap() == 3);
    172  MOZ_RELEASE_ASSERT(Task2(true, 3).unwrapOr(6) == 3);
    173  MOZ_RELEASE_ASSERT(Task2(false, 3).isErr());
    174  MOZ_RELEASE_ASSERT(Task2(false, 3).unwrapOr(6) == 6);
    175 
    176  MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(true, 3).isOk());
    177  MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(true, 3).unwrap() == 3);
    178  MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(true, 3).unwrapOr(6) ==
    179                                3);
    180  MOZ_RELEASE_ASSERT(Task2UnusedZeroEnumErr(false, 3).isErr());
    181  MOZ_RELEASE_ASSERT(Task2UnusedZeroEnumErr(false, 3).unwrapOr(6) == 6);
    182 
    183  MOZ_RELEASE_ASSERT(Task3(true, true, 3).isOk());
    184  MOZ_RELEASE_ASSERT(Task3(true, true, 3).unwrap() == 6);
    185  MOZ_RELEASE_ASSERT(Task3(true, false, 3).isErr());
    186  MOZ_RELEASE_ASSERT(Task3(false, true, 3).isErr());
    187  MOZ_RELEASE_ASSERT(Task3(false, true, 3).unwrapOr(6) == 6);
    188 
    189  // Lvalues should work too.
    190  {
    191    constexpr Result<Ok, Failed> res1 = Task1(true);
    192    MOZ_STATIC_AND_RELEASE_ASSERT(res1.isOk());
    193    MOZ_STATIC_AND_RELEASE_ASSERT(!res1.isErr());
    194 
    195    constexpr Result<Ok, Failed> res2 = Task1(false);
    196    MOZ_STATIC_AND_RELEASE_ASSERT(!res2.isOk());
    197    MOZ_STATIC_AND_RELEASE_ASSERT(res2.isErr());
    198  }
    199 
    200  {
    201    Result<int, Failed> res = Task2(true, 3);
    202    MOZ_RELEASE_ASSERT(res.isOk());
    203    MOZ_RELEASE_ASSERT(res.unwrap() == 3);
    204 
    205    res = Task2(false, 4);
    206    MOZ_RELEASE_ASSERT(res.isErr());
    207  }
    208 
    209  // Some tests for pointer tagging.
    210  {
    211    int i = 123;
    212 
    213    Result<int*, Failed> res = &i;
    214    static_assert(sizeof(res) == sizeof(uintptr_t),
    215                  "should use pointer tagging to fit in a word");
    216 
    217    MOZ_RELEASE_ASSERT(res.isOk());
    218    MOZ_RELEASE_ASSERT(*res.unwrap() == 123);
    219 
    220    res = Err(Failed());
    221    MOZ_RELEASE_ASSERT(res.isErr());
    222  }
    223 }
    224 
    225 struct NonCopyableNonMovable {
    226  explicit constexpr NonCopyableNonMovable(uint32_t aValue) : mValue(aValue) {}
    227 
    228  NonCopyableNonMovable(const NonCopyableNonMovable&) = delete;
    229  NonCopyableNonMovable(NonCopyableNonMovable&&) = delete;
    230  NonCopyableNonMovable& operator=(const NonCopyableNonMovable&) = delete;
    231  NonCopyableNonMovable& operator=(NonCopyableNonMovable&&) = delete;
    232 
    233  uint32_t mValue;
    234 };
    235 
    236 static void InPlaceConstructionTests() {
    237  {
    238    // PackingStrategy == NullIsOk
    239    static_assert(mozilla::detail::SelectResultImpl<NonCopyableNonMovable,
    240                                                    Failed>::value ==
    241                  mozilla::detail::PackingStrategy::NullIsOk);
    242    constexpr Result<NonCopyableNonMovable, Failed> result{std::in_place, 42u};
    243    MOZ_STATIC_AND_RELEASE_ASSERT(42 == result.inspect().mValue);
    244  }
    245 
    246  {
    247    // PackingStrategy == Variant
    248    static_assert(
    249        mozilla::detail::SelectResultImpl<NonCopyableNonMovable, int>::value ==
    250        mozilla::detail::PackingStrategy::Variant);
    251    const Result<NonCopyableNonMovable, int> result{std::in_place, 42};
    252    MOZ_RELEASE_ASSERT(42 == result.inspect().mValue);
    253  }
    254 }
    255 
    256 /* * */
    257 
    258 struct Snafu : Failed {};
    259 
    260 static Result<Ok, Snafu*> Explode() {
    261  static Snafu snafu;
    262  return Err(&snafu);
    263 }
    264 
    265 static Result<Ok, Failed*> ErrorGeneralization() {
    266  MOZ_TRY(Explode());  // change error type from Snafu* to more general Failed*
    267  return Ok();
    268 }
    269 
    270 static void TypeConversionTests() {
    271  MOZ_RELEASE_ASSERT(ErrorGeneralization().isErr());
    272 
    273  {
    274    const Result<Ok, Failed*> res = Explode();
    275    MOZ_RELEASE_ASSERT(res.isErr());
    276  }
    277 
    278  {
    279    const Result<Ok, Failed*> res = Result<Ok, Snafu*>{Ok{}};
    280    MOZ_RELEASE_ASSERT(res.isOk());
    281  }
    282 }
    283 
    284 static void EmptyValueTest() {
    285  struct Fine {};
    286  mozilla::Result<Fine, Failed> res((Fine()));
    287  res.unwrap();
    288  MOZ_RELEASE_ASSERT(res.isOk());
    289  static_assert(sizeof(res) == sizeof(uint8_t),
    290                "Result with empty value and error types should be size 1");
    291 }
    292 
    293 static void MapTest() {
    294  struct MyError {
    295    int x;
    296 
    297    explicit MyError(int y) : x(y) {}
    298  };
    299 
    300  // Mapping over success values, to the same success type.
    301  {
    302    Result<int, MyError> res(5);
    303    bool invoked = false;
    304    auto res2 = res.map([&invoked](int x) {
    305      MOZ_RELEASE_ASSERT(x == 5);
    306      invoked = true;
    307      return 6;
    308    });
    309    MOZ_RELEASE_ASSERT(res2.isOk());
    310    MOZ_RELEASE_ASSERT(invoked);
    311    MOZ_RELEASE_ASSERT(res2.unwrap() == 6);
    312  }
    313 
    314  // Mapping over success values, to a different success type.
    315  {
    316    Result<int, MyError> res(5);
    317    bool invoked = false;
    318    auto res2 = res.map([&invoked](int x) {
    319      MOZ_RELEASE_ASSERT(x == 5);
    320      invoked = true;
    321      return "hello";
    322    });
    323    MOZ_RELEASE_ASSERT(res2.isOk());
    324    MOZ_RELEASE_ASSERT(invoked);
    325    MOZ_RELEASE_ASSERT(strcmp(res2.unwrap(), "hello") == 0);
    326  }
    327 
    328  // Mapping over success values (constexpr).
    329  {
    330    constexpr uint64_t kValue = 42u;
    331    constexpr auto res2a = Result<int32_t, Failed>{5}.map([](int32_t x) {
    332      MOZ_RELEASE_ASSERT(x == 5);
    333      return kValue;
    334    });
    335    MOZ_STATIC_AND_RELEASE_ASSERT(res2a.isOk());
    336    MOZ_STATIC_AND_RELEASE_ASSERT(kValue == res2a.inspect());
    337  }
    338 
    339  // Mapping over error values.
    340  {
    341    MyError err(1);
    342    Result<char, MyError> res(err);
    343    MOZ_RELEASE_ASSERT(res.isErr());
    344    Result<char, MyError> res2 = res.map([](int x) {
    345      MOZ_RELEASE_ASSERT(false);
    346      return 'a';
    347    });
    348    MOZ_RELEASE_ASSERT(res2.isErr());
    349    MOZ_RELEASE_ASSERT(res2.unwrapErr().x == err.x);
    350  }
    351 
    352  // Function pointers instead of lambdas as the mapping function.
    353  {
    354    Result<const char*, MyError> res("hello");
    355    auto res2 = res.map(strlen);
    356    MOZ_RELEASE_ASSERT(res2.isOk());
    357    MOZ_RELEASE_ASSERT(res2.unwrap() == 5);
    358  }
    359 }
    360 
    361 static void MapErrTest() {
    362  struct MyError {
    363    int x;
    364 
    365    explicit MyError(int y) : x(y) {}
    366  };
    367 
    368  struct MyError2 {
    369    int a;
    370 
    371    explicit MyError2(int b) : a(b) {}
    372  };
    373 
    374  // Mapping over error values, to the same error type.
    375  {
    376    MyError err(1);
    377    Result<char, MyError> res(err);
    378    MOZ_RELEASE_ASSERT(res.isErr());
    379    bool invoked = false;
    380    auto res2 = res.mapErr([&invoked](const auto err) {
    381      MOZ_RELEASE_ASSERT(err.x == 1);
    382      invoked = true;
    383      return MyError(2);
    384    });
    385    MOZ_RELEASE_ASSERT(res2.isErr());
    386    MOZ_RELEASE_ASSERT(invoked);
    387    MOZ_RELEASE_ASSERT(res2.unwrapErr().x == 2);
    388  }
    389 
    390  // Mapping over error values, to a different error type.
    391  {
    392    MyError err(1);
    393    Result<char, MyError> res(err);
    394    MOZ_RELEASE_ASSERT(res.isErr());
    395    bool invoked = false;
    396    auto res2 = res.mapErr([&invoked](const auto err) {
    397      MOZ_RELEASE_ASSERT(err.x == 1);
    398      invoked = true;
    399      return MyError2(2);
    400    });
    401    MOZ_RELEASE_ASSERT(res2.isErr());
    402    MOZ_RELEASE_ASSERT(invoked);
    403    MOZ_RELEASE_ASSERT(res2.unwrapErr().a == 2);
    404  }
    405 
    406  // Mapping over success values.
    407  {
    408    Result<int, MyError> res(5);
    409    auto res2 = res.mapErr([](const auto err) {
    410      MOZ_RELEASE_ASSERT(false);
    411      return MyError(1);
    412    });
    413    MOZ_RELEASE_ASSERT(res2.isOk());
    414    MOZ_RELEASE_ASSERT(res2.unwrap() == 5);
    415  }
    416 
    417  // Function pointers instead of lambdas as the mapping function.
    418  {
    419    Result<Ok, const char*> res("hello");
    420    auto res2 = res.mapErr(strlen);
    421    MOZ_RELEASE_ASSERT(res2.isErr());
    422    MOZ_RELEASE_ASSERT(res2.unwrapErr() == 5);
    423  }
    424 }
    425 
    426 static Result<Ok, size_t> strlen_ResultWrapper(const char* aValue) {
    427  return Err(strlen(aValue));
    428 }
    429 
    430 static void OrElseTest() {
    431  struct MyError {
    432    int x;
    433 
    434    explicit constexpr MyError(int y) : x(y) {}
    435  };
    436 
    437  struct MyError2 {
    438    int a;
    439 
    440    explicit constexpr MyError2(int b) : a(b) {}
    441  };
    442 
    443  // `orElse`ing over error values, to Result<V, E> (the same error type) error
    444  // variant.
    445  {
    446    MyError err(1);
    447    Result<char, MyError> res(err);
    448    MOZ_RELEASE_ASSERT(res.isErr());
    449    bool invoked = false;
    450    auto res2 = res.orElse([&invoked](const auto err) -> Result<char, MyError> {
    451      MOZ_RELEASE_ASSERT(err.x == 1);
    452      invoked = true;
    453      if (err.x != 42) {
    454        return Err(MyError(2));
    455      }
    456      return 'a';
    457    });
    458    MOZ_RELEASE_ASSERT(res2.isErr());
    459    MOZ_RELEASE_ASSERT(invoked);
    460    MOZ_RELEASE_ASSERT(res2.unwrapErr().x == 2);
    461  }
    462 
    463  // `orElse`ing over error values, to Result<V, E> (the same error type)
    464  // success variant.
    465  {
    466    MyError err(42);
    467    Result<char, MyError> res(err);
    468    MOZ_RELEASE_ASSERT(res.isErr());
    469    bool invoked = false;
    470    auto res2 = res.orElse([&invoked](const auto err) -> Result<char, MyError> {
    471      MOZ_RELEASE_ASSERT(err.x == 42);
    472      invoked = true;
    473      if (err.x != 42) {
    474        return Err(MyError(2));
    475      }
    476      return 'a';
    477    });
    478    MOZ_RELEASE_ASSERT(res2.isOk());
    479    MOZ_RELEASE_ASSERT(invoked);
    480    MOZ_RELEASE_ASSERT(res2.unwrap() == 'a');
    481  }
    482 
    483  // `orElse`ing over error values, to Result<V, E2> (a different error type)
    484  // error variant.
    485  {
    486    MyError err(1);
    487    Result<char, MyError> res(err);
    488    MOZ_RELEASE_ASSERT(res.isErr());
    489    bool invoked = false;
    490    auto res2 =
    491        res.orElse([&invoked](const auto err) -> Result<char, MyError2> {
    492          MOZ_RELEASE_ASSERT(err.x == 1);
    493          invoked = true;
    494          if (err.x != 42) {
    495            return Err(MyError2(2));
    496          }
    497          return 'a';
    498        });
    499    MOZ_RELEASE_ASSERT(res2.isErr());
    500    MOZ_RELEASE_ASSERT(invoked);
    501    MOZ_RELEASE_ASSERT(res2.unwrapErr().a == 2);
    502  }
    503 
    504  // `orElse`ing over error values, to Result<V, E2> (a different error type)
    505  // success variant.
    506  {
    507    MyError err(42);
    508    Result<char, MyError> res(err);
    509    MOZ_RELEASE_ASSERT(res.isErr());
    510    bool invoked = false;
    511    auto res2 =
    512        res.orElse([&invoked](const auto err) -> Result<char, MyError2> {
    513          MOZ_RELEASE_ASSERT(err.x == 42);
    514          invoked = true;
    515          if (err.x != 42) {
    516            return Err(MyError2(2));
    517          }
    518          return 'a';
    519        });
    520    MOZ_RELEASE_ASSERT(res2.isOk());
    521    MOZ_RELEASE_ASSERT(invoked);
    522    MOZ_RELEASE_ASSERT(res2.unwrap() == 'a');
    523  }
    524 
    525  // `orElse`ing over success values.
    526  {
    527    Result<int, MyError> res(5);
    528    auto res2 = res.orElse([](const auto err) -> Result<int, MyError> {
    529      MOZ_RELEASE_ASSERT(false);
    530      return Err(MyError(1));
    531    });
    532    MOZ_RELEASE_ASSERT(res2.isOk());
    533    MOZ_RELEASE_ASSERT(res2.unwrap() == 5);
    534  }
    535 
    536  // Function pointers instead of lambdas as the `orElse`ing function.
    537  {
    538    Result<Ok, const char*> res("hello");
    539    auto res2 = res.orElse(strlen_ResultWrapper);
    540    MOZ_RELEASE_ASSERT(res2.isErr());
    541    MOZ_RELEASE_ASSERT(res2.unwrapErr() == 5);
    542  }
    543 }
    544 
    545 static void AndThenTest() {
    546  // `andThen`ing over success results.
    547  {
    548    Result<int, const char*> r1(10);
    549    Result<int, const char*> r2 =
    550        r1.andThen([](int x) { return Result<int, const char*>(x + 1); });
    551    MOZ_RELEASE_ASSERT(r2.isOk());
    552    MOZ_RELEASE_ASSERT(r2.unwrap() == 11);
    553  }
    554 
    555  // `andThen`ing over success results (constexpr).
    556  {
    557    constexpr Result<int, Failed> r2a = Result<int, Failed>{10}.andThen(
    558        [](int x) { return Result<int, Failed>(x + 1); });
    559    MOZ_STATIC_AND_RELEASE_ASSERT(r2a.isOk());
    560    MOZ_STATIC_AND_RELEASE_ASSERT(r2a.inspect() == 11);
    561  }
    562 
    563  // `andThen`ing over error results.
    564  {
    565    Result<int, const char*> r3("error");
    566    Result<int, const char*> r4 = r3.andThen([](int x) {
    567      MOZ_RELEASE_ASSERT(false);
    568      return Result<int, const char*>(1);
    569    });
    570    MOZ_RELEASE_ASSERT(r4.isErr());
    571    MOZ_RELEASE_ASSERT(r3.unwrapErr() == r4.unwrapErr());
    572  }
    573 
    574  // andThen with a function accepting an rvalue
    575  {
    576    Result<int, const char*> r1(10);
    577    Result<int, const char*> r2 =
    578        r1.andThen([](int&& x) { return Result<int, const char*>(x + 1); });
    579    MOZ_RELEASE_ASSERT(r2.isOk());
    580    MOZ_RELEASE_ASSERT(r2.unwrap() == 11);
    581  }
    582 
    583  // `andThen`ing over error results (constexpr).
    584  {
    585    constexpr Result<int, Failed> r4a =
    586        Result<int, Failed>{Failed{}}.andThen([](int x) {
    587          MOZ_RELEASE_ASSERT(false);
    588          return Result<int, Failed>(1);
    589        });
    590    MOZ_STATIC_AND_RELEASE_ASSERT(r4a.isErr());
    591  }
    592 }
    593 
    594 using UniqueResult = Result<UniquePtr<int>, const char*>;
    595 
    596 static UniqueResult UniqueTask() { return mozilla::MakeUnique<int>(3); }
    597 static UniqueResult UniqueTaskError() { return Err("bad"); }
    598 
    599 using UniqueErrorResult = Result<int, UniquePtr<int>>;
    600 static UniqueErrorResult UniqueError() {
    601  return Err(mozilla::MakeUnique<int>(4));
    602 }
    603 
    604 static Result<Ok, UniquePtr<int>> TryUniqueErrorResult() {
    605  MOZ_TRY(UniqueError());
    606  return Ok();
    607 }
    608 
    609 static void UniquePtrTest() {
    610  {
    611    auto result = UniqueTask();
    612    MOZ_RELEASE_ASSERT(result.isOk());
    613    auto ptr = result.unwrap();
    614    MOZ_RELEASE_ASSERT(ptr);
    615    MOZ_RELEASE_ASSERT(*ptr == 3);
    616    auto moved = result.unwrap();
    617    MOZ_RELEASE_ASSERT(!moved);
    618  }
    619 
    620  {
    621    auto err = UniqueTaskError();
    622    MOZ_RELEASE_ASSERT(err.isErr());
    623    auto ptr = err.unwrapOr(mozilla::MakeUnique<int>(4));
    624    MOZ_RELEASE_ASSERT(ptr);
    625    MOZ_RELEASE_ASSERT(*ptr == 4);
    626  }
    627 
    628  {
    629    auto result = UniqueTaskError();
    630    result = UniqueResult(mozilla::MakeUnique<int>(6));
    631    MOZ_RELEASE_ASSERT(result.isOk());
    632    MOZ_RELEASE_ASSERT(result.inspect() && *result.inspect() == 6);
    633  }
    634 
    635  {
    636    auto result = UniqueError();
    637    MOZ_RELEASE_ASSERT(result.isErr());
    638    MOZ_RELEASE_ASSERT(result.inspectErr());
    639    MOZ_RELEASE_ASSERT(*result.inspectErr() == 4);
    640    auto err = result.unwrapErr();
    641    MOZ_RELEASE_ASSERT(!result.inspectErr());
    642    MOZ_RELEASE_ASSERT(err);
    643    MOZ_RELEASE_ASSERT(*err == 4);
    644 
    645    result = UniqueErrorResult(0);
    646    MOZ_RELEASE_ASSERT(result.isOk() && result.unwrap() == 0);
    647  }
    648 
    649  {
    650    auto result = TryUniqueErrorResult();
    651    MOZ_RELEASE_ASSERT(result.isErr());
    652    auto err = result.unwrapErr();
    653    MOZ_RELEASE_ASSERT(err && *err == 4);
    654    MOZ_RELEASE_ASSERT(!result.inspectErr());
    655  }
    656 }
    657 
    658 struct ZeroIsUnusedStructForPointer {
    659  int x = 1;
    660 };
    661 enum class ZeroIsUnusedEnum1 : uint8_t {
    662  V1 = 1,
    663  V2 = 2,
    664 };
    665 enum class ZeroIsUnusedEnum2 : uint16_t {
    666  V1 = 1,
    667  V2 = 2,
    668 };
    669 enum class ZeroIsUnusedEnum4 : uint32_t {
    670  V1 = 1,
    671  V2 = 2,
    672 };
    673 enum class ZeroIsUnusedEnum8 : uint64_t {
    674  V1 = 1,
    675  V2 = 2,
    676 };
    677 struct EmptyErrorStruct {};
    678 
    679 template <>
    680 struct mozilla::detail::UnusedZero<ZeroIsUnusedStructForPointer*> {
    681  static const bool value = true;
    682 };
    683 template <>
    684 struct mozilla::detail::UnusedZero<ZeroIsUnusedEnum1> {
    685  static const bool value = true;
    686 };
    687 template <>
    688 struct mozilla::detail::UnusedZero<ZeroIsUnusedEnum2> {
    689  static const bool value = true;
    690 };
    691 template <>
    692 struct mozilla::detail::UnusedZero<ZeroIsUnusedEnum4> {
    693  static const bool value = true;
    694 };
    695 template <>
    696 struct mozilla::detail::UnusedZero<ZeroIsUnusedEnum8> {
    697  static const bool value = true;
    698 };
    699 
    700 static void ZeroIsEmptyErrorTest() {
    701  {
    702    ZeroIsUnusedStructForPointer s;
    703 
    704    using V = ZeroIsUnusedStructForPointer*;
    705 
    706    mozilla::Result<V, EmptyErrorStruct> result(&s);
    707    MOZ_RELEASE_ASSERT(sizeof(result) == sizeof(V));
    708 
    709    MOZ_RELEASE_ASSERT(result.isOk());
    710    MOZ_RELEASE_ASSERT(result.inspect() == &s);
    711  }
    712 
    713  {
    714    using V = ZeroIsUnusedStructForPointer*;
    715 
    716    mozilla::Result<V, EmptyErrorStruct> result(Err(EmptyErrorStruct{}));
    717 
    718    MOZ_RELEASE_ASSERT(result.isErr());
    719    MOZ_RELEASE_ASSERT(*mozilla::BitwiseCast<V*>(&result) == nullptr);
    720  }
    721 
    722  {
    723    ZeroIsUnusedEnum1 e = ZeroIsUnusedEnum1::V1;
    724 
    725    using V = ZeroIsUnusedEnum1;
    726 
    727    mozilla::Result<V, EmptyErrorStruct> result(e);
    728    MOZ_RELEASE_ASSERT(sizeof(result) == sizeof(V));
    729 
    730    MOZ_RELEASE_ASSERT(result.isOk());
    731    MOZ_RELEASE_ASSERT(result.inspect() == e);
    732  }
    733 
    734  {
    735    using V = ZeroIsUnusedEnum1;
    736 
    737    mozilla::Result<V, EmptyErrorStruct> result(Err(EmptyErrorStruct()));
    738 
    739    MOZ_RELEASE_ASSERT(result.isErr());
    740    MOZ_RELEASE_ASSERT(*mozilla::BitwiseCast<uint8_t*>(&result) == 0);
    741  }
    742 
    743  {
    744    ZeroIsUnusedEnum2 e = ZeroIsUnusedEnum2::V1;
    745 
    746    using V = ZeroIsUnusedEnum2;
    747 
    748    mozilla::Result<V, EmptyErrorStruct> result(e);
    749    MOZ_RELEASE_ASSERT(sizeof(result) == sizeof(V));
    750 
    751    MOZ_RELEASE_ASSERT(result.isOk());
    752    MOZ_RELEASE_ASSERT(result.inspect() == e);
    753  }
    754 
    755  {
    756    using V = ZeroIsUnusedEnum2;
    757 
    758    mozilla::Result<V, EmptyErrorStruct> result(Err(EmptyErrorStruct()));
    759 
    760    MOZ_RELEASE_ASSERT(result.isErr());
    761    MOZ_RELEASE_ASSERT(*mozilla::BitwiseCast<uint16_t*>(&result) == 0);
    762  }
    763 
    764  {
    765    ZeroIsUnusedEnum4 e = ZeroIsUnusedEnum4::V1;
    766 
    767    using V = ZeroIsUnusedEnum4;
    768 
    769    mozilla::Result<V, EmptyErrorStruct> result(e);
    770    MOZ_RELEASE_ASSERT(sizeof(result) == sizeof(V));
    771 
    772    MOZ_RELEASE_ASSERT(result.isOk());
    773    MOZ_RELEASE_ASSERT(result.inspect() == e);
    774  }
    775 
    776  {
    777    using V = ZeroIsUnusedEnum4;
    778 
    779    mozilla::Result<V, EmptyErrorStruct> result(Err(EmptyErrorStruct()));
    780 
    781    MOZ_RELEASE_ASSERT(result.isErr());
    782    MOZ_RELEASE_ASSERT(*mozilla::BitwiseCast<uint32_t*>(&result) == 0);
    783  }
    784 
    785  {
    786    ZeroIsUnusedEnum8 e = ZeroIsUnusedEnum8::V1;
    787 
    788    using V = ZeroIsUnusedEnum8;
    789 
    790    mozilla::Result<V, EmptyErrorStruct> result(e);
    791    MOZ_RELEASE_ASSERT(sizeof(result) == sizeof(V));
    792 
    793    MOZ_RELEASE_ASSERT(result.isOk());
    794    MOZ_RELEASE_ASSERT(result.inspect() == e);
    795  }
    796 
    797  {
    798    using V = ZeroIsUnusedEnum8;
    799 
    800    mozilla::Result<V, EmptyErrorStruct> result(Err(EmptyErrorStruct()));
    801 
    802    MOZ_RELEASE_ASSERT(result.isErr());
    803    MOZ_RELEASE_ASSERT(*mozilla::BitwiseCast<uint64_t*>(&result) == 0);
    804  }
    805 }
    806 
    807 class Foo {};
    808 
    809 class C1 {};
    810 class C2 : public C1 {};
    811 
    812 class E1 {};
    813 class E2 : public E1 {};
    814 
    815 void UpcastTest() {
    816  {
    817    C2 c2;
    818 
    819    mozilla::Result<C2*, Failed> result(&c2);
    820    mozilla::Result<C1*, Failed> copied(std::move(result));
    821 
    822    MOZ_RELEASE_ASSERT(copied.inspect() == &c2);
    823  }
    824 
    825  {
    826    E2 e2;
    827 
    828    mozilla::Result<Foo, E2*> result(Err(&e2));
    829    mozilla::Result<Foo, E1*> copied(std::move(result));
    830 
    831    MOZ_RELEASE_ASSERT(copied.inspectErr() == &e2);
    832  }
    833 
    834  {
    835    C2 c2;
    836 
    837    mozilla::Result<C2*, E2*> result(&c2);
    838    mozilla::Result<C1*, E1*> copied(std::move(result));
    839 
    840    MOZ_RELEASE_ASSERT(copied.inspect() == &c2);
    841  }
    842 
    843  {
    844    E2 e2;
    845 
    846    mozilla::Result<C2*, E2*> result(Err(&e2));
    847    mozilla::Result<C1*, E1*> copied(std::move(result));
    848 
    849    MOZ_RELEASE_ASSERT(copied.inspectErr() == &e2);
    850  }
    851 }
    852 
    853 void EqualityTest() {
    854  {
    855    Result<int, bool> result(1);
    856    Result<int, bool> other(1);
    857 
    858    MOZ_RELEASE_ASSERT(result == other);
    859  }
    860 
    861  {
    862    Result<int, bool> result(true);
    863    Result<int, bool> other(true);
    864 
    865    MOZ_RELEASE_ASSERT(result == other);
    866  }
    867 
    868  {
    869    Result<int, bool> result(1);
    870    Result<int, bool> other(2);
    871 
    872    MOZ_RELEASE_ASSERT(result != other);
    873  }
    874 
    875  {
    876    Result<int, bool> result(true);
    877    Result<int, bool> other(false);
    878 
    879    MOZ_RELEASE_ASSERT(result != other);
    880  }
    881 
    882  {
    883    Result<int, bool> result(0);
    884    Result<int, bool> other(false);
    885 
    886    MOZ_RELEASE_ASSERT(result != other);
    887  }
    888 
    889  {
    890    Result<int, unsigned int> result(1);
    891    Result<int, unsigned int> other(1u);
    892 
    893    MOZ_RELEASE_ASSERT(result != other);
    894  }
    895 }
    896 
    897 /* * */
    898 
    899 int main() {
    900  BasicTests();
    901  InPlaceConstructionTests();
    902  TypeConversionTests();
    903  EmptyValueTest();
    904  MapTest();
    905  MapErrTest();
    906  OrElseTest();
    907  AndThenTest();
    908  UniquePtrTest();
    909  ZeroIsEmptyErrorTest();
    910  UpcastTest();
    911  EqualityTest();
    912  return 0;
    913 }