https://gcc.gnu.org/g:cbef2c1dbd0a609f68862c0a9aa9bf80a502411e

commit r15-6692-gcbef2c1dbd0a609f68862c0a9aa9bf80a502411e
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Wed Dec 11 22:56:08 2024 +0000

    libstdc++: Avoid redundant assertions in std::span constructors
    
    Any std::span<T, N> constructor with a runtime length has a precondition
    that the length is equal to N (except when N == std::dynamic_extent).
    
    Currently every constructor with a runtime length does:
    
    if constexpr (extent != dynamic_extent)
      __glibcxx_assert(n == extent);
    
    We can move those assertions into the __detail::__extent_storage<N>
    constructor so they are only done in one place. To avoid checking the
    assertions when we have a constant length we can add a second
    constructor which is consteval and takes a integral_constant<size_t, N>
    argument. The std::span constructors can pass a size_t for runtime
    lengths and a std::integral_constant<size_t, N> for constant lengths
    that don't need to be checked.
    
    The __detail::__extent_storage<dynamic_extent> specialization only needs
    one constructor, as a std::integral_constant<size_t, N> argument can
    implicitly convert to size_t.
    
    For the member functions that return a subspan with a constant extent we
    return std::span<T,C>(ptr, C) which is redundant in two ways. Repeating
    the constant length C when it's already a template argument is
    redundant, and using the std::span(T*, size_t) constructor implies a
    runtime length which will do a redundant assertion check.  Even though
    that assertion won't fail and should be optimized away, it's still
    unnecessary code that doesn't need to be instantiated and then optimized
    away again. We can avoid that by adding a new private constructor that
    only takes a pointer (wrapped in a custom tag struct to avoid
    accidentally using that constructor) and automatically sets _M_extent to
    the correct value.
    
    libstdc++-v3/ChangeLog:
    
            * include/std/span (__detail::__extent_storage): Check
            precondition in constructor. Add consteval constructor for valid
            lengths and deleted constructor for invalid constant lengths.
            Make member functions always_inline.
            (__detail::__span_ptr): New class template.
            (span): Adjust constructors to use a std::integral_constant
            value for constant lengths. Declare all specializations of
            std::span as friends.
            (span::first<C>, span::last<C>, span::subspan<O,C>): Use new
            private constructor.
            (span(__span_ptr<T>)): New private constructor for constant
            lengths.

Diff:
---
 libstdc++-v3/include/std/span | 78 ++++++++++++++++++++++++-------------------
 1 file changed, 44 insertions(+), 34 deletions(-)

diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span
index f1833b06cf60..4cee89002ad6 100644
--- a/libstdc++-v3/include/std/span
+++ b/libstdc++-v3/include/std/span
@@ -73,10 +73,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       class __extent_storage
       {
       public:
+       // Used for runtime sizes that must satisfy the precondition.
        constexpr
-       __extent_storage(size_t) noexcept
+       __extent_storage([[maybe_unused]] size_t __n) noexcept
+       { __glibcxx_assert(__n == _Extent); }
+
+       // Used for constant sizes that are already known to be correct.
+       consteval
+       __extent_storage(integral_constant<size_t, _Extent>) noexcept
        { }
 
+       // "I've made a huge mistake" - George Oscar Bluth II
+       template<size_t _Gob>
+         __extent_storage(integral_constant<size_t, _Gob>) = delete;
+
+       [[__gnu__::__always_inline__]]
        static constexpr size_t
        _M_extent() noexcept
        { return _Extent; }
@@ -86,11 +97,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       class __extent_storage<dynamic_extent>
       {
       public:
+       [[__gnu__::__always_inline__]]
        constexpr
        __extent_storage(size_t __extent) noexcept
        : _M_extent_value(__extent)
        { }
 
+       [[__gnu__::__always_inline__]]
        constexpr size_t
        _M_extent() const noexcept
        { return this->_M_extent_value; }
@@ -98,6 +111,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       private:
        size_t _M_extent_value;
       };
+
+    template<typename _Type> struct __span_ptr { _Type* const _M_ptr; };
+
   } // namespace __detail
 
   template<typename _Type, size_t _Extent = dynamic_extent>
@@ -128,6 +144,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // Nested type so that _Type is not an associated class of iterator.
       struct __iter_tag;
 
+      template<size_t _Nm>
+       static inline constexpr integral_constant<size_t, _Nm> __v{};
+
     public:
       // member types
       using element_type           = _Type;
@@ -153,7 +172,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       constexpr
       span() noexcept
       requires (_Extent == dynamic_extent || _Extent == 0)
-      : _M_ptr(nullptr), _M_extent(0)
+      : _M_ptr(nullptr), _M_extent(__v<0>)
       { }
 
       template<contiguous_iterator _It>
@@ -162,13 +181,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        span(_It __first, size_type __count)
        noexcept
        : _M_ptr(std::to_address(__first)), _M_extent(__count)
-       {
-         if constexpr (_Extent != dynamic_extent)
-           {
-             __glibcxx_assert(__count == _Extent);
-           }
-         __glibcxx_requires_valid_range(__first, __first + __count);
-       }
+       { __glibcxx_requires_valid_range(__first, __first + __count); }
 
       template<contiguous_iterator _It, sized_sentinel_for<_It> _End>
        requires __is_compatible_ref<iter_reference_t<_It>>::value
@@ -178,33 +191,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        noexcept(noexcept(__last - __first))
        : _M_ptr(std::to_address(__first)),
          _M_extent(static_cast<size_type>(__last - __first))
-       {
-         if constexpr (_Extent != dynamic_extent)
-           {
-             __glibcxx_assert((__last - __first) == _Extent);
-           }
-         __glibcxx_requires_valid_range(__first, __last);
-       }
+       { __glibcxx_requires_valid_range(__first, __last); }
 
       template<size_t _ArrayExtent>
        requires (_Extent == dynamic_extent || _ArrayExtent == _Extent)
        constexpr
        span(type_identity_t<element_type> (&__arr)[_ArrayExtent]) noexcept
-       : _M_ptr(__arr), _M_extent(_ArrayExtent)
+       : _M_ptr(__arr), _M_extent(__v<_ArrayExtent>)
        { }
 
       template<typename _Tp, size_t _ArrayExtent>
        requires __is_compatible_array<_Tp, _ArrayExtent>::value
        constexpr
        span(array<_Tp, _ArrayExtent>& __arr) noexcept
-       : _M_ptr(__arr.data()), _M_extent(_ArrayExtent)
+       : _M_ptr(__arr.data()), _M_extent(__v<_ArrayExtent>)
        { }
 
       template<typename _Tp, size_t _ArrayExtent>
        requires __is_compatible_array<const _Tp, _ArrayExtent>::value
        constexpr
        span(const array<_Tp, _ArrayExtent>& __arr) noexcept
-       : _M_ptr(__arr.data()), _M_extent(_ArrayExtent)
+       : _M_ptr(__arr.data()), _M_extent(__v<_ArrayExtent>)
        { }
 
       template<typename _Range>
@@ -219,12 +226,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        noexcept(noexcept(ranges::data(__range))
                  && noexcept(ranges::size(__range)))
        : _M_ptr(ranges::data(__range)), _M_extent(ranges::size(__range))
-       {
-         if constexpr (extent != dynamic_extent)
-           {
-             __glibcxx_assert(ranges::size(__range) == extent);
-           }
-       }
+       { }
 
       constexpr
       span(const span&) noexcept = default;
@@ -237,12 +239,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        explicit(extent != dynamic_extent && _OExtent == dynamic_extent)
        span(const span<_OType, _OExtent>& __s) noexcept
        : _M_ptr(__s.data()), _M_extent(__s.size())
-       {
-         if constexpr (extent != dynamic_extent)
-           {
-             __glibcxx_assert(__s.size() == extent);
-           }
-       }
+       { }
 
       ~span() noexcept = default;
 
@@ -365,7 +362,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          else
            static_assert(_Count <= extent);
          using _Sp = span<element_type, _Count>;
-         return _Sp{ this->data(), _Count };
+         return _Sp{ _SizedPtr{this->data()} };
        }
 
       [[nodiscard]]
@@ -386,7 +383,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          else
            static_assert(_Count <= extent);
          using _Sp = span<element_type, _Count>;
-         return _Sp{ this->data() + (this->size() - _Count), _Count };
+         return _Sp{ _SizedPtr{this->data() + (this->size() - _Count)} };
        }
 
       [[nodiscard]]
@@ -426,7 +423,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                  static_assert(_Count <= extent);
                  static_assert(_Count <= (extent - _Offset));
                }
-             return _Sp{ this->data() + _Offset, _Count };
+             return _Sp{ _SizedPtr{this->data() + _Offset} };
            }
        }
 
@@ -447,6 +444,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
     private:
+      template<typename, size_t> friend class span;
+
+      // Tag type for pointer that has known extent.
+      using _SizedPtr = __detail::__span_ptr<_Type>;
+
+      // Private constructor with an implied extent.
+      [[__gnu__::__always_inline__]]
+      constexpr explicit
+      span(_SizedPtr __ptr) noexcept
+      requires (extent != dynamic_extent)
+      : _M_ptr(__ptr._M_ptr), _M_extent(__v<extent>)
+      { }
+
       pointer _M_ptr;
       [[no_unique_address]] __detail::__extent_storage<extent> _M_extent;
     };

Reply via email to