TestMaybe.cpp (60740B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include <type_traits> 7 #include <utility> 8 9 #include "mozilla/Assertions.h" 10 #include "mozilla/Attributes.h" 11 #include "mozilla/Maybe.h" 12 13 using mozilla::Maybe; 14 using mozilla::Nothing; 15 using mozilla::Some; 16 using mozilla::SomeRef; 17 using mozilla::ToMaybe; 18 using mozilla::ToMaybeRef; 19 20 #define RUN_TEST(t) \ 21 do { \ 22 bool cond = (t()); \ 23 MOZ_RELEASE_ASSERT(cond, "Unexpectedly returned false during test: " #t); \ 24 cond = AllDestructorsWereCalled(); \ 25 MOZ_RELEASE_ASSERT(cond, \ 26 "Failed to destroy all objects during test: " #t); \ 27 } while (false) 28 29 enum Status { 30 eWasDefaultConstructed, 31 eWasConstructed, 32 eWasCopyConstructed, 33 eWasMoveConstructed, 34 eWasConstMoveConstructed, 35 eWasAssigned, 36 eWasCopyAssigned, 37 eWasMoveAssigned, 38 eWasCopiedFrom, 39 eWasMovedFrom, 40 eWasConstMovedFrom, 41 }; 42 43 static size_t sUndestroyedObjects = 0; 44 45 static bool AllDestructorsWereCalled() { return sUndestroyedObjects == 0; } 46 47 struct BasicValue { 48 BasicValue() : mStatus(eWasDefaultConstructed), mTag(0) { 49 ++sUndestroyedObjects; 50 } 51 52 explicit BasicValue(int aTag) : mStatus(eWasConstructed), mTag(aTag) { 53 ++sUndestroyedObjects; 54 } 55 56 BasicValue(const BasicValue& aOther) 57 : mStatus(eWasCopyConstructed), mTag(aOther.mTag) { 58 ++sUndestroyedObjects; 59 } 60 61 BasicValue(BasicValue&& aOther) 62 : mStatus(eWasMoveConstructed), mTag(aOther.mTag) { 63 ++sUndestroyedObjects; 64 aOther.mStatus = eWasMovedFrom; 65 aOther.mTag = 0; 66 } 67 68 BasicValue(const BasicValue&& aOther) 69 : mStatus(eWasConstMoveConstructed), mTag(aOther.mTag) { 70 ++sUndestroyedObjects; 71 aOther.mStatus = eWasConstMovedFrom; 72 } 73 74 ~BasicValue() { --sUndestroyedObjects; } 75 76 BasicValue& operator=(const BasicValue& aOther) { 77 mStatus = eWasCopyAssigned; 78 mTag = aOther.mTag; 79 return *this; 80 } 81 82 BasicValue& operator=(BasicValue&& aOther) { 83 mStatus = eWasMoveAssigned; 84 mTag = aOther.mTag; 85 aOther.mStatus = eWasMovedFrom; 86 aOther.mTag = 0; 87 return *this; 88 } 89 90 bool operator==(const BasicValue& aOther) const { 91 return mTag == aOther.mTag; 92 } 93 94 bool operator<(const BasicValue& aOther) const { return mTag < aOther.mTag; } 95 96 Status GetStatus() const { return mStatus; } 97 void SetTag(int aValue) { mTag = aValue; } 98 int GetTag() const { return mTag; } 99 100 private: 101 mutable Status mStatus; 102 int mTag; 103 }; 104 105 struct UncopyableValue { 106 UncopyableValue() : mStatus(eWasDefaultConstructed) { ++sUndestroyedObjects; } 107 108 UncopyableValue(UncopyableValue&& aOther) : mStatus(eWasMoveConstructed) { 109 ++sUndestroyedObjects; 110 aOther.mStatus = eWasMovedFrom; 111 } 112 113 ~UncopyableValue() { --sUndestroyedObjects; } 114 115 UncopyableValue& operator=(UncopyableValue&& aOther) { 116 mStatus = eWasMoveAssigned; 117 aOther.mStatus = eWasMovedFrom; 118 return *this; 119 } 120 121 Status GetStatus() { return mStatus; } 122 123 private: 124 UncopyableValue(const UncopyableValue& aOther) = delete; 125 UncopyableValue& operator=(const UncopyableValue& aOther) = delete; 126 127 Status mStatus; 128 }; 129 130 struct UnmovableValue { 131 UnmovableValue() : mStatus(eWasDefaultConstructed) { ++sUndestroyedObjects; } 132 133 UnmovableValue(const UnmovableValue& aOther) : mStatus(eWasCopyConstructed) { 134 ++sUndestroyedObjects; 135 } 136 137 ~UnmovableValue() { --sUndestroyedObjects; } 138 139 UnmovableValue& operator=(const UnmovableValue& aOther) { 140 mStatus = eWasCopyAssigned; 141 return *this; 142 } 143 144 Status GetStatus() { return mStatus; } 145 146 UnmovableValue(UnmovableValue&& aOther) = delete; 147 UnmovableValue& operator=(UnmovableValue&& aOther) = delete; 148 149 private: 150 Status mStatus; 151 }; 152 153 struct UncopyableUnmovableValue { 154 UncopyableUnmovableValue() : mStatus(eWasDefaultConstructed) { 155 ++sUndestroyedObjects; 156 } 157 158 explicit UncopyableUnmovableValue(int) : mStatus(eWasConstructed) { 159 ++sUndestroyedObjects; 160 } 161 162 ~UncopyableUnmovableValue() { --sUndestroyedObjects; } 163 164 Status GetStatus() const { return mStatus; } 165 166 private: 167 UncopyableUnmovableValue(const UncopyableUnmovableValue& aOther) = delete; 168 UncopyableUnmovableValue& operator=(const UncopyableUnmovableValue& aOther) = 169 delete; 170 UncopyableUnmovableValue(UncopyableUnmovableValue&& aOther) = delete; 171 UncopyableUnmovableValue& operator=(UncopyableUnmovableValue&& aOther) = 172 delete; 173 174 Status mStatus; 175 }; 176 177 static_assert(std::is_trivially_destructible_v<Maybe<int>>); 178 static_assert(std::is_trivially_copy_constructible_v<Maybe<int>>); 179 static_assert(std::is_trivially_copy_assignable_v<Maybe<int>>); 180 181 static_assert(42 == Some(42).value()); 182 static_assert(42 == Some(42).valueOr(43)); 183 static_assert(42 == Maybe<int>{}.valueOr(42)); 184 static_assert(42 == Some(42).valueOrFrom([] { return 43; })); 185 static_assert(42 == Maybe<int>{}.valueOrFrom([] { return 42; })); 186 static_assert(Some(43) == [] { 187 auto val = Some(42); 188 val.apply([](int& val) { val += 1; }); 189 return val; 190 }()); 191 static_assert(Some(43) == Some(42).map([](int val) { return val + 1; })); 192 static_assert(Maybe<int>(std::in_place, 43) == 193 Maybe<int>(std::in_place, 42).map([](int val) { 194 return val + 1; 195 })); 196 197 struct TriviallyDestructible { 198 TriviallyDestructible() { // not trivially constructible 199 } 200 }; 201 202 static_assert(std::is_trivially_destructible_v<Maybe<TriviallyDestructible>>); 203 204 struct UncopyableValueLiteralType { 205 explicit constexpr UncopyableValueLiteralType(int aValue) : mValue{aValue} {} 206 207 UncopyableValueLiteralType(UncopyableValueLiteralType&&) = default; 208 UncopyableValueLiteralType& operator=(UncopyableValueLiteralType&&) = default; 209 210 int mValue; 211 }; 212 213 static_assert( 214 std::is_trivially_destructible_v<Maybe<UncopyableValueLiteralType>>); 215 static_assert(!std::is_copy_constructible_v<Maybe<UncopyableValueLiteralType>>); 216 static_assert(!std::is_copy_assignable_v<Maybe<UncopyableValueLiteralType>>); 217 static_assert(std::is_move_constructible_v<Maybe<UncopyableValueLiteralType>>); 218 static_assert(std::is_move_assignable_v<Maybe<UncopyableValueLiteralType>>); 219 220 constexpr Maybe<UncopyableValueLiteralType> someUncopyable = 221 Some(UncopyableValueLiteralType{42}); 222 static_assert(someUncopyable.isSome()); 223 static_assert(42 == someUncopyable->mValue); 224 225 constexpr Maybe<UncopyableValueLiteralType> someUncopyableAssigned = [] { 226 auto res = Maybe<UncopyableValueLiteralType>{}; 227 res = Some(UncopyableValueLiteralType{42}); 228 return res; 229 }(); 230 static_assert(someUncopyableAssigned.isSome()); 231 static_assert(42 == someUncopyableAssigned->mValue); 232 233 static bool TestBasicFeatures() { 234 // Check that a Maybe<T> is initialized to Nothing. 235 Maybe<BasicValue> mayValue; 236 static_assert(std::is_same_v<BasicValue, decltype(mayValue)::ValueType>, 237 "Should have BasicValue ValueType"); 238 MOZ_RELEASE_ASSERT(!mayValue); 239 MOZ_RELEASE_ASSERT(!mayValue.isSome()); 240 MOZ_RELEASE_ASSERT(mayValue.isNothing()); 241 242 // Check that emplace() default constructs and the accessors work. 243 mayValue.emplace(); 244 MOZ_RELEASE_ASSERT(mayValue); 245 MOZ_RELEASE_ASSERT(mayValue.isSome()); 246 MOZ_RELEASE_ASSERT(!mayValue.isNothing()); 247 MOZ_RELEASE_ASSERT(*mayValue == BasicValue()); 248 static_assert(std::is_same_v<BasicValue&, decltype(*mayValue)>, 249 "operator*() should return a BasicValue&"); 250 MOZ_RELEASE_ASSERT(mayValue.value() == BasicValue()); 251 static_assert(std::is_same_v<BasicValue, decltype(mayValue.value())>, 252 "value() should return a BasicValue"); 253 MOZ_RELEASE_ASSERT(mayValue.ref() == BasicValue()); 254 static_assert(std::is_same_v<BasicValue&, decltype(mayValue.ref())>, 255 "ref() should return a BasicValue&"); 256 MOZ_RELEASE_ASSERT(mayValue.ptr() != nullptr); 257 static_assert(std::is_same_v<BasicValue*, decltype(mayValue.ptr())>, 258 "ptr() should return a BasicValue*"); 259 MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasDefaultConstructed); 260 261 // Check that reset() works. 262 mayValue.reset(); 263 MOZ_RELEASE_ASSERT(!mayValue); 264 MOZ_RELEASE_ASSERT(!mayValue.isSome()); 265 MOZ_RELEASE_ASSERT(mayValue.isNothing()); 266 267 // Check that emplace(T1) calls the correct constructor. 268 mayValue.emplace(1); 269 MOZ_RELEASE_ASSERT(mayValue); 270 MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasConstructed); 271 MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1); 272 mayValue.reset(); 273 MOZ_RELEASE_ASSERT(!mayValue); 274 275 { 276 // Check that Maybe(std::in_place, T1) calls the correct constructor. 277 const auto mayValueConstructed = Maybe<BasicValue>(std::in_place, 1); 278 MOZ_RELEASE_ASSERT(mayValueConstructed); 279 MOZ_RELEASE_ASSERT(mayValueConstructed->GetStatus() == eWasConstructed); 280 MOZ_RELEASE_ASSERT(mayValueConstructed->GetTag() == 1); 281 } 282 283 // Check that Some() and Nothing() work. 284 mayValue = Some(BasicValue(2)); 285 MOZ_RELEASE_ASSERT(mayValue); 286 MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasMoveConstructed); 287 MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2); 288 mayValue = Nothing(); 289 MOZ_RELEASE_ASSERT(!mayValue); 290 291 // Check that the accessors work through a const ref. 292 mayValue.emplace(); 293 const Maybe<BasicValue>& mayValueCRef = mayValue; 294 MOZ_RELEASE_ASSERT(mayValueCRef); 295 MOZ_RELEASE_ASSERT(mayValueCRef.isSome()); 296 MOZ_RELEASE_ASSERT(!mayValueCRef.isNothing()); 297 MOZ_RELEASE_ASSERT(*mayValueCRef == BasicValue()); 298 static_assert(std::is_same_v<const BasicValue&, decltype(*mayValueCRef)>, 299 "operator*() should return a BasicValue"); 300 MOZ_RELEASE_ASSERT(mayValueCRef.value() == BasicValue()); 301 static_assert(std::is_same_v<BasicValue, decltype(mayValueCRef.value())>, 302 "value() should return a BasicValue"); 303 MOZ_RELEASE_ASSERT(mayValueCRef.ref() == BasicValue()); 304 static_assert(std::is_same_v<const BasicValue&, decltype(mayValueCRef.ref())>, 305 "ref() should return a const BasicValue&"); 306 MOZ_RELEASE_ASSERT(mayValueCRef.ptr() != nullptr); 307 static_assert(std::is_same_v<const BasicValue*, decltype(mayValueCRef.ptr())>, 308 "ptr() should return a const BasicValue*"); 309 MOZ_RELEASE_ASSERT(mayValueCRef->GetStatus() == eWasDefaultConstructed); 310 mayValue.reset(); 311 312 // Check that we can create and reference Maybe<const Type>. 313 Maybe<const BasicValue> mayCValue1 = Some(BasicValue(5)); 314 MOZ_RELEASE_ASSERT(mayCValue1); 315 MOZ_RELEASE_ASSERT(mayCValue1.isSome()); 316 MOZ_RELEASE_ASSERT(*mayCValue1 == BasicValue(5)); 317 const Maybe<const BasicValue>& mayCValue1Ref = mayCValue1; 318 MOZ_RELEASE_ASSERT(mayCValue1Ref == mayCValue1); 319 MOZ_RELEASE_ASSERT(*mayCValue1Ref == BasicValue(5)); 320 Maybe<const BasicValue> mayCValue2; 321 mayCValue2.emplace(6); 322 MOZ_RELEASE_ASSERT(mayCValue2); 323 MOZ_RELEASE_ASSERT(mayCValue2.isSome()); 324 MOZ_RELEASE_ASSERT(*mayCValue2 == BasicValue(6)); 325 326 // Check that accessors work through rvalue-references. 327 MOZ_RELEASE_ASSERT(Some(BasicValue())); 328 MOZ_RELEASE_ASSERT(Some(BasicValue()).isSome()); 329 MOZ_RELEASE_ASSERT(!Some(BasicValue()).isNothing()); 330 MOZ_RELEASE_ASSERT(*Some(BasicValue()) == BasicValue()); 331 static_assert(std::is_same_v<BasicValue&&, decltype(*Some(BasicValue()))>, 332 "operator*() should return a BasicValue&&"); 333 MOZ_RELEASE_ASSERT(Some(BasicValue()).value() == BasicValue()); 334 static_assert( 335 std::is_same_v<BasicValue, decltype(Some(BasicValue()).value())>, 336 "value() should return a BasicValue"); 337 MOZ_RELEASE_ASSERT(Some(BasicValue()).ref() == BasicValue()); 338 static_assert( 339 std::is_same_v<BasicValue&&, decltype(Some(BasicValue()).ref())>, 340 "ref() should return a BasicValue&&"); 341 MOZ_RELEASE_ASSERT(Some(BasicValue()).ptr() != nullptr); 342 static_assert(std::is_same_v<BasicValue*, decltype(Some(BasicValue()).ptr())>, 343 "ptr() should return a BasicValue*"); 344 MOZ_RELEASE_ASSERT(Some(BasicValue())->GetStatus() == eWasMoveConstructed); 345 346 // Check that accessors work through const-rvalue-references. 347 auto MakeConstMaybe = []() -> const Maybe<BasicValue> { 348 return Some(BasicValue()); 349 }; 350 MOZ_RELEASE_ASSERT(MakeConstMaybe()); 351 MOZ_RELEASE_ASSERT(MakeConstMaybe().isSome()); 352 MOZ_RELEASE_ASSERT(!MakeConstMaybe().isNothing()); 353 MOZ_RELEASE_ASSERT(*MakeConstMaybe() == BasicValue()); 354 static_assert(std::is_same_v<const BasicValue&&, decltype(*MakeConstMaybe())>, 355 "operator*() should return a const BasicValue&&"); 356 MOZ_RELEASE_ASSERT(MakeConstMaybe().value() == BasicValue()); 357 static_assert(std::is_same_v<BasicValue, decltype(MakeConstMaybe().value())>, 358 "value() should return a BasicValue"); 359 MOZ_RELEASE_ASSERT(MakeConstMaybe().ref() == BasicValue()); 360 static_assert( 361 std::is_same_v<const BasicValue&&, decltype(MakeConstMaybe().ref())>, 362 "ref() should return a const BasicValue&&"); 363 MOZ_RELEASE_ASSERT(MakeConstMaybe().ptr() != nullptr); 364 static_assert( 365 std::is_same_v<const BasicValue*, decltype(MakeConstMaybe().ptr())>, 366 "ptr() should return a const BasicValue*"); 367 MOZ_RELEASE_ASSERT(MakeConstMaybe()->GetStatus() == eWasMoveConstructed); 368 MOZ_RELEASE_ASSERT(BasicValue(*MakeConstMaybe()).GetStatus() == 369 eWasConstMoveConstructed); 370 371 // Check that take works 372 mayValue = Some(BasicValue(6)); 373 Maybe taken = mayValue.take(); 374 MOZ_RELEASE_ASSERT(taken->GetStatus() == eWasMoveConstructed); 375 MOZ_RELEASE_ASSERT(taken == Some(BasicValue(6))); 376 MOZ_RELEASE_ASSERT(!mayValue.isSome()); 377 MOZ_RELEASE_ASSERT(mayValue.take() == Nothing()); 378 379 // Check that extract works 380 mayValue = Some(BasicValue(7)); 381 BasicValue extracted = mayValue.extract(); 382 MOZ_RELEASE_ASSERT(extracted.GetStatus() == eWasMoveConstructed); 383 MOZ_RELEASE_ASSERT(extracted == BasicValue(7)); 384 MOZ_RELEASE_ASSERT(!mayValue.isSome()); 385 386 return true; 387 } 388 389 template <typename T> 390 static void TestCopyMaybe() { 391 { 392 MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); 393 394 Maybe<T> src = Some(T()); 395 Maybe<T> dstCopyConstructed = src; 396 397 MOZ_RELEASE_ASSERT(2 == sUndestroyedObjects); 398 MOZ_RELEASE_ASSERT(dstCopyConstructed->GetStatus() == eWasCopyConstructed); 399 } 400 401 { 402 MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); 403 404 Maybe<T> src = Some(T()); 405 Maybe<T> dstCopyAssigned; 406 dstCopyAssigned = src; 407 408 MOZ_RELEASE_ASSERT(2 == sUndestroyedObjects); 409 MOZ_RELEASE_ASSERT(dstCopyAssigned->GetStatus() == eWasCopyConstructed); 410 } 411 } 412 413 template <typename T> 414 static void TestMoveMaybe() { 415 { 416 MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); 417 418 Maybe<T> src = Some(T()); 419 Maybe<T> dstMoveConstructed = std::move(src); 420 421 MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects); 422 MOZ_RELEASE_ASSERT(dstMoveConstructed->GetStatus() == eWasMoveConstructed); 423 } 424 425 { 426 MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); 427 428 Maybe<T> src = Some(T()); 429 Maybe<T> dstMoveAssigned; 430 dstMoveAssigned = std::move(src); 431 432 MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects); 433 MOZ_RELEASE_ASSERT(dstMoveAssigned->GetStatus() == eWasMoveConstructed); 434 } 435 436 { 437 MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); 438 439 Maybe<T> src = Some(T()); 440 Maybe<T> dstMoveConstructed = src.take(); 441 442 MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects); 443 MOZ_RELEASE_ASSERT(dstMoveConstructed->GetStatus() == eWasMoveConstructed); 444 } 445 446 { 447 MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); 448 449 Maybe<T> src = Some(T()); 450 T dstMoveConstructed = src.extract(); 451 452 MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects); 453 MOZ_RELEASE_ASSERT(dstMoveConstructed.GetStatus() == eWasMoveConstructed); 454 } 455 } 456 457 static bool TestCopyAndMove() { 458 MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); 459 460 { 461 // Check that we get moves when possible for types that can support both 462 // moves and copies. 463 { 464 Maybe<BasicValue> mayBasicValue = Some(BasicValue(1)); 465 MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects); 466 MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveConstructed); 467 MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 1); 468 mayBasicValue = Some(BasicValue(2)); 469 MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects); 470 MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveAssigned); 471 MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 2); 472 mayBasicValue.reset(); 473 MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); 474 mayBasicValue.emplace(BasicValue(3)); 475 MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects); 476 MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveConstructed); 477 MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 3); 478 479 // Check that we get copies when moves aren't possible. 480 Maybe<BasicValue> mayBasicValue2 = Some(*mayBasicValue); 481 MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyConstructed); 482 MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 3); 483 mayBasicValue->SetTag(4); 484 mayBasicValue2 = mayBasicValue; 485 // This test should work again when we fix bug 1052940. 486 // MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyAssigned); 487 MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 4); 488 mayBasicValue->SetTag(5); 489 mayBasicValue2.reset(); 490 mayBasicValue2.emplace(*mayBasicValue); 491 MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyConstructed); 492 MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 5); 493 494 // Check that std::move() works. (Another sanity check for move support.) 495 Maybe<BasicValue> mayBasicValue3 = Some(std::move(*mayBasicValue)); 496 MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMoveConstructed); 497 MOZ_RELEASE_ASSERT(mayBasicValue3->GetTag() == 5); 498 MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMovedFrom); 499 mayBasicValue2->SetTag(6); 500 mayBasicValue3 = Some(std::move(*mayBasicValue2)); 501 MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMoveAssigned); 502 MOZ_RELEASE_ASSERT(mayBasicValue3->GetTag() == 6); 503 MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasMovedFrom); 504 Maybe<BasicValue> mayBasicValue4; 505 mayBasicValue4.emplace(std::move(*mayBasicValue3)); 506 MOZ_RELEASE_ASSERT(mayBasicValue4->GetStatus() == eWasMoveConstructed); 507 MOZ_RELEASE_ASSERT(mayBasicValue4->GetTag() == 6); 508 MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMovedFrom); 509 } 510 511 TestCopyMaybe<BasicValue>(); 512 TestMoveMaybe<BasicValue>(); 513 } 514 515 MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); 516 517 { 518 // Check that we always get copies for types that don't support moves. 519 { 520 Maybe<UnmovableValue> mayUnmovableValue = Some(UnmovableValue()); 521 MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyConstructed); 522 mayUnmovableValue.reset(); 523 mayUnmovableValue.emplace(UnmovableValue()); 524 MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyConstructed); 525 } 526 527 TestCopyMaybe<UnmovableValue>(); 528 529 static_assert(std::is_copy_constructible_v<Maybe<UnmovableValue>>); 530 static_assert(std::is_copy_assignable_v<Maybe<UnmovableValue>>); 531 static_assert(!std::is_move_constructible_v<Maybe<UnmovableValue>>); 532 static_assert(!std::is_move_assignable_v<Maybe<UnmovableValue>>); 533 } 534 535 MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); 536 537 { 538 // Check that types that only support moves, but not copies, work. 539 { 540 Maybe<UncopyableValue> mayUncopyableValue = Some(UncopyableValue()); 541 MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() == 542 eWasMoveConstructed); 543 mayUncopyableValue = Some(UncopyableValue()); 544 MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() == eWasMoveAssigned); 545 mayUncopyableValue.reset(); 546 mayUncopyableValue.emplace(UncopyableValue()); 547 MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() == 548 eWasMoveConstructed); 549 mayUncopyableValue = Nothing(); 550 } 551 552 TestMoveMaybe<BasicValue>(); 553 554 static_assert(!std::is_copy_constructible_v<Maybe<UncopyableValue>>); 555 static_assert(!std::is_copy_assignable_v<Maybe<UncopyableValue>>); 556 static_assert(std::is_move_constructible_v<Maybe<UncopyableValue>>); 557 static_assert(std::is_move_assignable_v<Maybe<UncopyableValue>>); 558 } 559 560 MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); 561 562 { // Check that types that support neither moves or copies work. 563 { 564 const auto mayUncopyableUnmovableValueConstructed = 565 Maybe<UncopyableUnmovableValue>{std::in_place}; 566 MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValueConstructed->GetStatus() == 567 eWasDefaultConstructed); 568 } 569 570 Maybe<UncopyableUnmovableValue> mayUncopyableUnmovableValue; 571 mayUncopyableUnmovableValue.emplace(); 572 MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValue->GetStatus() == 573 eWasDefaultConstructed); 574 mayUncopyableUnmovableValue.reset(); 575 mayUncopyableUnmovableValue.emplace(0); 576 MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValue->GetStatus() == 577 eWasConstructed); 578 mayUncopyableUnmovableValue = Nothing(); 579 580 static_assert( 581 !std::is_copy_constructible_v<Maybe<UncopyableUnmovableValue>>); 582 static_assert(!std::is_copy_assignable_v<Maybe<UncopyableUnmovableValue>>); 583 static_assert( 584 !std::is_move_constructible_v<Maybe<UncopyableUnmovableValue>>); 585 static_assert(!std::is_move_assignable_v<Maybe<UncopyableUnmovableValue>>); 586 } 587 588 { 589 // Test copy and move with a trivially copyable and trivially destructible 590 // type. 591 { 592 constexpr Maybe<int> src = Some(42); 593 constexpr Maybe<int> dstCopyConstructed = src; 594 595 static_assert(src.isSome()); 596 static_assert(dstCopyConstructed.isSome()); 597 static_assert(42 == *src); 598 static_assert(42 == *dstCopyConstructed); 599 static_assert(42 == dstCopyConstructed.value()); 600 } 601 602 { 603 const Maybe<int> src = Some(42); 604 Maybe<int> dstCopyAssigned; 605 dstCopyAssigned = src; 606 607 MOZ_RELEASE_ASSERT(src.isSome()); 608 MOZ_RELEASE_ASSERT(dstCopyAssigned.isSome()); 609 MOZ_RELEASE_ASSERT(42 == *src); 610 MOZ_RELEASE_ASSERT(42 == *dstCopyAssigned); 611 } 612 613 { 614 Maybe<int> src = Some(42); 615 const Maybe<int> dstMoveConstructed = std::move(src); 616 617 MOZ_RELEASE_ASSERT(!src.isSome()); 618 MOZ_RELEASE_ASSERT(dstMoveConstructed.isSome()); 619 MOZ_RELEASE_ASSERT(42 == *dstMoveConstructed); 620 } 621 622 { 623 Maybe<int> src = Some(42); 624 Maybe<int> dstMoveAssigned; 625 dstMoveAssigned = std::move(src); 626 627 MOZ_RELEASE_ASSERT(!src.isSome()); 628 MOZ_RELEASE_ASSERT(dstMoveAssigned.isSome()); 629 MOZ_RELEASE_ASSERT(42 == *dstMoveAssigned); 630 } 631 } 632 633 return true; 634 } 635 636 static BasicValue* sStaticBasicValue = nullptr; 637 638 static BasicValue MakeBasicValue() { return BasicValue(9); } 639 640 static BasicValue& MakeBasicValueRef() { return *sStaticBasicValue; } 641 642 static BasicValue* MakeBasicValuePtr() { return sStaticBasicValue; } 643 644 static bool TestFunctionalAccessors() { 645 BasicValue value(9); 646 sStaticBasicValue = new BasicValue(9); 647 648 // Check that the 'some' case of functional accessors works. 649 Maybe<BasicValue> someValue = Some(BasicValue(3)); 650 MOZ_RELEASE_ASSERT(someValue.valueOr(value) == BasicValue(3)); 651 static_assert(std::is_same_v<BasicValue, decltype(someValue.valueOr(value))>, 652 "valueOr should return a BasicValue"); 653 MOZ_RELEASE_ASSERT(someValue.valueOrFrom(&MakeBasicValue) == BasicValue(3)); 654 static_assert( 655 std::is_same_v<BasicValue, 656 decltype(someValue.valueOrFrom(&MakeBasicValue))>, 657 "valueOrFrom should return a BasicValue"); 658 MOZ_RELEASE_ASSERT(someValue.ptrOr(&value) != &value); 659 static_assert(std::is_same_v<BasicValue*, decltype(someValue.ptrOr(&value))>, 660 "ptrOr should return a BasicValue*"); 661 MOZ_RELEASE_ASSERT(*someValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(3)); 662 static_assert( 663 std::is_same_v<BasicValue*, 664 decltype(someValue.ptrOrFrom(&MakeBasicValuePtr))>, 665 "ptrOrFrom should return a BasicValue*"); 666 MOZ_RELEASE_ASSERT(someValue.refOr(value) == BasicValue(3)); 667 static_assert(std::is_same_v<BasicValue&, decltype(someValue.refOr(value))>, 668 "refOr should return a BasicValue&"); 669 MOZ_RELEASE_ASSERT(someValue.refOrFrom(&MakeBasicValueRef) == BasicValue(3)); 670 static_assert( 671 std::is_same_v<BasicValue&, 672 decltype(someValue.refOrFrom(&MakeBasicValueRef))>, 673 "refOrFrom should return a BasicValue&"); 674 675 // Check that the 'some' case works through a const reference. 676 const Maybe<BasicValue>& someValueCRef = someValue; 677 MOZ_RELEASE_ASSERT(someValueCRef.valueOr(value) == BasicValue(3)); 678 static_assert( 679 std::is_same_v<BasicValue, decltype(someValueCRef.valueOr(value))>, 680 "valueOr should return a BasicValue"); 681 MOZ_RELEASE_ASSERT(someValueCRef.valueOrFrom(&MakeBasicValue) == 682 BasicValue(3)); 683 static_assert( 684 std::is_same_v<BasicValue, 685 decltype(someValueCRef.valueOrFrom(&MakeBasicValue))>, 686 "valueOrFrom should return a BasicValue"); 687 MOZ_RELEASE_ASSERT(someValueCRef.ptrOr(&value) != &value); 688 static_assert( 689 std::is_same_v<const BasicValue*, decltype(someValueCRef.ptrOr(&value))>, 690 "ptrOr should return a const BasicValue*"); 691 MOZ_RELEASE_ASSERT(*someValueCRef.ptrOrFrom(&MakeBasicValuePtr) == 692 BasicValue(3)); 693 static_assert( 694 std::is_same_v<const BasicValue*, 695 decltype(someValueCRef.ptrOrFrom(&MakeBasicValuePtr))>, 696 "ptrOrFrom should return a const BasicValue*"); 697 MOZ_RELEASE_ASSERT(someValueCRef.refOr(value) == BasicValue(3)); 698 static_assert( 699 std::is_same_v<const BasicValue&, decltype(someValueCRef.refOr(value))>, 700 "refOr should return a const BasicValue&"); 701 MOZ_RELEASE_ASSERT(someValueCRef.refOrFrom(&MakeBasicValueRef) == 702 BasicValue(3)); 703 static_assert( 704 std::is_same_v<const BasicValue&, 705 decltype(someValueCRef.refOrFrom(&MakeBasicValueRef))>, 706 "refOrFrom should return a const BasicValue&"); 707 708 // Check that the 'none' case of functional accessors works. 709 Maybe<BasicValue> noneValue; 710 MOZ_RELEASE_ASSERT(noneValue.valueOr(value) == BasicValue(9)); 711 static_assert(std::is_same_v<BasicValue, decltype(noneValue.valueOr(value))>, 712 "valueOr should return a BasicValue"); 713 MOZ_RELEASE_ASSERT(noneValue.valueOrFrom(&MakeBasicValue) == BasicValue(9)); 714 static_assert( 715 std::is_same_v<BasicValue, 716 decltype(noneValue.valueOrFrom(&MakeBasicValue))>, 717 "valueOrFrom should return a BasicValue"); 718 MOZ_RELEASE_ASSERT(noneValue.ptrOr(&value) == &value); 719 static_assert(std::is_same_v<BasicValue*, decltype(noneValue.ptrOr(&value))>, 720 "ptrOr should return a BasicValue*"); 721 MOZ_RELEASE_ASSERT(*noneValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(9)); 722 static_assert( 723 std::is_same_v<BasicValue*, 724 decltype(noneValue.ptrOrFrom(&MakeBasicValuePtr))>, 725 "ptrOrFrom should return a BasicValue*"); 726 MOZ_RELEASE_ASSERT(noneValue.refOr(value) == BasicValue(9)); 727 static_assert(std::is_same_v<BasicValue&, decltype(noneValue.refOr(value))>, 728 "refOr should return a BasicValue&"); 729 MOZ_RELEASE_ASSERT(noneValue.refOrFrom(&MakeBasicValueRef) == BasicValue(9)); 730 static_assert( 731 std::is_same_v<BasicValue&, 732 decltype(noneValue.refOrFrom(&MakeBasicValueRef))>, 733 "refOrFrom should return a BasicValue&"); 734 735 // Check that the 'none' case works through a const reference. 736 const Maybe<BasicValue>& noneValueCRef = noneValue; 737 MOZ_RELEASE_ASSERT(noneValueCRef.valueOr(value) == BasicValue(9)); 738 static_assert( 739 std::is_same_v<BasicValue, decltype(noneValueCRef.valueOr(value))>, 740 "valueOr should return a BasicValue"); 741 MOZ_RELEASE_ASSERT(noneValueCRef.valueOrFrom(&MakeBasicValue) == 742 BasicValue(9)); 743 static_assert( 744 std::is_same_v<BasicValue, 745 decltype(noneValueCRef.valueOrFrom(&MakeBasicValue))>, 746 "valueOrFrom should return a BasicValue"); 747 MOZ_RELEASE_ASSERT(noneValueCRef.ptrOr(&value) == &value); 748 static_assert( 749 std::is_same_v<const BasicValue*, decltype(noneValueCRef.ptrOr(&value))>, 750 "ptrOr should return a const BasicValue*"); 751 MOZ_RELEASE_ASSERT(*noneValueCRef.ptrOrFrom(&MakeBasicValuePtr) == 752 BasicValue(9)); 753 static_assert( 754 std::is_same_v<const BasicValue*, 755 decltype(noneValueCRef.ptrOrFrom(&MakeBasicValuePtr))>, 756 "ptrOrFrom should return a const BasicValue*"); 757 MOZ_RELEASE_ASSERT(noneValueCRef.refOr(value) == BasicValue(9)); 758 static_assert( 759 std::is_same_v<const BasicValue&, decltype(noneValueCRef.refOr(value))>, 760 "refOr should return a const BasicValue&"); 761 MOZ_RELEASE_ASSERT(noneValueCRef.refOrFrom(&MakeBasicValueRef) == 762 BasicValue(9)); 763 static_assert( 764 std::is_same_v<const BasicValue&, 765 decltype(noneValueCRef.refOrFrom(&MakeBasicValueRef))>, 766 "refOrFrom should return a const BasicValue&"); 767 768 // Clean up so the undestroyed objects count stays accurate. 769 delete sStaticBasicValue; 770 sStaticBasicValue = nullptr; 771 772 return true; 773 } 774 775 static bool gFunctionWasApplied = false; 776 777 static void IncrementTag(BasicValue& aValue) { 778 gFunctionWasApplied = true; 779 aValue.SetTag(aValue.GetTag() + 1); 780 } 781 782 static void AccessValue(const BasicValue&) { gFunctionWasApplied = true; } 783 784 struct IncrementTagFunctor { 785 IncrementTagFunctor() : mBy(1) {} 786 787 void operator()(BasicValue& aValue) { 788 aValue.SetTag(aValue.GetTag() + mBy.GetTag()); 789 } 790 791 BasicValue mBy; 792 }; 793 794 static bool TestApply() { 795 // Check that apply handles the 'Nothing' case. 796 gFunctionWasApplied = false; 797 Maybe<BasicValue> mayValue; 798 mayValue.apply(&IncrementTag); 799 mayValue.apply(&AccessValue); 800 MOZ_RELEASE_ASSERT(!gFunctionWasApplied); 801 802 // Check that apply handles the 'Some' case. 803 mayValue = Some(BasicValue(1)); 804 mayValue.apply(&IncrementTag); 805 MOZ_RELEASE_ASSERT(gFunctionWasApplied); 806 MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2); 807 gFunctionWasApplied = false; 808 mayValue.apply(&AccessValue); 809 MOZ_RELEASE_ASSERT(gFunctionWasApplied); 810 811 // Check that apply works with a const reference. 812 const Maybe<BasicValue>& mayValueCRef = mayValue; 813 gFunctionWasApplied = false; 814 mayValueCRef.apply(&AccessValue); 815 MOZ_RELEASE_ASSERT(gFunctionWasApplied); 816 817 // Check that apply works with functors. 818 IncrementTagFunctor tagIncrementer; 819 MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed); 820 mayValue = Some(BasicValue(1)); 821 mayValue.apply(tagIncrementer); 822 MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2); 823 MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed); 824 825 // Check that apply works with lambda expressions. 826 int32_t two = 2; 827 gFunctionWasApplied = false; 828 mayValue = Some(BasicValue(2)); 829 mayValue.apply([&](BasicValue& aVal) { aVal.SetTag(aVal.GetTag() * two); }); 830 MOZ_RELEASE_ASSERT(mayValue->GetTag() == 4); 831 mayValue.apply([=](BasicValue& aVal) { aVal.SetTag(aVal.GetTag() * two); }); 832 MOZ_RELEASE_ASSERT(mayValue->GetTag() == 8); 833 mayValueCRef.apply( 834 [&](const BasicValue& aVal) { gFunctionWasApplied = true; }); 835 MOZ_RELEASE_ASSERT(gFunctionWasApplied == true); 836 837 // Check that apply can move the contained value. 838 mayValue = Some(BasicValue(1)); 839 Maybe<BasicValue> otherValue; 840 std::move(mayValue).apply( 841 [&](BasicValue&& aVal) { otherValue = Some(std::move(aVal)); }); 842 MOZ_RELEASE_ASSERT(mayValue.isNothing()); 843 MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1); 844 MOZ_RELEASE_ASSERT(otherValue->GetStatus() == eWasMoveConstructed); 845 846 return true; 847 } 848 849 static int TimesTwo(const BasicValue& aValue) { return aValue.GetTag() * 2; } 850 851 static int TimesTwoAndResetOriginal(BasicValue& aValue) { 852 int tag = aValue.GetTag(); 853 aValue.SetTag(1); 854 return tag * 2; 855 } 856 857 struct MultiplyTagFunctor { 858 MultiplyTagFunctor() : mBy(2) {} 859 860 int operator()(BasicValue& aValue) { return aValue.GetTag() * mBy.GetTag(); } 861 862 BasicValue mBy; 863 }; 864 865 static bool TestMap() { 866 // Check that map handles the 'Nothing' case. 867 Maybe<BasicValue> mayValue; 868 MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwo) == Nothing()); 869 static_assert(std::is_same_v<Maybe<int>, decltype(mayValue.map(&TimesTwo))>, 870 "map(TimesTwo) should return a Maybe<int>"); 871 MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwoAndResetOriginal) == Nothing()); 872 873 // Check that map handles the 'Some' case. 874 mayValue = Some(BasicValue(2)); 875 MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwo) == Some(4)); 876 MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwoAndResetOriginal) == Some(4)); 877 MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1); 878 mayValue = Some(BasicValue(2)); 879 880 // Check that map works with a const reference. 881 mayValue->SetTag(2); 882 const Maybe<BasicValue>& mayValueCRef = mayValue; 883 MOZ_RELEASE_ASSERT(mayValueCRef.map(&TimesTwo) == Some(4)); 884 static_assert( 885 std::is_same_v<Maybe<int>, decltype(mayValueCRef.map(&TimesTwo))>, 886 "map(TimesTwo) should return a Maybe<int>"); 887 888 // Check that map works with functors. 889 MultiplyTagFunctor tagMultiplier; 890 MOZ_RELEASE_ASSERT(tagMultiplier.mBy.GetStatus() == eWasConstructed); 891 MOZ_RELEASE_ASSERT(mayValue.map(tagMultiplier) == Some(4)); 892 MOZ_RELEASE_ASSERT(tagMultiplier.mBy.GetStatus() == eWasConstructed); 893 894 // Check that map works with lambda expressions. 895 int two = 2; 896 mayValue = Some(BasicValue(2)); 897 Maybe<int> mappedValue = 898 mayValue.map([&](const BasicValue& aVal) { return aVal.GetTag() * two; }); 899 MOZ_RELEASE_ASSERT(mappedValue == Some(4)); 900 mappedValue = 901 mayValue.map([=](const BasicValue& aVal) { return aVal.GetTag() * two; }); 902 MOZ_RELEASE_ASSERT(mappedValue == Some(4)); 903 mappedValue = mayValueCRef.map( 904 [&](const BasicValue& aVal) { return aVal.GetTag() * two; }); 905 MOZ_RELEASE_ASSERT(mappedValue == Some(4)); 906 907 // Check that map can move the contained value. 908 mayValue = Some(BasicValue(1)); 909 Maybe<BasicValue> otherValue = std::move(mayValue).map( 910 [](BasicValue&& aValue) { return std::move(aValue); }); 911 MOZ_RELEASE_ASSERT(mayValue.isNothing()); 912 MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1); 913 MOZ_RELEASE_ASSERT(otherValue->GetStatus() == eWasMoveConstructed); 914 915 // Check that function object qualifiers are preserved when invoked. 916 struct F { 917 std::integral_constant<int, 1> operator()(int) & { return {}; } 918 std::integral_constant<int, 2> operator()(int) const& { return {}; } 919 std::integral_constant<int, 3> operator()(int) && { return {}; } 920 std::integral_constant<int, 4> operator()(int) const&& { return {}; } 921 }; 922 Maybe<int> mi = Some(0); 923 const Maybe<int> cmi = Some(0); 924 F f; 925 static_assert(std::is_same<decltype(mi.map(f)), 926 Maybe<std::integral_constant<int, 1>>>::value, 927 "Maybe.map(&)"); 928 MOZ_RELEASE_ASSERT(mi.map(f).value()() == 1); 929 static_assert(std::is_same<decltype(cmi.map(f)), 930 Maybe<std::integral_constant<int, 1>>>::value, 931 "const Maybe.map(&)"); 932 MOZ_RELEASE_ASSERT(cmi.map(f).value()() == 1); 933 const F cf; 934 static_assert(std::is_same<decltype(mi.map(cf)), 935 Maybe<std::integral_constant<int, 2>>>::value, 936 "Maybe.map(const &)"); 937 MOZ_RELEASE_ASSERT(mi.map(cf).value() == 2); 938 static_assert(std::is_same<decltype(cmi.map(cf)), 939 Maybe<std::integral_constant<int, 2>>>::value, 940 "const Maybe.map(const &)"); 941 MOZ_RELEASE_ASSERT(cmi.map(cf).value() == 2); 942 static_assert(std::is_same<decltype(mi.map(F{})), 943 Maybe<std::integral_constant<int, 3>>>::value, 944 "Maybe.map(&&)"); 945 MOZ_RELEASE_ASSERT(mi.map(F{}).value() == 3); 946 static_assert(std::is_same<decltype(cmi.map(F{})), 947 Maybe<std::integral_constant<int, 3>>>::value, 948 "const Maybe.map(&&)"); 949 MOZ_RELEASE_ASSERT(cmi.map(F{}).value() == 3); 950 using CF = const F; 951 static_assert(std::is_same<decltype(mi.map(CF{})), 952 Maybe<std::integral_constant<int, 4>>>::value, 953 "Maybe.map(const &&)"); 954 MOZ_RELEASE_ASSERT(mi.map(CF{}).value() == 4); 955 static_assert(std::is_same<decltype(cmi.map(CF{})), 956 Maybe<std::integral_constant<int, 4>>>::value, 957 "const Maybe.map(const &&)"); 958 MOZ_RELEASE_ASSERT(cmi.map(CF{}).value() == 4); 959 960 return true; 961 } 962 963 static bool TestToMaybe() { 964 BasicValue value(1); 965 BasicValue* nullPointer = nullptr; 966 967 // Check that a non-null pointer translates into a Some value. 968 Maybe<BasicValue> mayValue = ToMaybe(&value); 969 static_assert(std::is_same_v<Maybe<BasicValue>, decltype(ToMaybe(&value))>, 970 "ToMaybe should return a Maybe<BasicValue>"); 971 MOZ_RELEASE_ASSERT(mayValue.isSome()); 972 MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1); 973 MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasCopyConstructed); 974 MOZ_RELEASE_ASSERT(value.GetStatus() != eWasMovedFrom); 975 976 // Check that a null pointer translates into a Nothing value. 977 mayValue = ToMaybe(nullPointer); 978 static_assert( 979 std::is_same_v<Maybe<BasicValue>, decltype(ToMaybe(nullPointer))>, 980 "ToMaybe should return a Maybe<BasicValue>"); 981 MOZ_RELEASE_ASSERT(mayValue.isNothing()); 982 983 return true; 984 } 985 986 static bool TestComparisonOperators() { 987 Maybe<BasicValue> nothingValue = Nothing(); 988 Maybe<BasicValue> anotherNothingValue = Nothing(); 989 Maybe<BasicValue> oneValue = Some(BasicValue(1)); 990 Maybe<BasicValue> anotherOneValue = Some(BasicValue(1)); 991 Maybe<BasicValue> twoValue = Some(BasicValue(2)); 992 993 // Check equality. 994 MOZ_RELEASE_ASSERT(nothingValue == anotherNothingValue); 995 MOZ_RELEASE_ASSERT(oneValue == anotherOneValue); 996 997 // Check inequality. 998 MOZ_RELEASE_ASSERT(nothingValue != oneValue); 999 MOZ_RELEASE_ASSERT(oneValue != nothingValue); 1000 MOZ_RELEASE_ASSERT(oneValue != twoValue); 1001 1002 // Check '<'. 1003 MOZ_RELEASE_ASSERT(nothingValue < oneValue); 1004 MOZ_RELEASE_ASSERT(oneValue < twoValue); 1005 1006 // Check '<='. 1007 MOZ_RELEASE_ASSERT(nothingValue <= anotherNothingValue); 1008 MOZ_RELEASE_ASSERT(nothingValue <= oneValue); 1009 MOZ_RELEASE_ASSERT(oneValue <= oneValue); 1010 MOZ_RELEASE_ASSERT(oneValue <= twoValue); 1011 1012 // Check '>'. 1013 MOZ_RELEASE_ASSERT(oneValue > nothingValue); 1014 MOZ_RELEASE_ASSERT(twoValue > oneValue); 1015 1016 // Check '>='. 1017 MOZ_RELEASE_ASSERT(nothingValue >= anotherNothingValue); 1018 MOZ_RELEASE_ASSERT(oneValue >= nothingValue); 1019 MOZ_RELEASE_ASSERT(oneValue >= oneValue); 1020 MOZ_RELEASE_ASSERT(twoValue >= oneValue); 1021 1022 return true; 1023 } 1024 1025 // Check that Maybe<> can wrap a superclass that happens to also be a concrete 1026 // class (i.e. that the compiler doesn't warn when we invoke the superclass's 1027 // destructor explicitly in |reset()|. 1028 class MySuperClass { 1029 virtual void VirtualMethod() { /* do nothing */ } 1030 }; 1031 1032 class MyDerivedClass : public MySuperClass { 1033 void VirtualMethod() override { /* do nothing */ } 1034 }; 1035 1036 static bool TestVirtualFunction() { 1037 Maybe<MySuperClass> super; 1038 super.emplace(); 1039 super.reset(); 1040 1041 Maybe<MyDerivedClass> derived; 1042 derived.emplace(); 1043 derived.reset(); 1044 1045 // If this compiles successfully, we've passed. 1046 return true; 1047 } 1048 1049 static Maybe<int*> ReturnSomeNullptr() { return Some(nullptr); } 1050 1051 struct D { 1052 explicit D(const Maybe<int*>&) {} 1053 }; 1054 1055 static bool TestSomeNullptrConversion() { 1056 Maybe<int*> m1 = Some(nullptr); 1057 MOZ_RELEASE_ASSERT(m1.isSome()); 1058 MOZ_RELEASE_ASSERT(m1); 1059 MOZ_RELEASE_ASSERT(!*m1); 1060 1061 auto m2 = ReturnSomeNullptr(); 1062 MOZ_RELEASE_ASSERT(m2.isSome()); 1063 MOZ_RELEASE_ASSERT(m2); 1064 MOZ_RELEASE_ASSERT(!*m2); 1065 1066 Maybe<decltype(nullptr)> m3 = Some(nullptr); 1067 MOZ_RELEASE_ASSERT(m3.isSome()); 1068 MOZ_RELEASE_ASSERT(m3); 1069 MOZ_RELEASE_ASSERT(*m3 == nullptr); 1070 1071 D d(Some(nullptr)); 1072 1073 return true; 1074 } 1075 1076 struct Base {}; 1077 struct Derived : Base {}; 1078 1079 static Maybe<Base*> ReturnDerivedPointer() { 1080 Derived* d = nullptr; 1081 return Some(d); 1082 } 1083 1084 struct ExplicitConstructorBasePointer { 1085 explicit ExplicitConstructorBasePointer(const Maybe<Base*>&) {} 1086 }; 1087 1088 static bool TestSomePointerConversion() { 1089 Base base; 1090 Derived derived; 1091 1092 Maybe<Base*> m1 = Some(&derived); 1093 MOZ_RELEASE_ASSERT(m1.isSome()); 1094 MOZ_RELEASE_ASSERT(m1); 1095 MOZ_RELEASE_ASSERT(*m1 == &derived); 1096 1097 auto m2 = ReturnDerivedPointer(); 1098 MOZ_RELEASE_ASSERT(m2.isSome()); 1099 MOZ_RELEASE_ASSERT(m2); 1100 MOZ_RELEASE_ASSERT(*m2 == nullptr); 1101 1102 Maybe<Base*> m3 = Some(&base); 1103 MOZ_RELEASE_ASSERT(m3.isSome()); 1104 MOZ_RELEASE_ASSERT(m3); 1105 MOZ_RELEASE_ASSERT(*m3 == &base); 1106 1107 auto s1 = Some(&derived); 1108 Maybe<Base*> c1(s1); 1109 MOZ_RELEASE_ASSERT(c1.isSome()); 1110 MOZ_RELEASE_ASSERT(c1); 1111 MOZ_RELEASE_ASSERT(*c1 == &derived); 1112 1113 ExplicitConstructorBasePointer ecbp(Some(&derived)); 1114 1115 return true; 1116 } 1117 1118 struct SourceType1 { 1119 int mTag; 1120 1121 operator int() const { return mTag; } 1122 }; 1123 struct DestType { 1124 int mTag; 1125 Status mStatus; 1126 1127 DestType() : mTag(0), mStatus(eWasDefaultConstructed) {} 1128 1129 MOZ_IMPLICIT DestType(int aTag) : mTag(aTag), mStatus(eWasConstructed) {} 1130 1131 MOZ_IMPLICIT DestType(SourceType1&& aSrc) 1132 : mTag(aSrc.mTag), mStatus(eWasMoveConstructed) {} 1133 1134 MOZ_IMPLICIT DestType(const SourceType1& aSrc) 1135 : mTag(aSrc.mTag), mStatus(eWasCopyConstructed) {} 1136 1137 DestType& operator=(int aTag) { 1138 mTag = aTag; 1139 mStatus = eWasAssigned; 1140 return *this; 1141 } 1142 1143 DestType& operator=(SourceType1&& aSrc) { 1144 mTag = aSrc.mTag; 1145 mStatus = eWasMoveAssigned; 1146 return *this; 1147 } 1148 1149 DestType& operator=(const SourceType1& aSrc) { 1150 mTag = aSrc.mTag; 1151 mStatus = eWasCopyAssigned; 1152 return *this; 1153 } 1154 }; 1155 struct SourceType2 { 1156 int mTag; 1157 1158 operator DestType() const& { 1159 DestType result; 1160 result.mTag = mTag; 1161 result.mStatus = eWasCopiedFrom; 1162 return result; 1163 } 1164 1165 operator DestType() && { 1166 DestType result; 1167 result.mTag = mTag; 1168 result.mStatus = eWasMovedFrom; 1169 return result; 1170 } 1171 }; 1172 1173 static bool TestTypeConversion() { 1174 { 1175 Maybe<SourceType1> src = Some(SourceType1{1}); 1176 Maybe<DestType> dest = src; 1177 MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 1); 1178 MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1); 1179 MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopyConstructed); 1180 1181 src = Some(SourceType1{2}); 1182 dest = src; 1183 MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 2); 1184 MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2); 1185 MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopyAssigned); 1186 } 1187 1188 { 1189 Maybe<SourceType1> src = Some(SourceType1{1}); 1190 Maybe<DestType> dest = std::move(src); 1191 MOZ_RELEASE_ASSERT(src.isNothing()); 1192 MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1); 1193 MOZ_RELEASE_ASSERT(dest->mStatus == eWasMoveConstructed); 1194 1195 src = Some(SourceType1{2}); 1196 dest = std::move(src); 1197 MOZ_RELEASE_ASSERT(src.isNothing()); 1198 MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2); 1199 MOZ_RELEASE_ASSERT(dest->mStatus == eWasMoveAssigned); 1200 } 1201 1202 { 1203 Maybe<SourceType2> src = Some(SourceType2{1}); 1204 Maybe<DestType> dest = src; 1205 MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 1); 1206 MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1); 1207 MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopiedFrom); 1208 1209 src = Some(SourceType2{2}); 1210 dest = src; 1211 MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 2); 1212 MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2); 1213 MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopiedFrom); 1214 } 1215 1216 { 1217 Maybe<SourceType2> src = Some(SourceType2{1}); 1218 Maybe<DestType> dest = std::move(src); 1219 MOZ_RELEASE_ASSERT(src.isNothing()); 1220 MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1); 1221 MOZ_RELEASE_ASSERT(dest->mStatus == eWasMovedFrom); 1222 1223 src = Some(SourceType2{2}); 1224 dest = std::move(src); 1225 MOZ_RELEASE_ASSERT(src.isNothing()); 1226 MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2); 1227 MOZ_RELEASE_ASSERT(dest->mStatus == eWasMovedFrom); 1228 } 1229 1230 { 1231 Maybe<int> src = Some(1); 1232 Maybe<DestType> dest = src; 1233 MOZ_RELEASE_ASSERT(src.isSome() && *src == 1); 1234 MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1); 1235 MOZ_RELEASE_ASSERT(dest->mStatus == eWasConstructed); 1236 1237 src = Some(2); 1238 dest = src; 1239 MOZ_RELEASE_ASSERT(src.isSome() && *src == 2); 1240 MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2); 1241 MOZ_RELEASE_ASSERT(dest->mStatus == eWasAssigned); 1242 } 1243 1244 { 1245 Maybe<int> src = Some(1); 1246 Maybe<DestType> dest = std::move(src); 1247 MOZ_RELEASE_ASSERT(src.isNothing()); 1248 MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1); 1249 MOZ_RELEASE_ASSERT(dest->mStatus == eWasConstructed); 1250 1251 src = Some(2); 1252 dest = std::move(src); 1253 MOZ_RELEASE_ASSERT(src.isNothing()); 1254 MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2); 1255 MOZ_RELEASE_ASSERT(dest->mStatus == eWasAssigned); 1256 } 1257 1258 { 1259 Maybe<SourceType1> src = Some(SourceType1{1}); 1260 Maybe<int> dest = src; 1261 MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 1); 1262 MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1); 1263 1264 src = Some(SourceType1{2}); 1265 dest = src; 1266 MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 2); 1267 MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2); 1268 } 1269 1270 { 1271 Maybe<SourceType1> src = Some(SourceType1{1}); 1272 Maybe<int> dest = std::move(src); 1273 MOZ_RELEASE_ASSERT(src.isNothing()); 1274 MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1); 1275 1276 src = Some(SourceType1{2}); 1277 dest = std::move(src); 1278 MOZ_RELEASE_ASSERT(src.isNothing()); 1279 MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2); 1280 } 1281 1282 { 1283 Maybe<size_t> src = Some(1); 1284 Maybe<char16_t> dest = src; 1285 MOZ_RELEASE_ASSERT(src.isSome() && *src == 1); 1286 MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1); 1287 1288 src = Some(2); 1289 dest = src; 1290 MOZ_RELEASE_ASSERT(src.isSome() && *src == 2); 1291 MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2); 1292 } 1293 1294 { 1295 Maybe<size_t> src = Some(1); 1296 Maybe<char16_t> dest = std::move(src); 1297 MOZ_RELEASE_ASSERT(src.isNothing()); 1298 MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1); 1299 1300 src = Some(2); 1301 dest = std::move(src); 1302 MOZ_RELEASE_ASSERT(src.isNothing()); 1303 MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2); 1304 } 1305 1306 return true; 1307 } 1308 1309 static bool TestReference() { 1310 static_assert(std::is_trivially_destructible_v<Maybe<int&>>); 1311 static_assert(std::is_trivially_copy_constructible_v<Maybe<int&>>); 1312 static_assert(std::is_trivially_copy_assignable_v<Maybe<int&>>); 1313 1314 static_assert(Maybe<int&>{}.isNothing()); 1315 static_assert(Maybe<int&>{Nothing{}}.isNothing()); 1316 1317 { 1318 Maybe<int&> defaultConstructed; 1319 1320 MOZ_RELEASE_ASSERT(defaultConstructed.isNothing()); 1321 MOZ_RELEASE_ASSERT(!defaultConstructed.isSome()); 1322 MOZ_RELEASE_ASSERT(!defaultConstructed); 1323 } 1324 1325 { 1326 Maybe<int&> nothing = Nothing(); 1327 1328 MOZ_RELEASE_ASSERT(nothing.isNothing()); 1329 MOZ_RELEASE_ASSERT(!nothing.isSome()); 1330 MOZ_RELEASE_ASSERT(!nothing); 1331 } 1332 1333 { 1334 int foo = 42, bar = 42; 1335 Maybe<int&> some = SomeRef(foo); 1336 1337 MOZ_RELEASE_ASSERT(!some.isNothing()); 1338 MOZ_RELEASE_ASSERT(some.isSome()); 1339 MOZ_RELEASE_ASSERT(some); 1340 MOZ_RELEASE_ASSERT(&some.ref() == &foo); 1341 1342 MOZ_RELEASE_ASSERT(some.refEquals(foo)); 1343 MOZ_RELEASE_ASSERT(some.refEquals(SomeRef(foo))); 1344 MOZ_RELEASE_ASSERT(!some.refEquals(Nothing())); 1345 MOZ_RELEASE_ASSERT(!some.refEquals(bar)); 1346 MOZ_RELEASE_ASSERT(!some.refEquals(SomeRef(bar))); 1347 1348 some.ref()++; 1349 MOZ_RELEASE_ASSERT(43 == foo); 1350 1351 (*some)++; 1352 MOZ_RELEASE_ASSERT(44 == foo); 1353 } 1354 1355 { 1356 int foo = 42, bar = 42; 1357 Maybe<int&> some; 1358 some.emplace(foo); 1359 1360 MOZ_RELEASE_ASSERT(!some.isNothing()); 1361 MOZ_RELEASE_ASSERT(some.isSome()); 1362 MOZ_RELEASE_ASSERT(some); 1363 MOZ_RELEASE_ASSERT(&some.ref() == &foo); 1364 1365 MOZ_RELEASE_ASSERT(some.refEquals(foo)); 1366 MOZ_RELEASE_ASSERT(some.refEquals(SomeRef(foo))); 1367 MOZ_RELEASE_ASSERT(!some.refEquals(Nothing())); 1368 MOZ_RELEASE_ASSERT(!some.refEquals(bar)); 1369 MOZ_RELEASE_ASSERT(!some.refEquals(SomeRef(bar))); 1370 1371 some.ref()++; 1372 MOZ_RELEASE_ASSERT(43 == foo); 1373 } 1374 1375 { 1376 Maybe<int&> defaultConstructed; 1377 defaultConstructed.reset(); 1378 1379 MOZ_RELEASE_ASSERT(defaultConstructed.isNothing()); 1380 MOZ_RELEASE_ASSERT(!defaultConstructed.isSome()); 1381 MOZ_RELEASE_ASSERT(!defaultConstructed); 1382 } 1383 1384 { 1385 int foo = 42; 1386 Maybe<int&> some = SomeRef(foo); 1387 some.reset(); 1388 1389 MOZ_RELEASE_ASSERT(some.isNothing()); 1390 MOZ_RELEASE_ASSERT(!some.isSome()); 1391 MOZ_RELEASE_ASSERT(!some); 1392 } 1393 1394 { 1395 int foo = 42; 1396 Maybe<int&> some = SomeRef(foo); 1397 1398 auto& applied = some.apply([](int& ref) { ref++; }); 1399 1400 MOZ_RELEASE_ASSERT(&some == &applied); 1401 MOZ_RELEASE_ASSERT(43 == foo); 1402 } 1403 1404 { 1405 Maybe<int&> nothing; 1406 1407 auto& applied = nothing.apply([](int& ref) { ref++; }); 1408 1409 MOZ_RELEASE_ASSERT(¬hing == &applied); 1410 } 1411 1412 { 1413 int foo = 42; 1414 Maybe<int&> some = SomeRef(foo); 1415 1416 auto mapped = some.map([](int& ref) { return &ref; }); 1417 static_assert(std::is_same_v<decltype(mapped), Maybe<int*>>); 1418 1419 MOZ_RELEASE_ASSERT(&foo == *mapped); 1420 } 1421 1422 { 1423 Maybe<int&> nothing; 1424 1425 auto mapped = nothing.map([](int& ref) { return &ref; }); 1426 1427 MOZ_RELEASE_ASSERT(mapped.isNothing()); 1428 MOZ_RELEASE_ASSERT(!mapped.isSome()); 1429 MOZ_RELEASE_ASSERT(!mapped); 1430 } 1431 1432 { 1433 int foo = 42; 1434 auto someRef = ToMaybeRef(&foo); 1435 1436 static_assert(std::is_same_v<decltype(someRef), Maybe<int&>>); 1437 1438 MOZ_RELEASE_ASSERT(someRef.isSome()); 1439 MOZ_RELEASE_ASSERT(&foo == &someRef.ref()); 1440 } 1441 1442 { 1443 int* fooPtr = nullptr; 1444 auto someRef = ToMaybeRef(fooPtr); 1445 1446 static_assert(std::is_same_v<decltype(someRef), Maybe<int&>>); 1447 1448 MOZ_RELEASE_ASSERT(someRef.isNothing()); 1449 } 1450 1451 return true; 1452 } 1453 1454 static Maybe<int> IncrementAndReturnTag(BasicValue& aValue) { 1455 gFunctionWasApplied = true; 1456 aValue.SetTag(aValue.GetTag() + 1); 1457 return Some(aValue.GetTag()); 1458 } 1459 1460 static Maybe<int> AccessValueAndReturnNothing(const BasicValue&) { 1461 gFunctionWasApplied = true; 1462 return Nothing(); 1463 } 1464 1465 static Maybe<int> AccessValueAndReturnOther(const BasicValue&) { 1466 gFunctionWasApplied = true; 1467 return Some(42); 1468 } 1469 1470 struct IncrementAndReturnTagFunctor { 1471 IncrementAndReturnTagFunctor() : mBy(1) {} 1472 1473 Maybe<int> operator()(BasicValue& aValue) { 1474 aValue.SetTag(aValue.GetTag() + mBy.GetTag()); 1475 return Some(aValue.GetTag()); 1476 } 1477 1478 BasicValue mBy; 1479 }; 1480 1481 struct AccessValueAndReturnOtherFunctor { 1482 explicit AccessValueAndReturnOtherFunctor(int aVal) : mBy(aVal) {} 1483 1484 Maybe<BasicValue> operator()() { 1485 gFunctionWasApplied = true; 1486 return Some(mBy); 1487 } 1488 1489 BasicValue mBy; 1490 }; 1491 1492 static bool TestAndThen() { 1493 // Check that andThen handles the 'Nothing' case. 1494 gFunctionWasApplied = false; 1495 Maybe<BasicValue> mayValue; 1496 Maybe<int> otherValue = mayValue.andThen(&AccessValueAndReturnOther); 1497 MOZ_RELEASE_ASSERT(!gFunctionWasApplied); 1498 MOZ_RELEASE_ASSERT(otherValue.isNothing()); 1499 1500 // Check that andThen handles the 'Some' case. 1501 mayValue = Some(BasicValue(1)); 1502 otherValue = mayValue.andThen(&AccessValueAndReturnNothing); 1503 MOZ_RELEASE_ASSERT(gFunctionWasApplied); 1504 MOZ_RELEASE_ASSERT(otherValue.isNothing()); 1505 gFunctionWasApplied = false; 1506 otherValue = mayValue.andThen(&IncrementAndReturnTag); 1507 MOZ_RELEASE_ASSERT(gFunctionWasApplied); 1508 MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2); 1509 MOZ_RELEASE_ASSERT(*otherValue == 2); 1510 gFunctionWasApplied = false; 1511 otherValue = mayValue.andThen(&AccessValueAndReturnOther); 1512 MOZ_RELEASE_ASSERT(gFunctionWasApplied); 1513 MOZ_RELEASE_ASSERT(*otherValue == 42); 1514 1515 // Check that andThen works with a const reference. 1516 const Maybe<BasicValue>& mayValueCRef = mayValue; 1517 gFunctionWasApplied = false; 1518 otherValue = mayValueCRef.andThen(&AccessValueAndReturnOther); 1519 MOZ_RELEASE_ASSERT(gFunctionWasApplied); 1520 MOZ_RELEASE_ASSERT(*otherValue == 42); 1521 1522 // Check that andThen works with functors. 1523 IncrementAndReturnTagFunctor tagIncrementer; 1524 MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed); 1525 mayValue = Some(BasicValue(1)); 1526 otherValue = mayValue.andThen(tagIncrementer); 1527 MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2); 1528 MOZ_RELEASE_ASSERT(*otherValue == 2); 1529 MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed); 1530 1531 // Check that andThen works with lambda expressions. 1532 gFunctionWasApplied = false; 1533 mayValue = Some(BasicValue(2)); 1534 otherValue = mayValue.andThen( 1535 [](BasicValue& aVal) { return Some(aVal.GetTag() * 2); }); 1536 MOZ_RELEASE_ASSERT(*otherValue == 4); 1537 otherValue = otherValue.andThen([](int aVal) { return Some(aVal * 2); }); 1538 MOZ_RELEASE_ASSERT(*otherValue == 8); 1539 otherValue = mayValueCRef.andThen([&](const BasicValue& aVal) { 1540 gFunctionWasApplied = true; 1541 return Some(42); 1542 }); 1543 MOZ_RELEASE_ASSERT(gFunctionWasApplied == true); 1544 MOZ_RELEASE_ASSERT(*otherValue == 42); 1545 1546 // Check that andThen can move the contained value. 1547 mayValue = Some(BasicValue(1)); 1548 otherValue = std::move(mayValue).andThen([&](BasicValue&& aVal) { 1549 BasicValue tmp = std::move(aVal); 1550 return Some(tmp.GetTag()); 1551 }); 1552 MOZ_RELEASE_ASSERT(mayValue.isNothing()); 1553 MOZ_RELEASE_ASSERT(*otherValue == 1); 1554 1555 return true; 1556 } 1557 1558 static bool TestOrElse() { 1559 const auto createValue = [&](int aTag) { 1560 return [&, aTag]() -> Maybe<BasicValue> { 1561 gFunctionWasApplied = true; 1562 return Some(BasicValue(aTag)); 1563 }; 1564 }; 1565 // Check that orElse handles the 'Some' case. 1566 gFunctionWasApplied = false; 1567 Maybe<BasicValue> mayValue = Some(BasicValue(1)); 1568 Maybe<BasicValue> otherValue = mayValue.orElse(createValue(2)); 1569 MOZ_RELEASE_ASSERT(!gFunctionWasApplied); 1570 MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1); 1571 1572 // Check that orElse handles the 'Nothing' case. 1573 mayValue = Nothing(); 1574 otherValue = mayValue.orElse(createValue(1)); 1575 MOZ_RELEASE_ASSERT(gFunctionWasApplied); 1576 MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1); 1577 gFunctionWasApplied = false; 1578 otherValue = otherValue.orElse(createValue(2)); 1579 MOZ_RELEASE_ASSERT(!gFunctionWasApplied); 1580 MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1); 1581 1582 // Check that orElse works with a const reference. 1583 mayValue = Nothing(); 1584 const Maybe<BasicValue>& mayValueCRef = mayValue; 1585 gFunctionWasApplied = false; 1586 otherValue = mayValueCRef.orElse(createValue(1)); 1587 MOZ_RELEASE_ASSERT(gFunctionWasApplied); 1588 MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1); 1589 1590 // Check that orElse works with functors. 1591 gFunctionWasApplied = false; 1592 AccessValueAndReturnOtherFunctor accesser(42); 1593 mayValue = Some(BasicValue(1)); 1594 otherValue = mayValue.orElse(accesser); 1595 MOZ_RELEASE_ASSERT(!gFunctionWasApplied); 1596 MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1); 1597 MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1); 1598 mayValue = Nothing(); 1599 otherValue = mayValue.orElse(accesser); 1600 MOZ_RELEASE_ASSERT(gFunctionWasApplied); 1601 MOZ_RELEASE_ASSERT(mayValue.isNothing()); 1602 MOZ_RELEASE_ASSERT(otherValue->GetTag() == 42); 1603 1604 // Check that orElse works with lambda expressions. 1605 gFunctionWasApplied = false; 1606 mayValue = Nothing(); 1607 otherValue = mayValue.orElse([] { return Some(BasicValue(1)); }); 1608 MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1); 1609 mayValue = otherValue.orElse([] { return Some(BasicValue(2)); }); 1610 MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1); 1611 otherValue = mayValueCRef.orElse([&] { 1612 gFunctionWasApplied = true; 1613 return Some(BasicValue(42)); 1614 }); 1615 MOZ_RELEASE_ASSERT(!gFunctionWasApplied); 1616 MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1); 1617 1618 // Check that orElse can move the contained value. 1619 mayValue = Some(BasicValue(1)); 1620 otherValue = std::move(mayValue).orElse([] { return Some(BasicValue(2)); }); 1621 MOZ_RELEASE_ASSERT(mayValue.isNothing()); 1622 MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1); 1623 1624 return true; 1625 } 1626 1627 static bool TestComposedMoves() { 1628 const auto moveAlong = [](UncopyableValue&& aValue) { 1629 return Some(std::move(aValue)); 1630 }; 1631 const auto justNothing = []() -> Maybe<UncopyableValue> { return Nothing(); }; 1632 const auto createValue = [] { return Some(UncopyableValue()); }; 1633 1634 // Check that andThen and orElse can propagate a non-copyable value created 1635 // mid-chain. 1636 Maybe<UncopyableValue> mayValue; 1637 Maybe<UncopyableValue> movedValue = std::move(mayValue) 1638 .andThen(moveAlong) 1639 .orElse(createValue) 1640 .andThen(moveAlong); 1641 MOZ_RELEASE_ASSERT(mayValue.isNothing()); 1642 MOZ_RELEASE_ASSERT(movedValue->GetStatus() == eWasMoveConstructed); 1643 1644 // Check that andThen and orElse can propagate a non-copyable value created 1645 // pre-chain. 1646 mayValue = Some(UncopyableValue{}); 1647 movedValue = std::move(mayValue) 1648 .andThen(moveAlong) 1649 .orElse(justNothing) 1650 .andThen(moveAlong); 1651 MOZ_RELEASE_ASSERT(mayValue.isNothing()); 1652 MOZ_RELEASE_ASSERT(movedValue->GetStatus() == eWasMoveAssigned); 1653 1654 // Check that andThen and orElse can propagate a reference. 1655 { 1656 UncopyableValue val{}; 1657 UncopyableValue otherVal{}; 1658 const auto passAlong = [](UncopyableValue& aValue) { 1659 return SomeRef(aValue); 1660 }; 1661 const auto fallbackToOther = [&]() { return SomeRef(otherVal); }; 1662 1663 Maybe<UncopyableValue&> mayRef = SomeRef(val); 1664 Maybe<UncopyableValue&> chainedRef = 1665 mayRef.andThen(passAlong).orElse(fallbackToOther).andThen(passAlong); 1666 MOZ_RELEASE_ASSERT(&val != &otherVal, 1667 "Distinct values should not compare equal"); 1668 MOZ_RELEASE_ASSERT(&*mayRef == &*chainedRef, 1669 "Chain should pass along the same reference"); 1670 } 1671 1672 // Check that andThen and orElse can propagate a const reference. 1673 { 1674 const UncopyableValue val{}; 1675 const UncopyableValue otherVal{}; 1676 const auto passAlong = [](const UncopyableValue& aValue) { 1677 return SomeRef(aValue); 1678 }; 1679 const auto fallbackToOther = [&]() { return SomeRef(otherVal); }; 1680 1681 Maybe<const UncopyableValue&> mayRef = SomeRef(val); 1682 Maybe<const UncopyableValue&> chainedRef = 1683 mayRef.andThen(passAlong).orElse(fallbackToOther).andThen(passAlong); 1684 MOZ_RELEASE_ASSERT(&val != &otherVal, 1685 "Distinct values should not compare equal"); 1686 MOZ_RELEASE_ASSERT(&*mayRef == &*chainedRef, 1687 "Chain should pass along the same reference"); 1688 } 1689 1690 return true; 1691 } 1692 1693 static bool TestBeginEnd() { 1694 for ([[maybe_unused]] auto v : static_cast<Maybe<int>>(Nothing())) { 1695 MOZ_CRASH("Nothing should not be iterated"); 1696 } 1697 1698 for (auto v : Some(42)) { 1699 MOZ_RELEASE_ASSERT(v == 42, "Some(42) should be iterated"); 1700 } 1701 1702 Maybe<int> maybe; 1703 for ([[maybe_unused]] auto v : maybe) { 1704 MOZ_CRASH("Nothing should not be iterated"); 1705 } 1706 1707 maybe = Some(42); 1708 1709 for (auto v : maybe) { 1710 MOZ_RELEASE_ASSERT(v == 42, "Some(42) should be iterated"); 1711 } 1712 1713 maybe = Nothing(); 1714 1715 for ([[maybe_unused]] auto v : maybe) { 1716 MOZ_CRASH("Nothing should not be iterated"); 1717 } 1718 1719 int v = 42; 1720 Maybe<int*> maybePtr; 1721 for ([[maybe_unused]] auto* v : maybePtr) { 1722 MOZ_CRASH("Nothing should not be iterated"); 1723 } 1724 1725 maybePtr = Some(&v); 1726 1727 for (auto* v : maybePtr) { 1728 MOZ_RELEASE_ASSERT(*v == 42, "Some(address) should be iterated"); 1729 } 1730 1731 maybePtr = Nothing(); 1732 for ([[maybe_unused]] auto* v : maybePtr) { 1733 MOZ_CRASH("Nothing should not be iterated"); 1734 } 1735 1736 return true; 1737 } 1738 1739 // These are quasi-implementation details, but we assert them here to prevent 1740 // backsliding to earlier times when Maybe<T> for smaller T took up more space 1741 // than T's alignment required. 1742 1743 static_assert(sizeof(Maybe<char>) == 2 * sizeof(char), 1744 "Maybe<char> shouldn't bloat at all "); 1745 static_assert(sizeof(Maybe<bool>) <= 2 * sizeof(bool), 1746 "Maybe<bool> shouldn't bloat"); 1747 static_assert(sizeof(Maybe<int>) <= 2 * sizeof(int), 1748 "Maybe<int> shouldn't bloat"); 1749 static_assert(sizeof(Maybe<long>) <= 2 * sizeof(long), 1750 "Maybe<long> shouldn't bloat"); 1751 static_assert(sizeof(Maybe<double>) <= 2 * sizeof(double), 1752 "Maybe<double> shouldn't bloat"); 1753 static_assert(sizeof(Maybe<int&>) == sizeof(int*)); 1754 1755 int main() { 1756 RUN_TEST(TestBasicFeatures); 1757 RUN_TEST(TestCopyAndMove); 1758 RUN_TEST(TestFunctionalAccessors); 1759 RUN_TEST(TestApply); 1760 RUN_TEST(TestMap); 1761 RUN_TEST(TestToMaybe); 1762 RUN_TEST(TestComparisonOperators); 1763 RUN_TEST(TestVirtualFunction); 1764 RUN_TEST(TestSomeNullptrConversion); 1765 RUN_TEST(TestSomePointerConversion); 1766 RUN_TEST(TestTypeConversion); 1767 RUN_TEST(TestReference); 1768 RUN_TEST(TestAndThen); 1769 RUN_TEST(TestOrElse); 1770 RUN_TEST(TestComposedMoves); 1771 RUN_TEST(TestBeginEnd); 1772 1773 return 0; 1774 }