|  | @@ -0,0 +1,519 @@
 | 
	
		
			
				|  |  | +// 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
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +//      http://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 <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"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +namespace absl {
 | 
	
		
			
				|  |  | +namespace {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +using ::testing::MakeExceptionSafetyTester;
 | 
	
		
			
				|  |  | +using ::testing::nothrow_guarantee;
 | 
	
		
			
				|  |  | +using ::testing::strong_guarantee;
 | 
	
		
			
				|  |  | +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 (ConversionException& /*e*/) {
 | 
	
		
			
				|  |  | +    // This space intentionally left blank.
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Check that variant is still in a usable state after an exception is thrown.
 | 
	
		
			
				|  |  | +testing::AssertionResult CheckInvariants(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 (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();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +Thrower ExpectedThrower() { return Thrower(42); }
 | 
	
		
			
				|  |  | +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();
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(MakeExceptionSafetyTester()
 | 
	
		
			
				|  |  | +                    .WithInitialValue(ValuelessByException())
 | 
	
		
			
				|  |  | +                    .WithInvariants(nothrow_guarantee)
 | 
	
		
			
				|  |  | +                    .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +    // - *this holds a value but rhs does not
 | 
	
		
			
				|  |  | +    const ThrowingVariant rhs = ValuelessByException();
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(MakeExceptionSafetyTester()
 | 
	
		
			
				|  |  | +                    .WithInitialValue(WithThrower())
 | 
	
		
			
				|  |  | +                    .WithInvariants(nothrow_guarantee)
 | 
	
		
			
				|  |  | +                    .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  // - index() == j
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +    const ThrowingVariant rhs(ExpectedThrower());
 | 
	
		
			
				|  |  | +    auto tester =
 | 
	
		
			
				|  |  | +        MakeExceptionSafetyTester()
 | 
	
		
			
				|  |  | +            .WithInitialValue(WithThrower())
 | 
	
		
			
				|  |  | +            .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(tester.WithInvariants(CheckInvariants).Test());
 | 
	
		
			
				|  |  | +    EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +    const ThrowingVariant rhs(ExpectedThrowerVec());
 | 
	
		
			
				|  |  | +    auto tester =
 | 
	
		
			
				|  |  | +        MakeExceptionSafetyTester()
 | 
	
		
			
				|  |  | +            .WithInitialValue(WithThrowerVec())
 | 
	
		
			
				|  |  | +            .WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; });
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(tester.WithInvariants(CheckInvariants).Test());
 | 
	
		
			
				|  |  | +    EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  // libstdc++ std::variant has bugs on copy assignment regarding exception
 | 
	
		
			
				|  |  | +  // safety.
 | 
	
		
			
				|  |  | +#if !(defined(ABSL_HAVE_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{});
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(MakeExceptionSafetyTester()
 | 
	
		
			
				|  |  | +                    .WithInitialValue(WithThrower())
 | 
	
		
			
				|  |  | +                    .WithInvariants(nothrow_guarantee)
 | 
	
		
			
				|  |  | +                    .Test([&rhs](ThrowingVariant* lhs) { *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
 | 
	
		
			
				|  |  | +                    .WithInvariants(CheckInvariants,
 | 
	
		
			
				|  |  | +                                    [](ThrowingVariant* lhs) {
 | 
	
		
			
				|  |  | +                                      return lhs->valueless_by_exception();
 | 
	
		
			
				|  |  | +                                    })
 | 
	
		
			
				|  |  | +                    .Test());
 | 
	
		
			
				|  |  | +    EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +#endif  // !(defined(ABSL_HAVE_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())
 | 
	
		
			
				|  |  | +                    .WithInvariants(CheckInvariants, 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();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(MakeExceptionSafetyTester()
 | 
	
		
			
				|  |  | +                    .WithInitialValue(ValuelessByException())
 | 
	
		
			
				|  |  | +                    .WithInvariants(nothrow_guarantee)
 | 
	
		
			
				|  |  | +                    .Test([rhs](ThrowingVariant* lhs) mutable {
 | 
	
		
			
				|  |  | +                      *lhs = std::move(rhs);
 | 
	
		
			
				|  |  | +                    }));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +    // - *this holds a value but rhs does not
 | 
	
		
			
				|  |  | +    ThrowingVariant rhs = ValuelessByException();
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(MakeExceptionSafetyTester()
 | 
	
		
			
				|  |  | +                    .WithInitialValue(WithThrower())
 | 
	
		
			
				|  |  | +                    .WithInvariants(nothrow_guarantee)
 | 
	
		
			
				|  |  | +                    .Test([rhs](ThrowingVariant* lhs) mutable {
 | 
	
		
			
				|  |  | +                      *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([rhs](ThrowingVariant* lhs) mutable {
 | 
	
		
			
				|  |  | +                        *lhs = std::move(rhs);
 | 
	
		
			
				|  |  | +                      });
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(tester
 | 
	
		
			
				|  |  | +                    .WithInvariants(
 | 
	
		
			
				|  |  | +                        CheckInvariants,
 | 
	
		
			
				|  |  | +                        [j](ThrowingVariant* lhs) { return lhs->index() == j; })
 | 
	
		
			
				|  |  | +                    .Test());
 | 
	
		
			
				|  |  | +    EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +    // - 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())
 | 
	
		
			
				|  |  | +                    .WithInvariants(CheckInvariants,
 | 
	
		
			
				|  |  | +                                    [](ThrowingVariant* lhs) {
 | 
	
		
			
				|  |  | +                                      return lhs->valueless_by_exception();
 | 
	
		
			
				|  |  | +                                    })
 | 
	
		
			
				|  |  | +                    .Test([rhs](ThrowingVariant* lhs) mutable {
 | 
	
		
			
				|  |  | +                      *lhs = std::move(rhs);
 | 
	
		
			
				|  |  | +                    }));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +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
 | 
	
		
			
				|  |  | +                    .WithInvariants(CheckInvariants,
 | 
	
		
			
				|  |  | +                                    [](ThrowingVariant* lhs) {
 | 
	
		
			
				|  |  | +                                      return !lhs->valueless_by_exception();
 | 
	
		
			
				|  |  | +                                    })
 | 
	
		
			
				|  |  | +                    .Test());
 | 
	
		
			
				|  |  | +    EXPECT_FALSE(copy_tester.WithInvariants(strong_guarantee).Test());
 | 
	
		
			
				|  |  | +    // move assign
 | 
	
		
			
				|  |  | +    auto move_tester = MakeExceptionSafetyTester()
 | 
	
		
			
				|  |  | +                           .WithInitialValue(WithThrower())
 | 
	
		
			
				|  |  | +                           .WithOperation([rhs](ThrowingVariant* lhs) mutable {
 | 
	
		
			
				|  |  | +                             *lhs = std::move(rhs);
 | 
	
		
			
				|  |  | +                           });
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(move_tester
 | 
	
		
			
				|  |  | +                    .WithInvariants(CheckInvariants,
 | 
	
		
			
				|  |  | +                                    [](ThrowingVariant* lhs) {
 | 
	
		
			
				|  |  | +                                      return !lhs->valueless_by_exception();
 | 
	
		
			
				|  |  | +                                    })
 | 
	
		
			
				|  |  | +                    .Test());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    EXPECT_FALSE(move_tester.WithInvariants(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;
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(MakeExceptionSafetyTester()
 | 
	
		
			
				|  |  | +                    .WithInitialValue(WithThrower())
 | 
	
		
			
				|  |  | +                    .WithInvariants(nothrow_guarantee)
 | 
	
		
			
				|  |  | +                    .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +    MoveNothrow rhs;
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(MakeExceptionSafetyTester()
 | 
	
		
			
				|  |  | +                    .WithInitialValue(WithThrower())
 | 
	
		
			
				|  |  | +                    .WithInvariants(nothrow_guarantee)
 | 
	
		
			
				|  |  | +                    .Test([rhs](ThrowingVariant* lhs) mutable {
 | 
	
		
			
				|  |  | +                      *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
 | 
	
		
			
				|  |  | +                    .WithInvariants(CheckInvariants,
 | 
	
		
			
				|  |  | +                                    [](ThrowingVariant* lhs) {
 | 
	
		
			
				|  |  | +                                      return lhs->valueless_by_exception();
 | 
	
		
			
				|  |  | +                                    })
 | 
	
		
			
				|  |  | +                    .Test());
 | 
	
		
			
				|  |  | +    EXPECT_FALSE(copy_tester.WithInvariants(strong_guarantee).Test());
 | 
	
		
			
				|  |  | +    // move
 | 
	
		
			
				|  |  | +    auto move_tester = MakeExceptionSafetyTester()
 | 
	
		
			
				|  |  | +                           .WithInitialValue(WithCopyNoThrow())
 | 
	
		
			
				|  |  | +                           .WithOperation([rhs](ThrowingVariant* lhs) mutable {
 | 
	
		
			
				|  |  | +                             *lhs = std::move(rhs);
 | 
	
		
			
				|  |  | +                           });
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(move_tester
 | 
	
		
			
				|  |  | +                    .WithInvariants(CheckInvariants,
 | 
	
		
			
				|  |  | +                                    [](ThrowingVariant* lhs) {
 | 
	
		
			
				|  |  | +                                      return lhs->valueless_by_exception();
 | 
	
		
			
				|  |  | +                                    })
 | 
	
		
			
				|  |  | +                    .Test());
 | 
	
		
			
				|  |  | +    EXPECT_FALSE(move_tester.WithInvariants(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_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +    MoveNothrow rhs;
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(MakeExceptionSafetyTester()
 | 
	
		
			
				|  |  | +                    .WithInitialValue(WithThrower())
 | 
	
		
			
				|  |  | +                    .WithInvariants(CheckInvariants, strong_guarantee)
 | 
	
		
			
				|  |  | +                    .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +#endif  // !(defined(ABSL_HAVE_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
 | 
	
		
			
				|  |  | +                    .WithInvariants(CheckInvariants,
 | 
	
		
			
				|  |  | +                                    [](ThrowingVariant* v) {
 | 
	
		
			
				|  |  | +                                      return v->valueless_by_exception();
 | 
	
		
			
				|  |  | +                                    })
 | 
	
		
			
				|  |  | +                    .Test());
 | 
	
		
			
				|  |  | +    EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +TEST(VariantExceptionSafetyTest, Swap) {
 | 
	
		
			
				|  |  | +  // if both are valueless_by_exception(), no effect
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +    ThrowingVariant rhs = ValuelessByException();
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(
 | 
	
		
			
				|  |  | +        MakeExceptionSafetyTester()
 | 
	
		
			
				|  |  | +            .WithInitialValue(ValuelessByException())
 | 
	
		
			
				|  |  | +            .WithInvariants(nothrow_guarantee)
 | 
	
		
			
				|  |  | +            .Test([rhs](ThrowingVariant* lhs) mutable { 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())
 | 
	
		
			
				|  |  | +            .WithInvariants(CheckInvariants)
 | 
	
		
			
				|  |  | +            .Test([rhs](ThrowingVariant* lhs) mutable { lhs->swap(rhs); }));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  // 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())
 | 
	
		
			
				|  |  | +            .WithInvariants(CheckInvariants)
 | 
	
		
			
				|  |  | +            .Test([rhs](ThrowingVariant* lhs) mutable { lhs->swap(rhs); }));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +    ThrowingVariant rhs = ExpectedThrower();
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(
 | 
	
		
			
				|  |  | +        MakeExceptionSafetyTester()
 | 
	
		
			
				|  |  | +            .WithInitialValue(WithCopyNoThrow())
 | 
	
		
			
				|  |  | +            .WithInvariants(CheckInvariants)
 | 
	
		
			
				|  |  | +            .Test([rhs](ThrowingVariant* lhs) mutable { rhs.swap(*lhs); }));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +}  // namespace
 | 
	
		
			
				|  |  | +}  // namespace absl
 |