[libc++][nfc] SFINAE on pair/tuple assignment operators: LWG 2729.
authorzoecarver <z.zoelec2@gmail.com>
Fri, 19 Feb 2021 21:24:30 +0000 (13:24 -0800)
committerzoecarver <z.zoelec2@gmail.com>
Fri, 19 Feb 2021 21:25:34 +0000 (13:25 -0800)
This patch ensures that SFINAE is used to delete assignment operators in pair and tuple based on issue 2729.

Differential Review: https://reviews.llvm.org/D62454

libcxx/docs/Cxx1zStatusIssuesStatus.csv
libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp
libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp
libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp
libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp
libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp
libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp
libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_pair_U_V.pass.cpp
libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_pair.pass.cpp
libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_rv_pair.pass.cpp
libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_rv_pair_U_V.pass.cpp

index 91fa599..383c3af 100644 (file)
 "`2699 <https://wg21.link/LWG2699>`__","Missing restriction in [numeric.requirements]","Issaquah","|Complete|",""
 "`2712 <https://wg21.link/LWG2712>`__","copy_file(from, to, ...) has a number of unspecified error conditions","Issaquah","|Complete|",""
 "`2722 <https://wg21.link/LWG2722>`__","equivalent incorrectly specifies throws clause","Issaquah","|Complete|",""
-"`2729 <https://wg21.link/LWG2729>`__","Missing SFINAE on std::pair::operator=","Issaquah","",""
+"`2729 <https://wg21.link/LWG2729>`__","Missing SFINAE on std::pair::operator=","Issaquah","|Complete|",""
 "`2732 <https://wg21.link/LWG2732>`__","Questionable specification of path::operator/= and path::append","Issaquah","|Complete|",""
 "`2733 <https://wg21.link/LWG2733>`__","[fund.ts.v2] gcd / lcm and bool","Issaquah","|Complete|",""
 "`2735 <https://wg21.link/LWG2735>`__","std::abs(short), std::abs(signed char) and others should return int instead of double in order to be compatible with C++98 and C","Issaquah","|Complete|",""
index 41940ec..8edb818 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <tuple>
 #include <utility>
+#include <memory>
 #include <cassert>
 
 #include "test_macros.h"
@@ -32,6 +33,13 @@ int main(int, char**)
         assert(std::get<0>(t1) == 2);
         assert(std::get<1>(t1) == short('a'));
     }
+    {
+        // test that the implicitly generated copy assignment operator
+        // is properly deleted
+        using T = std::tuple<int, int>;
+        using P = std::tuple<std::unique_ptr<int>, std::unique_ptr<int>>;
+        static_assert(!std::is_assignable<T, const P &>::value, "");
+    }
 
   return 0;
 }
index 8803f29..e02fc84 100644 (file)
@@ -34,6 +34,11 @@ struct D
     explicit D(int i = 0) : B(i) {}
 };
 
+struct NonAssignable {
+  NonAssignable& operator=(NonAssignable const&) = delete;
+  NonAssignable& operator=(NonAssignable&&) = delete;
+};
+
 int main(int, char**)
 {
     {
@@ -87,6 +92,12 @@ int main(int, char**)
         assert(std::get<0>(t) == 43);
         assert(&std::get<0>(t) == &x);
     }
+    {
+      using T = std::tuple<int, NonAssignable>;
+      using U = std::tuple<NonAssignable, int>;
+      static_assert(!std::is_assignable<T, U const&>::value, "");
+      static_assert(!std::is_assignable<U, T const&>::value, "");
+    }
 
   return 0;
 }
index ec51c21..39bcc5d 100644 (file)
@@ -45,6 +45,11 @@ struct E {
   }
 };
 
+struct NonAssignable {
+  NonAssignable& operator=(NonAssignable const&) = delete;
+  NonAssignable& operator=(NonAssignable&&) = delete;
+};
+
 int main(int, char**)
 {
     {
@@ -108,6 +113,12 @@ int main(int, char**)
         assert(std::get<0>(t) == 43);
         assert(&std::get<0>(t) == &x);
     }
+    {
+      using T = std::tuple<int, NonAssignable>;
+      using U = std::tuple<NonAssignable, int>;
+      static_assert(!std::is_assignable<T, U&&>::value, "");
+      static_assert(!std::is_assignable<U, T&&>::value, "");
+    }
 
   return 0;
 }
index 9111133..bbccd67 100644 (file)
@@ -35,6 +35,10 @@ struct MoveAssignable {
   MoveAssignable& operator=(MoveAssignable&&) = default;
 };
 
+struct CopyAssignableInt {
+  CopyAssignableInt& operator=(int&) { return *this; }
+};
+
 int main(int, char**)
 {
     {
@@ -89,8 +93,8 @@ int main(int, char**)
         static_assert(!std::is_copy_assignable<T>::value, "");
     }
     {
-        using T = std::tuple<int, NonAssignable>;
-        static_assert(!std::is_copy_assignable<T>::value, "");
+      using T = std::tuple<int, NonAssignable>;
+      static_assert(!std::is_copy_assignable<T>::value, "");
     }
     {
         using T = std::tuple<int, CopyAssignable>;
@@ -100,6 +104,21 @@ int main(int, char**)
         using T = std::tuple<int, MoveAssignable>;
         static_assert(!std::is_copy_assignable<T>::value, "");
     }
+    {
+      using T = std::tuple<int, int, int>;
+      using P = std::pair<int, int>;
+      static_assert(!std::is_assignable<T, P>::value, "");
+    }
+    { // test const requirement
+      using T = std::tuple<CopyAssignableInt, CopyAssignableInt>;
+      using P = std::pair<int, int>;
+      static_assert(!std::is_assignable<T&, P const>::value, "");
+    }
+    {
+      using T = std::tuple<int, MoveAssignable>;
+      using P = std::pair<int, MoveAssignable>;
+      static_assert(!std::is_assignable<T&, P&>::value, "");
+    }
 
   return 0;
 }
index e642f4a..292cfc2 100644 (file)
@@ -105,8 +105,8 @@ int main(int, char**)
 
     }
     {
-        using T = std::tuple<int, NonAssignable>;
-        static_assert(!std::is_move_assignable<T>::value, "");
+      using T = std::tuple<int, NonAssignable>;
+      static_assert(!std::is_move_assignable<T>::value, "");
     }
     {
         using T = std::tuple<int, MoveAssignable>;
index c60927f..20315c0 100644 (file)
@@ -37,6 +37,11 @@ struct D
     explicit D(int i) : B(i) {}
 };
 
+struct NonMoveAssignable {
+  NonMoveAssignable& operator=(NonMoveAssignable const&) = default;
+  NonMoveAssignable& operator=(NonMoveAssignable&&) = delete;
+};
+
 int main(int, char**)
 {
     {
@@ -48,6 +53,16 @@ int main(int, char**)
         assert(std::get<0>(t1) == 2);
         assert(std::get<1>(t1)->id_ == 3);
     }
+    {
+      using T = std::tuple<int, NonMoveAssignable>;
+      using P = std::pair<int, NonMoveAssignable>;
+      static_assert(!std::is_assignable<T&, P&&>::value, "");
+    }
+    {
+      using T = std::tuple<int, int, int>;
+      using P = std::pair<int, int>;
+      static_assert(!std::is_assignable<T&, P&&>::value, "");
+    }
 
   return 0;
 }
index becf36e..fab9f64 100644 (file)
 #include "archetypes.h"
 #endif
 
+struct CopyAssignableInt {
+  CopyAssignableInt& operator=(int&) { return *this; }
+};
+
+struct Unrelated {};
+
 TEST_CONSTEXPR_CXX20 bool test() {
   {
     typedef std::pair<int, short> P1;
@@ -58,6 +64,18 @@ TEST_CONSTEXPR_CXX20 bool test() {
     assert(p.second.value == -42);
   }
 #endif
+
+  { // test const requirement
+    using T = std::pair<CopyAssignableInt, CopyAssignableInt>;
+    using P = std::pair<int, int>;
+    static_assert(!std::is_assignable<T&, P const>::value, "");
+  }
+  {
+    using T = std::pair<int, Unrelated>;
+    using P = std::pair<Unrelated, int>;
+    static_assert(!std::is_assignable<T&, P&>::value, "");
+    static_assert(!std::is_assignable<P&, T&>::value, "");
+  }
   return true;
 }
 
index 94e30ae..253bd1b 100644 (file)
@@ -78,6 +78,10 @@ TEST_CONSTEXPR_CXX20 bool test() {
     static_assert(!std::is_copy_assignable<P>::value, "");
   }
   {
+    using P = std::pair<int, std::unique_ptr<int> >;
+    static_assert(!std::is_copy_assignable<P>::value, "");
+  }
+  {
     using P = std::pair<int, Incomplete&>;
     static_assert(!std::is_copy_assignable<P>::value, "");
     P p(42, inc_obj);
index cc4e4f5..7dbf98f 100644 (file)
@@ -35,6 +35,11 @@ struct CountAssign {
   }
 };
 
+struct NotAssignable {
+  NotAssignable& operator=(NotAssignable const&) = delete;
+  NotAssignable& operator=(NotAssignable&&) = delete;
+};
+
 TEST_CONSTEXPR_CXX20 bool test() {
   {
     typedef std::pair<ConstexprTestTypes::MoveOnly, int> P;
@@ -83,6 +88,11 @@ TEST_CONSTEXPR_CXX20 bool test() {
     assert(p2.first.moved == 0);
     assert(p2.first.copied == 0);
   }
+  {
+    using T = std::pair<int, NotAssignable>;
+    using P = std::pair<int, NotAssignable>;
+    static_assert(!std::is_assignable<T, P&&>::value, "");
+  }
   return true;
 }
 
index 7267f7a..cd0c39a 100644 (file)
@@ -40,6 +40,10 @@ struct CountAssign {
   }
 };
 
+struct CopyAssignableInt {
+  CopyAssignableInt& operator=(int&) { return *this; }
+};
+
 TEST_CONSTEXPR_CXX20 bool test() {
   {
     typedef std::pair<Derived, short> P1;
@@ -61,6 +65,12 @@ TEST_CONSTEXPR_CXX20 bool test() {
     assert(t.second.moved == 0);
     assert(t.second.copied == 0);
   }
+  { // test const requirement
+    using T = std::pair<CopyAssignableInt, CopyAssignableInt>;
+    using P = std::pair<int, int>;
+    static_assert(!std::is_assignable<T, P&&>::value, "");
+    static_assert(!std::is_assignable<P, T&&>::value, "");
+  }
   return true;
 }