public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [ PATCH ] C++20 <span>
@ 2019-08-30 20:05 JeanHeyd Meneide
  2019-08-30 21:37 ` Jonathan Wakely
  0 siblings, 1 reply; 9+ messages in thread
From: JeanHeyd Meneide @ 2019-08-30 20:05 UTC (permalink / raw)
  To: gcc-patches, libstdc++

[-- Attachment #1: Type: text/plain, Size: 719 bytes --]

This patch implements <span> as it currently exists in the C++20 Working Draft.

Notes:
- __gnu_cxx::__normal_iterator is not fully constexpr, so its not used here
- P1394 might be slated to end up in C++20 per National Body Comments.
Therefore, an early implementation is left in an
implementation-defined _GLIBCXX_P1394 define.

2019-08-30  JeanHeyd "ThePhD" Meneide  <phdofthehouse@gmail.com>

        * include/std/span: Implement the entirety of span.
        * include/bits/range_access.h: Add __adl_* versions of access functions.
        * testsuite/23_containers/span/everything.cc: constexpr and
non-constexpr tests.
        * include/Makefile.in: Add span to install.
        * include/Makefile.am: Likewise

[-- Attachment #2: ThePhD.span.patch --]
[-- Type: application/octet-stream, Size: 31541 bytes --]

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 3fe80f32cc4..b8b786d9260 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -68,6 +68,7 @@ std_headers = \
 	${std_srcdir}/scoped_allocator \
 	${std_srcdir}/set \
 	${std_srcdir}/shared_mutex \
+	${std_srcdir}/span \
 	${std_srcdir}/sstream \
 	${std_srcdir}/stack \
 	${std_srcdir}/stdexcept \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index b675d356cd4..cd1e9df5482 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -412,6 +412,7 @@ std_headers = \
 	${std_srcdir}/scoped_allocator \
 	${std_srcdir}/set \
 	${std_srcdir}/shared_mutex \
+	${std_srcdir}/span \
 	${std_srcdir}/sstream \
 	${std_srcdir}/stack \
 	${std_srcdir}/stdexcept \
diff --git a/libstdc++-v3/include/bits/range_access.h b/libstdc++-v3/include/bits/range_access.h
index d1e74711433..3acaebadcf1 100644
--- a/libstdc++-v3/include/bits/range_access.h
+++ b/libstdc++-v3/include/bits/range_access.h
@@ -318,6 +318,72 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 #endif // C++17
 
+#if __cplusplus > 201703L
+  // "why are these in namespace std:: and not __gnu_cxx:: ?"
+  // because if we don't put them here it's impossible to 
+  // have implicit ADL with "using std::begin/end/size/data;".
+  template <typename _Container>
+    constexpr auto
+    __adl_begin(_Container& __cont) noexcept(noexcept(begin(__cont)))
+    { return begin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_end(_Container& __cont) noexcept(noexcept(end(__cont)))
+    { return end(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_cbegin(_Container& __cont) noexcept(noexcept(cbegin(__cont)))
+    { return cbegin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_cend(_Container& __cont) noexcept(noexcept(cend(__cont)))
+    { return cend(__cont); }
+
+  template <typename _Container>
+    constexpr auto
+    __adl_rbegin(_Container& __cont) noexcept(noexcept(rbegin(__cont)))
+    { return rbegin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_rend(_Container& __cont) noexcept(noexcept(rend(__cont)))
+    { return rend(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_crbegin(_Container& __cont) noexcept(noexcept(crbegin(__cont)))
+    { return crbegin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_crend(_Container& __cont) noexcept(noexcept(crend(__cont)))
+    { return crend(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_data(_Container& __cont) noexcept(noexcept(data(__cont)))
+    { return data(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_cdata(_Container& __cont) noexcept(noexcept(cdata(__cont)))
+    { return cdata(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_size(_Container& __cont) noexcept(noexcept(size(__cont)))
+    { return size(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_empty(_Container& __cont) noexcept(noexcept(empty(__cont)))
+    { return empty(__cont); }
+  
+#endif // C++20
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 
diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span
new file mode 100644
index 00000000000..9bbf9ed9e23
--- /dev/null
+++ b/libstdc++-v3/include/std/span
@@ -0,0 +1,549 @@
+// Components for manipulating non-owning sequences of objects -*- C++ -*-
+
+// Copyright (C) 2019-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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file span
+ *  This is a Standard C++ Library header.
+ */
+
+//
+// P0122 span library
+// Contributed by ThePhD
+//
+
+#ifndef _GLIBCXX_SPAN
+#define _GLIBCXX_SPAN 1
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+
+#include <cstddef>
+#include <type_traits>
+#include <tuple>
+#include <utility>
+#include <array>
+#include <bits/stl_iterator.h>
+#include <bits/range_access.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+  _GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+// WARNING: they forgot this feature test macro
+// get on someone's back about it in Belfast!!!
+#define __cpp_lib_span 201911
+
+  inline constexpr ::std::size_t dynamic_extent = static_cast<::std::size_t>(-1);
+
+  namespace __detail
+  {
+
+    template<typename _Element, typename _ToElement>
+    using __is_base_derived_safe_convertible =
+      ::std::is_convertible<_Element (*)[], _ToElement (*)[]>;
+
+    template<typename _Element, typename _ToElement>
+    static constexpr inline bool __is_base_derived_safe_convertible_v =
+      __is_base_derived_safe_convertible<_Element, _ToElement>::value;
+
+    template<typename>
+    struct __is_std_array : ::std::false_type
+    {
+    };
+
+    template<typename _Element, ::std::size_t _Extent>
+    struct __is_std_array<::std::array<_Element, _Extent>> : ::std::true_type
+    {
+    };
+
+#ifdef _GLIBCXX_DEBUG
+    template<typename _Element, ::std::size_t _Extent>
+    struct __is_std_array<::std::__debug::array<_Element, _Extent>> : ::std::true_type
+    {
+    };
+#endif // debug/array
+
+    template<typename _Type>
+    inline constexpr bool __is_std_array_v = __is_std_array<_Type>::value;
+
+    template<::std::size_t _Extent>
+    struct __extent_storage
+    {
+
+      constexpr __extent_storage() noexcept = default;
+      constexpr __extent_storage(::std::size_t) noexcept
+      {
+      }
+
+      static constexpr ::std::size_t
+      _M_extent() noexcept
+      {
+        return _Extent;
+      }
+    };
+
+    template<>
+    struct __extent_storage<static_cast<::std::size_t>(-1)>
+    {
+      ::std::size_t _M_extent_value;
+
+      constexpr __extent_storage() noexcept : _M_extent_value(0){};
+      constexpr __extent_storage(::std::size_t __extent) noexcept : _M_extent_value(__extent)
+      {
+      }
+
+      constexpr ::std::size_t
+      _M_extent() const noexcept
+      {
+        return this->_M_extent_value;
+      }
+    };
+
+  } // namespace __detail
+
+  template<typename _Type, ::std::size_t _Extent = dynamic_extent>
+  class span : private ::std::__detail::__extent_storage<_Extent>
+  {
+  public:
+    // member types
+    using value_type             = ::std::remove_cv_t<_Type>;
+    using element_type           = _Type;
+    using index_type             = ::std::size_t;
+    using reference              = element_type&;
+    using const_reference        = const element_type&;
+    using pointer                = _Type*;
+    using const_pointer          = const _Type*;
+    using iterator               = pointer;
+    using const_iterator         = const_pointer;
+    using reverse_iterator       = ::std::reverse_iterator<iterator>;
+    using const_reverse_iterator = ::std::reverse_iterator<const_iterator>;
+    using difference_type        = ::std::ptrdiff_t;
+    // Official wording has no size_type -- why??
+    // using size_type = ::std::size_t;
+
+    // member constants
+    static inline constexpr ::std::size_t extent = _Extent;
+
+  private:
+    using __base_t = ::std::__detail::__extent_storage<_Extent>;
+
+  public:
+    // constructors
+    constexpr span() noexcept : __base_t(), _M_ptr(nullptr)
+    {
+    }
+
+    constexpr span(const span&) noexcept = default;
+
+    template<::std::size_t _ArrayExtent,
+      ::std::enable_if_t<(_Extent == ::std::dynamic_extent || _ArrayExtent == _Extent) &&
+                         ::std::__detail::__is_base_derived_safe_convertible_v<
+                           ::std::remove_pointer_t<decltype(
+                             ::std::__adl_data(::std::declval<element_type (&)[_ArrayExtent]>()))>,
+                           element_type>>* = nullptr>
+    constexpr span(element_type (&__arr)[_ArrayExtent]) noexcept(noexcept(::std::__adl_data(__arr)))
+    : span(::std::__adl_data(__arr), _ArrayExtent)
+    {
+    }
+
+    template<::std::size_t _ArrayExtent,
+      ::std::enable_if_t<(_Extent == ::std::dynamic_extent || _ArrayExtent == _Extent) &&
+                         ::std::__detail::__is_base_derived_safe_convertible_v<
+                           ::std::remove_pointer_t<decltype(::std::__adl_data(
+                             ::std::declval<::std::array<value_type, _ArrayExtent>&>()))>,
+                           element_type>>* = nullptr>
+    constexpr span(::std::array<value_type, _ArrayExtent>& __arr) noexcept(
+      noexcept(::std::__adl_data(__arr)))
+    : span(::std::__adl_data(__arr), _ArrayExtent)
+    {
+    }
+
+    template<::std::size_t _ArrayExtent,
+      ::std::enable_if_t<(_Extent == ::std::dynamic_extent || _ArrayExtent == _Extent) &&
+                         ::std::__detail::__is_base_derived_safe_convertible_v<
+                           ::std::remove_pointer_t<decltype(::std::__adl_data(
+                             ::std::declval<const ::std::array<value_type, _ArrayExtent>&>()))>,
+                           element_type>>* = nullptr>
+    constexpr span(const ::std::array<value_type, _ArrayExtent>& __arr) noexcept(
+      noexcept(::std::__adl_data(__arr)))
+    : span(::std::__adl_data(__arr), _ArrayExtent)
+    {
+    }
+
+    // NOTE: when the time comes, and P1394 -
+    // range constructors for std::span - ships in
+    // the standard, delete the #else block and remove
+    // the conditional
+    // if the paper fails, delete #if block
+    // and keep the crappy #else block
+    // and then cry that NB comments failed C++20...
+    // but maybe for C++23?
+#if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
+    template<typename _Range,
+      ::std::enable_if_t<
+        (_Extent == dynamic_extent) &&
+        !::std::is_same_v<::std::remove_cvref_t<_Range>, span> &&
+        !::std::__detail::__is_std_array_v<::std::remove_cvref_t<_Range>> &&
+        !::std::is_array_v<::std::remove_cvref_t<_Range>> &&
+        ::std::__detail::__is_base_derived_safe_convertible_v<
+          ::std::remove_pointer_t<decltype(::std::__adl_data(::std::declval<_Range&>()))>,
+          element_type>>* = nullptr>
+    constexpr span(_Range&& __range) noexcept(
+      noexcept(::std::__adl_data(__range)) && noexcept(::std::__adl_size(__range)))
+    : span(::std::__adl_data(__range), ::std::__adl_size(__range))
+    {
+    }
+
+    template<typename _ContiguousIterator, typename _Sentinel,
+      ::std::enable_if_t<!::std::is_convertible_v<_Sentinel, index_type> &&
+                         ::std::__detail::__is_base_derived_safe_convertible_v<
+                           ::std::remove_reference_t<
+                             typename ::std::iterator_traits<_ContiguousIterator>::reference>,
+                           element_type>>* = nullptr>
+    constexpr span(_ContiguousIterator __first, _Sentinel __last)
+    : span(::std::move(__first), static_cast<index_type>(__last - __first))
+    {
+    }
+
+    template<typename _ContiguousIterator>
+    constexpr span(_ContiguousIterator __first, index_type __count) noexcept(
+      noexcept(::std::to_address(__first)))
+    : __base_t(__count), _M_ptr(::std::to_address(__first))
+    {
+    }
+#else
+    template<typename _Container,
+      ::std::enable_if_t<
+        (_Extent == dynamic_extent) &&
+        !::std::is_same_v<::std::remove_cvref_t<_Container>, span> &&
+        !::std::__detail::__is_std_array_v<
+          ::std::remove_cvref_t<_Container>> &&
+        !::std::is_array_v<::std::remove_cvref_t<_Container>> &&
+        ::std::__detail::__is_base_derived_safe_convertible_v<
+          ::std::remove_pointer_t<decltype(::std::__adl_data(::std::declval<_Container&>()))>,
+          element_type>>* = nullptr>
+    constexpr span(_Container& __range) noexcept(
+      noexcept(::std::__adl_data(__range)) && noexcept(::std::__adl_size(__range)))
+    : span(::std::__adl_data(__range), ::std::__adl_size(__range))
+    {
+    }
+
+    template<typename _Container,
+      ::std::enable_if_t<
+        (_Extent == dynamic_extent) &&
+        !::std::is_same_v<::std::remove_cvref_t<_Container>, span> &&
+        !::std::__detail::__is_std_array_v<
+          ::std::remove_cvref_t<_Container>> &&
+        !::std::is_array_v<::std::remove_cvref_t<_Container>> &&
+        ::std::__detail::__is_base_derived_safe_convertible_v<
+          ::std::remove_pointer_t<decltype(::std::__adl_data(::std::declval<_Container&>()))>,
+          element_type>>* = nullptr>
+    constexpr span(const _Container& __range) noexcept(
+      noexcept(::std::__adl_data(__range)) && noexcept(::std::__adl_size(__range)))
+    : span(::std::__adl_data(__range), ::std::__adl_size(__range))
+    {
+    }
+
+    constexpr span(pointer __first, pointer __last) noexcept
+    : span(::std::move(__first), static_cast<index_type>(__last - __first))
+    {
+    }
+    constexpr span(pointer __first, index_type __count) noexcept
+    : __base_t(__count), _M_ptr(static_cast<pointer>(__first))
+    {
+    }
+#endif // P1394
+
+    // assignment
+    constexpr span&
+    operator=(const span&) noexcept = default;
+
+    // observers
+    constexpr reference
+    front() noexcept
+    {
+      return *this->begin();
+    }
+
+    constexpr const_reference
+    front() const noexcept
+    {
+      return *this->begin();
+    }
+
+    constexpr reference
+    back() noexcept
+    {
+      return *(this->begin() + 1);
+    }
+
+    constexpr const_reference
+    back() const noexcept
+    {
+      return *(this->end() - 1);
+    }
+
+    constexpr reference operator[](index_type __idx) noexcept
+    {
+      return *(this->_M_ptr + __idx);
+    }
+
+    constexpr const_reference operator[](index_type __idx) const noexcept
+    {
+      return *(this->_M_ptr + __idx);
+    }
+
+    constexpr pointer
+    data() const noexcept
+    {
+      return this->_M_ptr;
+    }
+
+    constexpr index_type
+    size() const noexcept
+    {
+      return this->__base_t::_M_extent();
+    }
+
+    constexpr index_type
+    size_bytes() const noexcept
+    {
+      return this->__base_t::_M_extent() * sizeof(element_type);
+    }
+
+    constexpr bool
+    empty() const noexcept
+    {
+      return size() == 0;
+    }
+
+    // observers: iterators
+    constexpr iterator
+    begin() const noexcept
+    {
+      return iterator(this->_M_ptr);
+    }
+
+    constexpr const_iterator
+    cbegin() const noexcept
+    {
+      return const_iterator(this->_M_ptr);
+    }
+
+    constexpr iterator
+    end() const noexcept
+    {
+      return iterator(this->_M_ptr + this->size());
+    }
+
+    constexpr const_iterator
+    cend() const noexcept
+    {
+      return const_iterator(this->_M_ptr + this->size());
+    }
+
+    constexpr reverse_iterator
+    rbegin() const noexcept
+    {
+      return reverse_iterator(this->begin());
+    }
+
+    constexpr const_reverse_iterator
+    crbegin() const noexcept
+    {
+      return const_reverse_iterator(this->cbegin());
+    }
+
+    constexpr reverse_iterator
+    rend() const noexcept
+    {
+      return reverse_iterator(this->end());
+    }
+
+    constexpr const_reverse_iterator
+    crend() const noexcept
+    {
+      return const_reverse_iterator(this->cend());
+    }
+
+    // observers: subranges
+    template<::std::size_t _Count>
+    constexpr auto
+    first() const
+    {
+      using __span_t = ::std::span<element_type, _Count>;
+      return __span_t(this->data(), _Count);
+    }
+
+    constexpr auto
+    first(index_type __count) const
+    {
+      using __span_t = ::std::span<element_type, ::std::dynamic_extent>;
+      return __span_t(this->data(), __count);
+    }
+
+    template<::std::size_t _Count>
+    constexpr ::std::span<element_type, _Count>
+    last() const
+    {
+      static_assert(
+        _Count == ::std::dynamic_extent || _Extent == ::std::dynamic_extent || _Count <= _Extent,
+        "bad span length");
+      using __span_t = ::std::span<element_type, _Count>;
+      return __span_t(this->data() + (this->size() - _Count), _Count);
+    }
+
+    constexpr auto
+    last(index_type __count) const
+    {
+      using __span_t = ::std::span<element_type, ::std::dynamic_extent>;
+      return __span_t(this->data() + (this->size() - __count), __count);
+    }
+
+    template<::std::size_t _Offset, ::std::size_t _Count = ::std::dynamic_extent>
+    constexpr auto
+    subspan() const
+    {
+      static_assert(_Count == ::std::dynamic_extent || _Extent == ::std::dynamic_extent ||
+                      (_Offset + _Count) <= _Extent,
+        "bad span length");
+      constexpr ::std::size_t __span_extent =
+        (_Count != ::std::dynamic_extent
+            ? _Count
+            : (_Extent != ::std::dynamic_extent ? _Extent - _Offset : ::std::dynamic_extent));
+      using __span_t = ::std::span<element_type, __span_extent>;
+      return __span_t(this->data() + _Offset,
+        (_Count == ::std::dynamic_extent ? this->size() - _Offset : _Count));
+    }
+
+    constexpr auto
+    subspan(index_type __offset, index_type __count = ::std::dynamic_extent) const
+    {
+      using __span_t = ::std::span<element_type, ::std::dynamic_extent>;
+      return __span_t(this->data() + __offset,
+        __count == ::std::dynamic_extent ? this->size() - __offset : __count);
+    }
+
+    // observers: range helpers
+    friend constexpr iterator
+    begin(span __sp) noexcept
+    {
+      return __sp.begin();
+    }
+
+    friend constexpr iterator
+    end(span __sp) noexcept
+    {
+      return __sp.end();
+    }
+
+  private:
+    pointer _M_ptr;
+  };
+
+  template<typename _Type, ::std::size_t _Extent>
+  auto as_bytes(::std::span<_Type, _Extent> __sp) noexcept
+  {
+    constexpr ::std::size_t __byte_extent =
+      (_Extent == ::std::dynamic_extent
+          ? _Extent
+          : (_Extent * sizeof(typename ::std::span<_Type, _Extent>::element_type)));
+    using __byte_span_t = ::std::span<const ::std::byte, __byte_extent>;
+    return __byte_span_t(reinterpret_cast<const ::std::byte*>(__sp.data()), __sp.size_bytes());
+  }
+
+  template<typename _Type, ::std::size_t _Extent>
+  auto as_writable_bytes(::std::span<_Type, _Extent> __sp) noexcept
+  {
+    constexpr ::std::size_t __byte_extent =
+      (_Extent == ::std::dynamic_extent
+          ? _Extent
+          : (_Extent * sizeof(typename ::std::span<_Type, _Extent>::element_type)));
+    using __byte_span_t = ::std::span<::std::byte, __byte_extent>;
+    return __byte_span_t(reinterpret_cast<::std::byte*>(__sp.data()), __sp.size_bytes());
+  }
+
+  // tuple helpers
+  template<::std::size_t _Index, typename _Type, ::std::size_t _Extent,
+    ::std::enable_if_t<(_Extent > 0) && (_Index < _Extent)>* = nullptr>
+  constexpr typename ::std::span<_Type, _Extent>::reference get(
+    ::std::span<_Type, _Extent> __sp) noexcept
+  {
+    return __sp[_Index];
+  }
+
+  template<typename _Type, ::std::size_t _Extent>
+  class tuple_size<::std::span<_Type, _Extent>>
+  : public ::std::integral_constant<::std::size_t, _Extent>
+  {
+  };
+
+  template<typename _Type>
+  class tuple_size<::std::span<_Type, ::std::dynamic_extent>>;
+
+  template<::std::size_t _Index, typename _Type, ::std::size_t _Extent>
+  struct tuple_element<_Index, ::std::span<_Type, _Extent>>
+  {
+    static_assert(_Index < _Extent, "invalid index");
+    using type = typename ::std::span<_Type, _Extent>::element_type;
+  };
+
+  // deduction guides
+  template<typename _Type, ::std::size_t _ArrayExtent>
+  span(_Type(&)[_ArrayExtent])->span<_Type, _ArrayExtent>;
+
+  template<typename _Type, ::std::size_t _ArrayExtent>
+  span(::std::array<_Type, _ArrayExtent>&)->span<_Type, _ArrayExtent>;
+
+  template<typename _Type, ::std::size_t _ArrayExtent>
+  span(const ::std::array<_Type, _ArrayExtent>&)->span<const _Type, _ArrayExtent>;
+
+#if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
+
+  template<typename _ContiguousIterator, typename _Sentinel>
+  span(_ContiguousIterator, _Sentinel)
+    ->span<
+      ::std::remove_reference_t<typename ::std::iterator_traits<_ContiguousIterator>::reference>>;
+
+  template<typename _Range>
+  span(_Range &&)
+    ->span<::std::remove_reference_t<typename ::std::iterator_traits<decltype(
+      ::std::__adl_begin(::std::declval<_Range&>()))>::reference>>;
+
+#else
+
+  template<typename _Container>
+  span(_Container&)->span<typename _Container::value_type>;
+
+  template<typename _Container>
+  span(const _Container&)->span<const typename _Container::value_type>;
+
+#endif // P1394
+
+  _GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std_GLIBCXX_VISIBILITY(default)
+
+#endif // C++20
+
+#endif
diff --git a/libstdc++-v3/testsuite/23_containers/span/everything.cc b/libstdc++-v3/testsuite/23_containers/span/everything.cc
new file mode 100644
index 00000000000..7e82767ea7f
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/everything.cc
@@ -0,0 +1,202 @@
+// Copyright (C) 2019-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=c++2a" }
+// { dg-do run { target c++2a } }
+
+#include <testsuite_hooks.h>
+
+#include <span>
+#include <type_traits>
+#include <cstdint>
+#include <algorithm>
+#include <cassert>
+
+int
+main()
+{
+  struct alignas(256) strawman
+  {
+    int x;
+    int y;
+    bool z;
+    int w;
+  };
+
+  struct naked_span
+  {
+    char* p;
+    std::size_t n;
+  };
+
+  struct strawman_span
+  {
+    strawman* p;
+    std::size_t n;
+  };
+
+  static_assert(sizeof(std::span<char, 0>) <= sizeof(char*));
+  static_assert(sizeof(std::span<const char, 0>) <= sizeof(const char*));
+  static_assert(sizeof(std::span<strawman, 0>) <= sizeof(strawman*));
+  static_assert(sizeof(std::span<strawman, 1>) <= sizeof(strawman*));
+  static_assert(sizeof(std::span<char>) <= sizeof(naked_span));
+  static_assert(sizeof(std::span<strawman>) <= sizeof(naked_span));
+
+  constexpr static std::array<int, 9> arr_data{ 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+  constexpr auto arr_data_span = std::span(arr_data);
+  static_assert(arr_data_span.size() == 9);
+  static_assert(arr_data_span.size_bytes() == 9 * sizeof(int));
+  static_assert(*arr_data_span.begin() == 0);
+  static_assert(*arr_data_span.data() == 0);
+  static_assert(arr_data_span.front() == 0);
+  static_assert(arr_data_span.back() == 8);
+  static_assert(arr_data_span[0] == 0);
+  static_assert(arr_data_span[1] == 1);
+  static_assert(arr_data_span[2] == 2);
+  static_assert(arr_data_span[3] == 3);
+  static_assert(arr_data_span[4] == 4);
+  static_assert(arr_data_span[5] == 5);
+  static_assert(arr_data_span[6] == 6);
+  static_assert(arr_data_span[7] == 7);
+  static_assert(arr_data_span[8] == 8);
+  static_assert(!arr_data_span.empty());
+  static_assert(decltype(arr_data_span)::extent == 9);
+
+  constexpr static int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+  constexpr auto data_span    = std::span(data);
+  static_assert(data_span.size() == 9);
+  static_assert(data_span.size_bytes() == 9 * sizeof(int));
+  static_assert(*data_span.begin() == 0);
+  static_assert(*data_span.data() == 0);
+  static_assert(data_span.front() == 0);
+  static_assert(data_span.back() == 8);
+  static_assert(data_span[0] == 0);
+  static_assert(data_span[1] == 1);
+  static_assert(data_span[2] == 2);
+  static_assert(data_span[3] == 3);
+  static_assert(data_span[4] == 4);
+  static_assert(data_span[5] == 5);
+  static_assert(data_span[6] == 6);
+  static_assert(data_span[7] == 7);
+  static_assert(data_span[8] == 8);
+  static_assert(!data_span.empty());
+  static_assert(decltype(data_span)::extent == 9);
+
+  constexpr auto data_span_first = data_span.first<3>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_first)>, std::span<const int, 3>>);
+  static_assert(decltype(data_span_first)::extent == 3);
+  static_assert(data_span_first.size() == 3);
+  static_assert(data_span_first.front() == 0);
+  static_assert(data_span_first.back() == 2);
+  static_assert(std::tuple_size_v<decltype(data_span_first)> == 3);
+  static_assert(std::is_same_v<std::tuple_element_t<0, decltype(data_span_first)>, const int>);
+
+  constexpr auto data_span_first_dyn = data_span.first(4);
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_first_dyn)>, std::span<const int>>);
+  static_assert(decltype(data_span_first_dyn)::extent == std::dynamic_extent);
+  static_assert(data_span_first_dyn.size() == 4);
+  static_assert(data_span_first_dyn.front() == 0);
+  static_assert(data_span_first_dyn.back() == 3);
+
+  constexpr auto data_span_last = data_span.last<5>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_last)>, std::span<const int, 5>>);
+  static_assert(decltype(data_span_last)::extent == 5);
+  static_assert(data_span_last.size() == 5);
+  static_assert(data_span_last.front() == 4);
+  static_assert(data_span_last.back() == 8);
+  static_assert(std::tuple_size_v<decltype(data_span_last)> == 5);
+  static_assert(std::is_same_v<std::tuple_element_t<0, decltype(data_span_last)>, const int>);
+
+  constexpr auto data_span_last_dyn = data_span.last(6);
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_last_dyn)>, std::span<const int>>);
+  static_assert(decltype(data_span_last_dyn)::extent == std::dynamic_extent);
+  static_assert(data_span_last_dyn.size() == 6);
+  static_assert(data_span_last_dyn.front() == 3);
+  static_assert(data_span_last_dyn.back() == 8);
+
+  constexpr auto data_span_subspan = data_span.subspan<1, 3>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_subspan)>, std::span<const int, 3>>);
+  static_assert(decltype(data_span_subspan)::extent == 3);
+  static_assert(data_span_subspan.size() == 3);
+  static_assert(data_span_subspan.front() == 1);
+  static_assert(data_span_subspan.back() == 3);
+
+  constexpr auto data_span_subspan_offset = data_span.subspan<8>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_subspan_offset)>, std::span<const int, 1>>);
+  static_assert(decltype(data_span_subspan_offset)::extent == 1);
+  static_assert(data_span_subspan_offset.size() == 1);
+  static_assert(data_span_subspan_offset.front() == 8);
+  static_assert(data_span_subspan_offset.back() == 8);
+
+  constexpr auto data_span_subspan_empty = data_span.subspan(9, 0);
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_subspan_empty)>, std::span<const int>>);
+  static_assert(decltype(data_span_subspan_empty)::extent == std::dynamic_extent);
+  static_assert(data_span_subspan_empty.size() == 0);
+
+  constexpr auto data_span_subspan_empty_static = data_span.subspan<9>();
+  static_assert(std::is_same_v<std::remove_cv_t<decltype(data_span_subspan_empty_static)>,
+    std::span<const int, 0>>);
+  static_assert(decltype(data_span_subspan_empty_static)::extent == 0);
+  static_assert(data_span_subspan_empty.size() == 0);
+
+  std::span<short> shorts{};
+  bool really_empty0 = shorts.empty();
+  bool really_empty1 = std::empty(shorts);
+  bool really_empty2 = shorts.data() == nullptr;
+  bool really_empty3 = shorts.begin() == shorts.end();
+  bool really_empty4 = shorts.cbegin() == shorts.cend();
+  bool really_empty =
+    really_empty0 && really_empty1 && really_empty2 && really_empty3 && really_empty4;
+  (void)really_empty;
+  VERIFY(really_empty);
+
+  std::vector<std::int_least32_t> value{ 0 };
+  std::span<int32_t> muh_span(value);
+  VERIFY(muh_span.size() == 1);
+  std::byte* original_bytes                  = reinterpret_cast<std::byte*>(value.data());
+  original_bytes[0]                          = static_cast<std::byte>(1);
+  original_bytes[1]                          = static_cast<std::byte>(2);
+  original_bytes[2]                          = static_cast<std::byte>(3);
+  original_bytes[3]                          = static_cast<std::byte>(4);
+  std::span<const std::byte> muh_byte_span   = std::as_bytes(muh_span);
+  std::span<std::byte> muh_mutable_byte_span = std::as_writable_bytes(muh_span);
+  std::span<std::byte> muh_original_byte_span(original_bytes, original_bytes + 4);
+  bool definitely_reinterpret_casted0 = std::equal(muh_byte_span.cbegin(), muh_byte_span.cend(),
+    muh_original_byte_span.cbegin(), muh_original_byte_span.cend());
+  bool definitely_reinterpret_casted1 = std::equal(muh_mutable_byte_span.cbegin(),
+    muh_byte_span.cend(), muh_original_byte_span.cbegin(), muh_original_byte_span.cend());
+  bool definitely_reinterpret_casted =
+    definitely_reinterpret_casted0 && definitely_reinterpret_casted1;
+  (void)definitely_reinterpret_casted;
+  VERIFY(definitely_reinterpret_casted);
+
+  std::span<std::byte> muh_original_byte_span_ptr_size(original_bytes, 4);
+  bool definitely_equivalent =
+    std::equal(muh_original_byte_span_ptr_size.cbegin(), muh_original_byte_span_ptr_size.cend(),
+      muh_original_byte_span.cbegin(), muh_original_byte_span.cend());
+  (void)definitely_equivalent;
+  VERIFY(definitely_equivalent);
+
+  return definitely_equivalent && definitely_reinterpret_casted && really_empty ? 0 : 1;
+}

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [ PATCH ] C++20 <span>
  2019-08-30 20:05 [ PATCH ] C++20 <span> JeanHeyd Meneide
@ 2019-08-30 21:37 ` Jonathan Wakely
  2019-08-30 23:42   ` JeanHeyd Meneide
  0 siblings, 1 reply; 9+ messages in thread
From: Jonathan Wakely @ 2019-08-30 21:37 UTC (permalink / raw)
  To: JeanHeyd Meneide; +Cc: gcc-patches, libstdc++

On 30/08/19 15:22 -0400, JeanHeyd Meneide wrote:
>This patch implements <span> as it currently exists in the C++20 Working Draft.

Nice!


>Notes:
>- __gnu_cxx::__normal_iterator is not fully constexpr, so its not used here

I'd prefer to make __normal_iterator constexpr, and use it.
It needs to be constexpr anyway for string and vector.

>- P1394 might be slated to end up in C++20 per National Body Comments.
>Therefore, an early implementation is left in an
>implementation-defined _GLIBCXX_P1394 define.
>
>2019-08-30  JeanHeyd "ThePhD" Meneide  <phdofthehouse@gmail.com>
>
>        * include/std/span: Implement the entirety of span.
>        * include/bits/range_access.h: Add __adl_* versions of access functions.
>        * testsuite/23_containers/span/everything.cc: constexpr and
>non-constexpr tests.
>        * include/Makefile.in: Add span to install.
>        * include/Makefile.am: Likewise

>+++ b/libstdc++-v3/include/std/span
>@@ -0,0 +1,549 @@
>+// Components for manipulating non-owning sequences of objects -*- C++ -*-
>+
>+// Copyright (C) 2019-2019 Free Software Foundation, Inc.

Just 2019 please, not 2019-2019.

>+// WARNING: they forgot this feature test macro
>+// get on someone's back about it in Belfast!!!

Please use FIXME: instead of WARNING: (for consistency with the rest
of the sources, so people grepping for FIXME: can find this).

The new feature test macro should be in <version> too.

There's no need to qualify ::std::true_type and ::std::size_t when
within namespace std already. There's no ADL for type names, and
normal unqualified lookup will find the right ones (and is easier to
read).

>+      static_assert(
>+        _Count == ::std::dynamic_extent || _Extent == ::std::dynamic_extent || _Count <= _Extent,
>+        "bad span length");

There are a number of lines that are too long, they need to be broken
before 80 columns.

Our static_assert messages should be stated as the positive condition
that is being asserted. So the diagnostic reads like
"assertion failed: thing being asserted"

So "bad span length" makes it look like we asserted the length is bad,
but actually it was good. I prefer to write something saying "X must
be true", e.g. "count must be equal to dynamic_extent, or less than
the span's extent".

Do we need to check _Extent == ::std::dynamic_extent here, give nthat
if it's true then _Count <= _Extent will be true as well?

>+  std::vector<std::int_least32_t> value{ 0 };

Your new testcase uses std::vector without including <vector>.


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [ PATCH ] C++20 <span>
  2019-08-30 21:37 ` Jonathan Wakely
@ 2019-08-30 23:42   ` JeanHeyd Meneide
  2019-08-31  1:06     ` JeanHeyd Meneide
  0 siblings, 1 reply; 9+ messages in thread
From: JeanHeyd Meneide @ 2019-08-30 23:42 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: gcc-patches, libstdc++

[-- Attachment #1: Type: text/plain, Size: 1025 bytes --]

On Fri, Aug 30, 2019 at 3:41 PM Jonathan Wakely <jwakely@redhat.com> wrote:
>
> On 30/08/19 15:22 -0400, JeanHeyd Meneide wrote:
> >This patch implements <span> as it currently exists in the C++20 Working Draft.
>
> Nice!
>
>
> >Notes:
> >- __gnu_cxx::__normal_iterator is not fully constexpr, so its not used here
>
> I'd prefer to make __normal_iterator constexpr, and use it.
> It needs to be constexpr anyway for string and vector.
> ...

Alright, thank you for the feedback. I fixed it up!

Tested x86_64-pc-linux-gnu.

2019-08-30  JeanHeyd "ThePhD" Meneide  <phdofthehouse@gmail.com>

        * include/std/span: Implement the entirety of span.
        * include/bits/stl_iterator.h: __gnu_cxx::__normal_iterator<T,
C> is now constexpr-qualified for C++11+.
       * include/bits/range_access.h: Add __adl_* versions of access functions.
        * testsuite/23_containers/span/everything.cc: constexpr and
non-constexpr tests.
        * include/Makefile.in: Add span to install.
        * include/Makefile.am: Likewise

[-- Attachment #2: ThePhD.span.patch.txt --]
[-- Type: text/plain, Size: 40737 bytes --]

diff --git a/.gitignore b/.gitignore
index d9d3967a12c..fd116c362ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -57,3 +57,7 @@ REVISION
 /mpc*
 /gmp*
 /isl*
+
+# ignore sprinkled in dev files that keep popping up
+.vscode/
+.vs/
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 3fe80f32cc4..b8b786d9260 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -68,6 +68,7 @@ std_headers = \
 	${std_srcdir}/scoped_allocator \
 	${std_srcdir}/set \
 	${std_srcdir}/shared_mutex \
+	${std_srcdir}/span \
 	${std_srcdir}/sstream \
 	${std_srcdir}/stack \
 	${std_srcdir}/stdexcept \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index b675d356cd4..cd1e9df5482 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -412,6 +412,7 @@ std_headers = \
 	${std_srcdir}/scoped_allocator \
 	${std_srcdir}/set \
 	${std_srcdir}/shared_mutex \
+	${std_srcdir}/span \
 	${std_srcdir}/sstream \
 	${std_srcdir}/stack \
 	${std_srcdir}/stdexcept \
diff --git a/libstdc++-v3/include/bits/range_access.h b/libstdc++-v3/include/bits/range_access.h
index d1e74711433..3acaebadcf1 100644
--- a/libstdc++-v3/include/bits/range_access.h
+++ b/libstdc++-v3/include/bits/range_access.h
@@ -318,6 +318,72 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 #endif // C++17
 
+#if __cplusplus > 201703L
+  // "why are these in namespace std:: and not __gnu_cxx:: ?"
+  // because if we don't put them here it's impossible to 
+  // have implicit ADL with "using std::begin/end/size/data;".
+  template <typename _Container>
+    constexpr auto
+    __adl_begin(_Container& __cont) noexcept(noexcept(begin(__cont)))
+    { return begin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_end(_Container& __cont) noexcept(noexcept(end(__cont)))
+    { return end(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_cbegin(_Container& __cont) noexcept(noexcept(cbegin(__cont)))
+    { return cbegin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_cend(_Container& __cont) noexcept(noexcept(cend(__cont)))
+    { return cend(__cont); }
+
+  template <typename _Container>
+    constexpr auto
+    __adl_rbegin(_Container& __cont) noexcept(noexcept(rbegin(__cont)))
+    { return rbegin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_rend(_Container& __cont) noexcept(noexcept(rend(__cont)))
+    { return rend(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_crbegin(_Container& __cont) noexcept(noexcept(crbegin(__cont)))
+    { return crbegin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_crend(_Container& __cont) noexcept(noexcept(crend(__cont)))
+    { return crend(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_data(_Container& __cont) noexcept(noexcept(data(__cont)))
+    { return data(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_cdata(_Container& __cont) noexcept(noexcept(cdata(__cont)))
+    { return cdata(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_size(_Container& __cont) noexcept(noexcept(size(__cont)))
+    { return size(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_empty(_Container& __cont) noexcept(noexcept(empty(__cont)))
+    { return empty(__cont); }
+  
+#endif // C++20
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 
diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
index 8ab0d72b0c2..4d432da0c6b 100644
--- a/libstdc++-v3/include/bits/stl_iterator.h
+++ b/libstdc++-v3/include/bits/stl_iterator.h
@@ -803,51 +803,51 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       : _M_current(_Iterator()) { }
 
       explicit
-      __normal_iterator(const _Iterator& __i) _GLIBCXX_NOEXCEPT
+      _GLIBCXX_CONSTEXPR __normal_iterator(const _Iterator& __i) _GLIBCXX_NOEXCEPT
       : _M_current(__i) { }
 
       // Allow iterator to const_iterator conversion
       template<typename _Iter>
-        __normal_iterator(const __normal_iterator<_Iter,
+        _GLIBCXX_CONSTEXPR __normal_iterator(const __normal_iterator<_Iter,
 			  typename __enable_if<
       	       (std::__are_same<_Iter, typename _Container::pointer>::__value),
 		      _Container>::__type>& __i) _GLIBCXX_NOEXCEPT
         : _M_current(__i.base()) { }
 
       // Forward iterator requirements
-      reference
+      _GLIBCXX_CONSTEXPR reference
       operator*() const _GLIBCXX_NOEXCEPT
       { return *_M_current; }
 
-      pointer
+      _GLIBCXX_CONSTEXPR pointer
       operator->() const _GLIBCXX_NOEXCEPT
       { return _M_current; }
 
-      __normal_iterator&
+      _GLIBCXX_CONSTEXPR __normal_iterator&
       operator++() _GLIBCXX_NOEXCEPT
       {
 	++_M_current;
 	return *this;
       }
 
-      __normal_iterator
+      _GLIBCXX_CONSTEXPR __normal_iterator
       operator++(int) _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current++); }
 
       // Bidirectional iterator requirements
-      __normal_iterator&
+      _GLIBCXX_CONSTEXPR __normal_iterator&
       operator--() _GLIBCXX_NOEXCEPT
       {
 	--_M_current;
 	return *this;
       }
 
-      __normal_iterator
+      _GLIBCXX_CONSTEXPR __normal_iterator
       operator--(int) _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current--); }
 
       // Random access iterator requirements
-      reference
+      _GLIBCXX_CONSTEXPR reference
       operator[](difference_type __n) const _GLIBCXX_NOEXCEPT
       { return _M_current[__n]; }
 
@@ -855,19 +855,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       operator+=(difference_type __n) _GLIBCXX_NOEXCEPT
       { _M_current += __n; return *this; }
 
-      __normal_iterator
+      _GLIBCXX_CONSTEXPR __normal_iterator
       operator+(difference_type __n) const _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current + __n); }
 
-      __normal_iterator&
+      _GLIBCXX_CONSTEXPR __normal_iterator&
       operator-=(difference_type __n) _GLIBCXX_NOEXCEPT
       { _M_current -= __n; return *this; }
 
-      __normal_iterator
+      _GLIBCXX_CONSTEXPR __normal_iterator
       operator-(difference_type __n) const _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current - __n); }
 
-      const _Iterator&
+      _GLIBCXX_CONSTEXPR const _Iterator&
       base() const _GLIBCXX_NOEXCEPT
       { return _M_current; }
     };
@@ -882,28 +882,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // Forward iterator requirements
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR inline bool
     operator==(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() == __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR inline bool
     operator==(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() == __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR inline bool
     operator!=(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() != __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR inline bool
     operator!=(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
@@ -911,56 +911,56 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // Random access iterator requirements
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR inline bool
     operator<(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() < __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR inline bool
     operator<(const __normal_iterator<_Iterator, _Container>& __lhs,
 	      const __normal_iterator<_Iterator, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() < __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR inline bool
     operator>(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() > __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR inline bool
     operator>(const __normal_iterator<_Iterator, _Container>& __lhs,
 	      const __normal_iterator<_Iterator, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() > __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR inline bool
     operator<=(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() <= __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR inline bool
     operator<=(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() <= __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR inline bool
     operator>=(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() >= __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR inline bool
     operator>=(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
@@ -973,26 +973,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _IteratorL, typename _IteratorR, typename _Container>
 #if __cplusplus >= 201103L
     // DR 685.
-    inline auto
+    _GLIBCXX_CONSTEXPR inline auto
     operator-(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs) noexcept
     -> decltype(__lhs.base() - __rhs.base())
 #else
-    inline typename __normal_iterator<_IteratorL, _Container>::difference_type
+    _GLIBCXX_CONSTEXPR inline typename __normal_iterator<_IteratorL, _Container>::difference_type
     operator-(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs)
 #endif
     { return __lhs.base() - __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline typename __normal_iterator<_Iterator, _Container>::difference_type
+    _GLIBCXX_CONSTEXPR inline typename __normal_iterator<_Iterator, _Container>::difference_type
     operator-(const __normal_iterator<_Iterator, _Container>& __lhs,
 	      const __normal_iterator<_Iterator, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() - __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline __normal_iterator<_Iterator, _Container>
+    _GLIBCXX_CONSTEXPR inline __normal_iterator<_Iterator, _Container>
     operator+(typename __normal_iterator<_Iterator, _Container>::difference_type
 	      __n, const __normal_iterator<_Iterator, _Container>& __i)
     _GLIBCXX_NOEXCEPT
@@ -1006,7 +1006,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Iterator, typename _Container>
-    _Iterator
+    _GLIBCXX_CONSTEXPR _Iterator
     __niter_base(__gnu_cxx::__normal_iterator<_Iterator, _Container> __it)
     _GLIBCXX_NOEXCEPT_IF(std::is_nothrow_copy_constructible<_Iterator>::value)
     { return __it.base(); }
diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span
new file mode 100644
index 00000000000..e4a30977585
--- /dev/null
+++ b/libstdc++-v3/include/std/span
@@ -0,0 +1,571 @@
+// Components for manipulating non-owning sequences of objects -*- C++ -*-
+
+// 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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file span
+ *  This is a Standard C++ Library header.
+ */
+
+//
+// P0122 span library
+// Contributed by ThePhD
+//
+
+#ifndef _GLIBCXX_SPAN
+#define _GLIBCXX_SPAN 1
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+
+#include <cstddef>
+#include <type_traits>
+#include <tuple>
+#include <utility>
+#include <array>
+#include <bits/stl_iterator.h>
+#include <bits/range_access.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+  _GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+// FIXME: they forgot this feature test macro
+// get on someone's back about it in Belfast!!!
+#define __cpp_lib_span 201911
+
+  inline constexpr size_t dynamic_extent =
+    static_cast<size_t>(-1);
+
+  namespace __detail
+  {
+
+    template<typename _Element, typename _ToElement>
+    using __is_base_derived_safe_convertible =
+      is_convertible<_Element (*)[], _ToElement (*)[]>;
+
+    template<typename _Element, typename _ToElement>
+    static constexpr inline bool __is_base_derived_safe_convertible_v =
+      __is_base_derived_safe_convertible<_Element, _ToElement>::value;
+
+    template<typename>
+    struct __is_std_array : false_type
+    {
+    };
+
+    template<typename _Element, size_t _Extent>
+    struct __is_std_array<::std::array<_Element, _Extent>> : true_type
+    {
+    };
+
+#ifdef _GLIBCXX_DEBUG
+    template<typename _Element, size_t _Extent>
+    struct __is_std_array<::std::__debug::array<_Element, _Extent>>
+    : true_type
+    {
+    };
+#endif // debug/array
+
+    template<typename _Type>
+    inline constexpr bool __is_std_array_v = __is_std_array<_Type>::value;
+
+    template<size_t _Extent>
+    struct __extent_storage
+    {
+
+      constexpr __extent_storage() noexcept = default;
+      constexpr __extent_storage(size_t) noexcept
+      {
+      }
+
+      static constexpr size_t
+      _M_extent() noexcept
+      {
+        return _Extent;
+      }
+    };
+
+    template<>
+    struct __extent_storage<static_cast<size_t>(-1)>
+    {
+      size_t _M_extent_value;
+
+      constexpr __extent_storage() noexcept : _M_extent_value(0){};
+      constexpr __extent_storage(size_t __extent) noexcept
+      : _M_extent_value(__extent)
+      {
+      }
+
+      constexpr size_t
+      _M_extent() const noexcept
+      {
+        return this->_M_extent_value;
+      }
+    };
+
+  } // namespace __detail
+
+  template<typename _Type, size_t _Extent = dynamic_extent>
+  class span : private __detail::__extent_storage<_Extent>
+  {
+  public:
+    // member types
+    using value_type             = remove_cv_t<_Type>;
+    using element_type           = _Type;
+    using index_type             = size_t;
+    using reference              = element_type&;
+    using const_reference        = const element_type&;
+    using pointer                = _Type*;
+    using const_pointer          = const _Type*;
+    using iterator               = __gnu_cxx::__normal_iterator<pointer, span>;
+    using const_iterator         = __gnu_cxx::__normal_iterator<const_pointer, span>;
+    using reverse_iterator       = ::std::reverse_iterator<iterator>;
+    using const_reverse_iterator = ::std::reverse_iterator<const_iterator>;
+    using difference_type        = ptrdiff_t;
+    // Official wording has no size_type -- why??
+    // using size_type = size_t;
+
+    // member constants
+    static inline constexpr size_t extent = _Extent;
+
+  private:
+    using __base_t = __detail::__extent_storage<_Extent>;
+
+  public:
+    // constructors
+    constexpr span() noexcept : __base_t(), _M_ptr(nullptr)
+    {
+    }
+
+    constexpr span(const span&) noexcept = default;
+
+    template<size_t _ArrayExtent,
+      enable_if_t<
+        (_Extent == dynamic_extent || _ArrayExtent == _Extent) &&
+        __detail::__is_base_derived_safe_convertible_v<
+          remove_pointer_t<decltype(::std::__adl_data(
+            ::std::declval<element_type (&)[_ArrayExtent]>()))>,
+          element_type>>* = nullptr>
+    constexpr span(element_type (&__arr)[_ArrayExtent]) noexcept(
+      noexcept(::std::__adl_data(__arr)))
+    : span(::std::__adl_data(__arr), _ArrayExtent)
+    {
+    }
+
+    template<size_t _ArrayExtent,
+      enable_if_t<
+        (_Extent == dynamic_extent || _ArrayExtent == _Extent) &&
+        __detail::__is_base_derived_safe_convertible_v<
+          remove_pointer_t<decltype(::std::__adl_data(
+            ::std::declval<array<value_type, _ArrayExtent>&>()))>,
+          element_type>>* = nullptr>
+    constexpr span(array<value_type, _ArrayExtent>& __arr) noexcept(
+      noexcept(::std::__adl_data(__arr)))
+    : span(::std::__adl_data(__arr), _ArrayExtent)
+    {
+    }
+
+    template<size_t _ArrayExtent,
+      enable_if_t<
+        (_Extent == dynamic_extent || _ArrayExtent == _Extent) &&
+        __detail::__is_base_derived_safe_convertible_v<
+          remove_pointer_t<decltype(::std::__adl_data(
+            ::std::declval<const array<value_type, _ArrayExtent>&>()))>,
+          element_type>>* = nullptr>
+    constexpr span(const array<value_type, _ArrayExtent>&
+        __arr) noexcept(noexcept(::std::__adl_data(__arr)))
+    : span(::std::__adl_data(__arr), _ArrayExtent)
+    {
+    }
+
+    // NOTE: when the time comes, and P1394 -
+    // range constructors for std::span - ships in
+    // the standard, delete the #else block and remove
+    // the conditional
+    // if the paper fails, delete #if block
+    // and keep the crappy #else block
+    // and then cry that NB comments failed C++20...
+    // but maybe for C++23?
+#if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
+    template<typename _Range,
+      enable_if_t<
+        (_Extent == dynamic_extent) &&
+        !is_same_v<remove_cvref_t<_Range>, span> &&
+        !__detail::__is_std_array_v<remove_cvref_t<_Range>> &&
+        !is_array_v<remove_cvref_t<_Range>> &&
+        __detail::__is_base_derived_safe_convertible_v<
+          remove_pointer_t<decltype(
+            ::std::__adl_data(::std::declval<_Range&>()))>,
+          element_type>>* = nullptr>
+    constexpr span(_Range&& __range) noexcept(
+      noexcept(::std::__adl_data(__range)) &&
+      noexcept(::std::__adl_size(__range)))
+    : span(::std::__adl_data(__range), ::std::__adl_size(__range))
+    {
+    }
+
+    template<typename _ContiguousIterator, typename _Sentinel,
+      enable_if_t<!is_convertible_v<_Sentinel, index_type> &&
+                         __detail::__is_base_derived_safe_convertible_v<
+                           remove_reference_t<typename 
+                               iterator_traits<_ContiguousIterator>::reference>,
+                           element_type>>* = nullptr>
+    constexpr span(_ContiguousIterator __first, _Sentinel __last)
+    : span(::std::move(__first), static_cast<index_type>(__last - __first))
+    {
+    }
+
+    template<typename _ContiguousIterator>
+    constexpr span(_ContiguousIterator __first, index_type __count) noexcept(
+      noexcept(::std::to_address(__first)))
+    : __base_t(__count), _M_ptr(::std::to_address(__first))
+    {
+    }
+#else
+    template<typename _Container,
+      enable_if_t<
+        (_Extent == dynamic_extent) &&
+        !is_same_v<remove_cvref_t<_Container>, span> &&
+        !__detail::__is_std_array_v<remove_cvref_t<_Container>> &&
+        !is_array_v<remove_cvref_t<_Container>> &&
+        __detail::__is_base_derived_safe_convertible_v<
+          remove_pointer_t<decltype(
+            ::std::__adl_data(::std::declval<_Container&>()))>,
+          element_type>>* = nullptr>
+    constexpr span(_Container& __range) noexcept(
+      noexcept(::std::__adl_data(__range)) &&
+      noexcept(::std::__adl_size(__range)))
+    : span(::std::__adl_data(__range), ::std::__adl_size(__range))
+    {
+    }
+
+    template<typename _Container,
+      enable_if_t<
+        (_Extent == dynamic_extent) &&
+        !is_same_v<remove_cvref_t<_Container>, span> &&
+        !__detail::__is_std_array_v<remove_cvref_t<_Container>> &&
+        !is_array_v<remove_cvref_t<_Container>> &&
+        __detail::__is_base_derived_safe_convertible_v<
+          remove_pointer_t<decltype(
+            ::std::__adl_data(::std::declval<_Container&>()))>,
+          element_type>>* = nullptr>
+    constexpr span(const _Container& __range) noexcept(
+      noexcept(::std::__adl_data(__range)) &&
+      noexcept(::std::__adl_size(__range)))
+    : span(::std::__adl_data(__range), ::std::__adl_size(__range))
+    {
+    }
+
+    constexpr span(pointer __first, pointer __last) noexcept
+    : span(::std::move(__first), static_cast<index_type>(__last - __first))
+    {
+    }
+    constexpr span(pointer __first, index_type __count) noexcept
+    : __base_t(__count), _M_ptr(static_cast<pointer>(__first))
+    {
+    }
+#endif // P1394
+
+    // assignment
+    constexpr span&
+    operator=(const span&) noexcept = default;
+
+    // observers
+    constexpr reference
+    front() noexcept
+    {
+      return *this->begin();
+    }
+
+    constexpr const_reference
+    front() const noexcept
+    {
+      return *this->begin();
+    }
+
+    constexpr reference
+    back() noexcept
+    {
+      return *(this->begin() + 1);
+    }
+
+    constexpr const_reference
+    back() const noexcept
+    {
+      return *(this->end() - 1);
+    }
+
+    constexpr reference operator[](index_type __idx) noexcept
+    {
+      return *(this->_M_ptr + __idx);
+    }
+
+    constexpr const_reference operator[](index_type __idx) const noexcept
+    {
+      return *(this->_M_ptr + __idx);
+    }
+
+    constexpr pointer
+    data() const noexcept
+    {
+      return this->_M_ptr;
+    }
+
+    constexpr index_type
+    size() const noexcept
+    {
+      return this->__base_t::_M_extent();
+    }
+
+    constexpr index_type
+    size_bytes() const noexcept
+    {
+      return this->__base_t::_M_extent() * sizeof(element_type);
+    }
+
+    constexpr bool
+    empty() const noexcept
+    {
+      return size() == 0;
+    }
+
+    // observers: iterators
+    constexpr iterator
+    begin() const noexcept
+    {
+      return iterator(this->_M_ptr);
+    }
+
+    constexpr const_iterator
+    cbegin() const noexcept
+    {
+      return const_iterator(this->_M_ptr);
+    }
+
+    constexpr iterator
+    end() const noexcept
+    {
+      return iterator(this->_M_ptr + this->size());
+    }
+
+    constexpr const_iterator
+    cend() const noexcept
+    {
+      return const_iterator(this->_M_ptr + this->size());
+    }
+
+    constexpr reverse_iterator
+    rbegin() const noexcept
+    {
+      return reverse_iterator(this->begin());
+    }
+
+    constexpr const_reverse_iterator
+    crbegin() const noexcept
+    {
+      return const_reverse_iterator(this->cbegin());
+    }
+
+    constexpr reverse_iterator
+    rend() const noexcept
+    {
+      return reverse_iterator(this->end());
+    }
+
+    constexpr const_reverse_iterator
+    crend() const noexcept
+    {
+      return const_reverse_iterator(this->cend());
+    }
+
+    // observers: subranges
+    template<size_t _Count>
+    constexpr auto
+    first() const
+    {
+      using __span_t = ::std::span<element_type, _Count>;
+      return __span_t(this->data(), _Count);
+    }
+
+    constexpr auto
+    first(index_type __count) const
+    {
+      using __span_t = ::std::span<element_type, dynamic_extent>;
+      return __span_t(this->data(), __count);
+    }
+
+    template<size_t _Count>
+    constexpr ::std::span<element_type, _Count>
+    last() const
+    {
+      static_assert(_Count == dynamic_extent ||
+                      _Extent == dynamic_extent || _Count <= _Extent,
+        "assertion failed: Count or Extent are dynamic, "
+        "or the Count is less than the static extent");
+      using __span_t = ::std::span<element_type, _Count>;
+      return __span_t(this->data() + (this->size() - _Count), _Count);
+    }
+
+    constexpr auto
+    last(index_type __count) const
+    {
+      using __span_t = ::std::span<element_type, dynamic_extent>;
+      return __span_t(this->data() + (this->size() - __count), __count);
+    }
+
+    template<size_t _Offset,
+      size_t _Count = dynamic_extent>
+    constexpr auto
+    subspan() const
+    {
+      static_assert(_Count == dynamic_extent ||
+                      _Extent == dynamic_extent ||
+                      (_Offset + _Count) <= _Extent,
+        "assertion failed: Count or Extent are dynamic, "
+        "or the Count + Offset is less than the static extent");
+      constexpr size_t __span_extent =
+        (_Count != dynamic_extent
+            ? _Count
+            : (_Extent != dynamic_extent ? _Extent - _Offset
+                                                : dynamic_extent));
+      using __span_t = ::std::span<element_type, __span_extent>;
+      return __span_t(this->data() + _Offset,
+        (_Count == dynamic_extent ? this->size() - _Offset : _Count));
+    }
+
+    constexpr auto
+    subspan(
+      index_type __offset, index_type __count = dynamic_extent) const
+    {
+      using __span_t = ::std::span<element_type, dynamic_extent>;
+      return __span_t(this->data() + __offset,
+        __count == dynamic_extent ? this->size() - __offset : __count);
+    }
+
+    // observers: range helpers
+    friend constexpr iterator
+    begin(span __sp) noexcept
+    {
+      return __sp.begin();
+    }
+
+    friend constexpr iterator
+    end(span __sp) noexcept
+    {
+      return __sp.end();
+    }
+
+  private:
+    pointer _M_ptr;
+  };
+
+  template<typename _Type, size_t _Extent>
+  auto as_bytes(::std::span<_Type, _Extent> __sp) noexcept
+  {
+    constexpr size_t __byte_extent =
+      (_Extent == ::std::dynamic_extent
+          ? _Extent
+          : (_Extent *
+              sizeof(typename ::std::span<_Type, _Extent>::element_type)));
+    using __byte_span_t = ::std::span<const byte, __byte_extent>;
+    return __byte_span_t(
+      reinterpret_cast<const byte*>(__sp.data()), __sp.size_bytes());
+  }
+
+  template<typename _Type, size_t _Extent>
+  auto as_writable_bytes(::std::span<_Type, _Extent> __sp) noexcept
+  {
+    constexpr size_t __byte_extent =
+      (_Extent == dynamic_extent
+          ? _Extent
+          : (_Extent *
+              sizeof(typename ::std::span<_Type, _Extent>::element_type)));
+    using __byte_span_t = ::std::span<byte, __byte_extent>;
+    return __byte_span_t(
+      reinterpret_cast<byte*>(__sp.data()), __sp.size_bytes());
+  }
+
+  // tuple helpers
+  template<size_t _Index, typename _Type, size_t _Extent,
+    enable_if_t<(_Extent > 0) && (_Index < _Extent)>* = nullptr>
+  constexpr typename ::std::span<_Type, _Extent>::reference get(
+    ::std::span<_Type, _Extent> __sp) noexcept
+  {
+    return __sp[_Index];
+  }
+
+  template<typename _Type, size_t _Extent>
+  class tuple_size<::std::span<_Type, _Extent>>
+  : public integral_constant<size_t, _Extent>
+  {
+  };
+
+  template<typename _Type>
+  class tuple_size<::std::span<_Type, dynamic_extent>>;
+
+  template<size_t _Index, typename _Type, size_t _Extent>
+  struct tuple_element<_Index, ::std::span<_Type, _Extent>>
+  {
+    static_assert(_Index < _Extent, "assertion failed: Index is less than Extent");
+    using type = typename ::std::span<_Type, _Extent>::element_type;
+  };
+
+  // deduction guides
+  template<typename _Type, size_t _ArrayExtent>
+  span(_Type(&)[_ArrayExtent])->span<_Type, _ArrayExtent>;
+
+  template<typename _Type, size_t _ArrayExtent>
+  span(array<_Type, _ArrayExtent>&)->span<_Type, _ArrayExtent>;
+
+  template<typename _Type, size_t _ArrayExtent>
+  span(const array<_Type, _ArrayExtent>&)
+    ->span<const _Type, _ArrayExtent>;
+
+#if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
+
+  template<typename _ContiguousIterator, typename _Sentinel>
+  span(_ContiguousIterator, _Sentinel)
+    ->span<remove_reference_t<
+      typename iterator_traits<_ContiguousIterator>::reference>>;
+
+  template<typename _Range>
+  span(_Range &&)
+    ->span<remove_reference_t<typename iterator_traits<decltype(
+      ::std::__adl_begin(::std::declval<_Range&>()))>::reference>>;
+
+#else
+
+  template<typename _Container>
+  span(_Container&)->span<typename _Container::value_type>;
+
+  template<typename _Container>
+  span(const _Container&)->span<const typename _Container::value_type>;
+
+#endif // P1394
+
+  _GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std_GLIBCXX_VISIBILITY(default)
+
+#endif // C++20
+
+#endif
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 7f7d05fd8f2..4341c9f0c1b 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -167,6 +167,9 @@
 #define __cpp_lib_list_remove_return_type 201806L
 #define __cpp_lib_math_constants 201907L
 #define __cpp_lib_to_array 201907L
+// FIXME: they forgot this feature test macro
+// get on someone's back about it in Belfast!!!
+#define __cpp_lib_span 201911
 #endif // C++2a
 #endif // C++17
 #endif // C++14
diff --git a/libstdc++-v3/testsuite/23_containers/span/everything.cc b/libstdc++-v3/testsuite/23_containers/span/everything.cc
new file mode 100644
index 00000000000..4a9ba442c68
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/everything.cc
@@ -0,0 +1,203 @@
+// 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=c++2a" }
+// { dg-do run { target c++2a } }
+
+#include <testsuite_hooks.h>
+
+#include <span>
+#include <type_traits>
+#include <cstdint>
+#include <vector>
+#include <algorithm>
+#include <cassert>
+
+int
+main()
+{
+  struct alignas(256) strawman
+  {
+    int x;
+    int y;
+    bool z;
+    int w;
+  };
+
+  struct naked_span
+  {
+    char* p;
+    std::size_t n;
+  };
+
+  struct strawman_span
+  {
+    strawman* p;
+    std::size_t n;
+  };
+
+  static_assert(sizeof(std::span<char, 0>) <= sizeof(char*));
+  static_assert(sizeof(std::span<const char, 0>) <= sizeof(const char*));
+  static_assert(sizeof(std::span<strawman, 0>) <= sizeof(strawman*));
+  static_assert(sizeof(std::span<strawman, 1>) <= sizeof(strawman*));
+  static_assert(sizeof(std::span<char>) <= sizeof(naked_span));
+  static_assert(sizeof(std::span<strawman>) <= sizeof(naked_span));
+
+  constexpr static std::array<int, 9> arr_data{ 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+  constexpr auto arr_data_span = std::span(arr_data);
+  static_assert(arr_data_span.size() == 9);
+  static_assert(arr_data_span.size_bytes() == 9 * sizeof(int));
+  static_assert(*arr_data_span.begin() == 0);
+  static_assert(*arr_data_span.data() == 0);
+  static_assert(arr_data_span.front() == 0);
+  static_assert(arr_data_span.back() == 8);
+  static_assert(arr_data_span[0] == 0);
+  static_assert(arr_data_span[1] == 1);
+  static_assert(arr_data_span[2] == 2);
+  static_assert(arr_data_span[3] == 3);
+  static_assert(arr_data_span[4] == 4);
+  static_assert(arr_data_span[5] == 5);
+  static_assert(arr_data_span[6] == 6);
+  static_assert(arr_data_span[7] == 7);
+  static_assert(arr_data_span[8] == 8);
+  static_assert(!arr_data_span.empty());
+  static_assert(decltype(arr_data_span)::extent == 9);
+
+  constexpr static int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+  constexpr auto data_span    = std::span(data);
+  static_assert(data_span.size() == 9);
+  static_assert(data_span.size_bytes() == 9 * sizeof(int));
+  static_assert(*data_span.begin() == 0);
+  static_assert(*data_span.data() == 0);
+  static_assert(data_span.front() == 0);
+  static_assert(data_span.back() == 8);
+  static_assert(data_span[0] == 0);
+  static_assert(data_span[1] == 1);
+  static_assert(data_span[2] == 2);
+  static_assert(data_span[3] == 3);
+  static_assert(data_span[4] == 4);
+  static_assert(data_span[5] == 5);
+  static_assert(data_span[6] == 6);
+  static_assert(data_span[7] == 7);
+  static_assert(data_span[8] == 8);
+  static_assert(!data_span.empty());
+  static_assert(decltype(data_span)::extent == 9);
+
+  constexpr auto data_span_first = data_span.first<3>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_first)>, std::span<const int, 3>>);
+  static_assert(decltype(data_span_first)::extent == 3);
+  static_assert(data_span_first.size() == 3);
+  static_assert(data_span_first.front() == 0);
+  static_assert(data_span_first.back() == 2);
+  static_assert(std::tuple_size_v<decltype(data_span_first)> == 3);
+  static_assert(std::is_same_v<std::tuple_element_t<0, decltype(data_span_first)>, const int>);
+
+  constexpr auto data_span_first_dyn = data_span.first(4);
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_first_dyn)>, std::span<const int>>);
+  static_assert(decltype(data_span_first_dyn)::extent == std::dynamic_extent);
+  static_assert(data_span_first_dyn.size() == 4);
+  static_assert(data_span_first_dyn.front() == 0);
+  static_assert(data_span_first_dyn.back() == 3);
+
+  constexpr auto data_span_last = data_span.last<5>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_last)>, std::span<const int, 5>>);
+  static_assert(decltype(data_span_last)::extent == 5);
+  static_assert(data_span_last.size() == 5);
+  static_assert(data_span_last.front() == 4);
+  static_assert(data_span_last.back() == 8);
+  static_assert(std::tuple_size_v<decltype(data_span_last)> == 5);
+  static_assert(std::is_same_v<std::tuple_element_t<0, decltype(data_span_last)>, const int>);
+
+  constexpr auto data_span_last_dyn = data_span.last(6);
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_last_dyn)>, std::span<const int>>);
+  static_assert(decltype(data_span_last_dyn)::extent == std::dynamic_extent);
+  static_assert(data_span_last_dyn.size() == 6);
+  static_assert(data_span_last_dyn.front() == 3);
+  static_assert(data_span_last_dyn.back() == 8);
+
+  constexpr auto data_span_subspan = data_span.subspan<1, 3>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_subspan)>, std::span<const int, 3>>);
+  static_assert(decltype(data_span_subspan)::extent == 3);
+  static_assert(data_span_subspan.size() == 3);
+  static_assert(data_span_subspan.front() == 1);
+  static_assert(data_span_subspan.back() == 3);
+
+  constexpr auto data_span_subspan_offset = data_span.subspan<8>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_subspan_offset)>, std::span<const int, 1>>);
+  static_assert(decltype(data_span_subspan_offset)::extent == 1);
+  static_assert(data_span_subspan_offset.size() == 1);
+  static_assert(data_span_subspan_offset.front() == 8);
+  static_assert(data_span_subspan_offset.back() == 8);
+
+  constexpr auto data_span_subspan_empty = data_span.subspan(9, 0);
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_subspan_empty)>, std::span<const int>>);
+  static_assert(decltype(data_span_subspan_empty)::extent == std::dynamic_extent);
+  static_assert(data_span_subspan_empty.size() == 0);
+
+  constexpr auto data_span_subspan_empty_static = data_span.subspan<9>();
+  static_assert(std::is_same_v<std::remove_cv_t<decltype(data_span_subspan_empty_static)>,
+    std::span<const int, 0>>);
+  static_assert(decltype(data_span_subspan_empty_static)::extent == 0);
+  static_assert(data_span_subspan_empty.size() == 0);
+
+  std::span<short> shorts{};
+  bool really_empty0 = shorts.empty();
+  bool really_empty1 = std::empty(shorts);
+  bool really_empty2 = shorts.data() == nullptr;
+  bool really_empty3 = shorts.begin() == shorts.end();
+  bool really_empty4 = shorts.cbegin() == shorts.cend();
+  bool really_empty =
+    really_empty0 && really_empty1 && really_empty2 && really_empty3 && really_empty4;
+  (void)really_empty;
+  VERIFY(really_empty);
+
+  std::vector<std::int_least32_t> value{ 0 };
+  std::span<int32_t> muh_span(value);
+  VERIFY(muh_span.size() == 1);
+  std::byte* original_bytes                  = reinterpret_cast<std::byte*>(value.data());
+  original_bytes[0]                          = static_cast<std::byte>(1);
+  original_bytes[1]                          = static_cast<std::byte>(2);
+  original_bytes[2]                          = static_cast<std::byte>(3);
+  original_bytes[3]                          = static_cast<std::byte>(4);
+  std::span<const std::byte> muh_byte_span   = std::as_bytes(muh_span);
+  std::span<std::byte> muh_mutable_byte_span = std::as_writable_bytes(muh_span);
+  std::span<std::byte> muh_original_byte_span(original_bytes, original_bytes + 4);
+  bool definitely_reinterpret_casted0 = std::equal(muh_byte_span.cbegin(), muh_byte_span.cend(),
+    muh_original_byte_span.cbegin(), muh_original_byte_span.cend());
+  bool definitely_reinterpret_casted1 = std::equal(muh_mutable_byte_span.cbegin(),
+    muh_mutable_byte_span.cend(), muh_original_byte_span.cbegin(), muh_original_byte_span.cend());
+  bool definitely_reinterpret_casted =
+    definitely_reinterpret_casted0 && definitely_reinterpret_casted1;
+  (void)definitely_reinterpret_casted;
+  VERIFY(definitely_reinterpret_casted);
+
+  std::span<std::byte> muh_original_byte_span_ptr_size(original_bytes, 4);
+  bool definitely_equivalent =
+    std::equal(muh_original_byte_span_ptr_size.cbegin(), muh_original_byte_span_ptr_size.cend(),
+      muh_original_byte_span.cbegin(), muh_original_byte_span.cend());
+  (void)definitely_equivalent;
+  VERIFY(definitely_equivalent);
+
+  return definitely_equivalent && definitely_reinterpret_casted && really_empty ? 0 : 1;
+}

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [ PATCH ] C++20 <span>
  2019-08-30 23:42   ` JeanHeyd Meneide
@ 2019-08-31  1:06     ` JeanHeyd Meneide
  2019-09-03 13:31       ` Jonathan Wakely
  0 siblings, 1 reply; 9+ messages in thread
From: JeanHeyd Meneide @ 2019-08-31  1:06 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: gcc-patches, libstdc++

[-- Attachment #1: Type: text/plain, Size: 1364 bytes --]

Ahem -- we were supposed to use the 20 version of the constexpr macro,
not the base one.
I will note that, for some reason, the default constructor was already
constexpr, so we don't change that one!

On Fri, Aug 30, 2019 at 5:11 PM JeanHeyd Meneide
<phdofthehouse@gmail.com> wrote:
>
> On Fri, Aug 30, 2019 at 3:41 PM Jonathan Wakely <jwakely@redhat.com> wrote:
> >
> > On 30/08/19 15:22 -0400, JeanHeyd Meneide wrote:
> > >This patch implements <span> as it currently exists in the C++20 Working Draft.
> >
> > Nice!
> >
> >
> > >Notes:
> > >- __gnu_cxx::__normal_iterator is not fully constexpr, so its not used here
> >
> > I'd prefer to make __normal_iterator constexpr, and use it.
> > It needs to be constexpr anyway for string and vector.
> > ...
>
> Alright, thank you for the feedback. I fixed it up!
>
> Tested x86_64-pc-linux-gnu.
>
> 2019-08-30  JeanHeyd "ThePhD" Meneide  <phdofthehouse@gmail.com>
>
>         * include/std/span: Implement the entirety of span.
>         * include/bits/stl_iterator.h: __gnu_cxx::__normal_iterator<T,
> C> is now constexpr-qualified for C++11+.
>        * include/bits/range_access.h: Add __adl_* versions of access functions.
>         * testsuite/23_containers/span/everything.cc: constexpr and
> non-constexpr tests.
>         * include/Makefile.in: Add span to install.
>         * include/Makefile.am: Likewise

[-- Attachment #2: ThePhD.span.patch.txt --]
[-- Type: text/plain, Size: 41090 bytes --]

diff --git a/.gitignore b/.gitignore
index d9d3967a12c..fd116c362ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -57,3 +57,7 @@ REVISION
 /mpc*
 /gmp*
 /isl*
+
+# ignore sprinkled in dev files that keep popping up
+.vscode/
+.vs/
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 3fe80f32cc4..b8b786d9260 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -68,6 +68,7 @@ std_headers = \
 	${std_srcdir}/scoped_allocator \
 	${std_srcdir}/set \
 	${std_srcdir}/shared_mutex \
+	${std_srcdir}/span \
 	${std_srcdir}/sstream \
 	${std_srcdir}/stack \
 	${std_srcdir}/stdexcept \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index b675d356cd4..cd1e9df5482 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -412,6 +412,7 @@ std_headers = \
 	${std_srcdir}/scoped_allocator \
 	${std_srcdir}/set \
 	${std_srcdir}/shared_mutex \
+	${std_srcdir}/span \
 	${std_srcdir}/sstream \
 	${std_srcdir}/stack \
 	${std_srcdir}/stdexcept \
diff --git a/libstdc++-v3/include/bits/range_access.h b/libstdc++-v3/include/bits/range_access.h
index d1e74711433..3acaebadcf1 100644
--- a/libstdc++-v3/include/bits/range_access.h
+++ b/libstdc++-v3/include/bits/range_access.h
@@ -318,6 +318,72 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 #endif // C++17
 
+#if __cplusplus > 201703L
+  // "why are these in namespace std:: and not __gnu_cxx:: ?"
+  // because if we don't put them here it's impossible to 
+  // have implicit ADL with "using std::begin/end/size/data;".
+  template <typename _Container>
+    constexpr auto
+    __adl_begin(_Container& __cont) noexcept(noexcept(begin(__cont)))
+    { return begin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_end(_Container& __cont) noexcept(noexcept(end(__cont)))
+    { return end(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_cbegin(_Container& __cont) noexcept(noexcept(cbegin(__cont)))
+    { return cbegin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_cend(_Container& __cont) noexcept(noexcept(cend(__cont)))
+    { return cend(__cont); }
+
+  template <typename _Container>
+    constexpr auto
+    __adl_rbegin(_Container& __cont) noexcept(noexcept(rbegin(__cont)))
+    { return rbegin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_rend(_Container& __cont) noexcept(noexcept(rend(__cont)))
+    { return rend(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_crbegin(_Container& __cont) noexcept(noexcept(crbegin(__cont)))
+    { return crbegin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_crend(_Container& __cont) noexcept(noexcept(crend(__cont)))
+    { return crend(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_data(_Container& __cont) noexcept(noexcept(data(__cont)))
+    { return data(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_cdata(_Container& __cont) noexcept(noexcept(cdata(__cont)))
+    { return cdata(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_size(_Container& __cont) noexcept(noexcept(size(__cont)))
+    { return size(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_empty(_Container& __cont) noexcept(noexcept(empty(__cont)))
+    { return empty(__cont); }
+  
+#endif // C++20
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 
diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
index 8ab0d72b0c2..f6a6060f2e3 100644
--- a/libstdc++-v3/include/bits/stl_iterator.h
+++ b/libstdc++-v3/include/bits/stl_iterator.h
@@ -799,55 +799,58 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef typename __traits_type::reference 	reference;
       typedef typename __traits_type::pointer   	pointer;
 
+      // this was already constexpr
+      // so it will not be changed to
+      // _GLIBCXX_CONSTEXPR20
       _GLIBCXX_CONSTEXPR __normal_iterator() _GLIBCXX_NOEXCEPT
       : _M_current(_Iterator()) { }
 
       explicit
-      __normal_iterator(const _Iterator& __i) _GLIBCXX_NOEXCEPT
+      _GLIBCXX_CONSTEXPR20 __normal_iterator(const _Iterator& __i) _GLIBCXX_NOEXCEPT
       : _M_current(__i) { }
 
       // Allow iterator to const_iterator conversion
       template<typename _Iter>
-        __normal_iterator(const __normal_iterator<_Iter,
+        _GLIBCXX_CONSTEXPR20 __normal_iterator(const __normal_iterator<_Iter,
 			  typename __enable_if<
       	       (std::__are_same<_Iter, typename _Container::pointer>::__value),
 		      _Container>::__type>& __i) _GLIBCXX_NOEXCEPT
         : _M_current(__i.base()) { }
 
       // Forward iterator requirements
-      reference
+      _GLIBCXX_CONSTEXPR20 reference
       operator*() const _GLIBCXX_NOEXCEPT
       { return *_M_current; }
 
-      pointer
+      _GLIBCXX_CONSTEXPR20 pointer
       operator->() const _GLIBCXX_NOEXCEPT
       { return _M_current; }
 
-      __normal_iterator&
+      _GLIBCXX_CONSTEXPR20 __normal_iterator&
       operator++() _GLIBCXX_NOEXCEPT
       {
 	++_M_current;
 	return *this;
       }
 
-      __normal_iterator
+      _GLIBCXX_CONSTEXPR20 __normal_iterator
       operator++(int) _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current++); }
 
       // Bidirectional iterator requirements
-      __normal_iterator&
+      _GLIBCXX_CONSTEXPR20 __normal_iterator&
       operator--() _GLIBCXX_NOEXCEPT
       {
 	--_M_current;
 	return *this;
       }
 
-      __normal_iterator
+      _GLIBCXX_CONSTEXPR20 __normal_iterator
       operator--(int) _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current--); }
 
       // Random access iterator requirements
-      reference
+      _GLIBCXX_CONSTEXPR20 reference
       operator[](difference_type __n) const _GLIBCXX_NOEXCEPT
       { return _M_current[__n]; }
 
@@ -855,19 +858,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       operator+=(difference_type __n) _GLIBCXX_NOEXCEPT
       { _M_current += __n; return *this; }
 
-      __normal_iterator
+      _GLIBCXX_CONSTEXPR20 __normal_iterator
       operator+(difference_type __n) const _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current + __n); }
 
-      __normal_iterator&
+      _GLIBCXX_CONSTEXPR20 __normal_iterator&
       operator-=(difference_type __n) _GLIBCXX_NOEXCEPT
       { _M_current -= __n; return *this; }
 
-      __normal_iterator
+      _GLIBCXX_CONSTEXPR20 __normal_iterator
       operator-(difference_type __n) const _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current - __n); }
 
-      const _Iterator&
+      _GLIBCXX_CONSTEXPR20 const _Iterator&
       base() const _GLIBCXX_NOEXCEPT
       { return _M_current; }
     };
@@ -882,28 +885,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // Forward iterator requirements
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR20 inline bool
     operator==(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() == __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR20 inline bool
     operator==(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() == __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR20 inline bool
     operator!=(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() != __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR20 inline bool
     operator!=(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
@@ -911,56 +914,56 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // Random access iterator requirements
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR20 inline bool
     operator<(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() < __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR20 inline bool
     operator<(const __normal_iterator<_Iterator, _Container>& __lhs,
 	      const __normal_iterator<_Iterator, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() < __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR20 inline bool
     operator>(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() > __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR20 inline bool
     operator>(const __normal_iterator<_Iterator, _Container>& __lhs,
 	      const __normal_iterator<_Iterator, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() > __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR20 inline bool
     operator<=(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() <= __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR20 inline bool
     operator<=(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() <= __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR20 inline bool
     operator>=(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() >= __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline bool
+    _GLIBCXX_CONSTEXPR20 inline bool
     operator>=(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
@@ -973,26 +976,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _IteratorL, typename _IteratorR, typename _Container>
 #if __cplusplus >= 201103L
     // DR 685.
-    inline auto
+    _GLIBCXX_CONSTEXPR20 inline auto
     operator-(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs) noexcept
     -> decltype(__lhs.base() - __rhs.base())
 #else
-    inline typename __normal_iterator<_IteratorL, _Container>::difference_type
+    _GLIBCXX_CONSTEXPR20 inline typename __normal_iterator<_IteratorL, _Container>::difference_type
     operator-(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs)
 #endif
     { return __lhs.base() - __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline typename __normal_iterator<_Iterator, _Container>::difference_type
+    _GLIBCXX_CONSTEXPR20 inline typename __normal_iterator<_Iterator, _Container>::difference_type
     operator-(const __normal_iterator<_Iterator, _Container>& __lhs,
 	      const __normal_iterator<_Iterator, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
     { return __lhs.base() - __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
-    inline __normal_iterator<_Iterator, _Container>
+    _GLIBCXX_CONSTEXPR20 inline __normal_iterator<_Iterator, _Container>
     operator+(typename __normal_iterator<_Iterator, _Container>::difference_type
 	      __n, const __normal_iterator<_Iterator, _Container>& __i)
     _GLIBCXX_NOEXCEPT
@@ -1006,7 +1009,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Iterator, typename _Container>
-    _Iterator
+    _GLIBCXX_CONSTEXPR20 _Iterator
     __niter_base(__gnu_cxx::__normal_iterator<_Iterator, _Container> __it)
     _GLIBCXX_NOEXCEPT_IF(std::is_nothrow_copy_constructible<_Iterator>::value)
     { return __it.base(); }
diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span
new file mode 100644
index 00000000000..e4a30977585
--- /dev/null
+++ b/libstdc++-v3/include/std/span
@@ -0,0 +1,571 @@
+// Components for manipulating non-owning sequences of objects -*- C++ -*-
+
+// 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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file span
+ *  This is a Standard C++ Library header.
+ */
+
+//
+// P0122 span library
+// Contributed by ThePhD
+//
+
+#ifndef _GLIBCXX_SPAN
+#define _GLIBCXX_SPAN 1
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+
+#include <cstddef>
+#include <type_traits>
+#include <tuple>
+#include <utility>
+#include <array>
+#include <bits/stl_iterator.h>
+#include <bits/range_access.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+  _GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+// FIXME: they forgot this feature test macro
+// get on someone's back about it in Belfast!!!
+#define __cpp_lib_span 201911
+
+  inline constexpr size_t dynamic_extent =
+    static_cast<size_t>(-1);
+
+  namespace __detail
+  {
+
+    template<typename _Element, typename _ToElement>
+    using __is_base_derived_safe_convertible =
+      is_convertible<_Element (*)[], _ToElement (*)[]>;
+
+    template<typename _Element, typename _ToElement>
+    static constexpr inline bool __is_base_derived_safe_convertible_v =
+      __is_base_derived_safe_convertible<_Element, _ToElement>::value;
+
+    template<typename>
+    struct __is_std_array : false_type
+    {
+    };
+
+    template<typename _Element, size_t _Extent>
+    struct __is_std_array<::std::array<_Element, _Extent>> : true_type
+    {
+    };
+
+#ifdef _GLIBCXX_DEBUG
+    template<typename _Element, size_t _Extent>
+    struct __is_std_array<::std::__debug::array<_Element, _Extent>>
+    : true_type
+    {
+    };
+#endif // debug/array
+
+    template<typename _Type>
+    inline constexpr bool __is_std_array_v = __is_std_array<_Type>::value;
+
+    template<size_t _Extent>
+    struct __extent_storage
+    {
+
+      constexpr __extent_storage() noexcept = default;
+      constexpr __extent_storage(size_t) noexcept
+      {
+      }
+
+      static constexpr size_t
+      _M_extent() noexcept
+      {
+        return _Extent;
+      }
+    };
+
+    template<>
+    struct __extent_storage<static_cast<size_t>(-1)>
+    {
+      size_t _M_extent_value;
+
+      constexpr __extent_storage() noexcept : _M_extent_value(0){};
+      constexpr __extent_storage(size_t __extent) noexcept
+      : _M_extent_value(__extent)
+      {
+      }
+
+      constexpr size_t
+      _M_extent() const noexcept
+      {
+        return this->_M_extent_value;
+      }
+    };
+
+  } // namespace __detail
+
+  template<typename _Type, size_t _Extent = dynamic_extent>
+  class span : private __detail::__extent_storage<_Extent>
+  {
+  public:
+    // member types
+    using value_type             = remove_cv_t<_Type>;
+    using element_type           = _Type;
+    using index_type             = size_t;
+    using reference              = element_type&;
+    using const_reference        = const element_type&;
+    using pointer                = _Type*;
+    using const_pointer          = const _Type*;
+    using iterator               = __gnu_cxx::__normal_iterator<pointer, span>;
+    using const_iterator         = __gnu_cxx::__normal_iterator<const_pointer, span>;
+    using reverse_iterator       = ::std::reverse_iterator<iterator>;
+    using const_reverse_iterator = ::std::reverse_iterator<const_iterator>;
+    using difference_type        = ptrdiff_t;
+    // Official wording has no size_type -- why??
+    // using size_type = size_t;
+
+    // member constants
+    static inline constexpr size_t extent = _Extent;
+
+  private:
+    using __base_t = __detail::__extent_storage<_Extent>;
+
+  public:
+    // constructors
+    constexpr span() noexcept : __base_t(), _M_ptr(nullptr)
+    {
+    }
+
+    constexpr span(const span&) noexcept = default;
+
+    template<size_t _ArrayExtent,
+      enable_if_t<
+        (_Extent == dynamic_extent || _ArrayExtent == _Extent) &&
+        __detail::__is_base_derived_safe_convertible_v<
+          remove_pointer_t<decltype(::std::__adl_data(
+            ::std::declval<element_type (&)[_ArrayExtent]>()))>,
+          element_type>>* = nullptr>
+    constexpr span(element_type (&__arr)[_ArrayExtent]) noexcept(
+      noexcept(::std::__adl_data(__arr)))
+    : span(::std::__adl_data(__arr), _ArrayExtent)
+    {
+    }
+
+    template<size_t _ArrayExtent,
+      enable_if_t<
+        (_Extent == dynamic_extent || _ArrayExtent == _Extent) &&
+        __detail::__is_base_derived_safe_convertible_v<
+          remove_pointer_t<decltype(::std::__adl_data(
+            ::std::declval<array<value_type, _ArrayExtent>&>()))>,
+          element_type>>* = nullptr>
+    constexpr span(array<value_type, _ArrayExtent>& __arr) noexcept(
+      noexcept(::std::__adl_data(__arr)))
+    : span(::std::__adl_data(__arr), _ArrayExtent)
+    {
+    }
+
+    template<size_t _ArrayExtent,
+      enable_if_t<
+        (_Extent == dynamic_extent || _ArrayExtent == _Extent) &&
+        __detail::__is_base_derived_safe_convertible_v<
+          remove_pointer_t<decltype(::std::__adl_data(
+            ::std::declval<const array<value_type, _ArrayExtent>&>()))>,
+          element_type>>* = nullptr>
+    constexpr span(const array<value_type, _ArrayExtent>&
+        __arr) noexcept(noexcept(::std::__adl_data(__arr)))
+    : span(::std::__adl_data(__arr), _ArrayExtent)
+    {
+    }
+
+    // NOTE: when the time comes, and P1394 -
+    // range constructors for std::span - ships in
+    // the standard, delete the #else block and remove
+    // the conditional
+    // if the paper fails, delete #if block
+    // and keep the crappy #else block
+    // and then cry that NB comments failed C++20...
+    // but maybe for C++23?
+#if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
+    template<typename _Range,
+      enable_if_t<
+        (_Extent == dynamic_extent) &&
+        !is_same_v<remove_cvref_t<_Range>, span> &&
+        !__detail::__is_std_array_v<remove_cvref_t<_Range>> &&
+        !is_array_v<remove_cvref_t<_Range>> &&
+        __detail::__is_base_derived_safe_convertible_v<
+          remove_pointer_t<decltype(
+            ::std::__adl_data(::std::declval<_Range&>()))>,
+          element_type>>* = nullptr>
+    constexpr span(_Range&& __range) noexcept(
+      noexcept(::std::__adl_data(__range)) &&
+      noexcept(::std::__adl_size(__range)))
+    : span(::std::__adl_data(__range), ::std::__adl_size(__range))
+    {
+    }
+
+    template<typename _ContiguousIterator, typename _Sentinel,
+      enable_if_t<!is_convertible_v<_Sentinel, index_type> &&
+                         __detail::__is_base_derived_safe_convertible_v<
+                           remove_reference_t<typename 
+                               iterator_traits<_ContiguousIterator>::reference>,
+                           element_type>>* = nullptr>
+    constexpr span(_ContiguousIterator __first, _Sentinel __last)
+    : span(::std::move(__first), static_cast<index_type>(__last - __first))
+    {
+    }
+
+    template<typename _ContiguousIterator>
+    constexpr span(_ContiguousIterator __first, index_type __count) noexcept(
+      noexcept(::std::to_address(__first)))
+    : __base_t(__count), _M_ptr(::std::to_address(__first))
+    {
+    }
+#else
+    template<typename _Container,
+      enable_if_t<
+        (_Extent == dynamic_extent) &&
+        !is_same_v<remove_cvref_t<_Container>, span> &&
+        !__detail::__is_std_array_v<remove_cvref_t<_Container>> &&
+        !is_array_v<remove_cvref_t<_Container>> &&
+        __detail::__is_base_derived_safe_convertible_v<
+          remove_pointer_t<decltype(
+            ::std::__adl_data(::std::declval<_Container&>()))>,
+          element_type>>* = nullptr>
+    constexpr span(_Container& __range) noexcept(
+      noexcept(::std::__adl_data(__range)) &&
+      noexcept(::std::__adl_size(__range)))
+    : span(::std::__adl_data(__range), ::std::__adl_size(__range))
+    {
+    }
+
+    template<typename _Container,
+      enable_if_t<
+        (_Extent == dynamic_extent) &&
+        !is_same_v<remove_cvref_t<_Container>, span> &&
+        !__detail::__is_std_array_v<remove_cvref_t<_Container>> &&
+        !is_array_v<remove_cvref_t<_Container>> &&
+        __detail::__is_base_derived_safe_convertible_v<
+          remove_pointer_t<decltype(
+            ::std::__adl_data(::std::declval<_Container&>()))>,
+          element_type>>* = nullptr>
+    constexpr span(const _Container& __range) noexcept(
+      noexcept(::std::__adl_data(__range)) &&
+      noexcept(::std::__adl_size(__range)))
+    : span(::std::__adl_data(__range), ::std::__adl_size(__range))
+    {
+    }
+
+    constexpr span(pointer __first, pointer __last) noexcept
+    : span(::std::move(__first), static_cast<index_type>(__last - __first))
+    {
+    }
+    constexpr span(pointer __first, index_type __count) noexcept
+    : __base_t(__count), _M_ptr(static_cast<pointer>(__first))
+    {
+    }
+#endif // P1394
+
+    // assignment
+    constexpr span&
+    operator=(const span&) noexcept = default;
+
+    // observers
+    constexpr reference
+    front() noexcept
+    {
+      return *this->begin();
+    }
+
+    constexpr const_reference
+    front() const noexcept
+    {
+      return *this->begin();
+    }
+
+    constexpr reference
+    back() noexcept
+    {
+      return *(this->begin() + 1);
+    }
+
+    constexpr const_reference
+    back() const noexcept
+    {
+      return *(this->end() - 1);
+    }
+
+    constexpr reference operator[](index_type __idx) noexcept
+    {
+      return *(this->_M_ptr + __idx);
+    }
+
+    constexpr const_reference operator[](index_type __idx) const noexcept
+    {
+      return *(this->_M_ptr + __idx);
+    }
+
+    constexpr pointer
+    data() const noexcept
+    {
+      return this->_M_ptr;
+    }
+
+    constexpr index_type
+    size() const noexcept
+    {
+      return this->__base_t::_M_extent();
+    }
+
+    constexpr index_type
+    size_bytes() const noexcept
+    {
+      return this->__base_t::_M_extent() * sizeof(element_type);
+    }
+
+    constexpr bool
+    empty() const noexcept
+    {
+      return size() == 0;
+    }
+
+    // observers: iterators
+    constexpr iterator
+    begin() const noexcept
+    {
+      return iterator(this->_M_ptr);
+    }
+
+    constexpr const_iterator
+    cbegin() const noexcept
+    {
+      return const_iterator(this->_M_ptr);
+    }
+
+    constexpr iterator
+    end() const noexcept
+    {
+      return iterator(this->_M_ptr + this->size());
+    }
+
+    constexpr const_iterator
+    cend() const noexcept
+    {
+      return const_iterator(this->_M_ptr + this->size());
+    }
+
+    constexpr reverse_iterator
+    rbegin() const noexcept
+    {
+      return reverse_iterator(this->begin());
+    }
+
+    constexpr const_reverse_iterator
+    crbegin() const noexcept
+    {
+      return const_reverse_iterator(this->cbegin());
+    }
+
+    constexpr reverse_iterator
+    rend() const noexcept
+    {
+      return reverse_iterator(this->end());
+    }
+
+    constexpr const_reverse_iterator
+    crend() const noexcept
+    {
+      return const_reverse_iterator(this->cend());
+    }
+
+    // observers: subranges
+    template<size_t _Count>
+    constexpr auto
+    first() const
+    {
+      using __span_t = ::std::span<element_type, _Count>;
+      return __span_t(this->data(), _Count);
+    }
+
+    constexpr auto
+    first(index_type __count) const
+    {
+      using __span_t = ::std::span<element_type, dynamic_extent>;
+      return __span_t(this->data(), __count);
+    }
+
+    template<size_t _Count>
+    constexpr ::std::span<element_type, _Count>
+    last() const
+    {
+      static_assert(_Count == dynamic_extent ||
+                      _Extent == dynamic_extent || _Count <= _Extent,
+        "assertion failed: Count or Extent are dynamic, "
+        "or the Count is less than the static extent");
+      using __span_t = ::std::span<element_type, _Count>;
+      return __span_t(this->data() + (this->size() - _Count), _Count);
+    }
+
+    constexpr auto
+    last(index_type __count) const
+    {
+      using __span_t = ::std::span<element_type, dynamic_extent>;
+      return __span_t(this->data() + (this->size() - __count), __count);
+    }
+
+    template<size_t _Offset,
+      size_t _Count = dynamic_extent>
+    constexpr auto
+    subspan() const
+    {
+      static_assert(_Count == dynamic_extent ||
+                      _Extent == dynamic_extent ||
+                      (_Offset + _Count) <= _Extent,
+        "assertion failed: Count or Extent are dynamic, "
+        "or the Count + Offset is less than the static extent");
+      constexpr size_t __span_extent =
+        (_Count != dynamic_extent
+            ? _Count
+            : (_Extent != dynamic_extent ? _Extent - _Offset
+                                                : dynamic_extent));
+      using __span_t = ::std::span<element_type, __span_extent>;
+      return __span_t(this->data() + _Offset,
+        (_Count == dynamic_extent ? this->size() - _Offset : _Count));
+    }
+
+    constexpr auto
+    subspan(
+      index_type __offset, index_type __count = dynamic_extent) const
+    {
+      using __span_t = ::std::span<element_type, dynamic_extent>;
+      return __span_t(this->data() + __offset,
+        __count == dynamic_extent ? this->size() - __offset : __count);
+    }
+
+    // observers: range helpers
+    friend constexpr iterator
+    begin(span __sp) noexcept
+    {
+      return __sp.begin();
+    }
+
+    friend constexpr iterator
+    end(span __sp) noexcept
+    {
+      return __sp.end();
+    }
+
+  private:
+    pointer _M_ptr;
+  };
+
+  template<typename _Type, size_t _Extent>
+  auto as_bytes(::std::span<_Type, _Extent> __sp) noexcept
+  {
+    constexpr size_t __byte_extent =
+      (_Extent == ::std::dynamic_extent
+          ? _Extent
+          : (_Extent *
+              sizeof(typename ::std::span<_Type, _Extent>::element_type)));
+    using __byte_span_t = ::std::span<const byte, __byte_extent>;
+    return __byte_span_t(
+      reinterpret_cast<const byte*>(__sp.data()), __sp.size_bytes());
+  }
+
+  template<typename _Type, size_t _Extent>
+  auto as_writable_bytes(::std::span<_Type, _Extent> __sp) noexcept
+  {
+    constexpr size_t __byte_extent =
+      (_Extent == dynamic_extent
+          ? _Extent
+          : (_Extent *
+              sizeof(typename ::std::span<_Type, _Extent>::element_type)));
+    using __byte_span_t = ::std::span<byte, __byte_extent>;
+    return __byte_span_t(
+      reinterpret_cast<byte*>(__sp.data()), __sp.size_bytes());
+  }
+
+  // tuple helpers
+  template<size_t _Index, typename _Type, size_t _Extent,
+    enable_if_t<(_Extent > 0) && (_Index < _Extent)>* = nullptr>
+  constexpr typename ::std::span<_Type, _Extent>::reference get(
+    ::std::span<_Type, _Extent> __sp) noexcept
+  {
+    return __sp[_Index];
+  }
+
+  template<typename _Type, size_t _Extent>
+  class tuple_size<::std::span<_Type, _Extent>>
+  : public integral_constant<size_t, _Extent>
+  {
+  };
+
+  template<typename _Type>
+  class tuple_size<::std::span<_Type, dynamic_extent>>;
+
+  template<size_t _Index, typename _Type, size_t _Extent>
+  struct tuple_element<_Index, ::std::span<_Type, _Extent>>
+  {
+    static_assert(_Index < _Extent, "assertion failed: Index is less than Extent");
+    using type = typename ::std::span<_Type, _Extent>::element_type;
+  };
+
+  // deduction guides
+  template<typename _Type, size_t _ArrayExtent>
+  span(_Type(&)[_ArrayExtent])->span<_Type, _ArrayExtent>;
+
+  template<typename _Type, size_t _ArrayExtent>
+  span(array<_Type, _ArrayExtent>&)->span<_Type, _ArrayExtent>;
+
+  template<typename _Type, size_t _ArrayExtent>
+  span(const array<_Type, _ArrayExtent>&)
+    ->span<const _Type, _ArrayExtent>;
+
+#if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
+
+  template<typename _ContiguousIterator, typename _Sentinel>
+  span(_ContiguousIterator, _Sentinel)
+    ->span<remove_reference_t<
+      typename iterator_traits<_ContiguousIterator>::reference>>;
+
+  template<typename _Range>
+  span(_Range &&)
+    ->span<remove_reference_t<typename iterator_traits<decltype(
+      ::std::__adl_begin(::std::declval<_Range&>()))>::reference>>;
+
+#else
+
+  template<typename _Container>
+  span(_Container&)->span<typename _Container::value_type>;
+
+  template<typename _Container>
+  span(const _Container&)->span<const typename _Container::value_type>;
+
+#endif // P1394
+
+  _GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std_GLIBCXX_VISIBILITY(default)
+
+#endif // C++20
+
+#endif
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 7f7d05fd8f2..4341c9f0c1b 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -167,6 +167,9 @@
 #define __cpp_lib_list_remove_return_type 201806L
 #define __cpp_lib_math_constants 201907L
 #define __cpp_lib_to_array 201907L
+// FIXME: they forgot this feature test macro
+// get on someone's back about it in Belfast!!!
+#define __cpp_lib_span 201911
 #endif // C++2a
 #endif // C++17
 #endif // C++14
diff --git a/libstdc++-v3/testsuite/23_containers/span/everything.cc b/libstdc++-v3/testsuite/23_containers/span/everything.cc
new file mode 100644
index 00000000000..4a9ba442c68
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/everything.cc
@@ -0,0 +1,203 @@
+// 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=c++2a" }
+// { dg-do run { target c++2a } }
+
+#include <testsuite_hooks.h>
+
+#include <span>
+#include <type_traits>
+#include <cstdint>
+#include <vector>
+#include <algorithm>
+#include <cassert>
+
+int
+main()
+{
+  struct alignas(256) strawman
+  {
+    int x;
+    int y;
+    bool z;
+    int w;
+  };
+
+  struct naked_span
+  {
+    char* p;
+    std::size_t n;
+  };
+
+  struct strawman_span
+  {
+    strawman* p;
+    std::size_t n;
+  };
+
+  static_assert(sizeof(std::span<char, 0>) <= sizeof(char*));
+  static_assert(sizeof(std::span<const char, 0>) <= sizeof(const char*));
+  static_assert(sizeof(std::span<strawman, 0>) <= sizeof(strawman*));
+  static_assert(sizeof(std::span<strawman, 1>) <= sizeof(strawman*));
+  static_assert(sizeof(std::span<char>) <= sizeof(naked_span));
+  static_assert(sizeof(std::span<strawman>) <= sizeof(naked_span));
+
+  constexpr static std::array<int, 9> arr_data{ 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+  constexpr auto arr_data_span = std::span(arr_data);
+  static_assert(arr_data_span.size() == 9);
+  static_assert(arr_data_span.size_bytes() == 9 * sizeof(int));
+  static_assert(*arr_data_span.begin() == 0);
+  static_assert(*arr_data_span.data() == 0);
+  static_assert(arr_data_span.front() == 0);
+  static_assert(arr_data_span.back() == 8);
+  static_assert(arr_data_span[0] == 0);
+  static_assert(arr_data_span[1] == 1);
+  static_assert(arr_data_span[2] == 2);
+  static_assert(arr_data_span[3] == 3);
+  static_assert(arr_data_span[4] == 4);
+  static_assert(arr_data_span[5] == 5);
+  static_assert(arr_data_span[6] == 6);
+  static_assert(arr_data_span[7] == 7);
+  static_assert(arr_data_span[8] == 8);
+  static_assert(!arr_data_span.empty());
+  static_assert(decltype(arr_data_span)::extent == 9);
+
+  constexpr static int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+  constexpr auto data_span    = std::span(data);
+  static_assert(data_span.size() == 9);
+  static_assert(data_span.size_bytes() == 9 * sizeof(int));
+  static_assert(*data_span.begin() == 0);
+  static_assert(*data_span.data() == 0);
+  static_assert(data_span.front() == 0);
+  static_assert(data_span.back() == 8);
+  static_assert(data_span[0] == 0);
+  static_assert(data_span[1] == 1);
+  static_assert(data_span[2] == 2);
+  static_assert(data_span[3] == 3);
+  static_assert(data_span[4] == 4);
+  static_assert(data_span[5] == 5);
+  static_assert(data_span[6] == 6);
+  static_assert(data_span[7] == 7);
+  static_assert(data_span[8] == 8);
+  static_assert(!data_span.empty());
+  static_assert(decltype(data_span)::extent == 9);
+
+  constexpr auto data_span_first = data_span.first<3>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_first)>, std::span<const int, 3>>);
+  static_assert(decltype(data_span_first)::extent == 3);
+  static_assert(data_span_first.size() == 3);
+  static_assert(data_span_first.front() == 0);
+  static_assert(data_span_first.back() == 2);
+  static_assert(std::tuple_size_v<decltype(data_span_first)> == 3);
+  static_assert(std::is_same_v<std::tuple_element_t<0, decltype(data_span_first)>, const int>);
+
+  constexpr auto data_span_first_dyn = data_span.first(4);
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_first_dyn)>, std::span<const int>>);
+  static_assert(decltype(data_span_first_dyn)::extent == std::dynamic_extent);
+  static_assert(data_span_first_dyn.size() == 4);
+  static_assert(data_span_first_dyn.front() == 0);
+  static_assert(data_span_first_dyn.back() == 3);
+
+  constexpr auto data_span_last = data_span.last<5>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_last)>, std::span<const int, 5>>);
+  static_assert(decltype(data_span_last)::extent == 5);
+  static_assert(data_span_last.size() == 5);
+  static_assert(data_span_last.front() == 4);
+  static_assert(data_span_last.back() == 8);
+  static_assert(std::tuple_size_v<decltype(data_span_last)> == 5);
+  static_assert(std::is_same_v<std::tuple_element_t<0, decltype(data_span_last)>, const int>);
+
+  constexpr auto data_span_last_dyn = data_span.last(6);
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_last_dyn)>, std::span<const int>>);
+  static_assert(decltype(data_span_last_dyn)::extent == std::dynamic_extent);
+  static_assert(data_span_last_dyn.size() == 6);
+  static_assert(data_span_last_dyn.front() == 3);
+  static_assert(data_span_last_dyn.back() == 8);
+
+  constexpr auto data_span_subspan = data_span.subspan<1, 3>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_subspan)>, std::span<const int, 3>>);
+  static_assert(decltype(data_span_subspan)::extent == 3);
+  static_assert(data_span_subspan.size() == 3);
+  static_assert(data_span_subspan.front() == 1);
+  static_assert(data_span_subspan.back() == 3);
+
+  constexpr auto data_span_subspan_offset = data_span.subspan<8>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_subspan_offset)>, std::span<const int, 1>>);
+  static_assert(decltype(data_span_subspan_offset)::extent == 1);
+  static_assert(data_span_subspan_offset.size() == 1);
+  static_assert(data_span_subspan_offset.front() == 8);
+  static_assert(data_span_subspan_offset.back() == 8);
+
+  constexpr auto data_span_subspan_empty = data_span.subspan(9, 0);
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_subspan_empty)>, std::span<const int>>);
+  static_assert(decltype(data_span_subspan_empty)::extent == std::dynamic_extent);
+  static_assert(data_span_subspan_empty.size() == 0);
+
+  constexpr auto data_span_subspan_empty_static = data_span.subspan<9>();
+  static_assert(std::is_same_v<std::remove_cv_t<decltype(data_span_subspan_empty_static)>,
+    std::span<const int, 0>>);
+  static_assert(decltype(data_span_subspan_empty_static)::extent == 0);
+  static_assert(data_span_subspan_empty.size() == 0);
+
+  std::span<short> shorts{};
+  bool really_empty0 = shorts.empty();
+  bool really_empty1 = std::empty(shorts);
+  bool really_empty2 = shorts.data() == nullptr;
+  bool really_empty3 = shorts.begin() == shorts.end();
+  bool really_empty4 = shorts.cbegin() == shorts.cend();
+  bool really_empty =
+    really_empty0 && really_empty1 && really_empty2 && really_empty3 && really_empty4;
+  (void)really_empty;
+  VERIFY(really_empty);
+
+  std::vector<std::int_least32_t> value{ 0 };
+  std::span<int32_t> muh_span(value);
+  VERIFY(muh_span.size() == 1);
+  std::byte* original_bytes                  = reinterpret_cast<std::byte*>(value.data());
+  original_bytes[0]                          = static_cast<std::byte>(1);
+  original_bytes[1]                          = static_cast<std::byte>(2);
+  original_bytes[2]                          = static_cast<std::byte>(3);
+  original_bytes[3]                          = static_cast<std::byte>(4);
+  std::span<const std::byte> muh_byte_span   = std::as_bytes(muh_span);
+  std::span<std::byte> muh_mutable_byte_span = std::as_writable_bytes(muh_span);
+  std::span<std::byte> muh_original_byte_span(original_bytes, original_bytes + 4);
+  bool definitely_reinterpret_casted0 = std::equal(muh_byte_span.cbegin(), muh_byte_span.cend(),
+    muh_original_byte_span.cbegin(), muh_original_byte_span.cend());
+  bool definitely_reinterpret_casted1 = std::equal(muh_mutable_byte_span.cbegin(),
+    muh_mutable_byte_span.cend(), muh_original_byte_span.cbegin(), muh_original_byte_span.cend());
+  bool definitely_reinterpret_casted =
+    definitely_reinterpret_casted0 && definitely_reinterpret_casted1;
+  (void)definitely_reinterpret_casted;
+  VERIFY(definitely_reinterpret_casted);
+
+  std::span<std::byte> muh_original_byte_span_ptr_size(original_bytes, 4);
+  bool definitely_equivalent =
+    std::equal(muh_original_byte_span_ptr_size.cbegin(), muh_original_byte_span_ptr_size.cend(),
+      muh_original_byte_span.cbegin(), muh_original_byte_span.cend());
+  (void)definitely_equivalent;
+  VERIFY(definitely_equivalent);
+
+  return definitely_equivalent && definitely_reinterpret_casted && really_empty ? 0 : 1;
+}

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [ PATCH ] C++20 <span>
  2019-08-31  1:06     ` JeanHeyd Meneide
@ 2019-09-03 13:31       ` Jonathan Wakely
  2019-09-04 22:47         ` JeanHeyd Meneide
  0 siblings, 1 reply; 9+ messages in thread
From: Jonathan Wakely @ 2019-09-03 13:31 UTC (permalink / raw)
  To: JeanHeyd Meneide; +Cc: gcc-patches, libstdc++

On 30/08/19 19:42 -0400, JeanHeyd Meneide wrote:
>Ahem -- we were supposed to use the 20 version of the constexpr macro,
>not the base one.
>I will note that, for some reason, the default constructor was already
>constexpr, so we don't change that one!

Thanks!

I've done a thorough review now, lots of comments below.


>On Fri, Aug 30, 2019 at 5:11 PM JeanHeyd Meneide
><phdofthehouse@gmail.com> wrote:
>>
>> On Fri, Aug 30, 2019 at 3:41 PM Jonathan Wakely <jwakely@redhat.com> wrote:
>> >
>> > On 30/08/19 15:22 -0400, JeanHeyd Meneide wrote:
>> > >This patch implements <span> as it currently exists in the C++20 Working Draft.
>> >
>> > Nice!
>> >
>> >
>> > >Notes:
>> > >- __gnu_cxx::__normal_iterator is not fully constexpr, so its not used here
>> >
>> > I'd prefer to make __normal_iterator constexpr, and use it.
>> > It needs to be constexpr anyway for string and vector.
>> > ...
>>
>> Alright, thank you for the feedback. I fixed it up!
>>
>> Tested x86_64-pc-linux-gnu.
>>
>> 2019-08-30  JeanHeyd "ThePhD" Meneide  <phdofthehouse@gmail.com>
>>
>>         * include/std/span: Implement the entirety of span.
>>         * include/bits/stl_iterator.h: __gnu_cxx::__normal_iterator<T,
>> C> is now constexpr-qualified for C++11+.
>>        * include/bits/range_access.h: Add __adl_* versions of access functions.
>>         * testsuite/23_containers/span/everything.cc: constexpr and
>> non-constexpr tests.
>>         * include/Makefile.in: Add span to install.
>>         * include/Makefile.am: Likewise

>diff --git a/.gitignore b/.gitignore
>index d9d3967a12c..fd116c362ac 100644
>--- a/.gitignore
>+++ b/.gitignore
>@@ -57,3 +57,7 @@ REVISION
> /mpc*
> /gmp*
> /isl*
>+
>+# ignore sprinkled in dev files that keep popping up
>+.vscode/
>+.vs/

No thanks :-)

If you want to add things to be ignored in your local repo then you
can put these entries in .git/info/exclude which is not checked in.

>diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
>index 3fe80f32cc4..b8b786d9260 100644
>--- a/libstdc++-v3/include/Makefile.am
>+++ b/libstdc++-v3/include/Makefile.am
>@@ -68,6 +68,7 @@ std_headers = \
> 	${std_srcdir}/scoped_allocator \
> 	${std_srcdir}/set \
> 	${std_srcdir}/shared_mutex \
>+	${std_srcdir}/span \
> 	${std_srcdir}/sstream \
> 	${std_srcdir}/stack \
> 	${std_srcdir}/stdexcept \
>diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
>index b675d356cd4..cd1e9df5482 100644
>--- a/libstdc++-v3/include/Makefile.in
>+++ b/libstdc++-v3/include/Makefile.in

This file is generated by running autoreconf, so doesn't need to be
included in patches. Not a problem here, but for patches that makes
lots of changes to the configury stuff, it's just noise to include the
generated parts in the patch.

>@@ -412,6 +412,7 @@ std_headers = \
> 	${std_srcdir}/scoped_allocator \
> 	${std_srcdir}/set \
> 	${std_srcdir}/shared_mutex \
>+	${std_srcdir}/span \
> 	${std_srcdir}/sstream \
> 	${std_srcdir}/stack \
> 	${std_srcdir}/stdexcept \
>diff --git a/libstdc++-v3/include/bits/range_access.h b/libstdc++-v3/include/bits/range_access.h
>index d1e74711433..3acaebadcf1 100644
>--- a/libstdc++-v3/include/bits/range_access.h
>+++ b/libstdc++-v3/include/bits/range_access.h
>@@ -318,6 +318,72 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> #endif // C++17
>
>+#if __cplusplus > 201703L
>+  // "why are these in namespace std:: and not __gnu_cxx:: ?"
>+  // because if we don't put them here it's impossible to
>+  // have implicit ADL with "using std::begin/end/size/data;".
>+  template <typename _Container>
>+    constexpr auto
>+    __adl_begin(_Container& __cont) noexcept(noexcept(begin(__cont)))
>+    { return begin(__cont); }

It looks like__adl_begin and __adl_end should be guarded by #ifdef
_GLIBCXX_P1394 because they're not needed otherwise.

>diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
>index 8ab0d72b0c2..f6a6060f2e3 100644
>--- a/libstdc++-v3/include/bits/stl_iterator.h
>+++ b/libstdc++-v3/include/bits/stl_iterator.h
>@@ -799,55 +799,58 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       typedef typename __traits_type::reference 	reference;
>       typedef typename __traits_type::pointer   	pointer;
>
>+      // this was already constexpr
>+      // so it will not be changed to
>+      // _GLIBCXX_CONSTEXPR20

There's no need to add a comment saying what you didn't change :-)

>       _GLIBCXX_CONSTEXPR __normal_iterator() _GLIBCXX_NOEXCEPT
>       : _M_current(_Iterator()) { }
>
>       explicit
>-      __normal_iterator(const _Iterator& __i) _GLIBCXX_NOEXCEPT
>+      _GLIBCXX_CONSTEXPR20 __normal_iterator(const _Iterator& __i) _GLIBCXX_NOEXCEPT

The correct macro is _GLIBCXX20_CONSTEXPR. Was this patch tested?

Please put it on the previous line with 'explicit' i.e.

      explicit _GLIBCXX20_CONSTEXPR
      __normal_iterator(...

>   template<typename _Iterator, typename _Container>
>-    inline bool
>+    _GLIBCXX_CONSTEXPR20 inline bool
>     operator>=(const __normal_iterator<_Iterator, _Container>& __lhs,
> 	       const __normal_iterator<_Iterator, _Container>& __rhs)
>     _GLIBCXX_NOEXCEPT
>@@ -973,26 +976,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   template<typename _IteratorL, typename _IteratorR, typename _Container>
> #if __cplusplus >= 201103L
>     // DR 685.
>-    inline auto
>+    _GLIBCXX_CONSTEXPR20 inline auto
>     operator-(const __normal_iterator<_IteratorL, _Container>& __lhs,
> 	      const __normal_iterator<_IteratorR, _Container>& __rhs) noexcept
>     -> decltype(__lhs.base() - __rhs.base())
> #else
>-    inline typename __normal_iterator<_IteratorL, _Container>::difference_type
>+    _GLIBCXX_CONSTEXPR20 inline typename __normal_iterator<_IteratorL, _Container>::difference_type

There's no need to put it here, because this #else branch is for C++98
and the macro will always expand to nothing for C++98.

>+// get on someone's back about it in Belfast!!!
>+#define __cpp_lib_span 201911
>+
>+  inline constexpr size_t dynamic_extent =
>+    static_cast<size_t>(-1);
>+
>+  namespace __detail
>+  {
>+
>+    template<typename _Element, typename _ToElement>
>+    using __is_base_derived_safe_convertible =
>+      is_convertible<_Element (*)[], _ToElement (*)[]>;

Do we need this alias template if the only place it's used is here ...

>+    template<typename _Element, typename _ToElement>
>+    static constexpr inline bool __is_base_derived_safe_convertible_v =
>+      __is_base_derived_safe_convertible<_Element, _ToElement>::value;

Could we just define the variable template in terms of
is_convertible_v?

>+    template<typename>

All your templates are indented wrong, there should be another level
of indentation after the template-head.

>+    struct __is_std_array : false_type
>+    {
>+    };
>+
>+    template<typename _Element, size_t _Extent>
>+    struct __is_std_array<::std::array<_Element, _Extent>> : true_type

This should be _GLIBCXX_STD_C::array because when _GLIBCXX_DEBUG is
defined std::array is the same type as std::__debug::array
(std::__debug is an inline namespace). That will mean the following
specialization a redefinition of the one above, which is an error.

>+    {
>+    };
>+
>+#ifdef _GLIBCXX_DEBUG
>+    template<typename _Element, size_t _Extent>
>+    struct __is_std_array<::std::__debug::array<_Element, _Extent>>
>+    : true_type
>+    {
>+    };
>+#endif // debug/array
>+
>+    template<typename _Type>
>+    inline constexpr bool __is_std_array_v = __is_std_array<_Type>::value;

If we don't need the __is_std_array class templates directly we could
just specialize this variable template instead.

  template<typename _Tp>
    inline constexpr bool __is_std_array_v = false;

  template<typename _Tp, size_t _Num>
    inline constexpr bool
    __is_std_array_v<_GLIBCXX_STD_C::array<_Tp, _Num>> = true;

#ifdef _GLIBCXX_DEBUG
  template<typename _Tp, size_t _Num>
    inline constexpr bool
    __is_std_array_v<std::__debug::array<_Tp, _Num>> = true;
#endif // debug/array

>+    template<size_t _Extent>
>+    struct __extent_storage
>+    {
>+
>+      constexpr __extent_storage() noexcept = default;

An empty line between these constructors please.

>+      constexpr __extent_storage(size_t) noexcept
>+      {
>+      }

And the closing brace can be on the same line as the opening one:

        { }
>+
>+      static constexpr size_t
>+      _M_extent() noexcept
>+      {
>+        return _Extent;
>+      }

Even if not empty, one line functions can be written like

        { return _Extent; }

>+    };

[...]

>+  template<typename _Type, size_t _Extent = dynamic_extent>
>+  class span : private __detail::__extent_storage<_Extent>
>+  {
>+  public:
>+    // member types
>+    using value_type             = remove_cv_t<_Type>;
>+    using element_type           = _Type;
>+    using index_type             = size_t;
>+    using reference              = element_type&;
>+    using const_reference        = const element_type&;
>+    using pointer                = _Type*;
>+    using const_pointer          = const _Type*;
>+    using iterator               = __gnu_cxx::__normal_iterator<pointer, span>;
>+    using const_iterator         = __gnu_cxx::__normal_iterator<const_pointer, span>;
>+    using reverse_iterator       = ::std::reverse_iterator<iterator>;
>+    using const_reverse_iterator = ::std::reverse_iterator<const_iterator>;
>+    using difference_type        = ptrdiff_t;
>+    // Official wording has no size_type -- why??
>+    // using size_type = size_t;
>+
>+    // member constants
>+    static inline constexpr size_t extent = _Extent;
>+
>+  private:
>+    using __base_t = __detail::__extent_storage<_Extent>;
>+
>+  public:
>+    // constructors
>+    constexpr span() noexcept : __base_t(), _M_ptr(nullptr)

This constructor needs SFINAE constraints:

  Constraints: Extent == dynamic_extent || Extent == 0 is true.


>+    {
>+    }
>+
>+    constexpr span(const span&) noexcept = default;
>+
>+    template<size_t _ArrayExtent,
>+      enable_if_t<
>+        (_Extent == dynamic_extent || _ArrayExtent == _Extent) &&
>+        __detail::__is_base_derived_safe_convertible_v<
>+          remove_pointer_t<decltype(::std::__adl_data(
>+            ::std::declval<element_type (&)[_ArrayExtent]>()))>,
>+          element_type>>* = nullptr>
>+    constexpr span(element_type (&__arr)[_ArrayExtent]) noexcept(
>+      noexcept(::std::__adl_data(__arr)))
>+    : span(::std::__adl_data(__arr), _ArrayExtent)
>+    {
>+    }
>+
>+    template<size_t _ArrayExtent,
>+      enable_if_t<
>+        (_Extent == dynamic_extent || _ArrayExtent == _Extent) &&
>+        __detail::__is_base_derived_safe_convertible_v<
>+          remove_pointer_t<decltype(::std::__adl_data(
>+            ::std::declval<array<value_type, _ArrayExtent>&>()))>,
>+          element_type>>* = nullptr>
>+    constexpr span(array<value_type, _ArrayExtent>& __arr) noexcept(
>+      noexcept(::std::__adl_data(__arr)))
>+    : span(::std::__adl_data(__arr), _ArrayExtent)
>+    {
>+    }
>+
>+    template<size_t _ArrayExtent,
>+      enable_if_t<
>+        (_Extent == dynamic_extent || _ArrayExtent == _Extent) &&
>+        __detail::__is_base_derived_safe_convertible_v<
>+          remove_pointer_t<decltype(::std::__adl_data(
>+            ::std::declval<const array<value_type, _ArrayExtent>&>()))>,
>+          element_type>>* = nullptr>
>+    constexpr span(const array<value_type, _ArrayExtent>&
>+        __arr) noexcept(noexcept(::std::__adl_data(__arr)))
>+    : span(::std::__adl_data(__arr), _ArrayExtent)
>+    {
>+    }
>+
>+    // NOTE: when the time comes, and P1394 -
>+    // range constructors for std::span - ships in
>+    // the standard, delete the #else block and remove
>+    // the conditional
>+    // if the paper fails, delete #if block
>+    // and keep the crappy #else block
>+    // and then cry that NB comments failed C++20...
>+    // but maybe for C++23?
>+#if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
>+    template<typename _Range,
>+      enable_if_t<
>+        (_Extent == dynamic_extent) &&
>+        !is_same_v<remove_cvref_t<_Range>, span> &&
>+        !__detail::__is_std_array_v<remove_cvref_t<_Range>> &&
>+        !is_array_v<remove_cvref_t<_Range>> &&
>+        __detail::__is_base_derived_safe_convertible_v<
>+          remove_pointer_t<decltype(
>+            ::std::__adl_data(::std::declval<_Range&>()))>,
>+          element_type>>* = nullptr>
>+    constexpr span(_Range&& __range) noexcept(
>+      noexcept(::std::__adl_data(__range)) &&
>+      noexcept(::std::__adl_size(__range)))
>+    : span(::std::__adl_data(__range), ::std::__adl_size(__range))
>+    {
>+    }
>+
>+    template<typename _ContiguousIterator, typename _Sentinel,
>+      enable_if_t<!is_convertible_v<_Sentinel, index_type> &&
>+                         __detail::__is_base_derived_safe_convertible_v<
>+                           remove_reference_t<typename
>+                               iterator_traits<_ContiguousIterator>::reference>,
>+                           element_type>>* = nullptr>
>+    constexpr span(_ContiguousIterator __first, _Sentinel __last)
>+    : span(::std::move(__first), static_cast<index_type>(__last - __first))
>+    {
>+    }
>+
>+    template<typename _ContiguousIterator>
>+    constexpr span(_ContiguousIterator __first, index_type __count) noexcept(
>+      noexcept(::std::to_address(__first)))
>+    : __base_t(__count), _M_ptr(::std::to_address(__first))
>+    {
>+    }
>+#else
>+    template<typename _Container,
>+      enable_if_t<
>+        (_Extent == dynamic_extent) &&
>+        !is_same_v<remove_cvref_t<_Container>, span> &&
>+        !__detail::__is_std_array_v<remove_cvref_t<_Container>> &&
>+        !is_array_v<remove_cvref_t<_Container>> &&
>+        __detail::__is_base_derived_safe_convertible_v<
>+          remove_pointer_t<decltype(
>+            ::std::__adl_data(::std::declval<_Container&>()))>,
>+          element_type>>* = nullptr>
>+    constexpr span(_Container& __range) noexcept(
>+      noexcept(::std::__adl_data(__range)) &&
>+      noexcept(::std::__adl_size(__range)))
>+    : span(::std::__adl_data(__range), ::std::__adl_size(__range))
>+    {
>+    }
>+
>+    template<typename _Container,
>+      enable_if_t<
>+        (_Extent == dynamic_extent) &&
>+        !is_same_v<remove_cvref_t<_Container>, span> &&
>+        !__detail::__is_std_array_v<remove_cvref_t<_Container>> &&
>+        !is_array_v<remove_cvref_t<_Container>> &&
>+        __detail::__is_base_derived_safe_convertible_v<
>+          remove_pointer_t<decltype(
>+            ::std::__adl_data(::std::declval<_Container&>()))>,
>+          element_type>>* = nullptr>
>+    constexpr span(const _Container& __range) noexcept(
>+      noexcept(::std::__adl_data(__range)) &&
>+      noexcept(::std::__adl_size(__range)))
>+    : span(::std::__adl_data(__range), ::std::__adl_size(__range))
>+    {
>+    }
>+
>+    constexpr span(pointer __first, pointer __last) noexcept
>+    : span(::std::move(__first), static_cast<index_type>(__last - __first))
>+    {
>+    }

Blank line between these ctors please. It would also be helpful to
order the ctors as shown in the draft.

>+    constexpr span(pointer __first, index_type __count) noexcept
>+    : __base_t(__count), _M_ptr(static_cast<pointer>(__first))
>+    {

This could use an assertion to check one of the "Expects:"
preconditions:

  __glibcxx_assert(extent == dynamic_extent || __count == extent);

We can't check that [ptr, ptr+count) is a valid range, that's just
something users need to ensure.

>+    }
>+#endif // P1394
>+
>+    // assignment
>+    constexpr span&
>+    operator=(const span&) noexcept = default;
>+
>+    // observers
>+    constexpr reference
>+    front() noexcept
>+    {
>+      return *this->begin();
>+    }

This member function isn't shown in the draft.

>+
>+    constexpr const_reference
>+    front() const noexcept
>+    {
>+      return *this->begin();
>+    }
>+
>+    constexpr reference
>+    back() noexcept
>+    {
>+      return *(this->begin() + 1);
>+    }

Not shown in the draft.

>+    constexpr const_reference
>+    back() const noexcept
>+    {
>+      return *(this->end() - 1);
>+    }
>+
>+    constexpr reference operator[](index_type __idx) noexcept
>+    {
>+      return *(this->_M_ptr + __idx);
>+    }

Not shown in the draft.

>+    constexpr const_reference operator[](index_type __idx) const noexcept
>+    {
>+      return *(this->_M_ptr + __idx);
>+    }
>+
>+    constexpr pointer
>+    data() const noexcept
>+    {
>+      return this->_M_ptr;
>+    }
>+
>+    constexpr index_type
>+    size() const noexcept
>+    {
>+      return this->__base_t::_M_extent();
>+    }
>+
>+    constexpr index_type
>+    size_bytes() const noexcept
>+    {
>+      return this->__base_t::_M_extent() * sizeof(element_type);
>+    }
>+
>+    constexpr bool
>+    empty() const noexcept
>+    {
>+      return size() == 0;
>+    }
>+
>+    // observers: iterators
>+    constexpr iterator
>+    begin() const noexcept
>+    {
>+      return iterator(this->_M_ptr);
>+    }
>+
>+    constexpr const_iterator
>+    cbegin() const noexcept
>+    {
>+      return const_iterator(this->_M_ptr);
>+    }
>+
>+    constexpr iterator
>+    end() const noexcept
>+    {
>+      return iterator(this->_M_ptr + this->size());
>+    }
>+
>+    constexpr const_iterator
>+    cend() const noexcept
>+    {
>+      return const_iterator(this->_M_ptr + this->size());
>+    }
>+
>+    constexpr reverse_iterator
>+    rbegin() const noexcept
>+    {
>+      return reverse_iterator(this->begin());
>+    }

The rbeing() function needs to return an iterator that starts from the
end, i.e. return reverse_iterator(end());

>+    constexpr const_reverse_iterator
>+    crbegin() const noexcept
>+    {
>+      return const_reverse_iterator(this->cbegin());

Same here.

>+    }
>+
>+    constexpr reverse_iterator
>+    rend() const noexcept
>+    {
>+      return reverse_iterator(this->end());

And this should be return reverse_iterator(begin());

>+    }
>+
>+    constexpr const_reverse_iterator
>+    crend() const noexcept
>+    {
>+      return const_reverse_iterator(this->cend());

Same here.

>+    }
>+
>+    // observers: subranges
>+    template<size_t _Count>
>+    constexpr auto

It doesn't seem beneficial to use an 'auto' return type here.

>+    first() const
>+    {
>+      using __span_t = ::std::span<element_type, _Count>;
>+      return __span_t(this->data(), _Count);
>+    }

The function would be shorter without 'auto' return, and users can see
what it returns without inspecting the function body:

  template<size_t _Count>
    constexpr span<element_type, _Count>
    first() const
    { return { data(), _Count }; }

Although we should put an assertion in there:

  template<size_t _Count>
    constexpr span<element_type, _Count>
    first() const
    {
      __glibcxx_assert(_Count < size());
      return { data(), _Count };
    }


>+    constexpr auto
>+    first(index_type __count) const
>+    {
>+      using __span_t = ::std::span<element_type, dynamic_extent>;
>+      return __span_t(this->data(), __count);

Same here.

>+    }
>+
>+    template<size_t _Count>
>+    constexpr ::std::span<element_type, _Count>
>+    last() const
>+    {
>+      static_assert(_Count == dynamic_extent ||
>+                      _Extent == dynamic_extent || _Count <= _Extent,
>+        "assertion failed: Count or Extent are dynamic, "
>+        "or the Count is less than the static extent");

Please also add a runtime assertion to check _Count <= size().

>+      using __span_t = ::std::span<element_type, _Count>;
>+      return __span_t(this->data() + (this->size() - _Count), _Count);

The return type is not needed here, it can be simply:

  return {this->data() + (this->size() - _Count), _Count};

>+    }
>+
>+    constexpr auto
>+    last(index_type __count) const
>+    {
>+      using __span_t = ::std::span<element_type, dynamic_extent>;
>+      return __span_t(this->data() + (this->size() - __count), __count);
>+    }
>+
>+    template<size_t _Offset,
>+      size_t _Count = dynamic_extent>
>+    constexpr auto
>+    subspan() const
>+    {
>+      static_assert(_Count == dynamic_extent ||
>+                      _Extent == dynamic_extent ||
>+                      (_Offset + _Count) <= _Extent,
>+        "assertion failed: Count or Extent are dynamic, "
>+        "or the Count + Offset is less than the static extent");
>+      constexpr size_t __span_extent =
>+        (_Count != dynamic_extent
>+            ? _Count
>+            : (_Extent != dynamic_extent ? _Extent - _Offset
>+                                                : dynamic_extent));
>+      using __span_t = ::std::span<element_type, __span_extent>;
>+      return __span_t(this->data() + _Offset,
>+        (_Count == dynamic_extent ? this->size() - _Offset : _Count));
>+    }
>+
>+    constexpr auto
>+    subspan(
>+      index_type __offset, index_type __count = dynamic_extent) const
>+    {
>+      using __span_t = ::std::span<element_type, dynamic_extent>;
>+      return __span_t(this->data() + __offset,
>+        __count == dynamic_extent ? this->size() - __offset : __count);
>+    }
>+
>+    // observers: range helpers
>+    friend constexpr iterator
>+    begin(span __sp) noexcept
>+    {
>+      return __sp.begin();
>+    }
>+
>+    friend constexpr iterator
>+    end(span __sp) noexcept
>+    {
>+      return __sp.end();
>+    }
>+
>+  private:
>+    pointer _M_ptr;
>+  };
>+
>+  template<typename _Type, size_t _Extent>
>+  auto as_bytes(::std::span<_Type, _Extent> __sp) noexcept
>+  {
>+    constexpr size_t __byte_extent =
>+      (_Extent == ::std::dynamic_extent
>+          ? _Extent
>+          : (_Extent *
>+              sizeof(typename ::std::span<_Type, _Extent>::element_type)));
>+    using __byte_span_t = ::std::span<const byte, __byte_extent>;
>+    return __byte_span_t(
>+      reinterpret_cast<const byte*>(__sp.data()), __sp.size_bytes());
>+  }

Wouldn't this be easier to read (and easier for the compiler to
evaluate) if written how it's specified in the standard?

template<typename _Type, size_t _Extent>
  span<const byte, _Extent == dynamic_extent
       ? dynamic_extent : _Extent * sizeof(_Type)>
  as_bytes(span<_Type, _Extent> __sp) noexcept
  {
    return {reinterpret_cast<const byte*>(__sp.data()), __sp.size_bytes()};
  }

>+  template<typename _Type, size_t _Extent>
>+  auto as_writable_bytes(::std::span<_Type, _Extent> __sp) noexcept
>+  {
>+    constexpr size_t __byte_extent =
>+      (_Extent == dynamic_extent
>+          ? _Extent
>+          : (_Extent *
>+              sizeof(typename ::std::span<_Type, _Extent>::element_type)));
>+    using __byte_span_t = ::std::span<byte, __byte_extent>;
>+    return __byte_span_t(
>+      reinterpret_cast<byte*>(__sp.data()), __sp.size_bytes());
>+  }

template<typename _Type, size_t _Extent>
  span<byte, _Extent == dynamic_extent
       ? dynamic_extent : _Extent * sizeof(_Type)>
  as_writable_bytes(span<_Type, _Extent> __sp) noexcept
  {
    return {reinterpret_cast<byte*>(__sp.data()), __sp.size_bytes()};
  }

>+
>+  // tuple helpers
>+  template<size_t _Index, typename _Type, size_t _Extent,
>+    enable_if_t<(_Extent > 0) && (_Index < _Extent)>* = nullptr>

The standard says this check should be "Mandates:" not "Constraints:"
so it should be checked as a static_assert, not using SFINAE. Also,
isn't _Extent > 0 wrong? It must be greater than zero if _Index <
_Extent, and it should be checking _Extent != dynamic_extent according
to the curent draft.

>+  constexpr typename ::std::span<_Type, _Extent>::reference get(

Please start a new line for "get(" instead of having it wandering off
somewhere on the right. Shouldn't the return type be simply _Type& ?

So:

template<size_t _Index, typename _Type, size_t _Extent>
  constexpr _Type&
  get(span<_Type, _Extent> __sp) noexcept
  {
    static_assert(_Extent != dynamic_extent && _Index < _Extent);
    return __sp[_Index];
  }

>+    ::std::span<_Type, _Extent> __sp) noexcept
>+  {
>+    return __sp[_Index];
>+  }
>+
>+  template<typename _Type, size_t _Extent>
>+  class tuple_size<::std::span<_Type, _Extent>>

Please use 'struct' not 'class' for all the tuple_size and
tuple_element specializations. I've created
https://github.com/cplusplus/draft/pull/3211 to make that consistent
in the draft.

>+  : public integral_constant<size_t, _Extent>
>+  {
>+  };
>+
>+  template<typename _Type>
>+  class tuple_size<::std::span<_Type, dynamic_extent>>;

If you put static_assert(_Extent != dynamic_extent) in the partial
specialization above then you wouldn't need this incomplete type, and
it might give a more user-friendly diagnostic.

>+  template<size_t _Index, typename _Type, size_t _Extent>
>+  struct tuple_element<_Index, ::std::span<_Type, _Extent>>
>+  {
>+    static_assert(_Index < _Extent, "assertion failed: Index is less than Extent");

The compiler will already print "assertion failed" for a failed static
assertion, so this message would mean it's printed twice.

I'd leave the message, and just do static_assert(_Index < _Extent);

I think you also want static_assert(_Extent != dynamic_extent);

>+    using type = typename ::std::span<_Type, _Extent>::element_type;
>+  };
>+
>+  // deduction guides

Please put the deduction guides immediately after the class definition
(that's where we put them for all other types).

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [ PATCH ] C++20 <span>
  2019-09-03 13:31       ` Jonathan Wakely
@ 2019-09-04 22:47         ` JeanHeyd Meneide
  2019-09-05 11:27           ` Jonathan Wakely
  0 siblings, 1 reply; 9+ messages in thread
From: JeanHeyd Meneide @ 2019-09-04 22:47 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: gcc-patches, libstdc++

[-- Attachment #1: Type: text/plain, Size: 758 bytes --]

     Thank you for the thorough review!

On Tue, Sep 3, 2019 at 9:31 AM Jonathan Wakely <jwakely@redhat.com> wrote:
> It looks like__adl_begin and __adl_end should be guarded by #ifdef
> _GLIBCXX_P1394 because they're not needed otherwise.

     I'm absolutely going to need it for the bit data structures (and
in tuning up other parts of the library). It will also be important
for things that need to behave nicely when working with things needing
[range.access] internally in libstdc++,
while we wait for the cmcstl2 implementation. I did gate
__adl_to_address behind p1394, though, because that is definitely
specific to that paper (and its friend, p1474).

     Let me know if I missed anything!

     Tested on x64_86 Linux with 23_containers/* tests.

[-- Attachment #2: ThePhD.span.patch.txt --]
[-- Type: text/plain, Size: 50365 bytes --]

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 3fe80f32cc4..b8b786d9260 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -68,6 +68,7 @@ std_headers = \
 	${std_srcdir}/scoped_allocator \
 	${std_srcdir}/set \
 	${std_srcdir}/shared_mutex \
+	${std_srcdir}/span \
 	${std_srcdir}/sstream \
 	${std_srcdir}/stack \
 	${std_srcdir}/stdexcept \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index b675d356cd4..cd1e9df5482 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -412,6 +412,7 @@ std_headers = \
 	${std_srcdir}/scoped_allocator \
 	${std_srcdir}/set \
 	${std_srcdir}/shared_mutex \
+	${std_srcdir}/span \
 	${std_srcdir}/sstream \
 	${std_srcdir}/stack \
 	${std_srcdir}/stdexcept \
diff --git a/libstdc++-v3/include/bits/range_access.h b/libstdc++-v3/include/bits/range_access.h
index d1e74711433..a80ec48248f 100644
--- a/libstdc++-v3/include/bits/range_access.h
+++ b/libstdc++-v3/include/bits/range_access.h
@@ -318,6 +318,78 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 #endif // C++17
 
+#if __cplusplus > 201703L
+  // "why are these in namespace std:: and not __gnu_cxx:: ?"
+  // because if we don't put them here it's impossible to 
+  // have implicit ADL with "using std::begin/end/size/data;".
+  template <typename _Container>
+    constexpr auto
+    __adl_begin(_Container& __cont) noexcept(noexcept(begin(__cont)))
+    { return begin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_end(_Container& __cont) noexcept(noexcept(end(__cont)))
+    { return end(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_cbegin(_Container& __cont) noexcept(noexcept(cbegin(__cont)))
+    { return cbegin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_cend(_Container& __cont) noexcept(noexcept(cend(__cont)))
+    { return cend(__cont); }
+
+  template <typename _Container>
+    constexpr auto
+    __adl_rbegin(_Container& __cont) noexcept(noexcept(rbegin(__cont)))
+    { return rbegin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_rend(_Container& __cont) noexcept(noexcept(rend(__cont)))
+    { return rend(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_crbegin(_Container& __cont) noexcept(noexcept(crbegin(__cont)))
+    { return crbegin(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_crend(_Container& __cont) noexcept(noexcept(crend(__cont)))
+    { return crend(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_data(_Container& __cont) noexcept(noexcept(data(__cont)))
+    { return data(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_cdata(_Container& __cont) noexcept(noexcept(cdata(__cont)))
+    { return cdata(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_size(_Container& __cont) noexcept(noexcept(size(__cont)))
+    { return size(__cont); }
+  
+  template <typename _Container>
+    constexpr auto
+    __adl_empty(_Container& __cont) noexcept(noexcept(empty(__cont)))
+    { return empty(__cont); }
+  
+#if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
+  template <typename _Container>
+    constexpr auto
+    __adl_to_address(_Container& __cont) noexcept(noexcept(to_address(__cont)))
+    { return to_address(__cont); }
+#endif // P1394 and new contiguous_iterator requirements [iterator.concept.contiguous]
+#endif // C++20
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 
diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
index 8ab0d72b0c2..e29775a42c5 100644
--- a/libstdc++-v3/include/bits/stl_iterator.h
+++ b/libstdc++-v3/include/bits/stl_iterator.h
@@ -446,7 +446,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 #if __cplusplus >= 201103L
   template<typename _Iterator>
-    _GLIBCXX20_CONSTEXPR auto
+    _GLIBCXX20_CONSTEXPR
+    auto
     __niter_base(reverse_iterator<_Iterator> __it)
     -> decltype(__make_reverse_iterator(__niter_base(__it.base())))
     { return __make_reverse_iterator(__niter_base(__it.base())); }
@@ -457,7 +458,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { };
 
   template<typename _Iterator>
-    _GLIBCXX20_CONSTEXPR auto
+    _GLIBCXX20_CONSTEXPR
+    auto
     __miter_base(reverse_iterator<_Iterator> __it)
     -> decltype(__make_reverse_iterator(__miter_base(__it.base())))
     { return __make_reverse_iterator(__miter_base(__it.base())); }
@@ -802,12 +804,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _GLIBCXX_CONSTEXPR __normal_iterator() _GLIBCXX_NOEXCEPT
       : _M_current(_Iterator()) { }
 
-      explicit
+      explicit _GLIBCXX20_CONSTEXPR
       __normal_iterator(const _Iterator& __i) _GLIBCXX_NOEXCEPT
       : _M_current(__i) { }
 
       // Allow iterator to const_iterator conversion
       template<typename _Iter>
+        _GLIBCXX20_CONSTEXPR
         __normal_iterator(const __normal_iterator<_Iter,
 			  typename __enable_if<
       	       (std::__are_same<_Iter, typename _Container::pointer>::__value),
@@ -815,14 +818,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         : _M_current(__i.base()) { }
 
       // Forward iterator requirements
+      _GLIBCXX20_CONSTEXPR
       reference
       operator*() const _GLIBCXX_NOEXCEPT
       { return *_M_current; }
 
+      _GLIBCXX20_CONSTEXPR
       pointer
       operator->() const _GLIBCXX_NOEXCEPT
       { return _M_current; }
 
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator&
       operator++() _GLIBCXX_NOEXCEPT
       {
@@ -830,11 +836,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	return *this;
       }
 
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator
       operator++(int) _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current++); }
 
       // Bidirectional iterator requirements
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator&
       operator--() _GLIBCXX_NOEXCEPT
       {
@@ -842,31 +850,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	return *this;
       }
 
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator
       operator--(int) _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current--); }
 
       // Random access iterator requirements
+      _GLIBCXX20_CONSTEXPR
       reference
       operator[](difference_type __n) const _GLIBCXX_NOEXCEPT
       { return _M_current[__n]; }
 
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator&
       operator+=(difference_type __n) _GLIBCXX_NOEXCEPT
       { _M_current += __n; return *this; }
 
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator
       operator+(difference_type __n) const _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current + __n); }
 
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator&
       operator-=(difference_type __n) _GLIBCXX_NOEXCEPT
       { _M_current -= __n; return *this; }
 
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator
       operator-(difference_type __n) const _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current - __n); }
 
+      _GLIBCXX20_CONSTEXPR
       const _Iterator&
       base() const _GLIBCXX_NOEXCEPT
       { return _M_current; }
@@ -882,6 +897,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // Forward iterator requirements
   template<typename _IteratorL, typename _IteratorR, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator==(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
@@ -889,6 +905,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() == __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator==(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
@@ -896,6 +913,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() == __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator!=(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
@@ -903,6 +921,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() != __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator!=(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
@@ -911,6 +930,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // Random access iterator requirements
   template<typename _IteratorL, typename _IteratorR, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator<(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs)
@@ -918,6 +938,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() < __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator<(const __normal_iterator<_Iterator, _Container>& __lhs,
 	      const __normal_iterator<_Iterator, _Container>& __rhs)
@@ -925,6 +946,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() < __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator>(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs)
@@ -932,6 +954,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() > __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator>(const __normal_iterator<_Iterator, _Container>& __lhs,
 	      const __normal_iterator<_Iterator, _Container>& __rhs)
@@ -939,6 +962,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() > __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator<=(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
@@ -946,6 +970,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() <= __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator<=(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
@@ -953,6 +978,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() <= __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator>=(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
@@ -960,6 +986,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() >= __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator>=(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
@@ -973,6 +1000,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _IteratorL, typename _IteratorR, typename _Container>
 #if __cplusplus >= 201103L
     // DR 685.
+    _GLIBCXX20_CONSTEXPR
     inline auto
     operator-(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs) noexcept
@@ -985,6 +1013,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() - __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline typename __normal_iterator<_Iterator, _Container>::difference_type
     operator-(const __normal_iterator<_Iterator, _Container>& __lhs,
 	      const __normal_iterator<_Iterator, _Container>& __rhs)
@@ -992,6 +1021,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() - __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline __normal_iterator<_Iterator, _Container>
     operator+(typename __normal_iterator<_Iterator, _Container>::difference_type
 	      __n, const __normal_iterator<_Iterator, _Container>& __i)
@@ -1006,6 +1036,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     _Iterator
     __niter_base(__gnu_cxx::__normal_iterator<_Iterator, _Container> __it)
     _GLIBCXX_NOEXCEPT_IF(std::is_nothrow_copy_constructible<_Iterator>::value)
diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span
new file mode 100644
index 00000000000..6bb373f1bcd
--- /dev/null
+++ b/libstdc++-v3/include/std/span
@@ -0,0 +1,515 @@
+// Components for manipulating non-owning sequences of objects -*- C++ -*-
+
+// 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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file span
+ *  This is a Standard C++ Library header.
+ */
+
+//
+// P0122 span library
+// Contributed by ThePhD
+//
+
+#ifndef _GLIBCXX_SPAN
+#define _GLIBCXX_SPAN 1
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+
+#include <cstddef>
+#include <type_traits>
+#include <tuple>
+#include <utility>
+#include <array>
+#include <bits/stl_iterator.h>
+#include <bits/range_access.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+  _GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+// FIXME: they forgot this feature test macro
+// get on someone's back about it in Belfast!!!
+#define __cpp_lib_span 201911
+
+  inline constexpr size_t dynamic_extent =
+    static_cast<size_t>(-1);
+
+  namespace __detail
+  {
+
+    template<typename _Element, typename _ToElement>
+      static constexpr inline bool __is_base_derived_safe_convertible_v =
+        is_convertible_v<_Element (*)[], _ToElement (*)[]>;
+
+    template<typename _Tp>
+      inline constexpr bool __is_std_array_v = false;
+
+    template<typename _Tp, size_t _Num>
+      inline constexpr bool
+      __is_std_array_v<_GLIBCXX_STD_C::array<_Tp, _Num>> = true;
+
+#ifdef _GLIBCXX_DEBUG
+    template<typename _Tp, size_t _Num>
+      inline constexpr bool
+      __is_std_array_v<std::__debug::array<_Tp, _Num>> = true;
+#endif // debug/array
+
+    template<size_t _Extent>
+      class __extent_storage
+      {
+      public:
+
+        constexpr 
+        __extent_storage() noexcept = default;
+        
+        constexpr 
+        __extent_storage(size_t) noexcept
+        {}
+
+        static constexpr size_t
+        _M_extent() noexcept
+        { return _Extent; }
+      };
+
+    template<>
+      class __extent_storage<static_cast<size_t>(-1)>
+      {
+      public:
+      
+        constexpr 
+        __extent_storage() noexcept : _M_extent_value(0){};
+
+        constexpr 
+        __extent_storage(size_t __extent) noexcept
+        : _M_extent_value(__extent)
+        {}
+
+        constexpr size_t
+        _M_extent() const noexcept
+        { return this->_M_extent_value; }
+
+      private:
+        size_t _M_extent_value;
+      };
+
+  } // namespace __detail
+
+  template<typename _Type, size_t _Extent = dynamic_extent>
+    class span : private __detail::__extent_storage<_Extent>
+    {
+    public:
+      // member types
+      using value_type             = remove_cv_t<_Type>;
+      using element_type           = _Type;
+      using index_type             = size_t;
+      using reference              = element_type&;
+      using const_reference        = const element_type&;
+      using pointer                = _Type*;
+      using const_pointer          = const _Type*;
+      using iterator               = __gnu_cxx::__normal_iterator<pointer, span>;
+      using const_iterator         = __gnu_cxx::__normal_iterator<const_pointer, span>;
+      using reverse_iterator       = ::std::reverse_iterator<iterator>;
+      using const_reverse_iterator = ::std::reverse_iterator<const_iterator>;
+      using difference_type        = ptrdiff_t;
+      // Official wording has no size_type -- why??
+      // using size_type = size_t;
+
+      // member constants
+      static inline constexpr size_t extent = _Extent;
+
+    private:
+      using __base_t = __detail::__extent_storage<extent>;
+
+    public:
+      // constructors
+      template <typename _Dummy = _Type, enable_if_t<is_same_v<_Dummy, _Type> && (_Extent == dynamic_extent || _Extent == 0)>* = nullptr>
+        constexpr span() noexcept : __base_t(0), _M_ptr(nullptr)
+        {}
+
+      constexpr 
+      span(const span&) noexcept = default;
+
+      template<size_t _ArrayExtent,
+        enable_if_t<
+          (_Extent == dynamic_extent || _ArrayExtent == _Extent) &&
+          __detail::__is_base_derived_safe_convertible_v<
+            remove_pointer_t<decltype(::std::__adl_data(
+              ::std::declval<element_type (&)[_ArrayExtent]>()))>,
+            element_type>>* = nullptr>
+        constexpr span(element_type (&__arr)[_ArrayExtent]) noexcept(
+          noexcept(::std::__adl_data(__arr)))
+        : span(::std::__adl_data(__arr), _ArrayExtent)
+        {}
+
+      template<size_t _ArrayExtent,
+        enable_if_t<
+          (_Extent == dynamic_extent || _ArrayExtent == _Extent) &&
+          __detail::__is_base_derived_safe_convertible_v<
+            remove_pointer_t<decltype(::std::__adl_data(
+              ::std::declval<const array<value_type, _ArrayExtent>&>()))>,
+            element_type>>* = nullptr>
+        constexpr span(array<value_type, _ArrayExtent>& __arr) noexcept(
+          noexcept(::std::__adl_data(__arr)))
+        : span(::std::__adl_data(__arr), _ArrayExtent)
+        {}
+
+      template<size_t _ArrayExtent,
+        enable_if_t<
+          (_Extent == dynamic_extent || _ArrayExtent == _Extent) &&
+          __detail::__is_base_derived_safe_convertible_v<
+            remove_pointer_t<decltype(::std::__adl_data(
+              ::std::declval<const array<value_type, _ArrayExtent>&>()))>,
+            element_type>>* = nullptr>
+        constexpr span(const array<value_type, _ArrayExtent>& __arr) 
+          noexcept(noexcept(::std::__adl_data(__arr)))
+        : span(::std::__adl_data(__arr), _ArrayExtent)
+        {}
+
+      // NOTE: when the time comes, and P1394 -
+      // range constructors for std::span - ships in
+      // the standard, delete the #else block and remove
+      // the conditional
+      // if the paper fails, delete #if block
+      // and keep the crappy #else block
+      // and then cry that NB comments failed C++20...
+      // but maybe for C++23?
+  #if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
+      template<typename _Range,
+        enable_if_t<
+          (_Extent == dynamic_extent) &&
+          !is_same_v<remove_cvref_t<_Range>, span> &&
+          !__detail::__is_std_array_v<remove_cvref_t<_Range>> &&
+          !is_array_v<remove_cvref_t<_Range>> &&
+          __detail::__is_base_derived_safe_convertible_v<
+            remove_pointer_t<decltype(
+              ::std::__adl_data(::std::declval<_Range&>())
+              + ::std::__adl_size(::std::declval<_Range&>()))>,
+            element_type>>* = nullptr>
+        constexpr span(_Range&& __range) noexcept(
+          noexcept(::std::__adl_data(__range)) &&
+          noexcept(::std::__adl_size(__range)))
+        : span(::std::__adl_data(__range), ::std::__adl_size(__range))
+        {}
+
+      template<typename _ContiguousIterator, typename _Sentinel,
+        enable_if_t<!is_convertible_v<_Sentinel, index_type> &&
+                          __detail::__is_base_derived_safe_convertible_v<
+                            remove_reference_t<typename 
+                                iterator_traits<_ContiguousIterator>::reference>,
+                            element_type>>* = nullptr>
+        constexpr span(_ContiguousIterator __first, _Sentinel __last)
+        : span(::std::move(__first), static_cast<index_type>(__last - __first))
+        {}
+
+      template<typename _ContiguousIterator>
+        constexpr span(_ContiguousIterator __first, index_type __count) noexcept(
+          noexcept(::std::__adl_to_address(__first)))
+        : __base_t(__count), _M_ptr(::std::__adl_to_address(__first))
+        { __glibcxx_assert(_Extent == dynamic_extent || __count == _Extent); }
+
+  #else
+
+      template<typename _Container,
+        enable_if_t<
+          (_Extent == dynamic_extent) &&
+          !is_same_v<remove_cvref_t<_Container>, span> &&
+          !__detail::__is_std_array_v<remove_cvref_t<_Container>> &&
+          !is_array_v<remove_cvref_t<_Container>> &&
+          __detail::__is_base_derived_safe_convertible_v<
+            remove_pointer_t<decltype(
+              ::std::__adl_data(::std::declval<_Container&>())
+              + ::std::__adl_size(::std::declval<_Container&>()))>,
+            element_type>>* = nullptr>
+        constexpr span(_Container& __range) noexcept(
+          noexcept(::std::__adl_data(__range)) &&
+          noexcept(::std::__adl_size(__range)))
+        : span(::std::__adl_data(__range), ::std::__adl_size(__range))
+        {}
+
+      template<typename _Container,
+        enable_if_t<
+          (_Extent == dynamic_extent) &&
+          !is_same_v<remove_cvref_t<_Container>, span> &&
+          !__detail::__is_std_array_v<remove_cvref_t<_Container>> &&
+          !is_array_v<remove_cvref_t<_Container>> &&
+          __detail::__is_base_derived_safe_convertible_v<
+            remove_pointer_t<decltype(
+              ::std::__adl_data(::std::declval<_Container&>())
+              + ::std::__adl_size(::std::declval<_Container&>()))>,
+            element_type>>* = nullptr>
+        constexpr span(const _Container& __range) noexcept(
+          noexcept(::std::__adl_data(__range)) &&
+          noexcept(::std::__adl_size(__range)))
+        : span(::std::__adl_data(__range), ::std::__adl_size(__range))
+        {}
+      
+      constexpr span(pointer __first, index_type __count) noexcept
+      : __base_t(__count), _M_ptr(static_cast<pointer>(__first))
+      { __glibcxx_assert(_Extent == dynamic_extent || __count == _Extent); }
+
+      constexpr span(pointer __first, pointer __last) noexcept
+      : span(::std::move(__first), static_cast<index_type>(__last - __first))
+      {}
+  #endif // P1394
+
+      // assignment
+      constexpr span&
+      operator=(const span&) noexcept = default;
+
+      // observers: element access
+      constexpr reference
+      front() const noexcept
+      { return *this->begin(); }
+
+      constexpr reference
+      back() const noexcept
+      {
+        iterator __it = this->end();
+        --__it;
+        return *__it;
+      }
+
+      constexpr reference 
+      operator[](index_type __idx) const noexcept
+      { return *(this->_M_ptr + __idx); }
+
+      constexpr pointer
+      data() const noexcept
+      { return this->_M_ptr; }
+
+      constexpr index_type
+      size() const noexcept
+      { return this->__base_t::_M_extent(); }
+
+      constexpr index_type
+      size_bytes() const noexcept
+      { return this->__base_t::_M_extent() * sizeof(element_type); }
+
+      constexpr bool
+      empty() const noexcept
+      { return size() == 0; }
+
+      // observers: iterator support
+      constexpr iterator
+      begin() const noexcept
+      { return iterator(this->_M_ptr); }
+
+      constexpr const_iterator
+      cbegin() const noexcept
+      { return const_iterator(this->_M_ptr); }
+
+      constexpr iterator
+      end() const noexcept
+      { return iterator(this->_M_ptr + this->size()); }
+
+      constexpr const_iterator
+      cend() const noexcept
+      { return const_iterator(this->_M_ptr + this->size()); }
+
+      constexpr reverse_iterator
+      rbegin() const noexcept
+      { return reverse_iterator(this->end()); }
+
+      constexpr const_reverse_iterator
+      crbegin() const noexcept
+      { return const_reverse_iterator(this->cend()); }
+
+      constexpr reverse_iterator
+      rend() const noexcept
+      { return reverse_iterator(this->begin()); }
+
+      constexpr const_reverse_iterator
+      crend() const noexcept
+      { return const_reverse_iterator(this->cbegin()); }
+
+      // observers: subranges
+      template<size_t _Count>
+        constexpr span<element_type, _Count>
+        first() const
+        {
+          __glibcxx_assert(_Count < size());
+          return { this->data(), _Count };
+        }
+
+      constexpr span<element_type, dynamic_extent>
+      first(index_type __count) const
+      {
+        __glibcxx_assert(__count < size());
+        return { this->data(), __count }; 
+      }
+
+      template<size_t _Count>
+        constexpr span<element_type, _Count>
+        last() const
+        {
+          static_assert(_Count == dynamic_extent ||
+                          _Extent == dynamic_extent || _Count <= _Extent,
+            "Count or Extent are dynamic, "
+            "or the Count is less than the static extent");
+          __glibcxx_assert(_Count <= size());
+          return { this->data() + (this->size() - _Count), _Count };
+        }
+
+      constexpr span<element_type, dynamic_extent>
+      last(index_type __count) const
+      {
+        __glibcxx_assert(__count < size());
+        index_type __offset = (this->size() - __count);
+        return { this->data() + __offset, __count };
+      }
+
+      template<size_t _Offset, size_t _Count = dynamic_extent>
+        constexpr auto
+        subspan() const
+        {
+          static_assert(_Count == dynamic_extent ||
+                          _Extent == dynamic_extent ||
+                          (_Offset + _Count) <= _Extent,
+            "Count or Extent are dynamic, "
+            "or the Count + Offset is less than the static extent");
+          constexpr size_t __span_extent =
+            (_Count != dynamic_extent
+                ? _Count
+                : (_Extent != dynamic_extent ? _Extent - _Offset
+                                                    : dynamic_extent));
+          using __span_t = span<element_type, __span_extent>;
+          if constexpr(_Count != dynamic_extent) 
+            {
+              __glibcxx_assert((_Offset + _Count) < size());
+            }
+          return __span_t(this->data() + _Offset,
+            (_Count == dynamic_extent ? this->size() - _Offset : _Count));
+        }
+
+      constexpr span<element_type, dynamic_extent>
+      subspan(
+        index_type __offset, index_type __count = dynamic_extent) const
+      {
+        return {this->data() + __offset,
+          __count == dynamic_extent ? this->size() - __offset : __count};
+      }
+
+      // observers: range helpers
+      friend constexpr iterator
+      begin(span __sp) noexcept
+      {
+        return __sp.begin();
+      }
+
+      friend constexpr iterator
+      end(span __sp) noexcept
+      {
+        return __sp.end();
+      }
+
+    private:
+      pointer _M_ptr;
+    };
+
+  // deduction guides
+  template<typename _Type, size_t _ArrayExtent>
+    span(_Type(&)[_ArrayExtent])->span<_Type, _ArrayExtent>;
+
+  template<typename _Type, size_t _ArrayExtent>
+    span(array<_Type, _ArrayExtent>&)->span<_Type, _ArrayExtent>;
+
+  template<typename _Type, size_t _ArrayExtent>
+    span(const array<_Type, _ArrayExtent>&)
+      ->span<const _Type, _ArrayExtent>;
+
+#if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
+
+  template<typename _ContiguousIterator, typename _Sentinel>
+    span(_ContiguousIterator, _Sentinel)
+      ->span<remove_reference_t<
+        typename iterator_traits<_ContiguousIterator>::reference>>;
+
+  template<typename _Range>
+    span(_Range &&)
+      ->span<remove_reference_t<typename iterator_traits<decltype(
+        ::std::__adl_begin(::std::declval<_Range&>()))>::reference>>;
+
+#else
+
+  template<typename _Container>
+    span(_Container&)->span<typename _Container::value_type>;
+
+  template<typename _Container>
+    span(const _Container&)->span<const typename _Container::value_type>;
+
+#endif // P1394
+
+  template<typename _Type, size_t _Extent>
+    span<const byte, _Extent == dynamic_extent
+        ? dynamic_extent : _Extent * sizeof(_Type)>
+    inline as_bytes(span<_Type, _Extent> __sp) noexcept
+    {
+      return {reinterpret_cast<const byte*>(__sp.data()), __sp.size_bytes()};
+    }
+
+  template<typename _Type, size_t _Extent>
+    span<byte, _Extent == dynamic_extent
+       ? dynamic_extent : _Extent * sizeof(_Type)>
+    as_writable_bytes(span<_Type, _Extent> __sp) noexcept
+    {
+      return {reinterpret_cast<byte*>(__sp.data()), __sp.size_bytes()};
+    }
+
+  // tuple helpers
+  template<size_t _Index, typename _Type, size_t _Extent>
+    constexpr _Type& 
+    get(span<_Type, _Extent> __sp) noexcept
+    {
+      static_assert(_Extent != dynamic_extent 
+        && (_Extent > 0) && (_Index < _Extent), 
+        "get can only be used with a span of non-dynamic "
+        "(fixed) extent.");
+      return __sp[_Index];
+    }
+
+  template<typename _Type, size_t _Extent>
+    struct tuple_size<span<_Type, _Extent>>
+    : public integral_constant<size_t, _Extent>
+    {
+      static_assert(_Extent != dynamic_extent, "tuple_size can only "
+        "be used with a span of non-dynamic (fixed) extent");
+    };
+
+  template<size_t _Index, typename _Type, size_t _Extent>
+    struct tuple_element<_Index, span<_Type, _Extent>>
+    {
+      static_assert(_Extent != dynamic_extent, "tuple_size can only "
+        "be used with a span of non-dynamic (fixed) extent");
+      static_assert(_Index < _Extent, "Index is less than Extent");
+      using type = typename span<_Type, _Extent>::element_type;
+    };
+
+  _GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std_GLIBCXX_VISIBILITY(default)
+
+#endif // C++20
+
+#endif
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 7f7d05fd8f2..4341c9f0c1b 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -167,6 +167,9 @@
 #define __cpp_lib_list_remove_return_type 201806L
 #define __cpp_lib_math_constants 201907L
 #define __cpp_lib_to_array 201907L
+// FIXME: they forgot this feature test macro
+// get on someone's back about it in Belfast!!!
+#define __cpp_lib_span 201911
 #endif // C++2a
 #endif // C++17
 #endif // C++14
diff --git a/libstdc++-v3/testsuite/23_containers/span/everything.cc b/libstdc++-v3/testsuite/23_containers/span/everything.cc
new file mode 100644
index 00000000000..1ee1f6e004c
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/everything.cc
@@ -0,0 +1,203 @@
+// 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=c++2a" }
+// { dg-do run { target c++2a } }
+
+#include <testsuite_hooks.h>
+
+#include <span>
+#include <type_traits>
+#include <cstdint>
+#include <vector>
+#include <algorithm>
+#include <cassert>
+
+int
+main()
+{
+  struct alignas(256) strawman
+  {
+    int x;
+    int y;
+    bool z;
+    int w;
+  };
+
+  struct naked_span
+  {
+    char* p;
+    std::size_t n;
+  };
+
+  struct strawman_span
+  {
+    strawman* p;
+    std::size_t n;
+  };
+
+  static_assert(sizeof(std::span<char, 0>) <= sizeof(char*));
+  static_assert(sizeof(std::span<const char, 0>) <= sizeof(const char*));
+  static_assert(sizeof(std::span<strawman, 0>) <= sizeof(strawman*));
+  static_assert(sizeof(std::span<strawman, 1>) <= sizeof(strawman*));
+  static_assert(sizeof(std::span<char>) <= sizeof(naked_span));
+  static_assert(sizeof(std::span<strawman>) <= sizeof(strawman_span));
+
+  constexpr static std::array<int, 9> arr_data{ 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+  constexpr auto arr_data_span = std::span(arr_data);
+  static_assert(arr_data_span.size() == 9);
+  static_assert(arr_data_span.size_bytes() == 9 * sizeof(int));
+  static_assert(*arr_data_span.begin() == 0);
+  static_assert(*arr_data_span.data() == 0);
+  static_assert(arr_data_span.front() == 0);
+  static_assert(arr_data_span.back() == 8);
+  static_assert(arr_data_span[0] == 0);
+  static_assert(arr_data_span[1] == 1);
+  static_assert(arr_data_span[2] == 2);
+  static_assert(arr_data_span[3] == 3);
+  static_assert(arr_data_span[4] == 4);
+  static_assert(arr_data_span[5] == 5);
+  static_assert(arr_data_span[6] == 6);
+  static_assert(arr_data_span[7] == 7);
+  static_assert(arr_data_span[8] == 8);
+  static_assert(!arr_data_span.empty());
+  static_assert(decltype(arr_data_span)::extent == 9);
+
+  constexpr static int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+  constexpr auto data_span    = std::span(data);
+  static_assert(data_span.size() == 9);
+  static_assert(data_span.size_bytes() == 9 * sizeof(int));
+  static_assert(*data_span.begin() == 0);
+  static_assert(*data_span.data() == 0);
+  static_assert(data_span.front() == 0);
+  static_assert(data_span.back() == 8);
+  static_assert(data_span[0] == 0);
+  static_assert(data_span[1] == 1);
+  static_assert(data_span[2] == 2);
+  static_assert(data_span[3] == 3);
+  static_assert(data_span[4] == 4);
+  static_assert(data_span[5] == 5);
+  static_assert(data_span[6] == 6);
+  static_assert(data_span[7] == 7);
+  static_assert(data_span[8] == 8);
+  static_assert(!data_span.empty());
+  static_assert(decltype(data_span)::extent == 9);
+
+  constexpr auto data_span_first = data_span.first<3>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_first)>, std::span<const int, 3>>);
+  static_assert(decltype(data_span_first)::extent == 3);
+  static_assert(data_span_first.size() == 3);
+  static_assert(data_span_first.front() == 0);
+  static_assert(data_span_first.back() == 2);
+  static_assert(std::tuple_size_v<decltype(data_span_first)> == 3);
+  static_assert(std::is_same_v<std::tuple_element_t<0, decltype(data_span_first)>, const int>);
+
+  constexpr auto data_span_first_dyn = data_span.first(4);
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_first_dyn)>, std::span<const int>>);
+  static_assert(decltype(data_span_first_dyn)::extent == std::dynamic_extent);
+  static_assert(data_span_first_dyn.size() == 4);
+  static_assert(data_span_first_dyn.front() == 0);
+  static_assert(data_span_first_dyn.back() == 3);
+
+  constexpr auto data_span_last = data_span.last<5>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_last)>, std::span<const int, 5>>);
+  static_assert(decltype(data_span_last)::extent == 5);
+  static_assert(data_span_last.size() == 5);
+  static_assert(data_span_last.front() == 4);
+  static_assert(data_span_last.back() == 8);
+  static_assert(std::tuple_size_v<decltype(data_span_last)> == 5);
+  static_assert(std::is_same_v<std::tuple_element_t<0, decltype(data_span_last)>, const int>);
+
+  constexpr auto data_span_last_dyn = data_span.last(6);
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_last_dyn)>, std::span<const int>>);
+  static_assert(decltype(data_span_last_dyn)::extent == std::dynamic_extent);
+  static_assert(data_span_last_dyn.size() == 6);
+  static_assert(data_span_last_dyn.front() == 3);
+  static_assert(data_span_last_dyn.back() == 8);
+
+  constexpr auto data_span_subspan = data_span.subspan<1, 3>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_subspan)>, std::span<const int, 3>>);
+  static_assert(decltype(data_span_subspan)::extent == 3);
+  static_assert(data_span_subspan.size() == 3);
+  static_assert(data_span_subspan.front() == 1);
+  static_assert(data_span_subspan.back() == 3);
+
+  constexpr auto data_span_subspan_offset = data_span.subspan<8>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_subspan_offset)>, std::span<const int, 1>>);
+  static_assert(decltype(data_span_subspan_offset)::extent == 1);
+  static_assert(data_span_subspan_offset.size() == 1);
+  static_assert(data_span_subspan_offset.front() == 8);
+  static_assert(data_span_subspan_offset.back() == 8);
+
+  constexpr auto data_span_subspan_empty = data_span.subspan(9, 0);
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_subspan_empty)>, std::span<const int>>);
+  static_assert(decltype(data_span_subspan_empty)::extent == std::dynamic_extent);
+  static_assert(data_span_subspan_empty.size() == 0);
+
+  constexpr auto data_span_subspan_empty_static = data_span.subspan<9>();
+  static_assert(std::is_same_v<std::remove_cv_t<decltype(data_span_subspan_empty_static)>,
+    std::span<const int, 0>>);
+  static_assert(decltype(data_span_subspan_empty_static)::extent == 0);
+  static_assert(data_span_subspan_empty.size() == 0);
+
+  std::span<short> shorts{};
+  bool really_empty0 = shorts.empty();
+  bool really_empty1 = std::empty(shorts);
+  bool really_empty2 = shorts.data() == nullptr;
+  bool really_empty3 = shorts.begin() == shorts.end();
+  bool really_empty4 = shorts.cbegin() == shorts.cend();
+  bool really_empty =
+    really_empty0 && really_empty1 && really_empty2 && really_empty3 && really_empty4;
+  (void)really_empty;
+  VERIFY(really_empty);
+
+  std::vector<std::int_least32_t> value{ 0 };
+  std::span<int32_t> muh_span(value);
+  VERIFY(muh_span.size() == 1);
+  std::byte* original_bytes                  = reinterpret_cast<std::byte*>(value.data());
+  original_bytes[0]                          = static_cast<std::byte>(1);
+  original_bytes[1]                          = static_cast<std::byte>(2);
+  original_bytes[2]                          = static_cast<std::byte>(3);
+  original_bytes[3]                          = static_cast<std::byte>(4);
+  std::span<const std::byte> muh_byte_span   = std::as_bytes(muh_span);
+  std::span<std::byte> muh_mutable_byte_span = std::as_writable_bytes(muh_span);
+  std::span<std::byte> muh_original_byte_span(original_bytes, original_bytes + 4);
+  bool definitely_reinterpret_casted0 = std::equal(muh_byte_span.cbegin(), muh_byte_span.cend(),
+    muh_original_byte_span.cbegin(), muh_original_byte_span.cend());
+  bool definitely_reinterpret_casted1 = std::equal(muh_mutable_byte_span.cbegin(),
+    muh_mutable_byte_span.cend(), muh_original_byte_span.cbegin(), muh_original_byte_span.cend());
+  bool definitely_reinterpret_casted =
+    definitely_reinterpret_casted0 && definitely_reinterpret_casted1;
+  (void)definitely_reinterpret_casted;
+  VERIFY(definitely_reinterpret_casted);
+
+  std::span<std::byte> muh_original_byte_span_ptr_size(original_bytes, 4);
+  bool definitely_equivalent =
+    std::equal(muh_original_byte_span_ptr_size.cbegin(), muh_original_byte_span_ptr_size.cend(),
+      muh_original_byte_span.cbegin(), muh_original_byte_span.cend());
+  (void)definitely_equivalent;
+  VERIFY(definitely_equivalent);
+
+  return definitely_equivalent && definitely_reinterpret_casted && really_empty ? 0 : 1;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/fail.contiguous_range.cc b/libstdc++-v3/testsuite/23_containers/span/fail.contiguous_range.cc
new file mode 100644
index 00000000000..a1135d8e63b
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/fail.contiguous_range.cc
@@ -0,0 +1,29 @@
+// 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-excess-errors { xfail c++2a } }
+
+#include <span>
+#include <deque>
+
+int
+main()
+{
+  std::deque<int> d{};
+  std::span<int, std::dynamic_extent> myspan(d);
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/fail.get.cc b/libstdc++-v3/testsuite/23_containers/span/fail.get.cc
new file mode 100644
index 00000000000..d07d454fd60
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/fail.get.cc
@@ -0,0 +1,29 @@
+// 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-excess-errors { xfail c++2a } }
+
+#include <span>
+#include <tuple>
+
+int
+main()
+{
+  std::span<int, std::dynamic_extent> myspan(nullptr, 0);
+  std::get<0>(myspan);
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/fail.last.cc b/libstdc++-v3/testsuite/23_containers/span/fail.last.cc
new file mode 100644
index 00000000000..3d3d1a323fc
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/fail.last.cc
@@ -0,0 +1,28 @@
+// 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-excess-errors { xfail c++2a } }
+
+#include <span>
+
+int
+main()
+{
+  std::span<int, 2> myspan(nullptr, 2);
+  myspan.last<3>();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/fail.subspan.cc b/libstdc++-v3/testsuite/23_containers/span/fail.subspan.cc
new file mode 100644
index 00000000000..6ac0cd6d049
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/fail.subspan.cc
@@ -0,0 +1,28 @@
+// 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-excess-errors { xfail c++2a } }
+
+#include <span>
+
+int
+main()
+{
+  std::span<int, 2> myspan(nullptr, 2);
+  myspan.subspan<3, 1>();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/fail.tuple_element.dynamic.cc b/libstdc++-v3/testsuite/23_containers/span/fail.tuple_element.dynamic.cc
new file mode 100644
index 00000000000..8dac14271c0
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/fail.tuple_element.dynamic.cc
@@ -0,0 +1,29 @@
+// 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-excess-errors { xfail c++2a } }
+
+#include <span>
+#include <tuple>
+
+int
+main()
+{
+  std::tuple_element<0, std::span<int, std::dynamic_extent>> ts;
+  (void)ts;
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/fail.tuple_element.oob.cc b/libstdc++-v3/testsuite/23_containers/span/fail.tuple_element.oob.cc
new file mode 100644
index 00000000000..8cf57e40e6c
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/fail.tuple_element.oob.cc
@@ -0,0 +1,29 @@
+// 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-excess-errors { xfail c++2a } }
+
+#include <span>
+#include <tuple>
+
+int
+main()
+{
+  std::tuple_element<3, std::span<int, 2>> te;
+  (void)te;
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/fail.tuple_size.cc b/libstdc++-v3/testsuite/23_containers/span/fail.tuple_size.cc
new file mode 100644
index 00000000000..bd6b1e814a7
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/fail.tuple_size.cc
@@ -0,0 +1,29 @@
+// 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-excess-errors { xfail c++2a } }
+
+#include <span>
+#include <tuple>
+
+int
+main()
+{
+  std::tuple_size<std::span<int, std::dynamic_extent>> ts;
+  (void)ts;
+  return 0;
+}

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [ PATCH ] C++20 <span>
  2019-09-04 22:47         ` JeanHeyd Meneide
@ 2019-09-05 11:27           ` Jonathan Wakely
  2019-09-05 13:52             ` Jonathan Wakely
  0 siblings, 1 reply; 9+ messages in thread
From: Jonathan Wakely @ 2019-09-05 11:27 UTC (permalink / raw)
  To: JeanHeyd Meneide; +Cc: gcc-patches, libstdc++

[-- Attachment #1: Type: text/plain, Size: 6017 bytes --]

On 04/09/19 18:47 -0400, JeanHeyd Meneide wrote:
>     Thank you for the thorough review!
>
>On Tue, Sep 3, 2019 at 9:31 AM Jonathan Wakely <jwakely@redhat.com> wrote:
>> It looks like__adl_begin and __adl_end should be guarded by #ifdef
>> _GLIBCXX_P1394 because they're not needed otherwise.
>
>     I'm absolutely going to need it for the bit data structures (and
>in tuning up other parts of the library). It will also be important
>for things that need to behave nicely when working with things needing
>[range.access] internally in libstdc++,
>while we wait for the cmcstl2 implementation. I did gate
>__adl_to_address behind p1394, though, because that is definitely
>specific to that paper (and its friend, p1474).

OK, then it's fine as is, thanks.

>+/** @file span
>+ *  This is a Standard C++ Library header.
>+ */
>+
>+//
>+// P0122 span library
>+// Contributed by ThePhD
>+//
>+
>+#ifndef _GLIBCXX_SPAN
>+#define _GLIBCXX_SPAN 1
>+
>+#pragma GCC system_header
>+
>+#if __cplusplus > 201703L
>+
>+#include <cstddef>

This header isn't needed. <bits/c++config.h> defines std::size_t and
std::ptrdiff_t, and every libstdc++ header should include
<bits/c++config.h> already. Not including <cstddef> is a minor
optimisation for compile time, because we don't need to open <cstddef>
and <stddef.h>.

>+    template<typename _Tp, size_t _Num>
>+      inline constexpr bool
>+      __is_std_array_v<_GLIBCXX_STD_C::array<_Tp, _Num>> = true;
>+
>+#ifdef _GLIBCXX_DEBUG
>+    template<typename _Tp, size_t _Num>
>+      inline constexpr bool
>+      __is_std_array_v<std::__debug::array<_Tp, _Num>> = true;
>+#endif // debug/array
>+
>+    template<size_t _Extent>
>+      class __extent_storage
>+      {
>+      public:
>+
>+        constexpr
>+        __extent_storage() noexcept = default;

Something else I forgot to mention (which I'll add to the coding
conventions documentation) is that we use TABs for indentation at the
start of lines. Tabstop should be set to 8, so anything indented by 8
chars or more should use TABs. Anything indented by fewer than 8 chars
just uses spaces.

>+
>+        constexpr
>+        __extent_storage(size_t) noexcept
>+        {}
>+
>+        static constexpr size_t
>+        _M_extent() noexcept
>+        { return _Extent; }
>+      };
>+
>+    template<>
>+      class __extent_storage<static_cast<size_t>(-1)>
>+      {
>+      public:
>+
>+        constexpr
>+        __extent_storage() noexcept : _M_extent_value(0){};
>+
>+        constexpr
>+        __extent_storage(size_t __extent) noexcept
>+        : _M_extent_value(__extent)
>+        {}
>+
>+        constexpr size_t
>+        _M_extent() const noexcept
>+        { return this->_M_extent_value; }
>+
>+      private:
>+        size_t _M_extent_value;
>+      };
>+
>+  } // namespace __detail
>+
>+  template<typename _Type, size_t _Extent = dynamic_extent>
>+    class span : private __detail::__extent_storage<_Extent>
>+    {
>+    public:
>+      // member types
>+      using value_type             = remove_cv_t<_Type>;
>+      using element_type           = _Type;
>+      using index_type             = size_t;
>+      using reference              = element_type&;
>+      using const_reference        = const element_type&;
>+      using pointer                = _Type*;
>+      using const_pointer          = const _Type*;
>+      using iterator               = __gnu_cxx::__normal_iterator<pointer, span>;
>+      using const_iterator         = __gnu_cxx::__normal_iterator<const_pointer, span>;
>+      using reverse_iterator       = ::std::reverse_iterator<iterator>;
>+      using const_reverse_iterator = ::std::reverse_iterator<const_iterator>;
>+      using difference_type        = ptrdiff_t;
>+      // Official wording has no size_type -- why??
>+      // using size_type = size_t;
>+
>+      // member constants
>+      static inline constexpr size_t extent = _Extent;
>+
>+    private:
>+      using __base_t = __detail::__extent_storage<extent>;
>+
>+    public:
>+      // constructors
>+      template <typename _Dummy = _Type, enable_if_t<is_same_v<_Dummy, _Type> && (_Extent == dynamic_extent || _Extent == 0)>* = nullptr>

This line is too long, it needs to be split before 80 columns.

>+        constexpr span() noexcept : __base_t(0), _M_ptr(nullptr)
>+        {}
>+
>+      constexpr
>+      span(const span&) noexcept = default;
>+
>+      template<size_t _ArrayExtent,
>+        enable_if_t<
>+          (_Extent == dynamic_extent || _ArrayExtent == _Extent) &&

N.B. line breaks should come before logical operators like && e.g.

      foo
      && bar

Not:

      foo &&
      bar

The rationale for this is to make it easy to see the operator. You can
look at the start of  the line (at a predictable, easily findable
position) instead of having to look at the right hand side to see if
it's a && or || operator.


>+      template<typename _Container,
>+        enable_if_t<
>+          (_Extent == dynamic_extent) &&
>+          !is_same_v<remove_cvref_t<_Container>, span> &&
>+          !__detail::__is_std_array_v<remove_cvref_t<_Container>> &&
>+          !is_array_v<remove_cvref_t<_Container>> &&

Here's a good example where the && operators are all at different
columns, but if you put line breaks before the operator it looks like:

          (_Extent == dynamic_extent)
          && !is_same_v<remove_cvref_t<_Container>, span>
          && !__detail::__is_std_array_v<remove_cvref_t<_Container>>
          && !is_array_v<remove_cvref_t<_Container>>
          && ...

This is easier to see that it's a series of AND conditions with a
quick glance.

As discussed on IRC the new XFAIL tests need some adjustment. The
attached patch changes the whitespace as discussed above, and fixes
the tests.

Our convention for XFAIL tests is to call them foo_neg.cc (not
fail.foo.cc like libc++ uses).

The attached patch is what I've tested and committed to trunk - thanks
very much!

I'll make some additional fixes (e.g. LWG 3103) and update the docs
later today.


[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 49737 bytes --]

commit 24bcbc6f622c6371d1fdf28bab25f64ff6d2dae1
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Sep 5 09:58:09 2019 +0100

    Implement std::span for C++20
    
    2019-09-05  JeanHeyd Meneide  <phdofthehouse@gmail.com>
    
            * include/Makefile.am: Add <span> header.
            * include/Makefile.in: Regenerate.
            * include/bits/range_access.h (__adl_begin, __adl_end, __adl_cbegin)
            (__adl_cend, __adl_rbegin, __adl_rend, __adl_crbegin, __adl_crend)
            (__adl_data, __adl_cdata, __adl_size, __adl_empty, __adl_to_address):
            New functions for performing argument-dependent lookup of range
            customization points.
            * include/bits/stl_iterator.h (__normal_iterator): Add
            _GLIBCXX20_CONSTEXPR to all functions.
            * include/std/span: New header.
            * include/std/version (__cpp_lib_span): Define feature test macro.
            * testsuite/23_containers/span/contiguous_range_neg.cc: New test.
            * testsuite/23_containers/span/everything.cc: New test.
            * testsuite/23_containers/span/get_neg.cc: New test.
            * testsuite/23_containers/span/last_neg.cc: New test.
            * testsuite/23_containers/span/subspan_neg.cc: New test.
            * testsuite/23_containers/span/tuple_element_dynamic_neg.cc: New test.
            * testsuite/23_containers/span/tuple_element_oob_neg.cc: New test.
            * testsuite/23_containers/span/tuple_size_neg.cc: New test.

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 3fe80f32cc4..b8b786d9260 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -68,6 +68,7 @@ std_headers = \
 	${std_srcdir}/scoped_allocator \
 	${std_srcdir}/set \
 	${std_srcdir}/shared_mutex \
+	${std_srcdir}/span \
 	${std_srcdir}/sstream \
 	${std_srcdir}/stack \
 	${std_srcdir}/stdexcept \
diff --git a/libstdc++-v3/include/bits/range_access.h b/libstdc++-v3/include/bits/range_access.h
index d1e74711433..44b9c6c3596 100644
--- a/libstdc++-v3/include/bits/range_access.h
+++ b/libstdc++-v3/include/bits/range_access.h
@@ -318,6 +318,78 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 #endif // C++17
 
+#if __cplusplus > 201703L
+  // "why are these in namespace std:: and not __gnu_cxx:: ?"
+  // because if we don't put them here it's impossible to
+  // have implicit ADL with "using std::begin/end/size/data;".
+  template <typename _Container>
+    constexpr auto
+    __adl_begin(_Container& __cont) noexcept(noexcept(begin(__cont)))
+    { return begin(__cont); }
+
+  template <typename _Container>
+    constexpr auto
+    __adl_end(_Container& __cont) noexcept(noexcept(end(__cont)))
+    { return end(__cont); }
+
+  template <typename _Container>
+    constexpr auto
+    __adl_cbegin(_Container& __cont) noexcept(noexcept(cbegin(__cont)))
+    { return cbegin(__cont); }
+
+  template <typename _Container>
+    constexpr auto
+    __adl_cend(_Container& __cont) noexcept(noexcept(cend(__cont)))
+    { return cend(__cont); }
+
+  template <typename _Container>
+    constexpr auto
+    __adl_rbegin(_Container& __cont) noexcept(noexcept(rbegin(__cont)))
+    { return rbegin(__cont); }
+
+  template <typename _Container>
+    constexpr auto
+    __adl_rend(_Container& __cont) noexcept(noexcept(rend(__cont)))
+    { return rend(__cont); }
+
+  template <typename _Container>
+    constexpr auto
+    __adl_crbegin(_Container& __cont) noexcept(noexcept(crbegin(__cont)))
+    { return crbegin(__cont); }
+
+  template <typename _Container>
+    constexpr auto
+    __adl_crend(_Container& __cont) noexcept(noexcept(crend(__cont)))
+    { return crend(__cont); }
+
+  template <typename _Container>
+    constexpr auto
+    __adl_data(_Container& __cont) noexcept(noexcept(data(__cont)))
+    { return data(__cont); }
+
+  template <typename _Container>
+    constexpr auto
+    __adl_cdata(_Container& __cont) noexcept(noexcept(cdata(__cont)))
+    { return cdata(__cont); }
+
+  template <typename _Container>
+    constexpr auto
+    __adl_size(_Container& __cont) noexcept(noexcept(size(__cont)))
+    { return size(__cont); }
+
+  template <typename _Container>
+    constexpr auto
+    __adl_empty(_Container& __cont) noexcept(noexcept(empty(__cont)))
+    { return empty(__cont); }
+
+#if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
+  template <typename _Container>
+    constexpr auto
+    __adl_to_address(_Container& __cont) noexcept(noexcept(to_address(__cont)))
+    { return to_address(__cont); }
+#endif // P1394 and new contiguous_iterator requirements [iterator.concept.contiguous]
+#endif // C++20
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 
diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
index 8ab0d72b0c2..e29775a42c5 100644
--- a/libstdc++-v3/include/bits/stl_iterator.h
+++ b/libstdc++-v3/include/bits/stl_iterator.h
@@ -446,7 +446,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 #if __cplusplus >= 201103L
   template<typename _Iterator>
-    _GLIBCXX20_CONSTEXPR auto
+    _GLIBCXX20_CONSTEXPR
+    auto
     __niter_base(reverse_iterator<_Iterator> __it)
     -> decltype(__make_reverse_iterator(__niter_base(__it.base())))
     { return __make_reverse_iterator(__niter_base(__it.base())); }
@@ -457,7 +458,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { };
 
   template<typename _Iterator>
-    _GLIBCXX20_CONSTEXPR auto
+    _GLIBCXX20_CONSTEXPR
+    auto
     __miter_base(reverse_iterator<_Iterator> __it)
     -> decltype(__make_reverse_iterator(__miter_base(__it.base())))
     { return __make_reverse_iterator(__miter_base(__it.base())); }
@@ -802,12 +804,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _GLIBCXX_CONSTEXPR __normal_iterator() _GLIBCXX_NOEXCEPT
       : _M_current(_Iterator()) { }
 
-      explicit
+      explicit _GLIBCXX20_CONSTEXPR
       __normal_iterator(const _Iterator& __i) _GLIBCXX_NOEXCEPT
       : _M_current(__i) { }
 
       // Allow iterator to const_iterator conversion
       template<typename _Iter>
+        _GLIBCXX20_CONSTEXPR
         __normal_iterator(const __normal_iterator<_Iter,
 			  typename __enable_if<
       	       (std::__are_same<_Iter, typename _Container::pointer>::__value),
@@ -815,14 +818,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         : _M_current(__i.base()) { }
 
       // Forward iterator requirements
+      _GLIBCXX20_CONSTEXPR
       reference
       operator*() const _GLIBCXX_NOEXCEPT
       { return *_M_current; }
 
+      _GLIBCXX20_CONSTEXPR
       pointer
       operator->() const _GLIBCXX_NOEXCEPT
       { return _M_current; }
 
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator&
       operator++() _GLIBCXX_NOEXCEPT
       {
@@ -830,11 +836,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	return *this;
       }
 
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator
       operator++(int) _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current++); }
 
       // Bidirectional iterator requirements
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator&
       operator--() _GLIBCXX_NOEXCEPT
       {
@@ -842,31 +850,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	return *this;
       }
 
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator
       operator--(int) _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current--); }
 
       // Random access iterator requirements
+      _GLIBCXX20_CONSTEXPR
       reference
       operator[](difference_type __n) const _GLIBCXX_NOEXCEPT
       { return _M_current[__n]; }
 
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator&
       operator+=(difference_type __n) _GLIBCXX_NOEXCEPT
       { _M_current += __n; return *this; }
 
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator
       operator+(difference_type __n) const _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current + __n); }
 
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator&
       operator-=(difference_type __n) _GLIBCXX_NOEXCEPT
       { _M_current -= __n; return *this; }
 
+      _GLIBCXX20_CONSTEXPR
       __normal_iterator
       operator-(difference_type __n) const _GLIBCXX_NOEXCEPT
       { return __normal_iterator(_M_current - __n); }
 
+      _GLIBCXX20_CONSTEXPR
       const _Iterator&
       base() const _GLIBCXX_NOEXCEPT
       { return _M_current; }
@@ -882,6 +897,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // Forward iterator requirements
   template<typename _IteratorL, typename _IteratorR, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator==(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
@@ -889,6 +905,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() == __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator==(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
@@ -896,6 +913,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() == __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator!=(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
@@ -903,6 +921,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() != __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator!=(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
@@ -911,6 +930,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // Random access iterator requirements
   template<typename _IteratorL, typename _IteratorR, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator<(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs)
@@ -918,6 +938,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() < __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator<(const __normal_iterator<_Iterator, _Container>& __lhs,
 	      const __normal_iterator<_Iterator, _Container>& __rhs)
@@ -925,6 +946,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() < __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator>(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs)
@@ -932,6 +954,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() > __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator>(const __normal_iterator<_Iterator, _Container>& __lhs,
 	      const __normal_iterator<_Iterator, _Container>& __rhs)
@@ -939,6 +962,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() > __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator<=(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
@@ -946,6 +970,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() <= __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator<=(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
@@ -953,6 +978,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() <= __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator>=(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
@@ -960,6 +986,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() >= __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline bool
     operator>=(const __normal_iterator<_Iterator, _Container>& __lhs,
 	       const __normal_iterator<_Iterator, _Container>& __rhs)
@@ -973,6 +1000,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _IteratorL, typename _IteratorR, typename _Container>
 #if __cplusplus >= 201103L
     // DR 685.
+    _GLIBCXX20_CONSTEXPR
     inline auto
     operator-(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs) noexcept
@@ -985,6 +1013,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() - __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline typename __normal_iterator<_Iterator, _Container>::difference_type
     operator-(const __normal_iterator<_Iterator, _Container>& __lhs,
 	      const __normal_iterator<_Iterator, _Container>& __rhs)
@@ -992,6 +1021,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() - __rhs.base(); }
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     inline __normal_iterator<_Iterator, _Container>
     operator+(typename __normal_iterator<_Iterator, _Container>::difference_type
 	      __n, const __normal_iterator<_Iterator, _Container>& __i)
@@ -1006,6 +1036,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Iterator, typename _Container>
+    _GLIBCXX20_CONSTEXPR
     _Iterator
     __niter_base(__gnu_cxx::__normal_iterator<_Iterator, _Container> __it)
     _GLIBCXX_NOEXCEPT_IF(std::is_nothrow_copy_constructible<_Iterator>::value)
diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span
new file mode 100644
index 00000000000..4ce2f31a131
--- /dev/null
+++ b/libstdc++-v3/include/std/span
@@ -0,0 +1,521 @@
+// Components for manipulating non-owning sequences of objects -*- C++ -*-
+
+// 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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file span
+ *  This is a Standard C++ Library header.
+ */
+
+//
+// P0122 span library
+// Contributed by ThePhD
+//
+
+#ifndef _GLIBCXX_SPAN
+#define _GLIBCXX_SPAN 1
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+
+#include <type_traits>
+#include <tuple>
+#include <utility>
+#include <array>
+#include <bits/stl_iterator.h>
+#include <bits/range_access.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+// FIXME: they forgot this feature test macro
+// get on someone's back about it in Belfast!!!
+#define __cpp_lib_span 201911
+
+  inline constexpr size_t dynamic_extent = static_cast<size_t>(-1);
+
+  namespace __detail
+  {
+    template<typename _Element, typename _ToElement>
+      static constexpr inline bool __is_base_derived_safe_convertible_v
+	= is_convertible_v<_Element (*)[], _ToElement (*)[]>;
+
+    template<typename _Tp>
+      inline constexpr bool __is_std_array_v = false;
+
+    template<typename _Tp, size_t _Num>
+      inline constexpr bool
+      __is_std_array_v<_GLIBCXX_STD_C::array<_Tp, _Num>> = true;
+
+#ifdef _GLIBCXX_DEBUG
+    template<typename _Tp, size_t _Num>
+      inline constexpr bool
+      __is_std_array_v<std::__debug::array<_Tp, _Num>> = true;
+#endif // debug/array
+
+    template<size_t _Extent>
+      class __extent_storage
+      {
+      public:
+	constexpr
+	__extent_storage() noexcept = default;
+
+	constexpr
+	__extent_storage(size_t) noexcept
+	{ }
+
+	static constexpr size_t
+	_M_extent() noexcept
+	{ return _Extent; }
+      };
+
+    template<>
+      class __extent_storage<static_cast<size_t>(-1)>
+      {
+      public:
+	constexpr
+	__extent_storage() noexcept : _M_extent_value(0)
+	{ };
+
+	constexpr
+	__extent_storage(size_t __extent) noexcept
+	: _M_extent_value(__extent)
+	{ }
+
+	constexpr size_t
+	_M_extent() const noexcept
+	{ return this->_M_extent_value; }
+
+      private:
+	size_t _M_extent_value;
+      };
+
+  } // namespace __detail
+
+  template<typename _Type, size_t _Extent = dynamic_extent>
+    class span : private __detail::__extent_storage<_Extent>
+    {
+    public:
+      // member types
+      using value_type             = remove_cv_t<_Type>;
+      using element_type           = _Type;
+      using index_type             = size_t;
+      using reference              = element_type&;
+      using const_reference        = const element_type&;
+      using pointer                = _Type*;
+      using const_pointer          = const _Type*;
+      using iterator
+	= __gnu_cxx::__normal_iterator<pointer, span>;
+      using const_iterator
+	= __gnu_cxx::__normal_iterator<const_pointer, span>;
+      using reverse_iterator       = std::reverse_iterator<iterator>;
+      using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+      using difference_type        = ptrdiff_t;
+      // Official wording has no size_type -- why??
+      // using size_type = size_t;
+
+      // member constants
+      static inline constexpr size_t extent = _Extent;
+
+    private:
+      using __base_t = __detail::__extent_storage<extent>;
+
+    public:
+      // constructors
+
+      template <typename _Dummy = _Type,
+		enable_if_t<is_same_v<_Dummy, _Type>
+		  && (_Extent == dynamic_extent || _Extent == 0)>* = nullptr>
+	constexpr
+	span() noexcept : __base_t(0), _M_ptr(nullptr)
+	{ }
+
+      constexpr
+      span(const span&) noexcept = default;
+
+      template<size_t _ArrayExtent,
+	enable_if_t<
+	  (_Extent == dynamic_extent || _ArrayExtent == _Extent)
+	  && __detail::__is_base_derived_safe_convertible_v<
+	    remove_pointer_t<decltype(::std::__adl_data(
+	      ::std::declval<element_type (&)[_ArrayExtent]>()))>,
+	    element_type>>* = nullptr>
+	constexpr span(element_type (&__arr)[_ArrayExtent])
+	noexcept(noexcept(::std::__adl_data(__arr)))
+	: span(::std::__adl_data(__arr), _ArrayExtent)
+	{ }
+
+      template<size_t _ArrayExtent,
+	enable_if_t<
+	  (_Extent == dynamic_extent || _ArrayExtent == _Extent)
+	  && __detail::__is_base_derived_safe_convertible_v<
+	    remove_pointer_t<decltype(::std::__adl_data(
+	      ::std::declval<const array<value_type, _ArrayExtent>&>()))>,
+	    element_type>>* = nullptr>
+	constexpr
+	span(array<value_type, _ArrayExtent>& __arr)
+	noexcept(noexcept(::std::__adl_data(__arr)))
+	: span(::std::__adl_data(__arr), _ArrayExtent)
+	{ }
+
+      template<size_t _ArrayExtent,
+	enable_if_t<
+	  (_Extent == dynamic_extent || _ArrayExtent == _Extent)
+	  && __detail::__is_base_derived_safe_convertible_v<
+	    remove_pointer_t<decltype(::std::__adl_data(
+	      ::std::declval<const array<value_type, _ArrayExtent>&>()))>,
+	    element_type>>* = nullptr>
+	constexpr
+	span(const array<value_type, _ArrayExtent>& __arr)
+	noexcept(noexcept(::std::__adl_data(__arr)))
+	: span(::std::__adl_data(__arr), _ArrayExtent)
+	{ }
+
+      // NOTE: when the time comes, and P1394 -
+      // range constructors for std::span - ships in
+      // the standard, delete the #else block and remove
+      // the conditional
+      // if the paper fails, delete #if block
+      // and keep the crappy #else block
+      // and then cry that NB comments failed C++20...
+      // but maybe for C++23?
+#if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
+      template<typename _Range,
+	enable_if_t<
+	  (_Extent == dynamic_extent)
+	  && !is_same_v<remove_cvref_t<_Range>, span>
+	  && !__detail::__is_std_array_v<remove_cvref_t<_Range>>
+	  && !is_array_v<remove_cvref_t<_Range>>
+	  && __detail::__is_base_derived_safe_convertible_v<
+	    remove_pointer_t<decltype(
+	      ::std::__adl_data(::std::declval<_Range&>())
+	      + ::std::__adl_size(::std::declval<_Range&>()))>,
+	    element_type>>* = nullptr>
+	constexpr
+	span(_Range&& __range)
+	noexcept(noexcept(::std::__adl_data(__range))
+		  && noexcept(::std::__adl_size(__range)))
+	: span(::std::__adl_data(__range), ::std::__adl_size(__range))
+	{ }
+
+      template<typename _ContiguousIterator, typename _Sentinel,
+	enable_if_t<!is_convertible_v<_Sentinel, index_type>
+	  && __detail::__is_base_derived_safe_convertible_v<
+	    remove_reference_t<typename
+	      iterator_traits<_ContiguousIterator>::reference>,
+	  element_type>>* = nullptr>
+	constexpr
+	span(_ContiguousIterator __first, _Sentinel __last)
+	: span(::std::move(__first), static_cast<index_type>(__last - __first))
+	{ }
+
+      template<typename _ContiguousIterator>
+	constexpr
+	span(_ContiguousIterator __first, index_type __count)
+	noexcept(noexcept(::std::__adl_to_address(__first)))
+	: __base_t(__count), _M_ptr(::std::__adl_to_address(__first))
+	{ __glibcxx_assert(_Extent == dynamic_extent || __count == _Extent); }
+
+#else
+
+      template<typename _Container,
+	enable_if_t<
+	  (_Extent == dynamic_extent)
+	  && !is_same_v<remove_cvref_t<_Container>, span>
+	  && !__detail::__is_std_array_v<remove_cvref_t<_Container>>
+	  && !is_array_v<remove_cvref_t<_Container>>
+	  && __detail::__is_base_derived_safe_convertible_v<
+	    remove_pointer_t<decltype(
+	      ::std::__adl_data(::std::declval<_Container&>())
+	      + ::std::__adl_size(::std::declval<_Container&>()))>,
+	    element_type>>* = nullptr>
+	constexpr
+	span(_Container& __range)
+	noexcept(noexcept(::std::__adl_data(__range))
+		  && noexcept(::std::__adl_size(__range)))
+	: span(::std::__adl_data(__range), ::std::__adl_size(__range))
+	{ }
+
+      template<typename _Container,
+	enable_if_t<
+	  (_Extent == dynamic_extent)
+	  && !is_same_v<remove_cvref_t<_Container>, span>
+	  && !__detail::__is_std_array_v<remove_cvref_t<_Container>>
+	  && !is_array_v<remove_cvref_t<_Container>>
+	  && __detail::__is_base_derived_safe_convertible_v<
+	    remove_pointer_t<decltype(
+	      ::std::__adl_data(::std::declval<_Container&>())
+	      + ::std::__adl_size(::std::declval<_Container&>()))>,
+	    element_type>>* = nullptr>
+	constexpr span(const _Container& __range)
+	noexcept(noexcept(::std::__adl_data(__range))
+		  && noexcept(::std::__adl_size(__range)))
+	: span(::std::__adl_data(__range), ::std::__adl_size(__range))
+	{ }
+
+      constexpr
+      span(pointer __first, index_type __count) noexcept
+      : __base_t(__count), _M_ptr(static_cast<pointer>(__first))
+      { __glibcxx_assert(_Extent == dynamic_extent || __count == _Extent); }
+
+      constexpr
+      span(pointer __first, pointer __last) noexcept
+      : span(::std::move(__first), static_cast<index_type>(__last - __first))
+      { }
+#endif // P1394
+
+      // assignment
+
+      constexpr span&
+      operator=(const span&) noexcept = default;
+
+      // observers: element access
+
+      constexpr reference
+      front() const noexcept
+      { return *this->begin(); }
+
+      constexpr reference
+      back() const noexcept
+      {
+	iterator __it = this->end();
+	--__it;
+	return *__it;
+      }
+
+      constexpr reference
+      operator[](index_type __idx) const noexcept
+      { return *(this->_M_ptr + __idx); }
+
+      constexpr pointer
+      data() const noexcept
+      { return this->_M_ptr; }
+
+      constexpr index_type
+      size() const noexcept
+      { return this->__base_t::_M_extent(); }
+
+      constexpr index_type
+      size_bytes() const noexcept
+      { return this->__base_t::_M_extent() * sizeof(element_type); }
+
+      constexpr bool
+      empty() const noexcept
+      { return size() == 0; }
+
+      // observers: iterator support
+      constexpr iterator
+      begin() const noexcept
+      { return iterator(this->_M_ptr); }
+
+      constexpr const_iterator
+      cbegin() const noexcept
+      { return const_iterator(this->_M_ptr); }
+
+      constexpr iterator
+      end() const noexcept
+      { return iterator(this->_M_ptr + this->size()); }
+
+      constexpr const_iterator
+      cend() const noexcept
+      { return const_iterator(this->_M_ptr + this->size()); }
+
+      constexpr reverse_iterator
+      rbegin() const noexcept
+      { return reverse_iterator(this->end()); }
+
+      constexpr const_reverse_iterator
+      crbegin() const noexcept
+      { return const_reverse_iterator(this->cend()); }
+
+      constexpr reverse_iterator
+      rend() const noexcept
+      { return reverse_iterator(this->begin()); }
+
+      constexpr const_reverse_iterator
+      crend() const noexcept
+      { return const_reverse_iterator(this->cbegin()); }
+
+      // observers: subranges
+      template<size_t _Count>
+	constexpr span<element_type, _Count>
+	first() const
+	{
+	  __glibcxx_assert(_Count < size());
+	  return { this->data(), _Count };
+	}
+
+      constexpr span<element_type, dynamic_extent>
+      first(index_type __count) const
+      {
+	__glibcxx_assert(__count < size());
+	return { this->data(), __count };
+      }
+
+      template<size_t _Count>
+	constexpr span<element_type, _Count>
+	last() const
+	{
+	  static_assert(_Count == dynamic_extent ||
+			  _Extent == dynamic_extent || _Count <= _Extent,
+	    "Count or Extent are dynamic, "
+	    "or the Count is less than the static extent");
+	  __glibcxx_assert(_Count <= size());
+	  return { this->data() + (this->size() - _Count), _Count };
+	}
+
+      constexpr span<element_type, dynamic_extent>
+      last(index_type __count) const
+      {
+	__glibcxx_assert(__count < size());
+	index_type __offset = (this->size() - __count);
+	return { this->data() + __offset, __count };
+      }
+
+      template<size_t _Offset, size_t _Count = dynamic_extent>
+	constexpr auto
+	subspan() const
+	{
+	  static_assert(_Count == dynamic_extent ||
+			  _Extent == dynamic_extent ||
+			  (_Offset + _Count) <= _Extent,
+	    "Count or Extent are dynamic, "
+	    "or the Count + Offset is less than the static extent");
+	  constexpr size_t __span_extent =
+	    (_Count != dynamic_extent
+		? _Count
+		: (_Extent != dynamic_extent ? _Extent - _Offset
+						    : dynamic_extent));
+	  using __span_t = span<element_type, __span_extent>;
+	  if constexpr(_Count != dynamic_extent)
+	    {
+	      __glibcxx_assert((_Offset + _Count) < size());
+	    }
+	  return __span_t(this->data() + _Offset,
+	    (_Count == dynamic_extent ? this->size() - _Offset : _Count));
+	}
+
+      constexpr span<element_type, dynamic_extent>
+      subspan(index_type __offset, index_type __count = dynamic_extent) const
+      {
+	return {this->data() + __offset,
+	  __count == dynamic_extent ? this->size() - __offset : __count};
+      }
+
+      // observers: range helpers
+
+      friend constexpr iterator
+      begin(span __sp) noexcept
+      { return __sp.begin(); }
+
+      friend constexpr iterator
+      end(span __sp) noexcept
+      { return __sp.end(); }
+
+    private:
+      pointer _M_ptr;
+    };
+
+  // deduction guides
+  template<typename _Type, size_t _ArrayExtent>
+    span(_Type(&)[_ArrayExtent])->span<_Type, _ArrayExtent>;
+
+  template<typename _Type, size_t _ArrayExtent>
+    span(array<_Type, _ArrayExtent>&)->span<_Type, _ArrayExtent>;
+
+  template<typename _Type, size_t _ArrayExtent>
+    span(const array<_Type, _ArrayExtent>&)
+      ->span<const _Type, _ArrayExtent>;
+
+#if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
+
+  template<typename _ContiguousIterator, typename _Sentinel>
+    span(_ContiguousIterator, _Sentinel)
+      ->span<remove_reference_t<
+	typename iterator_traits<_ContiguousIterator>::reference>>;
+
+  template<typename _Range>
+    span(_Range &&)
+      ->span<remove_reference_t<typename iterator_traits<decltype(
+	::std::__adl_begin(::std::declval<_Range&>()))>::reference>>;
+
+#else
+
+  template<typename _Container>
+    span(_Container&)->span<typename _Container::value_type>;
+
+  template<typename _Container>
+    span(const _Container&)->span<const typename _Container::value_type>;
+
+#endif // P1394
+
+  template<typename _Type, size_t _Extent>
+    span<const byte, _Extent == dynamic_extent
+	? dynamic_extent : _Extent * sizeof(_Type)>
+    inline as_bytes(span<_Type, _Extent> __sp) noexcept
+    {
+      return {reinterpret_cast<const byte*>(__sp.data()), __sp.size_bytes()};
+    }
+
+  template<typename _Type, size_t _Extent>
+    span<byte, _Extent == dynamic_extent
+       ? dynamic_extent : _Extent * sizeof(_Type)>
+    as_writable_bytes(span<_Type, _Extent> __sp) noexcept
+    {
+      return {reinterpret_cast<byte*>(__sp.data()), __sp.size_bytes()};
+    }
+
+  // tuple helpers
+  template<size_t _Index, typename _Type, size_t _Extent>
+    constexpr _Type&
+    get(span<_Type, _Extent> __sp) noexcept
+    {
+      static_assert(_Extent != dynamic_extent
+	&& (_Extent > 0) && (_Index < _Extent),
+	"std::get can only be used with a span of non-dynamic (fixed) extent");
+      return __sp[_Index];
+    }
+
+  template<typename _Type, size_t _Extent>
+    struct tuple_size<span<_Type, _Extent>>
+    : public integral_constant<size_t, _Extent>
+    {
+      static_assert(_Extent != dynamic_extent, "tuple_size can only "
+	"be used with a span of non-dynamic (fixed) extent");
+    };
+
+  template<size_t _Index, typename _Type, size_t _Extent>
+    struct tuple_element<_Index, span<_Type, _Extent>>
+    {
+      static_assert(_Extent != dynamic_extent, "tuple_size can only "
+	"be used with a span of non-dynamic (fixed) extent");
+      static_assert(_Index < _Extent, "Index is less than Extent");
+      using type = typename span<_Type, _Extent>::element_type;
+    };
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+
+#endif // C++20
+#endif // _GLIBCXX_SPAN
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 7f7d05fd8f2..4341c9f0c1b 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -167,6 +167,9 @@
 #define __cpp_lib_list_remove_return_type 201806L
 #define __cpp_lib_math_constants 201907L
 #define __cpp_lib_to_array 201907L
+// FIXME: they forgot this feature test macro
+// get on someone's back about it in Belfast!!!
+#define __cpp_lib_span 201911
 #endif // C++2a
 #endif // C++17
 #endif // C++14
diff --git a/libstdc++-v3/testsuite/23_containers/span/contiguous_range_neg.cc b/libstdc++-v3/testsuite/23_containers/span/contiguous_range_neg.cc
new file mode 100644
index 00000000000..475be1e900d
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/contiguous_range_neg.cc
@@ -0,0 +1,32 @@
+// 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=c++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <span>
+#include <deque>
+
+int
+main()
+{
+  std::deque<int> d{};
+  std::span<int, std::dynamic_extent> myspan(d); // { dg-error "no match" }
+}
+
+// { dg-prune-output "data" }
+// { dg-prune-output "invalid operands" }
diff --git a/libstdc++-v3/testsuite/23_containers/span/everything.cc b/libstdc++-v3/testsuite/23_containers/span/everything.cc
new file mode 100644
index 00000000000..719015a8714
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/everything.cc
@@ -0,0 +1,203 @@
+// 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=c++2a" }
+// { dg-do run { target c++2a } }
+
+#include <testsuite_hooks.h>
+
+#include <span>
+#include <type_traits>
+#include <cstdint>
+#include <vector>
+#include <algorithm>
+#include <cassert>
+
+int
+main()
+{
+  struct alignas(256) strawman
+  {
+    int x;
+    int y;
+    bool z;
+    int w;
+  };
+
+  struct naked_span
+  {
+    char* p;
+    std::size_t n;
+  };
+
+  struct strawman_span
+  {
+    strawman* p;
+    std::size_t n;
+  };
+
+  static_assert(sizeof(std::span<char, 0>) <= sizeof(char*));
+  static_assert(sizeof(std::span<const char, 0>) <= sizeof(const char*));
+  static_assert(sizeof(std::span<strawman, 0>) <= sizeof(strawman*));
+  static_assert(sizeof(std::span<strawman, 1>) <= sizeof(strawman*));
+  static_assert(sizeof(std::span<char>) <= sizeof(naked_span));
+  static_assert(sizeof(std::span<strawman>) <= sizeof(strawman_span));
+
+  constexpr static std::array<int, 9> arr_data{ 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+  constexpr auto arr_data_span = std::span(arr_data);
+  static_assert(arr_data_span.size() == 9);
+  static_assert(arr_data_span.size_bytes() == 9 * sizeof(int));
+  static_assert(*arr_data_span.begin() == 0);
+  static_assert(*arr_data_span.data() == 0);
+  static_assert(arr_data_span.front() == 0);
+  static_assert(arr_data_span.back() == 8);
+  static_assert(arr_data_span[0] == 0);
+  static_assert(arr_data_span[1] == 1);
+  static_assert(arr_data_span[2] == 2);
+  static_assert(arr_data_span[3] == 3);
+  static_assert(arr_data_span[4] == 4);
+  static_assert(arr_data_span[5] == 5);
+  static_assert(arr_data_span[6] == 6);
+  static_assert(arr_data_span[7] == 7);
+  static_assert(arr_data_span[8] == 8);
+  static_assert(!arr_data_span.empty());
+  static_assert(decltype(arr_data_span)::extent == 9);
+
+  constexpr static int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+  constexpr auto data_span    = std::span(data);
+  static_assert(data_span.size() == 9);
+  static_assert(data_span.size_bytes() == 9 * sizeof(int));
+  static_assert(*data_span.begin() == 0);
+  static_assert(*data_span.data() == 0);
+  static_assert(data_span.front() == 0);
+  static_assert(data_span.back() == 8);
+  static_assert(data_span[0] == 0);
+  static_assert(data_span[1] == 1);
+  static_assert(data_span[2] == 2);
+  static_assert(data_span[3] == 3);
+  static_assert(data_span[4] == 4);
+  static_assert(data_span[5] == 5);
+  static_assert(data_span[6] == 6);
+  static_assert(data_span[7] == 7);
+  static_assert(data_span[8] == 8);
+  static_assert(!data_span.empty());
+  static_assert(decltype(data_span)::extent == 9);
+
+  constexpr auto data_span_first = data_span.first<3>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_first)>, std::span<const int, 3>>);
+  static_assert(decltype(data_span_first)::extent == 3);
+  static_assert(data_span_first.size() == 3);
+  static_assert(data_span_first.front() == 0);
+  static_assert(data_span_first.back() == 2);
+  static_assert(std::tuple_size_v<decltype(data_span_first)> == 3);
+  static_assert(std::is_same_v<std::tuple_element_t<0, decltype(data_span_first)>, const int>);
+
+  constexpr auto data_span_first_dyn = data_span.first(4);
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_first_dyn)>, std::span<const int>>);
+  static_assert(decltype(data_span_first_dyn)::extent == std::dynamic_extent);
+  static_assert(data_span_first_dyn.size() == 4);
+  static_assert(data_span_first_dyn.front() == 0);
+  static_assert(data_span_first_dyn.back() == 3);
+
+  constexpr auto data_span_last = data_span.last<5>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_last)>, std::span<const int, 5>>);
+  static_assert(decltype(data_span_last)::extent == 5);
+  static_assert(data_span_last.size() == 5);
+  static_assert(data_span_last.front() == 4);
+  static_assert(data_span_last.back() == 8);
+  static_assert(std::tuple_size_v<decltype(data_span_last)> == 5);
+  static_assert(std::is_same_v<std::tuple_element_t<0, decltype(data_span_last)>, const int>);
+
+  constexpr auto data_span_last_dyn = data_span.last(6);
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_last_dyn)>, std::span<const int>>);
+  static_assert(decltype(data_span_last_dyn)::extent == std::dynamic_extent);
+  static_assert(data_span_last_dyn.size() == 6);
+  static_assert(data_span_last_dyn.front() == 3);
+  static_assert(data_span_last_dyn.back() == 8);
+
+  constexpr auto data_span_subspan = data_span.subspan<1, 3>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_subspan)>, std::span<const int, 3>>);
+  static_assert(decltype(data_span_subspan)::extent == 3);
+  static_assert(data_span_subspan.size() == 3);
+  static_assert(data_span_subspan.front() == 1);
+  static_assert(data_span_subspan.back() == 3);
+
+  constexpr auto data_span_subspan_offset = data_span.subspan<8>();
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_subspan_offset)>, std::span<const int, 1>>);
+  static_assert(decltype(data_span_subspan_offset)::extent == 1);
+  static_assert(data_span_subspan_offset.size() == 1);
+  static_assert(data_span_subspan_offset.front() == 8);
+  static_assert(data_span_subspan_offset.back() == 8);
+
+  constexpr auto data_span_subspan_empty = data_span.subspan(9, 0);
+  static_assert(
+    std::is_same_v<std::remove_cv_t<decltype(data_span_subspan_empty)>, std::span<const int>>);
+  static_assert(decltype(data_span_subspan_empty)::extent == std::dynamic_extent);
+  static_assert(data_span_subspan_empty.size() == 0);
+
+  constexpr auto data_span_subspan_empty_static = data_span.subspan<9>();
+  static_assert(std::is_same_v<std::remove_cv_t<decltype(data_span_subspan_empty_static)>,
+    std::span<const int, 0>>);
+  static_assert(decltype(data_span_subspan_empty_static)::extent == 0);
+  static_assert(data_span_subspan_empty.size() == 0);
+
+  std::span<short> shorts{};
+  bool really_empty0 = shorts.empty();
+  bool really_empty1 = std::empty(shorts);
+  bool really_empty2 = shorts.data() == nullptr;
+  bool really_empty3 = shorts.begin() == shorts.end();
+  bool really_empty4 = shorts.cbegin() == shorts.cend();
+  bool really_empty =
+    really_empty0 && really_empty1 && really_empty2 && really_empty3 && really_empty4;
+  (void)really_empty;
+  VERIFY(really_empty);
+
+  std::vector<std::int_least32_t> value{ 0 };
+  std::span<int32_t> muh_span(value);
+  VERIFY(muh_span.size() == 1);
+  std::byte* original_bytes                  = reinterpret_cast<std::byte*>(value.data());
+  original_bytes[0]                          = static_cast<std::byte>(1);
+  original_bytes[1]                          = static_cast<std::byte>(2);
+  original_bytes[2]                          = static_cast<std::byte>(3);
+  original_bytes[3]                          = static_cast<std::byte>(4);
+  std::span<const std::byte> muh_byte_span   = std::as_bytes(muh_span);
+  std::span<std::byte> muh_mutable_byte_span = std::as_writable_bytes(muh_span);
+  std::span<std::byte> muh_original_byte_span(original_bytes, original_bytes + 4);
+  bool definitely_reinterpret_casted0 = std::equal(muh_byte_span.cbegin(), muh_byte_span.cend(),
+    muh_original_byte_span.cbegin(), muh_original_byte_span.cend());
+  bool definitely_reinterpret_casted1 = std::equal(muh_mutable_byte_span.cbegin(),
+    muh_mutable_byte_span.cend(), muh_original_byte_span.cbegin(), muh_original_byte_span.cend());
+  bool definitely_reinterpret_casted =
+    definitely_reinterpret_casted0 && definitely_reinterpret_casted1;
+  (void)definitely_reinterpret_casted;
+  VERIFY(definitely_reinterpret_casted);
+
+  std::span<std::byte> muh_original_byte_span_ptr_size(original_bytes, 4);
+  bool definitely_equivalent =
+    std::equal(muh_original_byte_span_ptr_size.cbegin(), muh_original_byte_span_ptr_size.cend(),
+      muh_original_byte_span.cbegin(), muh_original_byte_span.cend());
+  (void)definitely_equivalent;
+  VERIFY(definitely_equivalent);
+
+  return definitely_equivalent && definitely_reinterpret_casted && really_empty ? 0 : 1;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/get_neg.cc b/libstdc++-v3/testsuite/23_containers/span/get_neg.cc
new file mode 100644
index 00000000000..37d188ed073
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/get_neg.cc
@@ -0,0 +1,30 @@
+// 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=c++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <span>
+#include <tuple>
+
+int
+main()
+{
+  std::span<int, std::dynamic_extent> myspan((int*)nullptr, 0ul);
+  std::get<0>(myspan); // { dg-error "here" }
+}
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/23_containers/span/last_neg.cc b/libstdc++-v3/testsuite/23_containers/span/last_neg.cc
new file mode 100644
index 00000000000..8f0145f7012
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/last_neg.cc
@@ -0,0 +1,29 @@
+// 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=c++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <span>
+
+int
+main()
+{
+  std::span<int, 2> myspan(nullptr, 2);
+  myspan.last<3>(); // { dg-error "here" }
+}
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/23_containers/span/subspan_neg.cc b/libstdc++-v3/testsuite/23_containers/span/subspan_neg.cc
new file mode 100644
index 00000000000..5ecbff937cc
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/subspan_neg.cc
@@ -0,0 +1,29 @@
+// 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=c++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <span>
+
+int
+main()
+{
+  std::span<int, 2> myspan(nullptr, 2);
+  myspan.subspan<3, 1>(); // { dg-error "here" }
+}
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/23_containers/span/tuple_element_dynamic_neg.cc b/libstdc++-v3/testsuite/23_containers/span/tuple_element_dynamic_neg.cc
new file mode 100644
index 00000000000..cf8436099ce
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/tuple_element_dynamic_neg.cc
@@ -0,0 +1,25 @@
+// 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=c++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <span>
+#include <tuple>
+
+std::tuple_element<0, std::span<int, std::dynamic_extent>> ts; // { dg-error "here" }
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/23_containers/span/tuple_element_oob_neg.cc b/libstdc++-v3/testsuite/23_containers/span/tuple_element_oob_neg.cc
new file mode 100644
index 00000000000..bf97735cd55
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/tuple_element_oob_neg.cc
@@ -0,0 +1,25 @@
+// 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=c++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <span>
+#include <tuple>
+
+std::tuple_element<3, std::span<int, 2>> te; // { dg-error "here" }
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/23_containers/span/tuple_size_neg.cc b/libstdc++-v3/testsuite/23_containers/span/tuple_size_neg.cc
new file mode 100644
index 00000000000..99e95b3243a
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/tuple_size_neg.cc
@@ -0,0 +1,25 @@
+// 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=c++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <span>
+#include <tuple>
+
+std::tuple_size<std::span<int, std::dynamic_extent>> ts; // { dg-error "here" }
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [ PATCH ] C++20 <span>
  2019-09-05 11:27           ` Jonathan Wakely
@ 2019-09-05 13:52             ` Jonathan Wakely
  2019-09-09 11:12               ` Jonathan Wakely
  0 siblings, 1 reply; 9+ messages in thread
From: Jonathan Wakely @ 2019-09-05 13:52 UTC (permalink / raw)
  To: JeanHeyd Meneide; +Cc: gcc-patches, libstdc++

[-- Attachment #1: Type: text/plain, Size: 6311 bytes --]

On 05/09/19 12:27 +0100, Jonathan Wakely wrote:
>On 04/09/19 18:47 -0400, JeanHeyd Meneide wrote:
>>    Thank you for the thorough review!
>>
>>On Tue, Sep 3, 2019 at 9:31 AM Jonathan Wakely <jwakely@redhat.com> wrote:
>>>It looks like__adl_begin and __adl_end should be guarded by #ifdef
>>>_GLIBCXX_P1394 because they're not needed otherwise.
>>
>>    I'm absolutely going to need it for the bit data structures (and
>>in tuning up other parts of the library). It will also be important
>>for things that need to behave nicely when working with things needing
>>[range.access] internally in libstdc++,
>>while we wait for the cmcstl2 implementation. I did gate
>>__adl_to_address behind p1394, though, because that is definitely
>>specific to that paper (and its friend, p1474).
>
>OK, then it's fine as is, thanks.
>
>>+/** @file span
>>+ *  This is a Standard C++ Library header.
>>+ */
>>+
>>+//
>>+// P0122 span library
>>+// Contributed by ThePhD
>>+//
>>+
>>+#ifndef _GLIBCXX_SPAN
>>+#define _GLIBCXX_SPAN 1
>>+
>>+#pragma GCC system_header
>>+
>>+#if __cplusplus > 201703L
>>+
>>+#include <cstddef>
>
>This header isn't needed. <bits/c++config.h> defines std::size_t and
>std::ptrdiff_t, and every libstdc++ header should include
><bits/c++config.h> already. Not including <cstddef> is a minor
>optimisation for compile time, because we don't need to open <cstddef>
>and <stddef.h>.
>
>>+    template<typename _Tp, size_t _Num>
>>+      inline constexpr bool
>>+      __is_std_array_v<_GLIBCXX_STD_C::array<_Tp, _Num>> = true;
>>+
>>+#ifdef _GLIBCXX_DEBUG
>>+    template<typename _Tp, size_t _Num>
>>+      inline constexpr bool
>>+      __is_std_array_v<std::__debug::array<_Tp, _Num>> = true;
>>+#endif // debug/array
>>+
>>+    template<size_t _Extent>
>>+      class __extent_storage
>>+      {
>>+      public:
>>+
>>+        constexpr
>>+        __extent_storage() noexcept = default;
>
>Something else I forgot to mention (which I'll add to the coding
>conventions documentation) is that we use TABs for indentation at the
>start of lines. Tabstop should be set to 8, so anything indented by 8
>chars or more should use TABs. Anything indented by fewer than 8 chars
>just uses spaces.
>
>>+
>>+        constexpr
>>+        __extent_storage(size_t) noexcept
>>+        {}
>>+
>>+        static constexpr size_t
>>+        _M_extent() noexcept
>>+        { return _Extent; }
>>+      };
>>+
>>+    template<>
>>+      class __extent_storage<static_cast<size_t>(-1)>
>>+      {
>>+      public:
>>+
>>+        constexpr
>>+        __extent_storage() noexcept : _M_extent_value(0){};
>>+
>>+        constexpr
>>+        __extent_storage(size_t __extent) noexcept
>>+        : _M_extent_value(__extent)
>>+        {}
>>+
>>+        constexpr size_t
>>+        _M_extent() const noexcept
>>+        { return this->_M_extent_value; }
>>+
>>+      private:
>>+        size_t _M_extent_value;
>>+      };
>>+
>>+  } // namespace __detail
>>+
>>+  template<typename _Type, size_t _Extent = dynamic_extent>
>>+    class span : private __detail::__extent_storage<_Extent>
>>+    {
>>+    public:
>>+      // member types
>>+      using value_type             = remove_cv_t<_Type>;
>>+      using element_type           = _Type;
>>+      using index_type             = size_t;
>>+      using reference              = element_type&;
>>+      using const_reference        = const element_type&;
>>+      using pointer                = _Type*;
>>+      using const_pointer          = const _Type*;
>>+      using iterator               = __gnu_cxx::__normal_iterator<pointer, span>;
>>+      using const_iterator         = __gnu_cxx::__normal_iterator<const_pointer, span>;
>>+      using reverse_iterator       = ::std::reverse_iterator<iterator>;
>>+      using const_reverse_iterator = ::std::reverse_iterator<const_iterator>;
>>+      using difference_type        = ptrdiff_t;
>>+      // Official wording has no size_type -- why??
>>+      // using size_type = size_t;
>>+
>>+      // member constants
>>+      static inline constexpr size_t extent = _Extent;
>>+
>>+    private:
>>+      using __base_t = __detail::__extent_storage<extent>;
>>+
>>+    public:
>>+      // constructors
>>+      template <typename _Dummy = _Type, enable_if_t<is_same_v<_Dummy, _Type> && (_Extent == dynamic_extent || _Extent == 0)>* = nullptr>
>
>This line is too long, it needs to be split before 80 columns.
>
>>+        constexpr span() noexcept : __base_t(0), _M_ptr(nullptr)
>>+        {}
>>+
>>+      constexpr
>>+      span(const span&) noexcept = default;
>>+
>>+      template<size_t _ArrayExtent,
>>+        enable_if_t<
>>+          (_Extent == dynamic_extent || _ArrayExtent == _Extent) &&
>
>N.B. line breaks should come before logical operators like && e.g.
>
>     foo
>     && bar
>
>Not:
>
>     foo &&
>     bar
>
>The rationale for this is to make it easy to see the operator. You can
>look at the start of  the line (at a predictable, easily findable
>position) instead of having to look at the right hand side to see if
>it's a && or || operator.
>
>
>>+      template<typename _Container,
>>+        enable_if_t<
>>+          (_Extent == dynamic_extent) &&
>>+          !is_same_v<remove_cvref_t<_Container>, span> &&
>>+          !__detail::__is_std_array_v<remove_cvref_t<_Container>> &&
>>+          !is_array_v<remove_cvref_t<_Container>> &&
>
>Here's a good example where the && operators are all at different
>columns, but if you put line breaks before the operator it looks like:
>
>         (_Extent == dynamic_extent)
>         && !is_same_v<remove_cvref_t<_Container>, span>
>         && !__detail::__is_std_array_v<remove_cvref_t<_Container>>
>         && !is_array_v<remove_cvref_t<_Container>>
>         && ...
>
>This is easier to see that it's a series of AND conditions with a
>quick glance.
>
>As discussed on IRC the new XFAIL tests need some adjustment. The
>attached patch changes the whitespace as discussed above, and fixes
>the tests.
>
>Our convention for XFAIL tests is to call them foo_neg.cc (not
>fail.foo.cc like libc++ uses).
>
>The attached patch is what I've tested and committed to trunk - thanks
>very much!
>
>I'll make some additional fixes (e.g. LWG 3103) and update the docs
>later today.

Here's the follow-up patch.

Tested x86_64-linux, committed to trunk.



[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 43109 bytes --]

commit 8b9507a5e5a4f98cace57780d638f24dccef5756
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Sep 5 13:26:14 2019 +0100

    Improve precondition checks for std::span
    
            * doc/xml/manual/status_cxx2020.xml: Update status for P0122R7 and
            P1024R3. Remove entry for P0920R2.
            * include/std/span  (__cpp_lib_span): Change value.
            (__extent_storage, __extent_storage<dynamic_extent>): Remove default
            constructor.
            (span): Replace __extent_storage base class with data member.
            (span::_S_subspan_extent): New function.
            (span::empty()): Add nodiscard attribute.
            (span::front, span::back, span::operator[]): Check preconditions.
            (span::first, span::last, span::subspan): Add noexcept. Improve
            precondition checks (LWG 3103).
            (get): Remove redundant condition from static_assert.
            (tuple_element<I, span<T, E>>): Fix static_assert message and simplify.
            (as_writable_bytes): Add inline specifier.
            * include/std/version (__cpp_lib_span): Change value.
            * testsuite/23_containers/span/back_neg.cc: Remove stray semi-colon.
            * testsuite/23_containers/span/front_neg.cc: Likewise.
            * testsuite/23_containers/span/index_op_neg.cc: Likewise.
            * testsuite/23_containers/span/last_neg.cc: Improve test.
            * testsuite/23_containers/span/subspan_neg.cc: Likewise.
            * testsuite/23_containers/span/1.cc: New test.
            * testsuite/23_containers/span/2.cc: New test.
            * testsuite/23_containers/span/back_assert_neg.cc: New test.
            * testsuite/23_containers/span/first_2_assert_neg.cc: New test.
            * testsuite/23_containers/span/first_assert_neg.cc: New test.
            * testsuite/23_containers/span/first_neg.cc: New test.
            * testsuite/23_containers/span/front_assert_neg.cc: New test.
            * testsuite/23_containers/span/index_op_assert_neg.cc: New test.
            * testsuite/23_containers/span/last_2_assert_neg.cc: New test.
            * testsuite/23_containers/span/last_assert_neg.cc: New test.
            * testsuite/23_containers/span/subspan_2_assert_neg.cc: New test.
            * testsuite/23_containers/span/subspan_3_assert_neg.cc: New test.
            * testsuite/23_containers/span/subspan_4_assert_neg.cc: New test.
            * testsuite/23_containers/span/subspan_5_assert_neg.cc: New test.
            * testsuite/23_containers/span/subspan_6_assert_neg.cc: New test.
            * testsuite/23_containers/span/subspan_assert_neg.cc: New test.

diff --git a/libstdc++-v3/doc/xml/manual/status_cxx2020.xml b/libstdc++-v3/doc/xml/manual/status_cxx2020.xml
index 3a5de38ad22..ffd73aefc96 100644
--- a/libstdc++-v3/doc/xml/manual/status_cxx2020.xml
+++ b/libstdc++-v3/doc/xml/manual/status_cxx2020.xml
@@ -237,15 +237,14 @@ Feature-testing recommendations for C++</link>.
     </row>
 
     <row>
-      <?dbhtml bgcolor="#C8B0B0" ?>
       <entry>  <code>&lt;span&gt;</code> </entry>
       <entry>
         <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0122r7.pdf">
 	P0122R7
 	</link>
       </entry>
-      <entry align="center"> </entry>
-      <entry />
+      <entry align="center"> 10.1 </entry>
+      <entry> <code>__cpp_lib_span &gt;= 201803L</code> </entry>
     </row>
 
     <row>
@@ -1111,7 +1110,6 @@ Feature-testing recommendations for C++</link>.
     </row>
 
     <row>
-      <?dbhtml bgcolor="#C8B0B0" ?>
       <entry>
         Usability Enhancements for <classname>std::span</classname>
       </entry>
@@ -1120,20 +1118,8 @@ Feature-testing recommendations for C++</link>.
         P1024R3
 	</link>
       </entry>
-      <entry align="center"> </entry>
-      <entry />
-    </row>
-
-    <row>
-      <?dbhtml bgcolor="#C8B0B0" ?>
-      <entry> Precalculated hash values in lookup </entry>
-      <entry>
-        <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0920r2.html">
-        P0920R2
-	</link>
-      </entry>
-      <entry align="center"> </entry>
-      <entry />
+      <entry align="center"> 10.1 </entry>
+      <entry> <code>__cpp_lib_span &gt;= 201902L</code> </entry>
     </row>
 
     <row>
diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span
index 4ce2f31a131..95d778b104b 100644
--- a/libstdc++-v3/include/std/span
+++ b/libstdc++-v3/include/std/span
@@ -49,9 +49,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
-// FIXME: they forgot this feature test macro
-// get on someone's back about it in Belfast!!!
-#define __cpp_lib_span 201911
+#define __cpp_lib_span 201902L
 
   inline constexpr size_t dynamic_extent = static_cast<size_t>(-1);
 
@@ -78,9 +76,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       class __extent_storage
       {
       public:
-	constexpr
-	__extent_storage() noexcept = default;
-
 	constexpr
 	__extent_storage(size_t) noexcept
 	{ }
@@ -91,13 +86,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       };
 
     template<>
-      class __extent_storage<static_cast<size_t>(-1)>
+      class __extent_storage<dynamic_extent>
       {
       public:
-	constexpr
-	__extent_storage() noexcept : _M_extent_value(0)
-	{ };
-
 	constexpr
 	__extent_storage(size_t __extent) noexcept
 	: _M_extent_value(__extent)
@@ -114,8 +105,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   } // namespace __detail
 
   template<typename _Type, size_t _Extent = dynamic_extent>
-    class span : private __detail::__extent_storage<_Extent>
+    class span
     {
+      template<size_t _Offset, size_t _Count>
+	static constexpr size_t
+	_S_subspan_extent()
+	{
+	  if constexpr (_Count != dynamic_extent)
+	    return _Count;
+	  else if constexpr (extent != dynamic_extent)
+	    return _Extent - _Offset;
+	  else
+	    return dynamic_extent;
+	}
+
     public:
       // member types
       using value_type             = remove_cv_t<_Type>;
@@ -138,17 +141,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // member constants
       static inline constexpr size_t extent = _Extent;
 
-    private:
-      using __base_t = __detail::__extent_storage<extent>;
-
-    public:
       // constructors
 
       template <typename _Dummy = _Type,
 		enable_if_t<is_same_v<_Dummy, _Type>
 		  && (_Extent == dynamic_extent || _Extent == 0)>* = nullptr>
 	constexpr
-	span() noexcept : __base_t(0), _M_ptr(nullptr)
+	span() noexcept : _M_extent(0), _M_ptr(nullptr)
 	{ }
 
       constexpr
@@ -234,7 +233,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	constexpr
 	span(_ContiguousIterator __first, index_type __count)
 	noexcept(noexcept(::std::__adl_to_address(__first)))
-	: __base_t(__count), _M_ptr(::std::__adl_to_address(__first))
+	: _M_extent(__count), _M_ptr(::std::__adl_to_address(__first))
 	{ __glibcxx_assert(_Extent == dynamic_extent || __count == _Extent); }
 
 #else
@@ -276,7 +275,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       constexpr
       span(pointer __first, index_type __count) noexcept
-      : __base_t(__count), _M_ptr(static_cast<pointer>(__first))
+      : _M_extent(__count), _M_ptr(static_cast<pointer>(__first))
       { __glibcxx_assert(_Extent == dynamic_extent || __count == _Extent); }
 
       constexpr
@@ -290,41 +289,52 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       constexpr span&
       operator=(const span&) noexcept = default;
 
-      // observers: element access
+      // observers
+
+      constexpr index_type
+      size() const noexcept
+      { return this->_M_extent._M_extent(); }
+
+      constexpr index_type
+      size_bytes() const noexcept
+      { return this->_M_extent._M_extent() * sizeof(element_type); }
+
+      [[nodiscard]] constexpr bool
+      empty() const noexcept
+      { return size() == 0; }
+
+      // element access
 
       constexpr reference
       front() const noexcept
-      { return *this->begin(); }
+      {
+	static_assert(extent != 0);
+	__glibcxx_assert(!empty());
+	return *this->_M_ptr;
+      }
 
       constexpr reference
       back() const noexcept
       {
-	iterator __it = this->end();
-	--__it;
-	return *__it;
+	static_assert(extent != 0);
+	__glibcxx_assert(!empty());
+	return *(this->_M_ptr + (size() - 1));
       }
 
       constexpr reference
       operator[](index_type __idx) const noexcept
-      { return *(this->_M_ptr + __idx); }
+      {
+	static_assert(extent != 0);
+	__glibcxx_assert(__idx < size());
+	return *(this->_M_ptr + __idx);
+      }
 
       constexpr pointer
       data() const noexcept
       { return this->_M_ptr; }
 
-      constexpr index_type
-      size() const noexcept
-      { return this->__base_t::_M_extent(); }
+      // iterator support
 
-      constexpr index_type
-      size_bytes() const noexcept
-      { return this->__base_t::_M_extent() * sizeof(element_type); }
-
-      constexpr bool
-      empty() const noexcept
-      { return size() == 0; }
-
-      // observers: iterator support
       constexpr iterator
       begin() const noexcept
       { return iterator(this->_M_ptr); }
@@ -357,70 +367,85 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       crend() const noexcept
       { return const_reverse_iterator(this->cbegin()); }
 
-      // observers: subranges
+      // subviews
+
       template<size_t _Count>
 	constexpr span<element_type, _Count>
-	first() const
+	first() const noexcept
 	{
-	  __glibcxx_assert(_Count < size());
+	  if constexpr (_Extent == dynamic_extent)
+	    __glibcxx_assert(_Count <= size());
+	  else
+	    static_assert(_Count <= extent);
 	  return { this->data(), _Count };
 	}
 
       constexpr span<element_type, dynamic_extent>
-      first(index_type __count) const
+      first(index_type __count) const noexcept
       {
-	__glibcxx_assert(__count < size());
+	__glibcxx_assert(__count <= size());
 	return { this->data(), __count };
       }
 
       template<size_t _Count>
 	constexpr span<element_type, _Count>
-	last() const
+	last() const noexcept
 	{
-	  static_assert(_Count == dynamic_extent ||
-			  _Extent == dynamic_extent || _Count <= _Extent,
-	    "Count or Extent are dynamic, "
-	    "or the Count is less than the static extent");
-	  __glibcxx_assert(_Count <= size());
+	  if constexpr (_Extent == dynamic_extent)
+	    __glibcxx_assert(_Count <= size());
+	  else
+	    static_assert(_Count <= extent);
 	  return { this->data() + (this->size() - _Count), _Count };
 	}
 
       constexpr span<element_type, dynamic_extent>
-      last(index_type __count) const
+      last(index_type __count) const noexcept
       {
-	__glibcxx_assert(__count < size());
-	index_type __offset = (this->size() - __count);
-	return { this->data() + __offset, __count };
+	__glibcxx_assert(__count <= size());
+	return { this->data() + (this->size() - __count), __count };
       }
 
       template<size_t _Offset, size_t _Count = dynamic_extent>
 	constexpr auto
-	subspan() const
+	subspan() const noexcept
+	-> span<element_type, _S_subspan_extent<_Offset, _Count>()>
 	{
-	  static_assert(_Count == dynamic_extent ||
-			  _Extent == dynamic_extent ||
-			  (_Offset + _Count) <= _Extent,
-	    "Count or Extent are dynamic, "
-	    "or the Count + Offset is less than the static extent");
-	  constexpr size_t __span_extent =
-	    (_Count != dynamic_extent
-		? _Count
-		: (_Extent != dynamic_extent ? _Extent - _Offset
-						    : dynamic_extent));
-	  using __span_t = span<element_type, __span_extent>;
-	  if constexpr(_Count != dynamic_extent)
+	  if constexpr (_Extent == dynamic_extent)
+	    __glibcxx_assert(_Offset <= size());
+	  else
+	    static_assert(_Offset <= extent);
+
+	  if constexpr (_Count == dynamic_extent)
+	    return { this->data() + _Offset, this->size() - _Offset };
+	  else
 	    {
-	      __glibcxx_assert((_Offset + _Count) < size());
+	      if constexpr (_Extent == dynamic_extent)
+		{
+		  __glibcxx_assert(_Count <= size());
+		  __glibcxx_assert(_Count <= (size() - _Offset));
+		}
+	      else
+		{
+		  static_assert(_Count <= extent);
+		  static_assert(_Count <= (extent - _Offset));
+		}
+	      return { this->data() + _Offset, _Count };
 	    }
-	  return __span_t(this->data() + _Offset,
-	    (_Count == dynamic_extent ? this->size() - _Offset : _Count));
 	}
 
       constexpr span<element_type, dynamic_extent>
       subspan(index_type __offset, index_type __count = dynamic_extent) const
+      noexcept
       {
-	return {this->data() + __offset,
-	  __count == dynamic_extent ? this->size() - __offset : __count};
+	__glibcxx_assert(__offset <= size());
+	if (__count == dynamic_extent)
+	  __count = this->size() - __offset;
+	else
+	  {
+	    __glibcxx_assert(__count <= size());
+	    __glibcxx_assert(__offset + __count <= size());
+	  }
+	return {this->data() + __offset, __count};
       }
 
       // observers: range helpers
@@ -434,51 +459,54 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return __sp.end(); }
 
     private:
+      [[no_unique_address]] __detail::__extent_storage<extent> _M_extent;
       pointer _M_ptr;
     };
 
   // deduction guides
   template<typename _Type, size_t _ArrayExtent>
-    span(_Type(&)[_ArrayExtent])->span<_Type, _ArrayExtent>;
+    span(_Type(&)[_ArrayExtent]) -> span<_Type, _ArrayExtent>;
 
   template<typename _Type, size_t _ArrayExtent>
-    span(array<_Type, _ArrayExtent>&)->span<_Type, _ArrayExtent>;
+    span(array<_Type, _ArrayExtent>&) -> span<_Type, _ArrayExtent>;
 
   template<typename _Type, size_t _ArrayExtent>
     span(const array<_Type, _ArrayExtent>&)
-      ->span<const _Type, _ArrayExtent>;
+      -> span<const _Type, _ArrayExtent>;
 
 #if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
 
   template<typename _ContiguousIterator, typename _Sentinel>
     span(_ContiguousIterator, _Sentinel)
-      ->span<remove_reference_t<
+      -> span<remove_reference_t<
 	typename iterator_traits<_ContiguousIterator>::reference>>;
 
   template<typename _Range>
     span(_Range &&)
-      ->span<remove_reference_t<typename iterator_traits<decltype(
-	::std::__adl_begin(::std::declval<_Range&>()))>::reference>>;
+      -> span<remove_reference_t<typename iterator_traits<
+	  decltype(std::__adl_begin(::std::declval<_Range&>()))>::reference>>;
 
 #else
 
   template<typename _Container>
-    span(_Container&)->span<typename _Container::value_type>;
+    span(_Container&) -> span<typename _Container::value_type>;
 
   template<typename _Container>
-    span(const _Container&)->span<const typename _Container::value_type>;
+    span(const _Container&) -> span<const typename _Container::value_type>;
 
 #endif // P1394
 
   template<typename _Type, size_t _Extent>
+    inline
     span<const byte, _Extent == dynamic_extent
 	? dynamic_extent : _Extent * sizeof(_Type)>
-    inline as_bytes(span<_Type, _Extent> __sp) noexcept
+    as_bytes(span<_Type, _Extent> __sp) noexcept
     {
       return {reinterpret_cast<const byte*>(__sp.data()), __sp.size_bytes()};
     }
 
   template<typename _Type, size_t _Extent>
+    inline
     span<byte, _Extent == dynamic_extent
        ? dynamic_extent : _Extent * sizeof(_Type)>
     as_writable_bytes(span<_Type, _Extent> __sp) noexcept
@@ -491,9 +519,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     constexpr _Type&
     get(span<_Type, _Extent> __sp) noexcept
     {
-      static_assert(_Extent != dynamic_extent
-	&& (_Extent > 0) && (_Index < _Extent),
-	"std::get can only be used with a span of non-dynamic (fixed) extent");
+      static_assert(_Extent != dynamic_extent && _Index < _Extent,
+	"get<I> can only be used with a span of non-dynamic (fixed) extent");
       return __sp[_Index];
     }
 
@@ -508,10 +535,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<size_t _Index, typename _Type, size_t _Extent>
     struct tuple_element<_Index, span<_Type, _Extent>>
     {
-      static_assert(_Extent != dynamic_extent, "tuple_size can only "
+      static_assert(_Extent != dynamic_extent, "tuple_element can only "
 	"be used with a span of non-dynamic (fixed) extent");
       static_assert(_Index < _Extent, "Index is less than Extent");
-      using type = typename span<_Type, _Extent>::element_type;
+      using type = _Type;
     };
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 4341c9f0c1b..7625d397a0d 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -166,10 +166,8 @@
 #endif
 #define __cpp_lib_list_remove_return_type 201806L
 #define __cpp_lib_math_constants 201907L
+#define __cpp_lib_span 201902L
 #define __cpp_lib_to_array 201907L
-// FIXME: they forgot this feature test macro
-// get on someone's back about it in Belfast!!!
-#define __cpp_lib_span 201911
 #endif // C++2a
 #endif // C++17
 #endif // C++14
diff --git a/libstdc++-v3/testsuite/23_containers/span/1.cc b/libstdc++-v3/testsuite/23_containers/span/1.cc
new file mode 100644
index 00000000000..c31d8d400ce
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/1.cc
@@ -0,0 +1,27 @@
+// 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>
+
+#ifndef __cpp_lib_span
+# error "Feature-test macro for span missing in <span>"
+#elif __cpp_lib_span != 201902L
+# error "Feature-test macro for span has wrong value in <span>"
+#endif
diff --git a/libstdc++-v3/testsuite/23_containers/span/2.cc b/libstdc++-v3/testsuite/23_containers/span/2.cc
new file mode 100644
index 00000000000..1870ba16973
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/2.cc
@@ -0,0 +1,27 @@
+// 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 <version>
+
+#ifndef __cpp_lib_span
+# error "Feature-test macro for span missing in <version>"
+#elif __cpp_lib_span != 201902L
+# error "Feature-test macro for span has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/23_containers/span/back_assert_neg.cc b/libstdc++-v3/testsuite/23_containers/span/back_assert_neg.cc
new file mode 100644
index 00000000000..0b8fc672a20
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/back_assert_neg.cc
@@ -0,0 +1,28 @@
+// 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 run { xfail c++2a } }
+
+#define _GLIBCXX_ASSERTIONS
+#include <span>
+
+int main()
+{
+  std::span<int, std::dynamic_extent> s;
+  s.back();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/back_neg.cc b/libstdc++-v3/testsuite/23_containers/span/back_neg.cc
new file mode 100644
index 00000000000..b1bb10f40d1
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/back_neg.cc
@@ -0,0 +1,29 @@
+// 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>
+
+void
+test01()
+{
+  std::span<int, 0> s;
+  s.back(); // { dg-error "here" }
+}
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/23_containers/span/first_2_assert_neg.cc b/libstdc++-v3/testsuite/23_containers/span/first_2_assert_neg.cc
new file mode 100644
index 00000000000..fd9c9204d1e
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/first_2_assert_neg.cc
@@ -0,0 +1,29 @@
+// 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 run { xfail c++2a } }
+
+#define _GLIBCXX_ASSERTIONS
+#include <span>
+
+int main()
+{
+  int a[4];
+  std::span<int, std::dynamic_extent> s(a);
+  s.first(5);
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/first_assert_neg.cc b/libstdc++-v3/testsuite/23_containers/span/first_assert_neg.cc
new file mode 100644
index 00000000000..94f1f03a64a
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/first_assert_neg.cc
@@ -0,0 +1,29 @@
+// 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 run { xfail c++2a } }
+
+#define _GLIBCXX_ASSERTIONS
+#include <span>
+
+int main()
+{
+  int a[4];
+  std::span<int, std::dynamic_extent> s(a);
+  s.first<5>();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/first_neg.cc b/libstdc++-v3/testsuite/23_containers/span/first_neg.cc
new file mode 100644
index 00000000000..42729347d33
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/first_neg.cc
@@ -0,0 +1,30 @@
+// 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>
+
+void
+test01()
+{
+  int a[4];
+  std::span<int, 4> s(a);
+  s.first<5>(); // { dg-error "here" }
+}
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/23_containers/span/front_assert_neg.cc b/libstdc++-v3/testsuite/23_containers/span/front_assert_neg.cc
new file mode 100644
index 00000000000..516bf2a2dcb
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/front_assert_neg.cc
@@ -0,0 +1,28 @@
+// 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 run { xfail c++2a } }
+
+#define _GLIBCXX_ASSERTIONS
+#include <span>
+
+int main()
+{
+  std::span<int, std::dynamic_extent> s;
+  s.front();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/front_neg.cc b/libstdc++-v3/testsuite/23_containers/span/front_neg.cc
new file mode 100644
index 00000000000..4c2b6e39c70
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/front_neg.cc
@@ -0,0 +1,29 @@
+// 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>
+
+void
+test01()
+{
+  std::span<int, 0> s;
+  s.front(); // { dg-error "here" }
+}
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/23_containers/span/index_op_assert_neg.cc b/libstdc++-v3/testsuite/23_containers/span/index_op_assert_neg.cc
new file mode 100644
index 00000000000..4109c4cdc7a
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/index_op_assert_neg.cc
@@ -0,0 +1,28 @@
+// 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 run { xfail c++2a } }
+
+#define _GLIBCXX_ASSERTIONS
+#include <span>
+
+int main()
+{
+  std::span<int, std::dynamic_extent> s;
+  s[99];
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/index_op_neg.cc b/libstdc++-v3/testsuite/23_containers/span/index_op_neg.cc
new file mode 100644
index 00000000000..5bd1e11dda0
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/index_op_neg.cc
@@ -0,0 +1,29 @@
+// 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>
+
+void
+test01()
+{
+  std::span<int, 0> s;
+  s[99]; // { dg-error "here" }
+}
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/23_containers/span/last_2_assert_neg.cc b/libstdc++-v3/testsuite/23_containers/span/last_2_assert_neg.cc
new file mode 100644
index 00000000000..1cc060f4735
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/last_2_assert_neg.cc
@@ -0,0 +1,29 @@
+// 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 run { xfail c++2a } }
+
+#define _GLIBCXX_ASSERTIONS
+#include <span>
+
+int main()
+{
+  int a[4];
+  std::span<int, std::dynamic_extent> s(a);
+  s.last(5);
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/last_assert_neg.cc b/libstdc++-v3/testsuite/23_containers/span/last_assert_neg.cc
new file mode 100644
index 00000000000..31b6cc0816f
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/last_assert_neg.cc
@@ -0,0 +1,29 @@
+// 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 run { xfail c++2a } }
+
+#define _GLIBCXX_ASSERTIONS
+#include <span>
+
+int main()
+{
+  int a[4];
+  std::span<int, std::dynamic_extent> s(a);
+  s.last<5>();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/last_neg.cc b/libstdc++-v3/testsuite/23_containers/span/last_neg.cc
index 8f0145f7012..ac77dbe4438 100644
--- a/libstdc++-v3/testsuite/23_containers/span/last_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/span/last_neg.cc
@@ -15,15 +15,16 @@
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// { dg-options "-std=c++2a" }
+// { dg-options "-std=gnu++2a" }
 // { dg-do compile { target c++2a } }
 
 #include <span>
 
-int
-main()
+void
+test01()
 {
-  std::span<int, 2> myspan(nullptr, 2);
-  myspan.last<3>(); // { dg-error "here" }
+  int a[2];
+  std::span<int, 2> s(a);
+  s.last<3>(); // { dg-error "here" }
 }
 // { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/23_containers/span/subspan_2_assert_neg.cc b/libstdc++-v3/testsuite/23_containers/span/subspan_2_assert_neg.cc
new file mode 100644
index 00000000000..355a71c9d9d
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/subspan_2_assert_neg.cc
@@ -0,0 +1,29 @@
+// 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 run { xfail c++2a } }
+
+#define _GLIBCXX_ASSERTIONS
+#include <span>
+
+int main()
+{
+  int a[4];
+  std::span<int, std::dynamic_extent> s(a);
+  s.subspan<2, 5>();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/subspan_3_assert_neg.cc b/libstdc++-v3/testsuite/23_containers/span/subspan_3_assert_neg.cc
new file mode 100644
index 00000000000..bdf4abad2c6
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/subspan_3_assert_neg.cc
@@ -0,0 +1,29 @@
+// 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 run { xfail c++2a } }
+
+#define _GLIBCXX_ASSERTIONS
+#include <span>
+
+int main()
+{
+  int a[4];
+  std::span<int, std::dynamic_extent> s(a);
+  s.subspan<2, 3>();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/subspan_4_assert_neg.cc b/libstdc++-v3/testsuite/23_containers/span/subspan_4_assert_neg.cc
new file mode 100644
index 00000000000..37ebed038fa
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/subspan_4_assert_neg.cc
@@ -0,0 +1,29 @@
+// 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 run { xfail c++2a } }
+
+#define _GLIBCXX_ASSERTIONS
+#include <span>
+
+int main()
+{
+  int a[4];
+  std::span<int, std::dynamic_extent> s(a);
+  s.subspan(5, 0);
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/subspan_5_assert_neg.cc b/libstdc++-v3/testsuite/23_containers/span/subspan_5_assert_neg.cc
new file mode 100644
index 00000000000..615ea0ba559
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/subspan_5_assert_neg.cc
@@ -0,0 +1,29 @@
+// 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 run { xfail c++2a } }
+
+#define _GLIBCXX_ASSERTIONS
+#include <span>
+
+int main()
+{
+  int a[4];
+  std::span<int, std::dynamic_extent> s(a);
+  s.subspan(2, 5);
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/subspan_6_assert_neg.cc b/libstdc++-v3/testsuite/23_containers/span/subspan_6_assert_neg.cc
new file mode 100644
index 00000000000..db5f427b35f
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/subspan_6_assert_neg.cc
@@ -0,0 +1,29 @@
+// 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 run { xfail c++2a } }
+
+#define _GLIBCXX_ASSERTIONS
+#include <span>
+
+int main()
+{
+  int a[4];
+  std::span<int, std::dynamic_extent> s(a);
+  s.subspan(2, 3);
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/subspan_assert_neg.cc b/libstdc++-v3/testsuite/23_containers/span/subspan_assert_neg.cc
new file mode 100644
index 00000000000..d120d436c23
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/subspan_assert_neg.cc
@@ -0,0 +1,29 @@
+// 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 run { xfail c++2a } }
+
+#define _GLIBCXX_ASSERTIONS
+#include <span>
+
+int main()
+{
+  int a[4];
+  std::span<int, std::dynamic_extent> s(a);
+  s.subspan<5, 0>();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/span/subspan_neg.cc b/libstdc++-v3/testsuite/23_containers/span/subspan_neg.cc
index 5ecbff937cc..70fcda6e91a 100644
--- a/libstdc++-v3/testsuite/23_containers/span/subspan_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/span/subspan_neg.cc
@@ -15,15 +15,33 @@
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// { dg-options "-std=c++2a" }
+// { dg-options "-std=gnu++2a" }
 // { dg-do compile { target c++2a } }
 
 #include <span>
 
-int
-main()
+void
+test01()
 {
-  std::span<int, 2> myspan(nullptr, 2);
-  myspan.subspan<3, 1>(); // { dg-error "here" }
+  int a[4];
+  std::span<int, 4> s(a);
+  s.subspan<5, 0>(); // { dg-error "here" }
 }
+
+void
+test02()
+{
+  int a[4];
+  std::span<int, 4> s(a);
+  s.subspan<3, 5>(); // { dg-error "here" }
+}
+
+void
+test03()
+{
+  int a[4];
+  std::span<int, 4> s(a);
+  s.subspan<3, 2>(); // { dg-error "here" }
+}
+
 // { dg-error "static assertion failed" "" { target *-*-* } 0 }

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [ PATCH ] C++20 <span>
  2019-09-05 13:52             ` Jonathan Wakely
@ 2019-09-09 11:12               ` Jonathan Wakely
  0 siblings, 0 replies; 9+ messages in thread
From: Jonathan Wakely @ 2019-09-09 11:12 UTC (permalink / raw)
  To: JeanHeyd Meneide; +Cc: gcc-patches, libstdc++

[-- Attachment #1: Type: text/plain, Size: 1054 bytes --]

And some further simplifications and improvements to the constructor
constraints for std::span.

    This patch simplifies the constraints on the constructors from arrays by
    removing the redundant checks that element_type and value_type are
    convertible to element_type. The incorrect uses of __adl_data in those
    constructors are removed as well (they should use std::data not
    std::ranges::data, and the former doesn't use ADL).

    The range/container constructors are now constrained to exclude all
    specializations of std::span, not just the current instantiation. The
    range constructor now also checks s subset of the contiguous_range
    requirements.

    All relevant constructor constraints now use the _Require helper in
    order to short circuit and avoid unnecessary instantiations after the
    first failed constraint.

    A new constructor supports initialization from different specializations
    of std::span<OtherType, OtherExtent>, as specified in the C++20 draft.


Tested x86_64-linux, committed to trunk.



[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 15703 bytes --]

commit cf9248752de63a49bde062eb00681ae1c6d1a546
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Mon Sep 9 10:56:54 2019 +0100

    Improve constraints for std::span constructors
    
    This patch simplifies the constraints on the constructors from arrays by
    removing the redundant checks that element_type and value_type are
    convertible to element_type. The incorrect uses of __adl_data in those
    constructors are removed as well (they should use std::data not
    std::ranges::data, and the former doesn't use ADL).
    
    The range/container constructors are now constrained to exclude all
    specializations of std::span, not just the current instantiation. The
    range constructor now also checks s subset of the contiguous_range
    requirements.
    
    All relevant constructor constraints now use the _Require helper in
    order to short circuit and avoid unnecessary instantiations after the
    first failed constraint.
    
    A new constructor supports initialization from different specializations
    of std::span<OtherType, OtherExtent>, as specified in the C++20 draft.
    
            * include/bits/range_access.h (__adl_to_address): Remove.
            * include/std/span (__is_base_derived_safe_convertible_v): Replace
            with span::__is_compatible.
            (__is_std_array_v): Replace with __is_std_array class template and
            partial specializations.
            (__is_std_array, __is_std_span): New class templates and partial
            specializations.
            (span::__is_compatible): New alias template for SFINAE constraints.
            (span::span(element_type (&)[N])): Remove redundant constraints. Do
            not use __adl_data to obtain a pointer.
            (span::span(array<value_type, N>&)): Likewise.
            (span::span(const array<value_type, N>&)): Likewise.
            [_GLIBCXX_P1394] (span::iter_reference_t, span::iterator_t)
            (span::iter_value_t, span::derived_from): New alias templates for
            SFINAE constraints, until the equivalents are supported in <concepts>
            and <iterator>.
            [_GLIBCXX_P1394] (span::__is_compatible_iterator): New alias template
            for SFINAE constraints.
            [_GLIBCXX_P1394] (span::is_compatible_range): New class template for
            SFINAE constraints.
            [_GLIBCXX_P1394] (span::span(Range&&)): Improve constraints.
            [_GLIBCXX_P1394] (span::span(ContiguousIterator, Sentinel)): Likewise.
            Use std::to_address instead of __adl_to_address.
            [_GLIBCXX_P1394] (span::span(ContiguousIterator, size_type)): Likewise.
            [!_GLIBCXX_P1394] (span::__is_compatible_container): New alias
            template for SFINAE constraints.
            [!_GLIBCXX_P1394] (span::span(Container&))
            (span::span(const Container&)): Improve constraints.
            [!_GLIBCXX_P1394] (span::span(pointer, size_type))
            (span::span(pointer, pointer)): Remove redundant cast of pointer.
            (span(const span<OType, OExtent>&)): New constructor.

diff --git a/libstdc++-v3/include/bits/range_access.h b/libstdc++-v3/include/bits/range_access.h
index c5744145590..bc137d7396e 100644
--- a/libstdc++-v3/include/bits/range_access.h
+++ b/libstdc++-v3/include/bits/range_access.h
@@ -396,13 +396,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     constexpr auto
     __adl_empty(_Container& __cont) noexcept(noexcept(empty(__cont)))
     { return empty(__cont); }
-
-#if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
-  template <typename _Container>
-    constexpr auto
-    __adl_to_address(_Container& __cont) noexcept(noexcept(to_address(__cont)))
-    { return to_address(__cont); }
-#endif // P1394 and new contiguous_iterator requirements [iterator.concept.contiguous]
 #endif // C++20
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span
index 95d778b104b..1a0d61c1947 100644
--- a/libstdc++-v3/include/std/span
+++ b/libstdc++-v3/include/std/span
@@ -53,24 +53,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   inline constexpr size_t dynamic_extent = static_cast<size_t>(-1);
 
+  template<typename _Type, size_t _Extent>
+    class span;
+
   namespace __detail
   {
-    template<typename _Element, typename _ToElement>
-      static constexpr inline bool __is_base_derived_safe_convertible_v
-	= is_convertible_v<_Element (*)[], _ToElement (*)[]>;
-
     template<typename _Tp>
-      inline constexpr bool __is_std_array_v = false;
+      struct __is_std_span : false_type { };
 
     template<typename _Tp, size_t _Num>
-      inline constexpr bool
-      __is_std_array_v<_GLIBCXX_STD_C::array<_Tp, _Num>> = true;
+      struct __is_std_span<span<_Tp, _Num>> : true_type { };
+
+    template<typename _Tp>
+      struct __is_std_array : false_type { };
+
+    template<typename _Tp, size_t _Num>
+      struct __is_std_array<_GLIBCXX_STD_C::array<_Tp, _Num>> : true_type { };
 
 #ifdef _GLIBCXX_DEBUG
     template<typename _Tp, size_t _Num>
-      inline constexpr bool
-      __is_std_array_v<std::__debug::array<_Tp, _Num>> = true;
-#endif // debug/array
+      struct __is_std_array<__debug::array<_Tp, _Num>> : true_type { };
+#endif
 
     template<size_t _Extent>
       class __extent_storage
@@ -119,6 +122,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    return dynamic_extent;
 	}
 
+      template<typename _Tp>
+	using __is_compatible = is_convertible<_Tp(*)[], _Type(*)[]>;
+
     public:
       // member types
       using value_type             = remove_cv_t<_Type>;
@@ -154,41 +160,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       span(const span&) noexcept = default;
 
       template<size_t _ArrayExtent,
-	enable_if_t<
-	  (_Extent == dynamic_extent || _ArrayExtent == _Extent)
-	  && __detail::__is_base_derived_safe_convertible_v<
-	    remove_pointer_t<decltype(::std::__adl_data(
-	      ::std::declval<element_type (&)[_ArrayExtent]>()))>,
-	    element_type>>* = nullptr>
-	constexpr span(element_type (&__arr)[_ArrayExtent])
-	noexcept(noexcept(::std::__adl_data(__arr)))
-	: span(::std::__adl_data(__arr), _ArrayExtent)
+	enable_if_t<_Extent == dynamic_extent || _ArrayExtent == _Extent>*
+	  = nullptr>
+	constexpr
+	span(element_type (&__arr)[_ArrayExtent]) noexcept
+	: span(static_cast<pointer>(__arr), _ArrayExtent)
 	{ }
 
       template<size_t _ArrayExtent,
-	enable_if_t<
-	  (_Extent == dynamic_extent || _ArrayExtent == _Extent)
-	  && __detail::__is_base_derived_safe_convertible_v<
-	    remove_pointer_t<decltype(::std::__adl_data(
-	      ::std::declval<const array<value_type, _ArrayExtent>&>()))>,
-	    element_type>>* = nullptr>
+	enable_if_t<_Extent == dynamic_extent || _ArrayExtent == _Extent>*
+	  = nullptr>
 	constexpr
-	span(array<value_type, _ArrayExtent>& __arr)
-	noexcept(noexcept(::std::__adl_data(__arr)))
-	: span(::std::__adl_data(__arr), _ArrayExtent)
+	span(array<value_type, _ArrayExtent>& __arr) noexcept
+	: span(__arr.data(), _ArrayExtent)
 	{ }
 
       template<size_t _ArrayExtent,
-	enable_if_t<
-	  (_Extent == dynamic_extent || _ArrayExtent == _Extent)
-	  && __detail::__is_base_derived_safe_convertible_v<
-	    remove_pointer_t<decltype(::std::__adl_data(
-	      ::std::declval<const array<value_type, _ArrayExtent>&>()))>,
-	    element_type>>* = nullptr>
+	enable_if_t<_Extent == dynamic_extent || _ArrayExtent == _Extent>*
+	  = nullptr>
 	constexpr
-	span(const array<value_type, _ArrayExtent>& __arr)
-	noexcept(noexcept(::std::__adl_data(__arr)))
-	: span(::std::__adl_data(__arr), _ArrayExtent)
+	span(const array<value_type, _ArrayExtent>& __arr) noexcept
+	: span(__arr.data(), _ArrayExtent)
 	{ }
 
       // NOTE: when the time comes, and P1394 -
@@ -199,18 +191,49 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // and keep the crappy #else block
       // and then cry that NB comments failed C++20...
       // but maybe for C++23?
-#if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
-      template<typename _Range,
-	enable_if_t<
-	  (_Extent == dynamic_extent)
-	  && !is_same_v<remove_cvref_t<_Range>, span>
-	  && !__detail::__is_std_array_v<remove_cvref_t<_Range>>
-	  && !is_array_v<remove_cvref_t<_Range>>
-	  && __detail::__is_base_derived_safe_convertible_v<
-	    remove_pointer_t<decltype(
-	      ::std::__adl_data(::std::declval<_Range&>())
-	      + ::std::__adl_size(::std::declval<_Range&>()))>,
-	    element_type>>* = nullptr>
+#ifdef _GLIBCXX_P1394
+    private:
+      // FIXME: use std::iter_reference_t
+      template<typename _Iterator>
+	using iter_reference_t = decltype(*std::declval<_Iterator&>());
+      // FIXME: use std::ranges::iterator_t
+      // N.B. constraint is needed to prevent a cycle when __adl_begin finds
+      // begin(span) which does overload resolution on span(Range&&).
+      template<typename _Rng,
+	       typename _Rng2 = remove_cvref_t<_Rng>,
+	       typename = enable_if_t<!__detail::__is_std_span<_Rng2>::value>>
+	using iterator_t = decltype(std::__adl_begin(std::declval<_Rng&>()));
+      // FIXME: use std::iter_value_t
+      template<typename _Iter>
+	using iter_value_t = typename iterator_traits<_Iter>::value_type;
+      // FIXME: use std::derived_from concept
+      template<typename _Derived, typename _Base>
+	using derived_from
+	  = __and_<is_base_of<_Base, _Derived>,
+	      is_convertible<const volatile _Derived*, const volatile _Base*>>;
+      // FIXME: require contiguous_iterator<_Iterator>
+      template<typename _Iter,
+	       typename _Ref = iter_reference_t<_Iter>,
+	       typename _Traits = iterator_traits<_Iter>,
+	       typename _Tag = typename _Traits::iterator_category>
+	using __is_compatible_iterator
+	  = __and_<derived_from<_Tag, random_access_iterator_tag>,
+		   is_lvalue_reference<_Ref>,
+		   is_same<iter_value_t<_Iter>, remove_cvref_t<_Ref>>,
+		   __is_compatible<remove_reference_t<_Ref>>>;
+
+      template<typename _Range>
+	using __is_compatible_range
+	  = __is_compatible_iterator<iterator_t<_Range>>;
+
+    public:
+      template<typename _Range, typename = _Require<
+	  bool_constant<_Extent == dynamic_extent>,
+	  __not_<__detail::__is_std_span<remove_cvref_t<_Range>>>,
+	  __not_<__detail::__is_std_array<remove_cvref_t<_Range>>>,
+	  __not_<is_array<remove_reference_t<_Range>>>,
+	  __is_compatible_range<_Range>>,
+	  typename = decltype(std::__adl_data(std::declval<_Range&>()))>
 	constexpr
 	span(_Range&& __range)
 	noexcept(noexcept(::std::__adl_data(__range))
@@ -218,72 +241,77 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: span(::std::__adl_data(__range), ::std::__adl_size(__range))
 	{ }
 
-      template<typename _ContiguousIterator, typename _Sentinel,
-	enable_if_t<!is_convertible_v<_Sentinel, index_type>
-	  && __detail::__is_base_derived_safe_convertible_v<
-	    remove_reference_t<typename
-	      iterator_traits<_ContiguousIterator>::reference>,
-	  element_type>>* = nullptr>
+      template<typename _ContiguousIterator, typename _Sentinel, typename
+		= _Require<__not_<is_convertible<_Sentinel, index_type>>,
+			   __is_compatible_iterator<_ContiguousIterator>>>
 	constexpr
 	span(_ContiguousIterator __first, _Sentinel __last)
-	: span(::std::move(__first), static_cast<index_type>(__last - __first))
-	{ }
+	: _M_extent(static_cast<index_type>(__last - __first)),
+	  _M_ptr(std::to_address(__first))
+	{
+	  if (_Extent != dynamic_extent)
+	    __glibcxx_assert((__last - __first) == _Extent);
+	}
 
-      template<typename _ContiguousIterator>
+      template<typename _ContiguousIterator, typename
+		= _Require<__is_compatible_iterator<_ContiguousIterator>>>
 	constexpr
 	span(_ContiguousIterator __first, index_type __count)
-	noexcept(noexcept(::std::__adl_to_address(__first)))
-	: _M_extent(__count), _M_ptr(::std::__adl_to_address(__first))
+	noexcept(noexcept(std::to_address(__first)))
+	: _M_extent(__count), _M_ptr(std::to_address(__first))
 	{ __glibcxx_assert(_Extent == dynamic_extent || __count == _Extent); }
-
 #else
-
+    private:
       template<typename _Container,
-	enable_if_t<
-	  (_Extent == dynamic_extent)
-	  && !is_same_v<remove_cvref_t<_Container>, span>
-	  && !__detail::__is_std_array_v<remove_cvref_t<_Container>>
-	  && !is_array_v<remove_cvref_t<_Container>>
-	  && __detail::__is_base_derived_safe_convertible_v<
-	    remove_pointer_t<decltype(
-	      ::std::__adl_data(::std::declval<_Container&>())
-	      + ::std::__adl_size(::std::declval<_Container&>()))>,
-	    element_type>>* = nullptr>
+	  typename _DataT = decltype(std::data(std::declval<_Container&>())),
+	  typename _SizeT = decltype(std::size(std::declval<_Container&>()))>
+	using __is_compatible_container
+	  = __is_compatible<remove_pointer_t<_DataT>>;
+
+    public:
+      template<typename _Container, typename = _Require<
+		bool_constant<_Extent == dynamic_extent>,
+		__not_<__detail::__is_std_span<_Container>>,
+		__not_<__detail::__is_std_array<_Container>>,
+		__not_<is_array<_Container>>,
+		__is_compatible_container<_Container>>>
 	constexpr
-	span(_Container& __range)
-	noexcept(noexcept(::std::__adl_data(__range))
-		  && noexcept(::std::__adl_size(__range)))
-	: span(::std::__adl_data(__range), ::std::__adl_size(__range))
+	span(_Container& __cont)
+	noexcept(noexcept(std::data(__cont)) && noexcept(std::size(__cont)))
+	: _M_extent(std::size(__cont)), _M_ptr(std::data(__cont))
 	{ }
 
-      template<typename _Container,
-	enable_if_t<
-	  (_Extent == dynamic_extent)
-	  && !is_same_v<remove_cvref_t<_Container>, span>
-	  && !__detail::__is_std_array_v<remove_cvref_t<_Container>>
-	  && !is_array_v<remove_cvref_t<_Container>>
-	  && __detail::__is_base_derived_safe_convertible_v<
-	    remove_pointer_t<decltype(
-	      ::std::__adl_data(::std::declval<_Container&>())
-	      + ::std::__adl_size(::std::declval<_Container&>()))>,
-	    element_type>>* = nullptr>
-	constexpr span(const _Container& __range)
-	noexcept(noexcept(::std::__adl_data(__range))
-		  && noexcept(::std::__adl_size(__range)))
-	: span(::std::__adl_data(__range), ::std::__adl_size(__range))
+      template<typename _Container, typename = _Require<
+		bool_constant<_Extent == dynamic_extent>,
+		__not_<__detail::__is_std_span<_Container>>,
+		__not_<__detail::__is_std_array<_Container>>,
+		__not_<is_array<_Container>>,
+		__is_compatible_container<const _Container>>>
+	constexpr
+	span(const _Container& __cont)
+	noexcept(noexcept(std::data(__cont)) && noexcept(std::size(__cont)))
+	: _M_extent(std::size(__cont)), _M_ptr(std::data(__cont))
 	{ }
 
       constexpr
       span(pointer __first, index_type __count) noexcept
-      : _M_extent(__count), _M_ptr(static_cast<pointer>(__first))
+      : _M_extent(__count), _M_ptr(__first)
       { __glibcxx_assert(_Extent == dynamic_extent || __count == _Extent); }
 
       constexpr
       span(pointer __first, pointer __last) noexcept
-      : span(::std::move(__first), static_cast<index_type>(__last - __first))
+      : span(__first, static_cast<index_type>(__last - __first))
       { }
 #endif // P1394
 
+      template<typename _OType, size_t _OExtent, typename = _Require<
+	  __bool_constant<_Extent == dynamic_extent || _Extent == _OExtent>,
+	  is_convertible<_OType(*)[], _Type(*)[]>>>
+	constexpr
+	span(const span<_OType, _OExtent>& __s) noexcept
+	: _M_extent(__s.size()), _M_ptr(__s.data())
+	{ }
+
       // assignment
 
       constexpr span&
@@ -474,7 +502,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     span(const array<_Type, _ArrayExtent>&)
       -> span<const _Type, _ArrayExtent>;
 
-#if defined(_GLIBCXX_P1394) && _GLIBCXX_P1394
+#ifdef _GLIBCXX_P1394
 
   template<typename _ContiguousIterator, typename _Sentinel>
     span(_ContiguousIterator, _Sentinel)

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2019-09-09 11:12 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-30 20:05 [ PATCH ] C++20 <span> JeanHeyd Meneide
2019-08-30 21:37 ` Jonathan Wakely
2019-08-30 23:42   ` JeanHeyd Meneide
2019-08-31  1:06     ` JeanHeyd Meneide
2019-09-03 13:31       ` Jonathan Wakely
2019-09-04 22:47         ` JeanHeyd Meneide
2019-09-05 11:27           ` Jonathan Wakely
2019-09-05 13:52             ` Jonathan Wakely
2019-09-09 11:12               ` Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).