[libcxx] r291019 - [libcxx] Re-implement LWG 2770 again: Fix tuple_size to work with structured bindings

Eric Fiselier via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 4 14:38:46 PST 2017


Author: ericwf
Date: Wed Jan  4 16:38:46 2017
New Revision: 291019

URL: http://llvm.org/viewvc/llvm-project?rev=291019&view=rev
Log:
[libcxx] Re-implement LWG 2770 again: Fix tuple_size to work with structured bindings

Summary:
This patch attempts to re-implement a fix for LWG 2770, but not the actual specified PR. 

The PR for 2770 specifies tuple_size<T const> as only conditionally providing a `::value` member. However C++17 structured bindings require `tuple_size<T const>` to be complete only if  `tuple_size<T>` is also complete. Therefore this patch implements only provides the specialization `tuple_size<T CV>` iff `tuple_size<T>` is a complete type.

This fixes http://llvm.org/PR31513.

Reviewers: mclow.lists, rsmith, mpark

Subscribers: mpark, cfe-commits

Differential Revision: https://reviews.llvm.org/D28222

Added:
    libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.fail.cpp
    libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.pass.cpp
    libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_structured_bindings.pass.cpp
    libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_value_sfinae.pass.cpp
Modified:
    libcxx/trunk/include/__tuple
    libcxx/trunk/test/libcxx/test/config.py
    libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size.pass.cpp

Modified: libcxx/trunk/include/__tuple
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/__tuple?rev=291019&r1=291018&r2=291019&view=diff
==============================================================================
--- libcxx/trunk/include/__tuple (original)
+++ libcxx/trunk/include/__tuple Wed Jan  4 16:38:46 2017
@@ -24,30 +24,35 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 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;
-};
+#if !defined(_LIBCPP_CXX03_LANG)
+template <class _Tp, class...>
+using __enable_if_tuple_size_imp = _Tp;
 
 template <class _Tp>
-class _LIBCPP_TYPE_VIS_ONLY tuple_size<const _Tp>
-    : public __tuple_size_base_type<_Tp>::type {};
+class _LIBCPP_TYPE_VIS_ONLY tuple_size<__enable_if_tuple_size_imp<
+    const _Tp,
+    typename enable_if<!is_volatile<_Tp>::value>::type,
+    integral_constant<size_t, sizeof(tuple_size<_Tp>)>>>
+    : public integral_constant<size_t, tuple_size<_Tp>::value> {};
 
 template <class _Tp>
-class _LIBCPP_TYPE_VIS_ONLY tuple_size<volatile _Tp>
-    : public __tuple_size_base_type<_Tp>::type {};
+class _LIBCPP_TYPE_VIS_ONLY tuple_size<__enable_if_tuple_size_imp<
+    volatile _Tp,
+    typename enable_if<!is_const<_Tp>::value>::type,
+    integral_constant<size_t, sizeof(tuple_size<_Tp>)>>>
+    : public integral_constant<size_t, tuple_size<_Tp>::value> {};
 
 template <class _Tp>
-class _LIBCPP_TYPE_VIS_ONLY tuple_size<const volatile _Tp>
-    : public __tuple_size_base_type<_Tp>::type {};
+class _LIBCPP_TYPE_VIS_ONLY tuple_size<__enable_if_tuple_size_imp<
+    const volatile _Tp,
+    integral_constant<size_t, sizeof(tuple_size<_Tp>)>>>
+    : public integral_constant<size_t, tuple_size<_Tp>::value> {};
+
+#else
+template <class _Tp> class _LIBCPP_TYPE_VIS_ONLY tuple_size<const _Tp> : tuple_size<_Tp> {};
+template <class _Tp> class _LIBCPP_TYPE_VIS_ONLY tuple_size<volatile _Tp> : tuple_size<_Tp> {};
+template <class _Tp> class _LIBCPP_TYPE_VIS_ONLY tuple_size<const volatile _Tp> : tuple_size<_Tp> {};
+#endif
 
 template <size_t _Ip, class _Tp> class _LIBCPP_TYPE_VIS_ONLY tuple_element;
 

Modified: libcxx/trunk/test/libcxx/test/config.py
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/libcxx/test/config.py?rev=291019&r1=291018&r2=291019&view=diff
==============================================================================
--- libcxx/trunk/test/libcxx/test/config.py (original)
+++ libcxx/trunk/test/libcxx/test/config.py Wed Jan  4 16:38:46 2017
@@ -335,9 +335,13 @@ class Configuration(object):
         if self.get_lit_bool('has_libatomic', False):
             self.config.available_features.add('libatomic')
 
-        if '__cpp_if_constexpr' not in self.cxx.dumpMacros():
+        macros = self.cxx.dumpMacros()
+        if '__cpp_if_constexpr' not in macros:
             self.config.available_features.add('libcpp-no-if-constexpr')
 
+        if '__cpp_structured_bindings' not in macros:
+            self.config.available_features.add('libcpp-no-structured-bindings')
+
     def configure_compile_flags(self):
         no_default_flags = self.get_lit_bool('no_default_flags', False)
         if not no_default_flags:

Modified: libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size.pass.cpp?rev=291019&r1=291018&r2=291019&view=diff
==============================================================================
--- libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size.pass.cpp (original)
+++ libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size.pass.cpp Wed Jan  4 16:38:46 2017
@@ -20,13 +20,6 @@
 #include <tuple>
 #include <type_traits>
 
-template <class T, class = decltype(std::tuple_size<T>::value)>
-constexpr bool has_value(int) { return true; }
-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()
 {
@@ -40,21 +33,10 @@ void test()
                                    std::tuple_size<const volatile T> >::value), "");
 }
 
-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()
 {
     test<std::tuple<>, 0>();
     test<std::tuple<int>, 1>();
     test<std::tuple<char, int>, 2>();
     test<std::tuple<char, char*, int>, 3>();
-    test_tuple_size_value_sfinae();
 }

Added: libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.fail.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.fail.cpp?rev=291019&view=auto
==============================================================================
--- libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.fail.cpp (added)
+++ libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.fail.cpp Wed Jan  4 16:38:46 2017
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... Types> class tuple;
+
+// template <class... Types>
+//   class tuple_size<tuple<Types...>>
+//     : public integral_constant<size_t, sizeof...(Types)> { };
+
+// UNSUPPORTED: c++98, c++03
+
+#include <tuple>
+#include <array>
+#include <type_traits>
+
+struct Dummy1 {};
+struct Dummy2 {};
+struct Dummy3 {};
+
+template <>
+class std::tuple_size<Dummy1> {
+public:
+  static size_t value;
+};
+
+template <>
+class std::tuple_size<Dummy2> {
+public:
+  static void value() {}
+};
+
+template <>
+class std::tuple_size<Dummy3> {};
+
+int main()
+{
+  // Test that tuple_size<const T> is not incomplete when tuple_size<T>::value
+  // is well-formed but not a constant expression.
+  {
+    // expected-error at __tuple:* 1 {{is not a constant expression}}
+    (void)std::tuple_size<const Dummy1>::value; // expected-note {{here}}
+  }
+  // Test that tuple_size<const T> is not incomplete when tuple_size<T>::value
+  // is well-formed but not convertible to size_t.
+  {
+    // expected-error at __tuple:* 1 {{value of type 'void ()' is not implicitly convertible to 'unsigned long'}}
+    (void)std::tuple_size<const Dummy2>::value; // expected-note {{here}}
+  }
+  // Test that tuple_size<const T> generates an error when tuple_size<T> is
+  // complete but ::value isn't a constant expression convertible to size_t.
+  {
+    // expected-error at __tuple:* 1 {{no member named 'value'}}
+    (void)std::tuple_size<const Dummy3>::value; // expected-note {{here}}
+  }
+}

Added: libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.pass.cpp?rev=291019&view=auto
==============================================================================
--- libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.pass.cpp (added)
+++ libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.pass.cpp Wed Jan  4 16:38:46 2017
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... Types> class tuple;
+
+// template <class... Types>
+//   class tuple_size<tuple<Types...>>
+//     : public integral_constant<size_t, sizeof...(Types)> { };
+
+// XFAIL: gcc-4.9
+// UNSUPPORTED: c++98, c++03
+
+#include <tuple>
+#include <array>
+#include <type_traits>
+
+template <class T, size_t Size = sizeof(std::tuple_size<T>)>
+constexpr bool is_complete(int) { static_assert(Size > 0, ""); return true; }
+template <class> constexpr bool is_complete(long) { return false; }
+template <class T> constexpr bool is_complete() { return is_complete<T>(0); }
+
+struct Dummy1 {};
+struct Dummy2 {};
+
+namespace std {
+template <> class tuple_size<Dummy1> : public integral_constant<size_t, 0> {};
+}
+
+template <class T>
+void test_complete() {
+  static_assert(is_complete<T>(), "");
+  static_assert(is_complete<const T>(), "");
+  static_assert(is_complete<volatile T>(), "");
+  static_assert(is_complete<const volatile T>(), "");
+}
+
+template <class T>
+void test_incomplete() {
+  static_assert(!is_complete<T>(), "");
+  static_assert(!is_complete<const T>(), "");
+  static_assert(!is_complete<volatile T>(), "");
+  static_assert(!is_complete<const volatile T>(), "");
+}
+
+
+int main()
+{
+  test_complete<std::tuple<> >();
+  test_complete<std::tuple<int&> >();
+  test_complete<std::tuple<int&&, int&, void*>>();
+  test_complete<std::pair<int, long> >();
+  test_complete<std::array<int, 5> >();
+  test_complete<Dummy1>();
+
+  test_incomplete<void>();
+  test_incomplete<int>();
+  test_incomplete<std::tuple<int>&>();
+  test_incomplete<Dummy2>();
+}

Added: libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_structured_bindings.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_structured_bindings.pass.cpp?rev=291019&view=auto
==============================================================================
--- libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_structured_bindings.pass.cpp (added)
+++ libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_structured_bindings.pass.cpp Wed Jan  4 16:38:46 2017
@@ -0,0 +1,138 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... Types> class tuple;
+
+// template <class... Types>
+//   class tuple_size<tuple<Types...>>
+//     : public integral_constant<size_t, sizeof...(Types)> { };
+
+// UNSUPPORTED: c++98, c++03, c++11, c++14
+// XFAIL: libcpp-no-structured-bindings
+
+#include <tuple>
+#include <array>
+#include <type_traits>
+#include <cassert>
+
+struct S { int x; };
+
+void test_decomp_user_type() {
+  {
+    S s{99};
+    auto [m1] = s;
+    auto& [r1] = s;
+    assert(m1 == 99);
+    assert(&r1 == &s.x);
+  }
+  {
+    S const s{99};
+    auto [m1] = s;
+    auto& [r1] = s;
+    assert(m1 == 99);
+    assert(&r1 == &s.x);
+  }
+}
+
+void test_decomp_tuple() {
+  typedef std::tuple<int> T;
+  {
+    T s{99};
+    auto [m1] = s;
+    auto& [r1] = s;
+    assert(m1 == 99);
+    assert(&r1 == &std::get<0>(s));
+  }
+  {
+    T const s{99};
+    auto [m1] = s;
+    auto& [r1] = s;
+    assert(m1 == 99);
+    assert(&r1 == &std::get<0>(s));
+  }
+}
+
+
+void test_decomp_pair() {
+  typedef std::pair<int, double> T;
+  {
+    T s{99, 42.1};
+    auto [m1, m2] = s;
+    auto& [r1, r2] = s;
+    assert(m1 == 99);
+    assert(&r1 == &std::get<0>(s));
+  }
+  {
+    T const s{99, 42.1};
+    auto [m1, m2] = s;
+    auto& [r1, r2] = s;
+    assert(m1 == 99);
+    assert(&r1 == &std::get<0>(s));
+  }
+}
+
+void test_decomp_array() {
+  typedef std::array<int, 3> T;
+  {
+    T s{{99, 42, -1}};
+    auto [m1, m2, m3] = s;
+    auto& [r1, r2, r3] = s;
+    assert(m1 == 99);
+    assert(&r1 == &std::get<0>(s));
+  }
+  {
+    T const s{{99, 42, -1}};
+    auto [m1, m2, m3] = s;
+    auto& [r1, r2, r3] = s;
+    assert(m1 == 99);
+    assert(&r1 == &std::get<0>(s));
+  }
+}
+
+struct Test {
+  int x;
+};
+
+template <size_t N>
+int get(Test const&) { static_assert(N == 0, ""); return -1; }
+
+template <>
+class std::tuple_element<0, Test> {
+public:
+  typedef int type;
+};
+
+void test_before_tuple_size_specialization() {
+  Test const t{99};
+  auto& [p] = t;
+  assert(p == 99);
+}
+
+template <>
+class std::tuple_size<Test> {
+public:
+  static const size_t value = 1;
+};
+
+void test_after_tuple_size_specialization() {
+  Test const t{99};
+  auto& [p] = t;
+  assert(p == -1);
+}
+
+int main() {
+  test_decomp_user_type();
+  test_decomp_tuple();
+  test_decomp_pair();
+  test_decomp_array();
+  test_before_tuple_size_specialization();
+  test_after_tuple_size_specialization();
+}

Added: libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_value_sfinae.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_value_sfinae.pass.cpp?rev=291019&view=auto
==============================================================================
--- libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_value_sfinae.pass.cpp (added)
+++ libcxx/trunk/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_value_sfinae.pass.cpp Wed Jan  4 16:38:46 2017
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... Types> class tuple;
+
+// template <class... Types>
+//   class tuple_size<tuple<Types...>>
+//     : public integral_constant<size_t, sizeof...(Types)> { };
+
+// XFAIL: gcc-4.9
+// UNSUPPORTED: c++98, c++03
+
+#include <tuple>
+#include <type_traits>
+
+template <class T, class = decltype(std::tuple_size<T>::value)>
+constexpr bool has_value(int) { return true; }
+template <class> constexpr bool has_value(long) { return false; }
+template <class T> constexpr bool has_value() { return has_value<T>(0); }
+
+struct Dummy {};
+
+int main() {
+  // 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>&>(), "");
+}




More information about the cfe-commits mailing list