diff options
Diffstat (limited to 'third_party/abseil_cpp/absl/types/variant_exception_safety_test.cc')
-rw-r--r-- | third_party/abseil_cpp/absl/types/variant_exception_safety_test.cc | 532 |
1 files changed, 0 insertions, 532 deletions
diff --git a/third_party/abseil_cpp/absl/types/variant_exception_safety_test.cc b/third_party/abseil_cpp/absl/types/variant_exception_safety_test.cc deleted file mode 100644 index 439c6e1df367..000000000000 --- a/third_party/abseil_cpp/absl/types/variant_exception_safety_test.cc +++ /dev/null @@ -1,532 +0,0 @@ -// Copyright 2017 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/types/variant.h" - -#include "absl/base/config.h" - -// This test is a no-op when absl::variant is an alias for std::variant and when -// exceptions are not enabled. -#if !defined(ABSL_USES_STD_VARIANT) && defined(ABSL_HAVE_EXCEPTIONS) - -#include <iostream> -#include <memory> -#include <utility> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/base/internal/exception_safety_testing.h" -#include "absl/memory/memory.h" - -// See comment in absl/base/config.h -#if !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE) - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace { - -using ::testing::MakeExceptionSafetyTester; -using ::testing::strong_guarantee; -using ::testing::TestNothrowOp; -using ::testing::TestThrowingCtor; - -using Thrower = testing::ThrowingValue<>; -using CopyNothrow = testing::ThrowingValue<testing::TypeSpec::kNoThrowCopy>; -using MoveNothrow = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>; -using ThrowingAlloc = testing::ThrowingAllocator<Thrower>; -using ThrowerVec = std::vector<Thrower, ThrowingAlloc>; -using ThrowingVariant = - absl::variant<Thrower, CopyNothrow, MoveNothrow, ThrowerVec>; - -struct ConversionException {}; - -template <class T> -struct ExceptionOnConversion { - operator T() const { // NOLINT - throw ConversionException(); - } -}; - -// Forces a variant into the valueless by exception state. -void ToValuelessByException(ThrowingVariant& v) { // NOLINT - try { - v.emplace<Thrower>(); - v.emplace<Thrower>(ExceptionOnConversion<Thrower>()); - } catch (const ConversionException&) { - // This space intentionally left blank. - } -} - -// Check that variant is still in a usable state after an exception is thrown. -testing::AssertionResult VariantInvariants(ThrowingVariant* v) { - using testing::AssertionFailure; - using testing::AssertionSuccess; - - // Try using the active alternative - if (absl::holds_alternative<Thrower>(*v)) { - auto& t = absl::get<Thrower>(*v); - t = Thrower{-100}; - if (t.Get() != -100) { - return AssertionFailure() << "Thrower should be assigned -100"; - } - } else if (absl::holds_alternative<ThrowerVec>(*v)) { - auto& tv = absl::get<ThrowerVec>(*v); - tv.clear(); - tv.emplace_back(-100); - if (tv.size() != 1 || tv[0].Get() != -100) { - return AssertionFailure() << "ThrowerVec should be {Thrower{-100}}"; - } - } else if (absl::holds_alternative<CopyNothrow>(*v)) { - auto& t = absl::get<CopyNothrow>(*v); - t = CopyNothrow{-100}; - if (t.Get() != -100) { - return AssertionFailure() << "CopyNothrow should be assigned -100"; - } - } else if (absl::holds_alternative<MoveNothrow>(*v)) { - auto& t = absl::get<MoveNothrow>(*v); - t = MoveNothrow{-100}; - if (t.Get() != -100) { - return AssertionFailure() << "MoveNothrow should be assigned -100"; - } - } - - // Try making variant valueless_by_exception - if (!v->valueless_by_exception()) ToValuelessByException(*v); - if (!v->valueless_by_exception()) { - return AssertionFailure() << "Variant should be valueless_by_exception"; - } - try { - auto unused = absl::get<Thrower>(*v); - static_cast<void>(unused); - return AssertionFailure() << "Variant should not contain Thrower"; - } catch (const absl::bad_variant_access&) { - } catch (...) { - return AssertionFailure() << "Unexpected exception throw from absl::get"; - } - - // Try using the variant - v->emplace<Thrower>(100); - if (!absl::holds_alternative<Thrower>(*v) || - absl::get<Thrower>(*v) != Thrower(100)) { - return AssertionFailure() << "Variant should contain Thrower(100)"; - } - v->emplace<ThrowerVec>({Thrower(100)}); - if (!absl::holds_alternative<ThrowerVec>(*v) || - absl::get<ThrowerVec>(*v)[0] != Thrower(100)) { - return AssertionFailure() - << "Variant should contain ThrowerVec{Thrower(100)}"; - } - return AssertionSuccess(); -} - -template <typename... Args> -Thrower ExpectedThrower(Args&&... args) { - return Thrower(42, args...); -} - -ThrowerVec ExpectedThrowerVec() { return {Thrower(100), Thrower(200)}; } -ThrowingVariant ValuelessByException() { - ThrowingVariant v; - ToValuelessByException(v); - return v; -} -ThrowingVariant WithThrower() { return Thrower(39); } -ThrowingVariant WithThrowerVec() { - return ThrowerVec{Thrower(1), Thrower(2), Thrower(3)}; -} -ThrowingVariant WithCopyNoThrow() { return CopyNothrow(39); } -ThrowingVariant WithMoveNoThrow() { return MoveNothrow(39); } - -TEST(VariantExceptionSafetyTest, DefaultConstructor) { - TestThrowingCtor<ThrowingVariant>(); -} - -TEST(VariantExceptionSafetyTest, CopyConstructor) { - { - ThrowingVariant v(ExpectedThrower()); - TestThrowingCtor<ThrowingVariant>(v); - } - { - ThrowingVariant v(ExpectedThrowerVec()); - TestThrowingCtor<ThrowingVariant>(v); - } - { - ThrowingVariant v(ValuelessByException()); - TestThrowingCtor<ThrowingVariant>(v); - } -} - -TEST(VariantExceptionSafetyTest, MoveConstructor) { - { - ThrowingVariant v(ExpectedThrower()); - TestThrowingCtor<ThrowingVariant>(std::move(v)); - } - { - ThrowingVariant v(ExpectedThrowerVec()); - TestThrowingCtor<ThrowingVariant>(std::move(v)); - } - { - ThrowingVariant v(ValuelessByException()); - TestThrowingCtor<ThrowingVariant>(std::move(v)); - } -} - -TEST(VariantExceptionSafetyTest, ValueConstructor) { - TestThrowingCtor<ThrowingVariant>(ExpectedThrower()); - TestThrowingCtor<ThrowingVariant>(ExpectedThrowerVec()); -} - -TEST(VariantExceptionSafetyTest, InPlaceTypeConstructor) { - TestThrowingCtor<ThrowingVariant>(absl::in_place_type_t<Thrower>{}, - ExpectedThrower()); - TestThrowingCtor<ThrowingVariant>(absl::in_place_type_t<ThrowerVec>{}, - ExpectedThrowerVec()); -} - -TEST(VariantExceptionSafetyTest, InPlaceIndexConstructor) { - TestThrowingCtor<ThrowingVariant>(absl::in_place_index_t<0>{}, - ExpectedThrower()); - TestThrowingCtor<ThrowingVariant>(absl::in_place_index_t<3>{}, - ExpectedThrowerVec()); -} - -TEST(VariantExceptionSafetyTest, CopyAssign) { - // variant& operator=(const variant& rhs); - // Let j be rhs.index() - { - // - neither *this nor rhs holds a value - const ThrowingVariant rhs = ValuelessByException(); - ThrowingVariant lhs = ValuelessByException(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs = rhs; })); - } - { - // - *this holds a value but rhs does not - const ThrowingVariant rhs = ValuelessByException(); - ThrowingVariant lhs = WithThrower(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs = rhs; })); - } - // - index() == j - { - const ThrowingVariant rhs(ExpectedThrower()); - auto tester = - MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }); - EXPECT_TRUE(tester.WithContracts(VariantInvariants).Test()); - EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test()); - } - { - const ThrowingVariant rhs(ExpectedThrowerVec()); - auto tester = - MakeExceptionSafetyTester() - .WithInitialValue(WithThrowerVec()) - .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }); - EXPECT_TRUE(tester.WithContracts(VariantInvariants).Test()); - EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test()); - } - // libstdc++ std::variant has bugs on copy assignment regarding exception - // safety. -#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) - // index() != j - // if is_nothrow_copy_constructible_v<Tj> or - // !is_nothrow_move_constructible<Tj> is true, equivalent to - // emplace<j>(get<j>(rhs)) - { - // is_nothrow_copy_constructible_v<Tj> == true - // should not throw because emplace() invokes Tj's copy ctor - // which should not throw. - const ThrowingVariant rhs(CopyNothrow{}); - ThrowingVariant lhs = WithThrower(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs = rhs; })); - } - { - // is_nothrow_copy_constructible<Tj> == false && - // is_nothrow_move_constructible<Tj> == false - // should provide basic guarantee because emplace() invokes Tj's copy ctor - // which may throw. - const ThrowingVariant rhs(ExpectedThrower()); - auto tester = - MakeExceptionSafetyTester() - .WithInitialValue(WithCopyNoThrow()) - .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }); - EXPECT_TRUE(tester - .WithContracts(VariantInvariants, - [](ThrowingVariant* lhs) { - return lhs->valueless_by_exception(); - }) - .Test()); - EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test()); - } -#endif // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) - { - // is_nothrow_copy_constructible_v<Tj> == false && - // is_nothrow_move_constructible_v<Tj> == true - // should provide strong guarantee because it is equivalent to - // operator=(variant(rhs)) which creates a temporary then invoke the move - // ctor which shouldn't throw. - const ThrowingVariant rhs(MoveNothrow{}); - EXPECT_TRUE(MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithContracts(VariantInvariants, strong_guarantee) - .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; })); - } -} - -TEST(VariantExceptionSafetyTest, MoveAssign) { - // variant& operator=(variant&& rhs); - // Let j be rhs.index() - { - // - neither *this nor rhs holds a value - ThrowingVariant rhs = ValuelessByException(); - ThrowingVariant lhs = ValuelessByException(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs = std::move(rhs); })); - } - { - // - *this holds a value but rhs does not - ThrowingVariant rhs = ValuelessByException(); - ThrowingVariant lhs = WithThrower(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs = std::move(rhs); })); - } - { - // - index() == j - // assign get<j>(std::move(rhs)) to the value contained in *this. - // If an exception is thrown during call to Tj's move assignment, the state - // of the contained value is as defined by the exception safety guarantee of - // Tj's move assignment; index() will be j. - ThrowingVariant rhs(ExpectedThrower()); - size_t j = rhs.index(); - // Since Thrower's move assignment has basic guarantee, so should variant's. - auto tester = MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithOperation([&](ThrowingVariant* lhs) { - auto copy = rhs; - *lhs = std::move(copy); - }); - EXPECT_TRUE(tester - .WithContracts( - VariantInvariants, - [&](ThrowingVariant* lhs) { return lhs->index() == j; }) - .Test()); - EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test()); - } - { - // libstdc++ introduced a regression between 2018-09-25 and 2019-01-06. - // The fix is targeted for gcc-9. - // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87431#c7 - // https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=267614 -#if !(defined(ABSL_USES_STD_VARIANT) && \ - defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8) - // - otherwise (index() != j), equivalent to - // emplace<j>(get<j>(std::move(rhs))) - // - If an exception is thrown during the call to Tj's move construction - // (with j being rhs.index()), the variant will hold no value. - ThrowingVariant rhs(CopyNothrow{}); - EXPECT_TRUE(MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithContracts(VariantInvariants, - [](ThrowingVariant* lhs) { - return lhs->valueless_by_exception(); - }) - .Test([&](ThrowingVariant* lhs) { - auto copy = rhs; - *lhs = std::move(copy); - })); -#endif // !(defined(ABSL_USES_STD_VARIANT) && - // defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8) - } -} - -TEST(VariantExceptionSafetyTest, ValueAssign) { - // template<class T> variant& operator=(T&& t); - // Let Tj be the type that is selected by overload resolution to be assigned. - { - // If *this holds a Tj, assigns std::forward<T>(t) to the value contained in - // *this. If an exception is thrown during the assignment of - // std::forward<T>(t) to the value contained in *this, the state of the - // contained value and t are as defined by the exception safety guarantee of - // the assignment expression; valueless_by_exception() will be false. - // Since Thrower's copy/move assignment has basic guarantee, so should - // variant's. - Thrower rhs = ExpectedThrower(); - // copy assign - auto copy_tester = - MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithOperation([rhs](ThrowingVariant* lhs) { *lhs = rhs; }); - EXPECT_TRUE(copy_tester - .WithContracts(VariantInvariants, - [](ThrowingVariant* lhs) { - return !lhs->valueless_by_exception(); - }) - .Test()); - EXPECT_FALSE(copy_tester.WithContracts(strong_guarantee).Test()); - // move assign - auto move_tester = MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithOperation([&](ThrowingVariant* lhs) { - auto copy = rhs; - *lhs = std::move(copy); - }); - EXPECT_TRUE(move_tester - .WithContracts(VariantInvariants, - [](ThrowingVariant* lhs) { - return !lhs->valueless_by_exception(); - }) - .Test()); - - EXPECT_FALSE(move_tester.WithContracts(strong_guarantee).Test()); - } - // Otherwise (*this holds something else), if is_nothrow_constructible_v<Tj, - // T> || !is_nothrow_move_constructible_v<Tj> is true, equivalent to - // emplace<j>(std::forward<T>(t)). - // We simplify the test by letting T = `const Tj&` or `Tj&&`, so we can reuse - // the CopyNothrow and MoveNothrow types. - - // if is_nothrow_constructible_v<Tj, T> - // (i.e. is_nothrow_copy/move_constructible_v<Tj>) is true, emplace() just - // invokes the copy/move constructor and it should not throw. - { - const CopyNothrow rhs; - ThrowingVariant lhs = WithThrower(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs = rhs; })); - } - { - MoveNothrow rhs; - ThrowingVariant lhs = WithThrower(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs = std::move(rhs); })); - } - // if is_nothrow_constructible_v<Tj, T> == false && - // is_nothrow_move_constructible<Tj> == false - // emplace() invokes the copy/move constructor which may throw so it should - // provide basic guarantee and variant object might not hold a value. - { - Thrower rhs = ExpectedThrower(); - // copy - auto copy_tester = - MakeExceptionSafetyTester() - .WithInitialValue(WithCopyNoThrow()) - .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }); - EXPECT_TRUE(copy_tester - .WithContracts(VariantInvariants, - [](ThrowingVariant* lhs) { - return lhs->valueless_by_exception(); - }) - .Test()); - EXPECT_FALSE(copy_tester.WithContracts(strong_guarantee).Test()); - // move - auto move_tester = MakeExceptionSafetyTester() - .WithInitialValue(WithCopyNoThrow()) - .WithOperation([](ThrowingVariant* lhs) { - *lhs = ExpectedThrower(testing::nothrow_ctor); - }); - EXPECT_TRUE(move_tester - .WithContracts(VariantInvariants, - [](ThrowingVariant* lhs) { - return lhs->valueless_by_exception(); - }) - .Test()); - EXPECT_FALSE(move_tester.WithContracts(strong_guarantee).Test()); - } - // Otherwise (if is_nothrow_constructible_v<Tj, T> == false && - // is_nothrow_move_constructible<Tj> == true), - // equivalent to operator=(variant(std::forward<T>(t))) - // This should have strong guarantee because it creates a temporary variant - // and operator=(variant&&) invokes Tj's move ctor which doesn't throw. - // libstdc++ std::variant has bugs on conversion assignment regarding - // exception safety. -#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) - { - MoveNothrow rhs; - EXPECT_TRUE(MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithContracts(VariantInvariants, strong_guarantee) - .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; })); - } -#endif // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) -} - -TEST(VariantExceptionSafetyTest, Emplace) { - // If an exception during the initialization of the contained value, the - // variant might not hold a value. The standard requires emplace() to provide - // only basic guarantee. - { - Thrower args = ExpectedThrower(); - auto tester = MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithOperation([&args](ThrowingVariant* v) { - v->emplace<Thrower>(args); - }); - EXPECT_TRUE(tester - .WithContracts(VariantInvariants, - [](ThrowingVariant* v) { - return v->valueless_by_exception(); - }) - .Test()); - EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test()); - } -} - -TEST(VariantExceptionSafetyTest, Swap) { - // if both are valueless_by_exception(), no effect - { - ThrowingVariant rhs = ValuelessByException(); - ThrowingVariant lhs = ValuelessByException(); - EXPECT_TRUE(TestNothrowOp([&]() { lhs.swap(rhs); })); - } - // if index() == rhs.index(), calls swap(get<i>(*this), get<i>(rhs)) - // where i is index(). - { - ThrowingVariant rhs = ExpectedThrower(); - EXPECT_TRUE(MakeExceptionSafetyTester() - .WithInitialValue(WithThrower()) - .WithContracts(VariantInvariants) - .Test([&](ThrowingVariant* lhs) { - auto copy = rhs; - lhs->swap(copy); - })); - } - // Otherwise, exchanges the value of rhs and *this. The exception safety - // involves variant in moved-from state which is not specified in the - // standard, and since swap is 3-step it's impossible for it to provide a - // overall strong guarantee. So, we are only checking basic guarantee here. - { - ThrowingVariant rhs = ExpectedThrower(); - EXPECT_TRUE(MakeExceptionSafetyTester() - .WithInitialValue(WithCopyNoThrow()) - .WithContracts(VariantInvariants) - .Test([&](ThrowingVariant* lhs) { - auto copy = rhs; - lhs->swap(copy); - })); - } - { - ThrowingVariant rhs = ExpectedThrower(); - EXPECT_TRUE(MakeExceptionSafetyTester() - .WithInitialValue(WithCopyNoThrow()) - .WithContracts(VariantInvariants) - .Test([&](ThrowingVariant* lhs) { - auto copy = rhs; - copy.swap(*lhs); - })); - } -} - -} // namespace -ABSL_NAMESPACE_END -} // namespace absl - -#endif // !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE) - -#endif // #if !defined(ABSL_USES_STD_VARIANT) && defined(ABSL_HAVE_EXCEPTIONS) |