This revision was not accepted when it landed; it landed in state "Needs 
Review".
This revision was automatically updated to reflect the committed changes.
Closed by commit rGa0839b14df6d: [libc++] Fix tuple assignment from types 
derived from a tuple-like (authored by ldionne).

Changed prior to commit:
  https://reviews.llvm.org/D50106?vs=325449&id=325524#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D50106/new/

https://reviews.llvm.org/D50106

Files:
  libcxx/include/tuple
  
libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/array.extension.pass.cpp
  
libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp
  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/derived_from_tuple_like.pass.cpp
  libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/laziness.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/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp
  libcxx/test/support/propagate_value_category.hpp

Index: libcxx/test/support/propagate_value_category.hpp
===================================================================
--- /dev/null
+++ libcxx/test/support/propagate_value_category.hpp
@@ -0,0 +1,153 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY
+#define TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY
+
+#include "test_macros.h"
+#include <type_traits>
+
+#if TEST_STD_VER < 11
+#error this header may only be used in C++11
+#endif
+
+using UnderlyingVCType = unsigned;
+enum ValueCategory : UnderlyingVCType {
+  VC_None = 0,
+  VC_LVal = 1 << 0,
+  VC_RVal = 1 << 1,
+  VC_Const = 1 << 2,
+  VC_Volatile = 1 << 3,
+  VC_ConstVolatile = VC_Const | VC_Volatile
+};
+
+inline constexpr ValueCategory operator&(ValueCategory LHS, ValueCategory RHS) {
+  return ValueCategory(LHS & (UnderlyingVCType)RHS);
+}
+
+inline constexpr ValueCategory operator|(ValueCategory LHS, ValueCategory RHS) {
+  return ValueCategory(LHS | (UnderlyingVCType)RHS);
+}
+
+inline constexpr ValueCategory operator^(ValueCategory LHS, ValueCategory RHS) {
+  return ValueCategory(LHS ^ (UnderlyingVCType)RHS);
+}
+
+inline constexpr bool isValidValueCategory(ValueCategory VC) {
+  return (VC & (VC_LVal | VC_RVal)) != (VC_LVal | VC_RVal);
+}
+
+inline constexpr bool hasValueCategory(ValueCategory Arg, ValueCategory Key) {
+  return Arg == Key || ((Arg & Key) == Key);
+}
+
+template <class Tp>
+using UnCVRef =
+    typename std::remove_cv<typename std::remove_reference<Tp>::type>::type;
+
+template <class Tp>
+constexpr ValueCategory getReferenceQuals() {
+  return std::is_lvalue_reference<Tp>::value
+             ? VC_LVal
+             : (std::is_rvalue_reference<Tp>::value ? VC_RVal : VC_None);
+}
+static_assert(getReferenceQuals<int>() == VC_None, "");
+static_assert(getReferenceQuals<int &>() == VC_LVal, "");
+static_assert(getReferenceQuals<int &&>() == VC_RVal, "");
+
+template <class Tp>
+constexpr ValueCategory getCVQuals() {
+  using Vp = typename std::remove_reference<Tp>::type;
+  return std::is_const<Vp>::value && std::is_volatile<Vp>::value
+             ? VC_ConstVolatile
+             : (std::is_const<Vp>::value
+                    ? VC_Const
+                    : (std::is_volatile<Vp>::value ? VC_Volatile : VC_None));
+}
+static_assert(getCVQuals<int>() == VC_None, "");
+static_assert(getCVQuals<int const>() == VC_Const, "");
+static_assert(getCVQuals<int volatile>() == VC_Volatile, "");
+static_assert(getCVQuals<int const volatile>() == VC_ConstVolatile, "");
+static_assert(getCVQuals<int &>() == VC_None, "");
+static_assert(getCVQuals<int const &>() == VC_Const, "");
+
+template <class Tp>
+inline constexpr ValueCategory getValueCategory() {
+  return getReferenceQuals<Tp>() | getCVQuals<Tp>();
+}
+static_assert(getValueCategory<int>() == VC_None, "");
+static_assert(getValueCategory<int const &>() == (VC_LVal | VC_Const), "");
+static_assert(getValueCategory<int const volatile &&>() ==
+                  (VC_RVal | VC_ConstVolatile),
+              "");
+
+template <ValueCategory VC>
+struct ApplyValueCategory {
+private:
+  static_assert(isValidValueCategory(VC), "");
+
+  template <bool Pred, class Then, class Else>
+  using CondT = typename std::conditional<Pred, Then, Else>::type;
+
+public:
+  template <class Tp, class Vp = UnCVRef<Tp>>
+  using ApplyCVQuals = CondT<
+      hasValueCategory(VC, VC_ConstVolatile), typename std::add_cv<Vp>::type,
+      CondT<hasValueCategory(VC, VC_Const), typename std::add_const<Vp>::type,
+            CondT<hasValueCategory(VC, VC_Volatile),
+                  typename std::add_volatile<Vp>::type, Tp>>>;
+
+  template <class Tp, class Vp = typename std::remove_reference<Tp>::type>
+  using ApplyReferenceQuals =
+      CondT<hasValueCategory(VC, VC_LVal),
+            typename std::add_lvalue_reference<Vp>::type,
+            CondT<hasValueCategory(VC, VC_RVal),
+                  typename std::add_rvalue_reference<Vp>::type, Vp>>;
+
+  template <class Tp>
+  using Apply = ApplyReferenceQuals<ApplyCVQuals<UnCVRef<Tp>>>;
+
+  template <class Tp, bool Dummy = true,
+            typename std::enable_if<Dummy && (VC & VC_LVal), bool>::type = true>
+  static Apply<UnCVRef<Tp>> cast(Tp &&t) {
+    using ToType = Apply<UnCVRef<Tp>>;
+    return static_cast<ToType>(t);
+  }
+
+  template <class Tp, bool Dummy = true,
+            typename std::enable_if<Dummy && (VC & VC_RVal), bool>::type = true>
+  static Apply<UnCVRef<Tp>> cast(Tp &&t) {
+    using ToType = Apply<UnCVRef<Tp>>;
+    return static_cast<ToType>(std::move(t));
+  }
+
+  template <
+      class Tp, bool Dummy = true,
+      typename std::enable_if<Dummy && ((VC & (VC_LVal | VC_RVal)) == VC_None),
+                              bool>::type = true>
+  static Apply<UnCVRef<Tp>> cast(Tp &&t) {
+    return t;
+  }
+};
+
+template <ValueCategory VC, class Tp>
+using ApplyValueCategoryT = typename ApplyValueCategory<VC>::template Apply<Tp>;
+
+template <class Tp>
+using PropagateValueCategory = ApplyValueCategory<getValueCategory<Tp>()>;
+
+template <class Tp, class Up>
+using PropagateValueCategoryT =
+    typename ApplyValueCategory<getValueCategory<Tp>()>::template Apply<Up>;
+
+template <ValueCategory VC, class Tp>
+typename ApplyValueCategory<VC>::template Apply<Tp> ValueCategoryCast(Tp &&t) {
+  return ApplyValueCategory<VC>::cast(std::forward<Tp>(t));
+};
+
+#endif // TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY
Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp
===================================================================
--- libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp
+++ libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp
@@ -37,9 +37,20 @@
     explicit D(int i) : B(i) {}
 };
 
-struct NonMoveAssignable {
-  NonMoveAssignable& operator=(NonMoveAssignable const&) = default;
-  NonMoveAssignable& operator=(NonMoveAssignable&&) = delete;
+struct NonAssignable
+{
+  NonAssignable& operator=(NonAssignable const&) = delete;
+  NonAssignable& operator=(NonAssignable&&) = delete;
+};
+
+struct NothrowMoveAssignable
+{
+    NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; }
+};
+
+struct PotentiallyThrowingMoveAssignable
+{
+    PotentiallyThrowingMoveAssignable& operator=(PotentiallyThrowingMoveAssignable&&) { return *this; }
 };
 
 int main(int, char**)
@@ -54,15 +65,28 @@
         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, NonAssignable>;
+        using P = std::pair<int, NonAssignable>;
+        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, "");
     }
+    {
+        typedef std::tuple<NothrowMoveAssignable, long> Tuple;
+        typedef std::pair<NothrowMoveAssignable, int> Pair;
+        static_assert(std::is_nothrow_assignable<Tuple&, Pair&&>::value, "");
+        static_assert(!std::is_assignable<Tuple&, Pair const&&>::value, "");
+    }
+    {
+        typedef std::tuple<PotentiallyThrowingMoveAssignable, long> Tuple;
+        typedef std::pair<PotentiallyThrowingMoveAssignable, int> Pair;
+        static_assert(std::is_assignable<Tuple&, Pair&&>::value, "");
+        static_assert(!std::is_nothrow_assignable<Tuple&, Pair&&>::value, "");
+        static_assert(!std::is_assignable<Tuple&, Pair const&&>::value, "");
+    }
 
-  return 0;
+    return 0;
 }
Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp
===================================================================
--- libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp
+++ libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp
@@ -35,7 +35,12 @@
   MoveAssignable& operator=(MoveAssignable const&) = delete;
   MoveAssignable& operator=(MoveAssignable&&) = default;
 };
-
+struct NothrowMoveAssignable {
+  NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; }
+};
+struct PotentiallyThrowingMoveAssignable {
+  PotentiallyThrowingMoveAssignable& operator=(PotentiallyThrowingMoveAssignable&&) { return *this; }
+};
 
 struct CountAssign {
   static int copied;
@@ -48,7 +53,6 @@
 int CountAssign::copied = 0;
 int CountAssign::moved = 0;
 
-
 int main(int, char**)
 {
     {
@@ -102,7 +106,6 @@
         using T = std::tuple<std::unique_ptr<int>>;
         static_assert(std::is_move_assignable<T>::value, "");
         static_assert(!std::is_copy_assignable<T>::value, "");
-
     }
     {
       using T = std::tuple<int, NonAssignable>;
@@ -123,6 +126,22 @@
         assert(CountAssign::copied == 1);
         assert(CountAssign::moved == 0);
     }
+    {
+        using T = std::tuple<int, NonAssignable>;
+        static_assert(!std::is_move_assignable<T>::value, "");
+    }
+    {
+        using T = std::tuple<int, MoveAssignable>;
+        static_assert(std::is_move_assignable<T>::value, "");
+    }
+    {
+        using T = std::tuple<NothrowMoveAssignable, int>;
+        static_assert(std::is_nothrow_move_assignable<T>::value, "");
+    }
+    {
+        using T = std::tuple<PotentiallyThrowingMoveAssignable, int>;
+        static_assert(!std::is_nothrow_move_assignable<T>::value, "");
+    }
 
-  return 0;
+    return 0;
 }
Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/laziness.pass.cpp
===================================================================
--- /dev/null
+++ libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/laziness.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03
+
+// This test ensures that std::tuple is lazy when it comes to checking whether
+// the elements it is assigned from can be used to assign to the types in
+// the tuple.
+
+#include <tuple>
+#include <array>
+
+template <bool Enable, class ...Class>
+constexpr typename std::enable_if<Enable, bool>::type BlowUp() {
+  static_assert(Enable && sizeof...(Class) != sizeof...(Class), "");
+  return true;
+}
+
+template<class T>
+struct Fail {
+  static_assert(sizeof(T) != sizeof(T), "");
+  using type = void;
+};
+
+struct NoAssign {
+  NoAssign() = default;
+  NoAssign(NoAssign const&) = default;
+  template <class T, class = typename std::enable_if<sizeof(T) != sizeof(T)>::type>
+  NoAssign& operator=(T) { return *this; }
+};
+
+template <int>
+struct DieOnAssign {
+  DieOnAssign() = default;
+  template <class T, class X = typename std::enable_if<!std::is_same<T, DieOnAssign>::value>::type,
+                     class = typename Fail<X>::type>
+  DieOnAssign& operator=(T) {
+    return *this;
+  }
+};
+
+void test_arity_checks() {
+  {
+    using T = std::tuple<int, DieOnAssign<0>, int>;
+    using P = std::pair<int, int>;
+    static_assert(!std::is_assignable<T&, P const&>::value, "");
+  }
+  {
+    using T = std::tuple<int, int, DieOnAssign<1> >;
+    using A = std::array<int, 1>;
+    static_assert(!std::is_assignable<T&, A const&>::value, "");
+  }
+}
+
+void test_assignability_checks() {
+  {
+    using T1 = std::tuple<int, NoAssign, DieOnAssign<2> >;
+    using T2 = std::tuple<long, long, long>;
+    static_assert(!std::is_assignable<T1&, T2 const&>::value, "");
+  }
+  {
+    using T1 = std::tuple<NoAssign, DieOnAssign<3> >;
+    using T2 = std::pair<long, double>;
+    static_assert(!std::is_assignable<T1&, T2 const&>::value, "");
+  }
+}
+
+int main(int, char**) {
+  test_arity_checks();
+  test_assignability_checks();
+  return 0;
+}
Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/derived_from_tuple_like.pass.cpp
===================================================================
--- /dev/null
+++ libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/derived_from_tuple_like.pass.cpp
@@ -0,0 +1,120 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... Types> class tuple;
+
+// template <class... UTypes>
+//   tuple& operator=(const tuple<UTypes...>& u);
+
+// UNSUPPORTED: c++03
+
+#include <tuple>
+#include <array>
+#include <string>
+#include <utility>
+#include <cassert>
+
+#include "propagate_value_category.hpp"
+
+struct TracksIntQuals {
+  TracksIntQuals() : value(-1), value_category(VC_None), assigned(false) {}
+
+  template <class Tp,
+            class = typename std::enable_if<!std::is_same<
+                typename std::decay<Tp>::type, TracksIntQuals>::value>::type>
+  TracksIntQuals(Tp &&x)
+      : value(x), value_category(getValueCategory<Tp &&>()), assigned(false) {
+    static_assert(std::is_same<UnCVRef<Tp>, int>::value, "");
+  }
+
+  template <class Tp,
+            class = typename std::enable_if<!std::is_same<
+                typename std::decay<Tp>::type, TracksIntQuals>::value>::type>
+  TracksIntQuals &operator=(Tp &&x) {
+    static_assert(std::is_same<UnCVRef<Tp>, int>::value, "");
+    value = x;
+    value_category = getValueCategory<Tp &&>();
+    assigned = true;
+    return *this;
+  }
+
+  void reset() {
+    value = -1;
+    value_category = VC_None;
+    assigned = false;
+  }
+
+  bool checkConstruct(int expect, ValueCategory expect_vc) const {
+    return value != 1 && value == expect && value_category == expect_vc &&
+           assigned == false;
+  }
+
+  bool checkAssign(int expect, ValueCategory expect_vc) const {
+    return value != 1 && value == expect && value_category == expect_vc &&
+           assigned == true;
+  }
+
+  int value;
+  ValueCategory value_category;
+  bool assigned;
+};
+
+template <class Tup>
+struct DerivedFromTup : Tup {
+  using Tup::Tup;
+};
+
+template <ValueCategory VC>
+void do_derived_assign_test() {
+  using Tup1 = std::tuple<long, TracksIntQuals>;
+  Tup1 t;
+  auto reset = [&]() {
+    std::get<0>(t) = -1;
+    std::get<1>(t).reset();
+  };
+  {
+    DerivedFromTup<std::tuple<int, int>> d;
+    std::get<0>(d) = 42;
+    std::get<1>(d) = 101;
+
+    t = ValueCategoryCast<VC>(d);
+    assert(std::get<0>(t) == 42);
+    assert(std::get<1>(t).checkAssign(101, VC));
+  }
+  reset();
+  {
+    DerivedFromTup<std::pair<int, int>> d;
+    std::get<0>(d) = 42;
+    std::get<1>(d) = 101;
+
+    t = ValueCategoryCast<VC>(d);
+    assert(std::get<0>(t) == 42);
+    assert(std::get<1>(t).checkAssign(101, VC));
+  }
+  reset();
+  {
+#ifdef _LIBCPP_VERSION // assignment from std::array is a libc++ extension
+    DerivedFromTup<std::array<int, 2>> d;
+    std::get<0>(d) = 42;
+    std::get<1>(d) = 101;
+
+    t = ValueCategoryCast<VC>(d);
+    assert(std::get<0>(t) == 42);
+    assert(std::get<1>(t).checkAssign(101, VC));
+#endif
+  }
+}
+
+int main(int, char**) {
+    do_derived_assign_test<VC_LVal | VC_Const>();
+    do_derived_assign_test<VC_RVal>();
+
+    return 0;
+}
Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp
===================================================================
--- libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp
+++ libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp
@@ -34,6 +34,12 @@
   MoveAssignable& operator=(MoveAssignable const&) = delete;
   MoveAssignable& operator=(MoveAssignable&&) = default;
 };
+struct NothrowCopyAssignable {
+  NothrowCopyAssignable& operator=(NothrowCopyAssignable const&) noexcept { return *this; }
+};
+struct PotentiallyThrowingCopyAssignable {
+  PotentiallyThrowingCopyAssignable& operator=(PotentiallyThrowingCopyAssignable const&) { return *this; }
+};
 
 struct CopyAssignableInt {
   CopyAssignableInt& operator=(int&) { return *this; }
@@ -119,6 +125,14 @@
       using P = std::pair<int, MoveAssignable>;
       static_assert(!std::is_assignable<T&, P&>::value, "");
     }
+    {
+        using T = std::tuple<NothrowCopyAssignable, int>;
+        static_assert(std::is_nothrow_copy_assignable<T>::value, "");
+    }
+    {
+        using T = std::tuple<PotentiallyThrowingCopyAssignable, int>;
+        static_assert(!std::is_nothrow_copy_assignable<T>::value, "");
+    }
 
-  return 0;
+    return 0;
 }
Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp
===================================================================
--- libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp
+++ libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp
@@ -50,6 +50,16 @@
   NonAssignable& operator=(NonAssignable&&) = delete;
 };
 
+struct NothrowMoveAssignable
+{
+    NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; }
+};
+
+struct PotentiallyThrowingMoveAssignable
+{
+    PotentiallyThrowingMoveAssignable& operator=(PotentiallyThrowingMoveAssignable&&) { return *this; }
+};
+
 int main(int, char**)
 {
     {
@@ -119,6 +129,16 @@
       static_assert(!std::is_assignable<T, U&&>::value, "");
       static_assert(!std::is_assignable<U, T&&>::value, "");
     }
+    {
+        typedef std::tuple<NothrowMoveAssignable, long> T0;
+        typedef std::tuple<NothrowMoveAssignable, int> T1;
+        static_assert(std::is_nothrow_assignable<T0&, T1&&>::value, "");
+    }
+    {
+        typedef std::tuple<PotentiallyThrowingMoveAssignable, long> T0;
+        typedef std::tuple<PotentiallyThrowingMoveAssignable, int> T1;
+        static_assert(!std::is_nothrow_assignable<T0&, T1&&>::value, "");
+    }
 
-  return 0;
+    return 0;
 }
Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.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_copy.pass.cpp
@@ -39,6 +39,16 @@
   NonAssignable& operator=(NonAssignable&&) = delete;
 };
 
+struct NothrowCopyAssignable
+{
+    NothrowCopyAssignable& operator=(NothrowCopyAssignable const&) noexcept { return *this; }
+};
+
+struct PotentiallyThrowingCopyAssignable
+{
+    PotentiallyThrowingCopyAssignable& operator=(PotentiallyThrowingCopyAssignable const&) { return *this; }
+};
+
 int main(int, char**)
 {
     {
@@ -98,6 +108,16 @@
       static_assert(!std::is_assignable<T, U const&>::value, "");
       static_assert(!std::is_assignable<U, T const&>::value, "");
     }
+    {
+        typedef std::tuple<NothrowCopyAssignable, long> T0;
+        typedef std::tuple<NothrowCopyAssignable, int> T1;
+        static_assert(std::is_nothrow_assignable<T0&, T1 const&>::value, "");
+    }
+    {
+        typedef std::tuple<PotentiallyThrowingCopyAssignable, long> T0;
+        typedef std::tuple<PotentiallyThrowingCopyAssignable, int> T1;
+        static_assert(!std::is_nothrow_assignable<T0&, T1 const&>::value, "");
+    }
 
-  return 0;
+    return 0;
 }
Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp
===================================================================
--- libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp
+++ libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp
@@ -15,10 +15,18 @@
 
 // UNSUPPORTED: c++03
 
+#include <cassert>
+#include <memory>
 #include <tuple>
+#include <type_traits>
 #include <utility>
-#include <memory>
-#include <cassert>
+
+struct NothrowCopyAssignable {
+    NothrowCopyAssignable& operator=(NothrowCopyAssignable const&) noexcept { return *this; }
+};
+struct PotentiallyThrowingCopyAssignable {
+    PotentiallyThrowingCopyAssignable& operator=(PotentiallyThrowingCopyAssignable const&) { return *this; }
+};
 
 #include "test_macros.h"
 
@@ -40,6 +48,25 @@
         using P = std::tuple<std::unique_ptr<int>, std::unique_ptr<int>>;
         static_assert(!std::is_assignable<T, const P &>::value, "");
     }
+    {
+        typedef std::tuple<NothrowCopyAssignable, long> Tuple;
+        typedef std::pair<NothrowCopyAssignable, int> Pair;
+        static_assert(std::is_nothrow_assignable<Tuple&, Pair const&>::value, "");
+        static_assert(std::is_nothrow_assignable<Tuple&, Pair&>::value, "");
+        static_assert(std::is_nothrow_assignable<Tuple&, Pair const&&>::value, "");
+    }
+    {
+        typedef std::tuple<PotentiallyThrowingCopyAssignable, long> Tuple;
+        typedef std::pair<PotentiallyThrowingCopyAssignable, int> Pair;
+        static_assert(std::is_assignable<Tuple&, Pair const&>::value, "");
+        static_assert(!std::is_nothrow_assignable<Tuple&, Pair const&>::value, "");
+
+        static_assert(std::is_assignable<Tuple&, Pair&>::value, "");
+        static_assert(!std::is_nothrow_assignable<Tuple&, Pair&>::value, "");
+
+        static_assert(std::is_assignable<Tuple&, Pair const&&>::value, "");
+        static_assert(!std::is_nothrow_assignable<Tuple&, Pair const&&>::value, "");
+    }
 
-  return 0;
+    return 0;
 }
Index: libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/array.extension.pass.cpp
===================================================================
--- /dev/null
+++ libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/array.extension.pass.cpp
@@ -0,0 +1,104 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... Types> class tuple;
+
+// EXTENSION
+// template <class U, size_t N>
+//   tuple& operator=(const array<U, N>& u);
+//
+// template <class U, size_t N>
+//   tuple& operator=(array<U, N>&& u);
+
+// UNSUPPORTED: c++03
+
+#include <array>
+#include <cassert>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+
+template <class T>
+struct NothrowAssignableFrom {
+    NothrowAssignableFrom& operator=(T) noexcept { return *this; }
+};
+
+template <class T>
+struct PotentiallyThrowingAssignableFrom {
+    PotentiallyThrowingAssignableFrom& operator=(T) { return *this; }
+};
+
+int main(int, char**) {
+    // Tests for the array const& overload
+    {
+        std::array<long, 3> array = {1l, 2l, 3l};
+        std::tuple<int, int, int> tuple;
+        tuple = array;
+        assert(std::get<0>(tuple) == 1);
+        assert(std::get<1>(tuple) == 2);
+        assert(std::get<2>(tuple) == 3);
+    }
+    {
+        typedef std::tuple<NothrowAssignableFrom<int>> Tuple;
+        typedef std::array<int, 1> Array;
+        static_assert(std::is_nothrow_assignable<Tuple&, Array const&>::value, "");
+    }
+    {
+        typedef std::tuple<PotentiallyThrowingAssignableFrom<int>> Tuple;
+        typedef std::array<int, 1> Array;
+        static_assert(std::is_assignable<Tuple&, Array const&>::value, "");
+        static_assert(!std::is_nothrow_assignable<Tuple&, Array const&>::value, "");
+    }
+
+    // Tests for the array&& overload
+    {
+        std::array<long, 3> array = {1l, 2l, 3l};
+        std::tuple<int, int, int> tuple;
+        tuple = std::move(array);
+        assert(std::get<0>(tuple) == 1);
+        assert(std::get<1>(tuple) == 2);
+        assert(std::get<2>(tuple) == 3);
+    }
+    {
+        typedef std::tuple<NothrowAssignableFrom<int>> Tuple;
+        typedef std::array<int, 1> Array;
+        static_assert(std::is_nothrow_assignable<Tuple&, Array&&>::value, "");
+    }
+    {
+        typedef std::tuple<PotentiallyThrowingAssignableFrom<int>> Tuple;
+        typedef std::array<int, 1> Array;
+        static_assert(std::is_assignable<Tuple&, Array&&>::value, "");
+        static_assert(!std::is_nothrow_assignable<Tuple&, Array&&>::value, "");
+    }
+
+    // Test lvalue-refs and const rvalue-ref
+    {
+        typedef std::tuple<NothrowAssignableFrom<int>> Tuple;
+        typedef std::array<int, 1> Array;
+        static_assert(std::is_nothrow_assignable<Tuple&, Array&>::value, "");
+        static_assert(std::is_nothrow_assignable<Tuple&, const Array&&>::value, "");
+    }
+
+    {
+        typedef std::tuple<NothrowAssignableFrom<int>> Tuple;
+        static_assert(!std::is_assignable<Tuple&, std::array<long, 2>&>::value, "");
+        static_assert(!std::is_assignable<Tuple&, std::array<long, 2>&&>::value, "");
+        static_assert(!std::is_assignable<Tuple&, const std::array<long, 2>&>::value, "");
+        static_assert(!std::is_assignable<Tuple&, const std::array<long, 2>&&>::value, "");
+
+        static_assert(!std::is_assignable<Tuple&, std::array<long, 4>&>::value, "");
+        static_assert(!std::is_assignable<Tuple&, std::array<long, 4>&&>::value, "");
+        static_assert(!std::is_assignable<Tuple&, const std::array<long, 4>&>::value, "");
+        static_assert(!std::is_assignable<Tuple&, const std::array<long, 4>&&>::value, "");
+    }
+
+    return 0;
+}
Index: libcxx/include/tuple
===================================================================
--- libcxx/include/tuple
+++ libcxx/include/tuple
@@ -55,8 +55,7 @@
         explicit(see-below) tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&&);
 
     tuple& operator=(const tuple&);
-    tuple&
-        operator=(tuple&&) noexcept(AND(is_nothrow_move_assignable<T>::value ...));
+    tuple& operator=(tuple&&) noexcept(is_nothrow_move_assignable_v<T> && ...);
     template <class... U>
         tuple& operator=(const tuple<U...>&);
     template <class... U>
@@ -66,6 +65,11 @@
     template <class U1, class U2>
         tuple& operator=(pair<U1, U2>&&); // iff sizeof...(T) == 2
 
+    template<class U, size_t N>
+        tuple& operator=(array<U, N> const&) // iff sizeof...(T) == N, EXTENSION
+    template<class U, size_t N>
+        tuple& operator=(array<U, N>&&) // iff sizeof...(T) == N, EXTENSION
+
     void swap(tuple&) noexcept(AND(swap(declval<T&>(), declval<T&>())...));
 };
 
@@ -257,15 +261,6 @@
     __tuple_leaf(const __tuple_leaf& __t) = default;
     __tuple_leaf(__tuple_leaf&& __t) = default;
 
-    template <class _Tp>
-        _LIBCPP_INLINE_VISIBILITY
-        __tuple_leaf&
-        operator=(_Tp&& __t) _NOEXCEPT_((is_nothrow_assignable<_Hp&, _Tp>::value))
-        {
-            __value_ = _VSTD::forward<_Tp>(__t);
-            return *this;
-        }
-
     _LIBCPP_INLINE_VISIBILITY
     int swap(__tuple_leaf& __t) _NOEXCEPT_(__is_nothrow_swappable<__tuple_leaf>::value)
     {
@@ -331,15 +326,6 @@
     __tuple_leaf(__tuple_leaf const &) = default;
     __tuple_leaf(__tuple_leaf &&) = default;
 
-    template <class _Tp>
-        _LIBCPP_INLINE_VISIBILITY
-        __tuple_leaf&
-        operator=(_Tp&& __t) _NOEXCEPT_((is_nothrow_assignable<_Hp&, _Tp>::value))
-        {
-            _Hp::operator=(_VSTD::forward<_Tp>(__t));
-            return *this;
-        }
-
     _LIBCPP_INLINE_VISIBILITY
     int
     swap(__tuple_leaf& __t) _NOEXCEPT_(__is_nothrow_swappable<__tuple_leaf>::value)
@@ -429,49 +415,30 @@
                                        typename __make_tuple_types<_Tuple>::type>::type>(_VSTD::get<_Indx>(__t)))...
             {}
 
-    template <class _Tuple>
-        _LIBCPP_INLINE_VISIBILITY
-        typename enable_if
-        <
-            __tuple_assignable<_Tuple, tuple<_Tp...> >::value,
-            __tuple_impl&
-        >::type
-        operator=(_Tuple&& __t) _NOEXCEPT_((__all<is_nothrow_assignable<_Tp&, typename tuple_element<_Indx,
-                                       typename __make_tuple_types<_Tuple>::type>::type>::value...>::value))
-        {
-            __swallow(__tuple_leaf<_Indx, _Tp>::operator=(_VSTD::forward<typename tuple_element<_Indx,
-                                       typename __make_tuple_types<_Tuple>::type>::type>(_VSTD::get<_Indx>(__t)))...);
-            return *this;
-        }
-
     __tuple_impl(const __tuple_impl&) = default;
     __tuple_impl(__tuple_impl&&) = default;
 
-    _LIBCPP_INLINE_VISIBILITY
-    __tuple_impl&
-    operator=(const __tuple_impl& __t) _NOEXCEPT_((__all<is_nothrow_copy_assignable<_Tp>::value...>::value))
-    {
-        __swallow(__tuple_leaf<_Indx, _Tp>::operator=(static_cast<const __tuple_leaf<_Indx, _Tp>&>(__t).get())...);
-        return *this;
-    }
-
-    _LIBCPP_INLINE_VISIBILITY
-    __tuple_impl&
-    operator=(__tuple_impl&& __t) _NOEXCEPT_((__all<is_nothrow_move_assignable<_Tp>::value...>::value))
-    {
-        __swallow(__tuple_leaf<_Indx, _Tp>::operator=(_VSTD::forward<_Tp>(static_cast<__tuple_leaf<_Indx, _Tp>&>(__t).get()))...);
-        return *this;
-    }
-
     _LIBCPP_INLINE_VISIBILITY
     void swap(__tuple_impl& __t)
         _NOEXCEPT_(__all<__is_nothrow_swappable<_Tp>::value...>::value)
     {
-        __swallow(__tuple_leaf<_Indx, _Tp>::swap(static_cast<__tuple_leaf<_Indx, _Tp>&>(__t))...);
+        _VSTD::__swallow(__tuple_leaf<_Indx, _Tp>::swap(static_cast<__tuple_leaf<_Indx, _Tp>&>(__t))...);
     }
 };
 
+template<class _Dest, class _Source, size_t ..._Np>
+_LIBCPP_INLINE_VISIBILITY
+void __memberwise_copy_assign(_Dest& __dest, _Source const& __source, __tuple_indices<_Np...>) {
+    _VSTD::__swallow(((_VSTD::get<_Np>(__dest) = _VSTD::get<_Np>(__source)), void(), 0)...);
+}
 
+template<class _Dest, class _Source, class ..._Up, size_t ..._Np>
+_LIBCPP_INLINE_VISIBILITY
+void __memberwise_forward_assign(_Dest& __dest, _Source&& __source, __tuple_types<_Up...>, __tuple_indices<_Np...>) {
+    _VSTD::__swallow(((
+        _VSTD::get<_Np>(__dest) = _VSTD::forward<_Up>(_VSTD::get<_Np>(_VSTD::forward<_Source>(__source)))
+    ), void(), 0)...);
+}
 
 template <class ..._Tp>
 class _LIBCPP_TEMPLATE_VIS tuple
@@ -916,39 +883,129 @@
         tuple(allocator_arg_t, const _Alloc& __a, _Tuple&& __t)
             : __base_(allocator_arg_t(), __a, _VSTD::forward<_Tuple>(__t)) {}
 
-    using _CanCopyAssign = __all<is_copy_assignable<_Tp>::value...>;
-    using _CanMoveAssign = __all<is_move_assignable<_Tp>::value...>;
+    // [tuple.assign]
+    _LIBCPP_INLINE_VISIBILITY
+    tuple& operator=(_If<_And<is_copy_assignable<_Tp>...>::value, tuple, __nat> const& __tuple)
+        _NOEXCEPT_((_And<is_nothrow_copy_assignable<_Tp>...>::value))
+    {
+        _VSTD::__memberwise_copy_assign(*this, __tuple,
+            typename __make_tuple_indices<sizeof...(_Tp)>::type());
+        return *this;
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    tuple& operator=(_If<_And<is_move_assignable<_Tp>...>::value, tuple, __nat>&& __tuple)
+        _NOEXCEPT_((_And<is_nothrow_move_assignable<_Tp>...>::value))
+    {
+        _VSTD::__memberwise_forward_assign(*this, _VSTD::move(__tuple),
+            __tuple_types<_Tp...>(),
+            typename __make_tuple_indices<sizeof...(_Tp)>::type());
+        return *this;
+    }
 
+    template<class... _Up, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Tp) == sizeof...(_Up)>,
+            is_assignable<_Tp&, _Up const&>...
+        >::value
+    ,int> = 0>
     _LIBCPP_INLINE_VISIBILITY
-    tuple& operator=(typename conditional<_CanCopyAssign::value, tuple, __nat>::type const& __t)
-        _NOEXCEPT_((__all<is_nothrow_copy_assignable<_Tp>::value...>::value))
+    tuple& operator=(tuple<_Up...> const& __tuple)
+        _NOEXCEPT_((_And<is_nothrow_assignable<_Tp&, _Up const&>...>::value))
     {
-        __base_.operator=(__t.__base_);
+        _VSTD::__memberwise_copy_assign(*this, __tuple,
+            typename __make_tuple_indices<sizeof...(_Tp)>::type());
         return *this;
     }
 
+    template<class... _Up, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Tp) == sizeof...(_Up)>,
+            is_assignable<_Tp&, _Up>...
+        >::value
+    ,int> = 0>
     _LIBCPP_INLINE_VISIBILITY
-    tuple& operator=(typename conditional<_CanMoveAssign::value, tuple, __nat>::type&& __t)
-        _NOEXCEPT_((__all<is_nothrow_move_assignable<_Tp>::value...>::value))
+    tuple& operator=(tuple<_Up...>&& __tuple)
+        _NOEXCEPT_((_And<is_nothrow_assignable<_Tp&, _Up>...>::value))
     {
-        __base_.operator=(static_cast<_BaseT&&>(__t.__base_));
+        _VSTD::__memberwise_forward_assign(*this, _VSTD::move(__tuple),
+            __tuple_types<_Up...>(),
+            typename __make_tuple_indices<sizeof...(_Tp)>::type());
         return *this;
     }
 
-    template <class _Tuple,
-              class = typename enable_if
-                      <
-                         __tuple_assignable<_Tuple, tuple>::value
-                      >::type
-             >
-        _LIBCPP_INLINE_VISIBILITY
-        tuple&
-        operator=(_Tuple&& __t) _NOEXCEPT_((is_nothrow_assignable<_BaseT&, _Tuple>::value))
-        {
-            __base_.operator=(_VSTD::forward<_Tuple>(__t));
-            return *this;
-        }
+    template<class _Up1, class _Up2, class _Dep = true_type, _EnableIf<
+        _And<_Dep,
+            _BoolConstant<sizeof...(_Tp) == 2>,
+            is_assignable<_FirstType<_Tp..., _Dep>&, _Up1 const&>,
+            is_assignable<_SecondType<_Tp..., _Dep>&, _Up2 const&>
+        >::value
+    ,int> = 0>
+    _LIBCPP_INLINE_VISIBILITY
+    tuple& operator=(pair<_Up1, _Up2> const& __pair)
+        _NOEXCEPT_((_And<
+            is_nothrow_assignable<_FirstType<_Tp...>&, _Up1 const&>,
+            is_nothrow_assignable<_SecondType<_Tp...>&, _Up2 const&>
+        >::value))
+    {
+        _VSTD::get<0>(*this) = __pair.first;
+        _VSTD::get<1>(*this) = __pair.second;
+        return *this;
+    }
+
+    template<class _Up1, class _Up2, class _Dep = true_type, _EnableIf<
+        _And<_Dep,
+            _BoolConstant<sizeof...(_Tp) == 2>,
+            is_assignable<_FirstType<_Tp..., _Dep>&, _Up1>,
+            is_assignable<_SecondType<_Tp..., _Dep>&, _Up2>
+        >::value
+    ,int> = 0>
+    _LIBCPP_INLINE_VISIBILITY
+    tuple& operator=(pair<_Up1, _Up2>&& __pair)
+        _NOEXCEPT_((_And<
+            is_nothrow_assignable<_FirstType<_Tp...>&, _Up1>,
+            is_nothrow_assignable<_SecondType<_Tp...>&, _Up2>
+        >::value))
+    {
+        _VSTD::get<0>(*this) = _VSTD::move(__pair.first);
+        _VSTD::get<1>(*this) = _VSTD::move(__pair.second);
+        return *this;
+    }
+
+    // EXTENSION
+    template<class _Up, size_t _Np, class = _EnableIf<
+        _And<
+            _BoolConstant<_Np == sizeof...(_Tp)>,
+            is_assignable<_Tp&, _Up const&>...
+        >::value
+    > >
+    _LIBCPP_INLINE_VISIBILITY
+    tuple& operator=(array<_Up, _Np> const& __array)
+        _NOEXCEPT_((_And<is_nothrow_assignable<_Tp&, _Up const&>...>::value))
+    {
+        _VSTD::__memberwise_copy_assign(*this, __array,
+            typename __make_tuple_indices<sizeof...(_Tp)>::type());
+        return *this;
+    }
+
+    // EXTENSION
+    template<class _Up, size_t _Np, class = void, class = _EnableIf<
+        _And<
+            _BoolConstant<_Np == sizeof...(_Tp)>,
+            is_assignable<_Tp&, _Up>...
+        >::value
+    > >
+    _LIBCPP_INLINE_VISIBILITY
+    tuple& operator=(array<_Up, _Np>&& __array)
+        _NOEXCEPT_((_And<is_nothrow_assignable<_Tp&, _Up>...>::value))
+    {
+        _VSTD::__memberwise_forward_assign(*this, _VSTD::move(__array),
+            __tuple_types<_If<true, _Up, _Tp>...>(),
+            typename __make_tuple_indices<sizeof...(_Tp)>::type());
+        return *this;
+    }
 
+    // [tuple.swap]
     _LIBCPP_INLINE_VISIBILITY
     void swap(tuple& __t) _NOEXCEPT_(__all<__is_nothrow_swappable<_Tp>::value...>::value)
         {__base_.swap(__t.__base_);}
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to