Implement LWG 3255 for std::span constructors
authorJonathan Wakely <jwakely@redhat.com>
Mon, 30 Sep 2019 11:52:08 +0000 (12:52 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Mon, 30 Sep 2019 11:52:08 +0000 (12:52 +0100)
Also fix the constraints on span(Container&) and span(const Container&)
constructors so that they aren't used for const spans or const arrays.

* include/std/span (span(element_type(&)[N]))
(span(array<value_type, N>&), span(const array<value_type, N>&)):
Deduce array element type to allow safe const conversions (LWG 3255).
[!_GLIBCXX_P1394] (span(Container&), span(const Container&)): Use
remove_cv_t on arguments to __is_std_span and __is_std_array.
* testsuite/23_containers/span/lwg3255.cc: New test.

From-SVN: r276298

libstdc++-v3/ChangeLog
libstdc++-v3/include/std/span
libstdc++-v3/testsuite/23_containers/span/lwg3255.cc [new file with mode: 0644]

index c537807107ca3d6d1d431819c82b07eb33efafc7..06b4d007bf0a23d6496de3dd1e43c000be648aab 100644 (file)
@@ -1,5 +1,12 @@
 2019-09-30  Jonathan Wakely  <jwakely@redhat.com>
 
+       * include/std/span (span(element_type(&)[N]))
+       (span(array<value_type, N>&), span(const array<value_type, N>&)):
+       Deduce array element type to allow safe const conversions (LWG 3255).
+       [!_GLIBCXX_P1394] (span(Container&), span(const Container&)): Use
+       remove_cv_t on arguments to __is_std_span and __is_std_array.
+       * testsuite/23_containers/span/lwg3255.cc: New test.
+
        PR libstdc++/77936
        * include/parallel/checkers.h (__is_sorted): Remove unused variable.
 
index 1a0d61c194772e3e9ced0137efe0605afca10ff4..fcec22a6c571aa838c7eb53be6725add8a323156 100644 (file)
@@ -125,6 +125,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       template<typename _Tp>
        using __is_compatible = is_convertible<_Tp(*)[], _Type(*)[]>;
 
+      // _GLIBCXX_RESOLVE_LIB_DEFECTS
+      // 3255. span's array constructor is too strict
+      template<typename _Tp, size_t _ArrayExtent,
+              typename = enable_if_t<_Extent == dynamic_extent
+                                     || _ArrayExtent == _Extent>>
+       using __is_compatible_array  = __is_compatible<_Tp>;
+
     public:
       // member types
       using value_type             = remove_cv_t<_Type>;
@@ -149,9 +156,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       // constructors
 
-      template <typename _Dummy = _Type,
-               enable_if_t<is_same_v<_Dummy, _Type>
-                 && (_Extent == dynamic_extent || _Extent == 0)>* = nullptr>
+      template<bool _DefaultConstructible = (_Extent + 1u) <= 1u,
+              enable_if_t<_DefaultConstructible>* = nullptr>
        constexpr
        span() noexcept : _M_extent(0), _M_ptr(nullptr)
        { }
@@ -159,28 +165,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       constexpr
       span(const span&) noexcept = default;
 
-      template<size_t _ArrayExtent,
-       enable_if_t<_Extent == dynamic_extent || _ArrayExtent == _Extent>*
-         = nullptr>
+      template<typename _Tp, size_t _ArrayExtent,
+              typename = _Require<__is_compatible_array<_Tp, _ArrayExtent>>>
        constexpr
-       span(element_type (&__arr)[_ArrayExtent]) noexcept
+       span(_Tp (&__arr)[_ArrayExtent]) noexcept
        : span(static_cast<pointer>(__arr), _ArrayExtent)
        { }
 
-      template<size_t _ArrayExtent,
-       enable_if_t<_Extent == dynamic_extent || _ArrayExtent == _Extent>*
-         = nullptr>
+      template<typename _Tp, size_t _ArrayExtent,
+              typename = _Require<__is_compatible_array<_Tp, _ArrayExtent>>>
        constexpr
-       span(array<value_type, _ArrayExtent>& __arr) noexcept
-       : span(__arr.data(), _ArrayExtent)
+       span(array<_Tp, _ArrayExtent>& __arr) noexcept
+       : span(static_cast<pointer>(__arr.data()), _ArrayExtent)
        { }
 
-      template<size_t _ArrayExtent,
-       enable_if_t<_Extent == dynamic_extent || _ArrayExtent == _Extent>*
-         = nullptr>
+      template<typename _Tp, size_t _ArrayExtent,
+              typename = _Require<__is_compatible_array<_Tp, _ArrayExtent>>>
        constexpr
-       span(const array<value_type, _ArrayExtent>& __arr) noexcept
-       : span(__arr.data(), _ArrayExtent)
+       span(const array<_Tp, _ArrayExtent>& __arr) noexcept
+       : span(static_cast<pointer>(__arr.data()), _ArrayExtent)
        { }
 
       // NOTE: when the time comes, and P1394 -
@@ -271,8 +274,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     public:
       template<typename _Container, typename = _Require<
                bool_constant<_Extent == dynamic_extent>,
-               __not_<__detail::__is_std_span<_Container>>,
-               __not_<__detail::__is_std_array<_Container>>,
+               __not_<__detail::__is_std_span<remove_cv_t<_Container>>>,
+               __not_<__detail::__is_std_array<remove_cv_t<_Container>>>,
                __not_<is_array<_Container>>,
                __is_compatible_container<_Container>>>
        constexpr
@@ -283,8 +286,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       template<typename _Container, typename = _Require<
                bool_constant<_Extent == dynamic_extent>,
-               __not_<__detail::__is_std_span<_Container>>,
-               __not_<__detail::__is_std_array<_Container>>,
+               __not_<__detail::__is_std_span<remove_cv_t<_Container>>>,
+               __not_<__detail::__is_std_array<remove_cv_t<_Container>>>,
                __not_<is_array<_Container>>,
                __is_compatible_container<const _Container>>>
        constexpr
diff --git a/libstdc++-v3/testsuite/23_containers/span/lwg3255.cc b/libstdc++-v3/testsuite/23_containers/span/lwg3255.cc
new file mode 100644 (file)
index 0000000..638c881
--- /dev/null
@@ -0,0 +1,69 @@
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <span>
+#include <array>
+
+using std::span;
+using std::dynamic_extent;
+using std::array;
+using std::is_constructible_v;
+
+// LWG 3255 span's array constructor is too strict
+
+// FIXME: remove '!' from next line when P0388R4 is implemented:
+static_assert( ! is_constructible_v<span<const int* const>, array<int*, 2>> );
+static_assert( is_constructible_v<span<const int>, array<const int, 4>> );
+
+static_assert( is_constructible_v<span<int, 1>, int(&)[1]> );
+static_assert( is_constructible_v<span<const int, 1>, int(&)[1]> );
+static_assert( is_constructible_v<span<const int, 1>, const int(&)[1]> );
+
+static_assert( is_constructible_v<span<int, 1>, array<int, 1>&> );
+static_assert( is_constructible_v<span<const int, 1>, array<int, 1>&> );
+static_assert( is_constructible_v<span<const int, 1>, array<const int, 1>&> );
+
+static_assert( is_constructible_v<span<int, 1>, const array<int, 1>&> );
+static_assert( is_constructible_v<span<const int, 1>, const array<int, 1>&> );
+static_assert( is_constructible_v<span<const int, 1>, const array<const int, 1>&> );
+
+static_assert( !is_constructible_v<span<int, 1>, int(&)[2]> );
+static_assert( !is_constructible_v<span<const int, 1>, int(&)[2]> );
+static_assert( !is_constructible_v<span<const int, 1>, const int(&)[2]> );
+
+static_assert( !is_constructible_v<span<int, 1>, array<int, 2>&> );
+static_assert( !is_constructible_v<span<const int, 1>, array<int, 2>&> );
+static_assert( !is_constructible_v<span<const int, 1>, array<const int, 2>&> );
+
+static_assert( !is_constructible_v<span<int, 1>, const array<int, 2>&> );
+static_assert( !is_constructible_v<span<const int, 1>, const array<int, 2>&> );
+static_assert( !is_constructible_v<span<const int, 1>, const array<const int, 2>&> );
+
+static_assert( is_constructible_v<span<int>, int(&)[2]> );
+static_assert( is_constructible_v<span<const int>, int(&)[2]> );
+static_assert( is_constructible_v<span<const int>, const int(&)[2]> );
+
+static_assert( is_constructible_v<span<int>, array<int, 2>&> );
+static_assert( is_constructible_v<span<const int>, array<int, 2>&> );
+static_assert( is_constructible_v<span<const int>, array<const int, 2>&> );
+
+static_assert( is_constructible_v<span<int>, const array<int, 2>&> );
+static_assert( is_constructible_v<span<const int>, const array<int, 2>&> );
+static_assert( is_constructible_v<span<const int>, const array<const int, 2>&> );