CaseyCarter created this revision.

NOTE: TEST CHANGES ONLY.

These tests will not pass with the current implementation of `variant`, but 
should give the implementor of LWG2904 a head start.

Details:

test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp:

- Make `CopyAssign`'s move operations noexcept so uses in variants continue to 
prefer copy-and-move over direct construction.
- Fix `makeEmpty`: Copy assignment syntax no longer invokes `MakeEmptyT`'s 
throwing move constructor, move assignment syntax still does.
- `test_copy_assignment_sfinae`: `variant<int, CopyOnly>` is now copy 
assignable.
- `test_copy_assignment_different_index`:
  - `CopyThrows` and `MoveThrows` have potentially-throwing move construction, 
so they are now copy constructed directly into variants instead of via indirect 
copy-and-move.
  - implement new `CopyCannotThrow` and verify that direct copy construction of 
such in variants is preferred over indirect copy-and-move when neither method 
throws.

test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp:

- `test_move_assignment_sfinae`: `variant<int, MoveOnly>` is now move 
assignable.

test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp:

- `test_T_assignment_performs_construction`:
  - assigning `ThrowsCtorT` into a variant now constructs a temporary and moves 
into the variant; the variant keeps its old value if the temporary construction 
throws.
  - test that direct construction is preferred to temporary-and-move when 
neither can throw.
  - test that direct construction is preferred to temporary-and-move when both 
can throw.

test/support/variant_test_helpers.hpp:
test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp:
test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp:

- Fix `makeEmpty`: Copy assignment syntax no longer invokes `MakeEmptyT`'s 
throwing move constructor, move assignment syntax still does.


https://reviews.llvm.org/D32515

Files:
  test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp
  test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp
  test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp
  test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp
  test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp
  test/support/variant_test_helpers.hpp

Index: test/support/variant_test_helpers.hpp
===================================================================
--- test/support/variant_test_helpers.hpp
+++ test/support/variant_test_helpers.hpp
@@ -69,9 +69,9 @@
 void makeEmpty(Variant& v) {
     Variant v2(std::in_place_type<MakeEmptyT>);
     try {
-        v = v2;
+        v = std::move(v2);
         assert(false);
-    }  catch (...) {
+    } catch (...) {
         assert(v.valueless_by_exception());
     }
 }
Index: test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp
===================================================================
--- test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp
+++ test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp
@@ -65,7 +65,7 @@
 template <class Variant> void makeEmpty(Variant &v) {
   Variant v2(std::in_place_type<MakeEmptyT>);
   try {
-    v = v2;
+    v = std::move(v2);
     assert(false);
   } catch (...) {
     assert(v.valueless_by_exception());
Index: test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp
===================================================================
--- test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp
+++ test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp
@@ -63,7 +63,7 @@
 template <class Variant> void makeEmpty(Variant &v) {
   Variant v2(std::in_place_type<MakeEmptyT>);
   try {
-    v = v2;
+    v = std::move(v2);
     assert(false);
   } catch (...) {
     assert(v.valueless_by_exception());
Index: test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp
===================================================================
--- test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp
+++ test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp
@@ -117,10 +117,8 @@
     static_assert(std::is_move_assignable<V>::value, "");
   }
   {
-    // variant only provides move assignment when both the move constructor
-    // and move assignment operator are well formed.
     using V = std::variant<int, CopyOnly>;
-    static_assert(!std::is_move_assignable<V>::value, "");
+    static_assert(std::is_move_assignable<V>::value, "");
   }
   {
     using V = std::variant<int, NoCopy>;
Index: test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp
===================================================================
--- test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp
+++ test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp
@@ -66,7 +66,7 @@
     ++alive;
     ++copy_construct;
   }
-  CopyAssign(CopyAssign &&o) : value(o.value) {
+  CopyAssign(CopyAssign &&o) noexcept : value(o.value) {
     o.value = -1;
     ++alive;
     ++move_construct;
@@ -76,7 +76,7 @@
     ++copy_assign;
     return *this;
   }
-  CopyAssign &operator=(CopyAssign &&o) {
+  CopyAssign &operator=(CopyAssign &&o) noexcept {
     value = o.value;
     o.value = -1;
     ++move_assign;
@@ -108,6 +108,17 @@
   CopyThrows &operator=(const CopyThrows &) { throw 42; }
 };
 
+struct CopyCannotThrow {
+  static int alive;
+  CopyCannotThrow() { ++alive; }
+  CopyCannotThrow(const CopyCannotThrow &) noexcept { ++alive; }
+  CopyCannotThrow(CopyCannotThrow &&) noexcept { assert(false); }
+  CopyCannotThrow &operator=(const CopyCannotThrow &) noexcept = default;
+  CopyCannotThrow &operator=(CopyCannotThrow &&) noexcept { assert(false); return *this; }
+};
+
+int CopyCannotThrow::alive = 0;
+
 struct MoveThrows {
   static int alive;
   MoveThrows() { ++alive; }
@@ -139,7 +150,7 @@
 template <class Variant> void makeEmpty(Variant &v) {
   Variant v2(std::in_place_type<MakeEmptyT>);
   try {
-    v = v2;
+    v = std::move(v2);
     assert(false);
   } catch (...) {
     assert(v.valueless_by_exception());
@@ -164,10 +175,8 @@
     static_assert(std::is_copy_assignable<V>::value, "");
   }
   {
-    // variant only provides copy assignment when both the copy and move
-    // constructors are well formed
     using V = std::variant<int, CopyOnly>;
-    static_assert(!std::is_copy_assignable<V>::value, "");
+    static_assert(std::is_copy_assignable<V>::value, "");
   }
   {
     using V = std::variant<int, NoCopy>;
@@ -331,34 +340,39 @@
   }
 #ifndef TEST_HAS_NO_EXCEPTIONS
   {
-    // Test that if copy construction throws then original value is
-    // unchanged.
+    // Test that copy construction is used directly if move construction may throw,
+    // which results in a valueless variant if copy throws.
     using V = std::variant<int, CopyThrows, std::string>;
     V v1(std::in_place_type<std::string>, "hello");
     V v2(std::in_place_type<CopyThrows>);
     try {
       v1 = v2;
       assert(false);
     } catch (...) { /* ... */
     }
-    assert(v1.index() == 2);
-    assert(std::get<2>(v1) == "hello");
+    assert(v1.valueless_by_exception());
   }
   {
-    // Test that if move construction throws then the variant is left
-    // valueless by exception.
+    // Test that copy construction is used directly if move construction may throw.
     using V = std::variant<int, MoveThrows, std::string>;
     V v1(std::in_place_type<std::string>, "hello");
     V v2(std::in_place_type<MoveThrows>);
     assert(MoveThrows::alive == 1);
-    try {
-      v1 = v2;
-      assert(false);
-    } catch (...) { /* ... */
-    }
-    assert(v1.valueless_by_exception());
+    v1 = v2;
+    assert(v1.index() == 1);
     assert(v2.index() == 1);
-    assert(MoveThrows::alive == 1);
+    assert(MoveThrows::alive == 2);
+  }
+  {
+    // Test that direct copy construction is preferred when it cannot throw.
+    using V = std::variant<int, CopyCannotThrow, std::string>;
+    V v1(std::in_place_type<std::string>, "hello");
+    V v2(std::in_place_type<CopyCannotThrow>);
+    assert(CopyCannotThrow::alive == 1);
+    v1 = v2;
+    assert(v1.index() == 1);
+    assert(v2.index() == 1);
+    assert(CopyCannotThrow::alive == 2);
   }
   {
     using V = std::variant<int, CopyThrows, std::string>;
Index: test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp
===================================================================
--- test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp
+++ test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp
@@ -61,6 +61,28 @@
   }
 };
 
+struct MoveCrashes {
+  int value;
+  MoveCrashes(int v = 0) noexcept : value{v} {}
+  MoveCrashes(MoveCrashes &&) noexcept { assert(false); }
+  MoveCrashes &operator=(MoveCrashes &&) noexcept { assert(false); return *this; }
+  MoveCrashes &operator=(int v) noexcept {
+    value = v;
+    return *this;
+  }
+};
+
+struct ThrowsCtorTandMove {
+  int value;
+  ThrowsCtorTandMove() : value(0) {}
+  ThrowsCtorTandMove(int) noexcept(false) { throw 42; }
+  ThrowsCtorTandMove(ThrowsCtorTandMove &&) noexcept(false) { assert(false); }
+  ThrowsCtorTandMove &operator=(int v) noexcept {
+    value = v;
+    return *this;
+  }
+};
+
 struct ThrowsAssignT {
   int value;
   ThrowsAssignT() : value(0) {}
@@ -167,17 +189,39 @@
     V v(std::in_place_type<std::string>, "hello");
     try {
       v = 42;
+      assert(false);
     } catch (...) { /* ... */
     }
-    assert(v.valueless_by_exception());
+    assert(v.index() == 0);
+    assert(std::get<0>(v) == "hello");
   }
   {
     using V = std::variant<ThrowsAssignT, std::string>;
     V v(std::in_place_type<std::string>, "hello");
     v = 42;
     assert(v.index() == 0);
     assert(std::get<0>(v).value == 42);
   }
+  {
+    // Test that nothrow direct construction is preferred to nothrow move.
+    using V = std::variant<MoveCrashes, std::string>;
+    V v(std::in_place_type<std::string>, "hello");
+    v = 42;
+    assert(v.index() == 0);
+    assert(std::get<0>(v).value == 42);
+  }
+  {
+    // Test that throwing direct construction is preferred to copy-and-move when
+    // move can throw.
+    using V = std::variant<ThrowsCtorTandMove, std::string>;
+    V v(std::in_place_type<std::string>, "hello");
+    try {
+      v = 42;
+      assert(false);
+    } catch(...) { /* ... */
+    }
+    assert(v.valueless_by_exception());
+  }
 #endif
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to