Re-implement LWG 2770 - Fix tuple_size with structured bindings.

This patch implements the correct PR for LWG 2770. It also makes the primary
tuple_size template incomplete again which fixes part of llvm.org/PR31513.

git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@290846 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/__tuple b/include/__tuple
index 775d734..7aad081 100644
--- a/include/__tuple
+++ b/include/__tuple
@@ -22,19 +22,32 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _Tp> class _LIBCPP_TYPE_VIS_ONLY tuple_size {};
+template <class _Tp> class _LIBCPP_TYPE_VIS_ONLY tuple_size;
+
+struct __empty_tuple_size_base {};
+
+template <class _Tp, class = void>
+struct __tuple_size_base_type {
+  typedef __empty_tuple_size_base type;
+};
+
+template <class _Tp>
+struct __tuple_size_base_type<_Tp, typename __void_t<decltype(tuple_size<_Tp>::value)>::type>
+{
+  typedef integral_constant<size_t, tuple_size<_Tp>::value> type;
+};
 
 template <class _Tp>
 class _LIBCPP_TYPE_VIS_ONLY tuple_size<const _Tp>
-    : public tuple_size<_Tp> {};
+    : public __tuple_size_base_type<_Tp>::type {};
 
 template <class _Tp>
 class _LIBCPP_TYPE_VIS_ONLY tuple_size<volatile _Tp>
-    : public tuple_size<_Tp> {};
+    : public __tuple_size_base_type<_Tp>::type {};
 
 template <class _Tp>
 class _LIBCPP_TYPE_VIS_ONLY tuple_size<const volatile _Tp>
-    : public tuple_size<_Tp> {};
+    : public __tuple_size_base_type<_Tp>::type {};
 
 template <size_t _Ip, class _Tp> class _LIBCPP_TYPE_VIS_ONLY tuple_element;
 
diff --git a/include/tuple b/include/tuple
index 553d8e5..2f71cce 100644
--- a/include/tuple
+++ b/include/tuple
@@ -84,7 +84,7 @@
   constexpr T make_from_tuple(Tuple&& t); // C++17
 
 // 20.4.1.4, tuple helper classes:
-template <class T> class tuple_size;
+template <class T> class tuple_size; // undefined
 template <class... T> class tuple_size<tuple<T...>>;
 template <class T>
  constexpr size_t tuple_size_v = tuple_size<T>::value; // C++17
diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size.fail.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size.fail.cpp
index 50c6f17..3f132e4 100644
--- a/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size.fail.cpp
+++ b/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size.fail.cpp
@@ -21,7 +21,7 @@
 
 int main()
 {
-    (void)std::tuple_size<std::tuple<> &>::value; // expected-error {{no member named 'value'}}
-    (void)std::tuple_size<int>::value; // expected-error {{no member named 'value'}}
-    (void)std::tuple_size<std::tuple<>*>::value; // expected-error {{no member named 'value'}}
+    (void)std::tuple_size<std::tuple<> &>::value; // expected-error {{implicit instantiation of undefined template}}
+    (void)std::tuple_size<int>::value; // expected-error {{implicit instantiation of undefined template}}
+    (void)std::tuple_size<std::tuple<>*>::value; // expected-error {{implicit instantiation of undefined template}}
 }
diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size.pass.cpp
index 40214f6..3e4145c 100644
--- a/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size.pass.cpp
+++ b/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size.pass.cpp
@@ -18,8 +18,6 @@
 // UNSUPPORTED: c++98, c++03
 
 #include <tuple>
-#include <utility>
-#include <array>
 #include <type_traits>
 
 template <class T, class = decltype(std::tuple_size<T>::value)>
@@ -27,27 +25,29 @@
 template <class> constexpr bool has_value(long) { return false; }
 template <class T> constexpr bool has_value() { return has_value<T>(0); }
 
+struct Dummy {};
 
 template <class T, std::size_t N>
 void test()
 {
-    static_assert(has_value<T>(), "");
     static_assert((std::is_base_of<std::integral_constant<std::size_t, N>,
                                    std::tuple_size<T> >::value), "");
-    static_assert(has_value<const T>(), "");
     static_assert((std::is_base_of<std::integral_constant<std::size_t, N>,
                                    std::tuple_size<const T> >::value), "");
-    static_assert(has_value<volatile T>(), "");
     static_assert((std::is_base_of<std::integral_constant<std::size_t, N>,
                                    std::tuple_size<volatile T> >::value), "");
-
-    static_assert(has_value<const volatile T>(), "");
     static_assert((std::is_base_of<std::integral_constant<std::size_t, N>,
                                    std::tuple_size<const volatile T> >::value), "");
-    {
-        static_assert(!has_value<T &>(), "");
-        static_assert(!has_value<T *>(), "");
-    }
+}
+
+void test_tuple_size_value_sfinae() {
+  // Test that the ::value member does not exist
+  static_assert(has_value<std::tuple<int> const>(), "");
+  static_assert(has_value<std::pair<int, long> volatile>(), "");
+  static_assert(!has_value<int>(), "");
+  static_assert(!has_value<const int>(), "");
+  static_assert(!has_value<volatile void>(), "");
+  static_assert(!has_value<const volatile std::tuple<int>&>(), "");
 }
 
 int main()
@@ -56,13 +56,5 @@
     test<std::tuple<int>, 1>();
     test<std::tuple<char, int>, 2>();
     test<std::tuple<char, char*, int>, 3>();
-    test<std::pair<int, void*>, 2>();
-    test<std::array<int, 42>, 42>();
-    {
-        static_assert(!has_value<void>(), "");
-        static_assert(!has_value<void*>(), "");
-        static_assert(!has_value<int>(), "");
-        static_assert(!has_value<std::pair<int, int>*>(), "");
-        static_assert(!has_value<std::array<int, 42>&>(), "");
-    }
+    test_tuple_size_value_sfinae();
 }
diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_v.fail.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_v.fail.cpp
index 700dd95..957a683 100644
--- a/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_v.fail.cpp
+++ b/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_v.fail.cpp
@@ -22,5 +22,5 @@
     (void)std::tuple_size_v<std::tuple<> &>; // expected-note {{requested here}}
     (void)std::tuple_size_v<int>; // expected-note {{requested here}}
     (void)std::tuple_size_v<std::tuple<>*>; // expected-note {{requested here}}
-    // expected-error@tuple:* 3 {{no member named 'value'}}
+    // expected-error@tuple:* 3 {{implicit instantiation of undefined template}}
 }