From 6d2599e4f776d0cd88438cb82a00c4fc25cc3f67 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Wed, 13 May 2020 09:50:06 -0400 Subject: [PATCH] [libcxx][span] Implement P1976R2 This resolves the NB comment about the construction of a fixed-size span from a dynamic range. Differential Revision: https://reviews.llvm.org/D74577 --- libcxx/docs/FeatureTestMacroTable.rst | 2 + libcxx/include/span | 50 ++++++++++++++------- libcxx/include/version | 4 +- .../std/containers/views/span.cons/assign.pass.cpp | 33 +++++++------- .../containers/views/span.cons/container.fail.cpp | 14 ++++-- .../containers/views/span.cons/container.pass.cpp | 33 +++++++++++++- .../containers/views/span.cons/ptr_len.fail.cpp | 10 +++++ .../containers/views/span.cons/ptr_ptr.fail.cpp | 10 +++++ .../std/containers/views/span.cons/span.fail.cpp | 10 +++++ .../support.limits.general/span.version.pass.cpp | 52 ++++++++++++++++++++++ .../version.version.pass.cpp | 20 +++++++++ .../generate_feature_test_macro_components.py | 6 +++ 12 files changed, 206 insertions(+), 38 deletions(-) create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/span.version.pass.cpp diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 937683d..77c7c68 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -198,6 +198,8 @@ Status ------------------------------------------------- ----------------- ``__cpp_lib_ranges`` *unimplemented* ------------------------------------------------- ----------------- + ``__cpp_lib_span`` ``202002L`` + ------------------------------------------------- ----------------- ``__cpp_lib_three_way_comparison`` *unimplemented* ------------------------------------------------- ----------------- ``__cpp_lib_to_array`` ``201907L`` diff --git a/libcxx/include/span b/libcxx/include/span index 1fe1496..018bf51 100644 --- a/libcxx/include/span +++ b/libcxx/include/span @@ -53,8 +53,8 @@ public: // [span.cons], span constructors, copy, assignment, and destructor constexpr span() noexcept; - constexpr span(pointer ptr, size_type count); - constexpr span(pointer firstElem, pointer lastElem); + constexpr explicit(Extent != dynamic_extent) span(pointer ptr, size_type count); + constexpr explicit(Extent != dynamic_extent) span(pointer firstElem, pointer lastElem); template constexpr span(element_type (&arr)[N]) noexcept; template @@ -62,12 +62,12 @@ public: template constexpr span(const array& arr) noexcept; template - constexpr span(Container& cont); + constexpr explicit(Extent != dynamic_extent) span(Container& cont); template - constexpr span(const Container& cont); + constexpr explicit(Extent != dynamic_extent) span(const Container& cont); constexpr span(const span& other) noexcept = default; template - constexpr span(const span& s) noexcept; + constexpr explicit(Extent != dynamic_extent) span(const span& s) noexcept; ~span() noexcept = default; constexpr span& operator=(const span& other) noexcept = default; @@ -214,15 +214,31 @@ public: constexpr span (const span&) noexcept = default; constexpr span& operator=(const span&) noexcept = default; - _LIBCPP_INLINE_VISIBILITY constexpr span(pointer __ptr, size_type __count) : __data{__ptr} + _LIBCPP_INLINE_VISIBILITY constexpr explicit span(pointer __ptr, size_type __count) : __data{__ptr} { (void)__count; _LIBCPP_ASSERT(_Extent == __count, "size mismatch in span's constructor (ptr, len)"); } - _LIBCPP_INLINE_VISIBILITY constexpr span(pointer __f, pointer __l) : __data{__f} + _LIBCPP_INLINE_VISIBILITY constexpr explicit span(pointer __f, pointer __l) : __data{__f} { (void)__l; _LIBCPP_ASSERT(_Extent == distance(__f, __l), "size mismatch in span's constructor (ptr, ptr)"); } _LIBCPP_INLINE_VISIBILITY constexpr span(element_type (&__arr)[_Extent]) noexcept : __data{__arr} {} _LIBCPP_INLINE_VISIBILITY constexpr span( array& __arr) noexcept : __data{__arr.data()} {} _LIBCPP_INLINE_VISIBILITY constexpr span(const array& __arr) noexcept : __data{__arr.data()} {} + template + _LIBCPP_INLINE_VISIBILITY + constexpr explicit span( _Container& __c, + enable_if_t<__is_span_compatible_container<_Container, _Tp>::value, nullptr_t> = nullptr) + : __data{_VSTD::data(__c)} { + _LIBCPP_ASSERT(_Extent == _VSTD::size(__c), "size mismatch in span's constructor (range)"); + } + + template + _LIBCPP_INLINE_VISIBILITY + constexpr explicit span(const _Container& __c, + enable_if_t<__is_span_compatible_container::value, nullptr_t> = nullptr) + : __data{_VSTD::data(__c)} { + _LIBCPP_ASSERT(_Extent == _VSTD::size(__c), "size mismatch in span's constructor (range)"); + } + template _LIBCPP_INLINE_VISIBILITY constexpr span(const span<_OtherElementType, _Extent>& __other, @@ -233,7 +249,7 @@ public: template _LIBCPP_INLINE_VISIBILITY - constexpr span(const span<_OtherElementType, dynamic_extent>& __other, + constexpr explicit span(const span<_OtherElementType, dynamic_extent>& __other, enable_if_t< is_convertible_v<_OtherElementType(*)[], element_type (*)[]>, nullptr_t> = nullptr) noexcept @@ -247,7 +263,7 @@ public: constexpr span first() const noexcept { static_assert(_Count <= _Extent, "Count out of range in span::first()"); - return {data(), _Count}; + return span{data(), _Count}; } template @@ -255,7 +271,7 @@ public: constexpr span last() const noexcept { static_assert(_Count <= _Extent, "Count out of range in span::last()"); - return {data() + size() - _Count, _Count}; + return span{data() + size() - _Count, _Count}; } _LIBCPP_INLINE_VISIBILITY @@ -279,7 +295,9 @@ public: { static_assert(_Offset <= _Extent, "Offset out of range in span::subspan()"); static_assert(_Count == dynamic_extent || _Count <= _Extent - _Offset, "Offset + count out of range in span::subspan()"); - return {data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count}; + + using _ReturnType = span; + return _ReturnType{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count}; } @@ -337,10 +355,10 @@ public: } _LIBCPP_INLINE_VISIBILITY span __as_bytes() const noexcept - { return {reinterpret_cast(data()), size_bytes()}; } + { return span{reinterpret_cast(data()), size_bytes()}; } _LIBCPP_INLINE_VISIBILITY span __as_writable_bytes() const noexcept - { return {reinterpret_cast(data()), size_bytes()}; } + { return span{reinterpret_cast(data()), size_bytes()}; } private: pointer __data; @@ -418,7 +436,7 @@ public: constexpr span first() const noexcept { _LIBCPP_ASSERT(_Count <= size(), "Count out of range in span::first()"); - return {data(), _Count}; + return span{data(), _Count}; } template @@ -426,7 +444,7 @@ public: constexpr span last() const noexcept { _LIBCPP_ASSERT(_Count <= size(), "Count out of range in span::last()"); - return {data() + size() - _Count, _Count}; + return span{data() + size() - _Count, _Count}; } _LIBCPP_INLINE_VISIBILITY @@ -449,7 +467,7 @@ public: { _LIBCPP_ASSERT(_Offset <= size(), "Offset out of range in span::subspan()"); _LIBCPP_ASSERT(_Count == dynamic_extent || _Count <= size() - _Offset, "Offset + count out of range in span::subspan()"); - return {data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count}; + return span{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count}; } constexpr span diff --git a/libcxx/include/version b/libcxx/include/version index c2e99cc..46b0d65 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -98,6 +98,7 @@ __cpp_lib_shared_mutex 201505L __cpp_lib_shared_ptr_arrays 201611L __cpp_lib_shared_ptr_weak_type 201606L __cpp_lib_shared_timed_mutex 201402L +__cpp_lib_span 202002L __cpp_lib_string_udls 201304L __cpp_lib_string_view 201606L __cpp_lib_three_way_comparison 201711L @@ -234,8 +235,9 @@ __cpp_lib_void_t 201411L # endif # define __cpp_lib_list_remove_return_type 201806L // # define __cpp_lib_ranges 201811L -# define __cpp_lib_to_array 201907L +# define __cpp_lib_span 202002L // # define __cpp_lib_three_way_comparison 201711L +# define __cpp_lib_to_array 201907L #endif #endif // _LIBCPP_VERSIONH diff --git a/libcxx/test/std/containers/views/span.cons/assign.pass.cpp b/libcxx/test/std/containers/views/span.cons/assign.pass.cpp index cde1e16..e0973e7 100644 --- a/libcxx/test/std/containers/views/span.cons/assign.pass.cpp +++ b/libcxx/test/std/containers/views/span.cons/assign.pass.cpp @@ -185,13 +185,14 @@ int main(int, char**) // constexpr statically sized assignment { - constexpr std::span spans[] = { - {carr1, 2}, - {carr1 + 1, 2}, - {carr1 + 2, 2}, - {carr2, 2}, - {carr2 + 1, 2}, - {carr3, 2} + using spanType = std::span; + constexpr spanType spans[] = { + spanType{carr1, 2}, + spanType{carr1 + 1, 2}, + spanType{carr1 + 2, 2}, + spanType{carr2, 2}, + spanType{carr2 + 1, 2}, + spanType{carr3, 2} }; static_assert(std::size(spans) == 6, "" ); @@ -247,10 +248,11 @@ int main(int, char**) // statically sized assignment { - std::span spans[] = { - {arr, arr + 2}, - {arr + 1, arr + 3}, - {arr + 2, arr + 4} + using spanType = std::span; + spanType spans[] = { + spanType{arr, arr + 2}, + spanType{arr + 1, arr + 3}, + spanType{arr + 2, arr + 4} }; for (size_t i = 0; i < std::size(spans); ++i) @@ -279,10 +281,11 @@ int main(int, char**) } { - std::span spans[] = { - {strs, strs + 1}, - {strs + 1, strs + 2}, - {strs + 2, strs + 3} + using spanType = std::span; + spanType spans[] = { + spanType{strs, strs + 1}, + spanType{strs + 1, strs + 2}, + spanType{strs + 2, strs + 3} }; for (size_t i = 0; i < std::size(spans); ++i) diff --git a/libcxx/test/std/containers/views/span.cons/container.fail.cpp b/libcxx/test/std/containers/views/span.cons/container.fail.cpp index b0b753c..81abde9 100644 --- a/libcxx/test/std/containers/views/span.cons/container.fail.cpp +++ b/libcxx/test/std/containers/views/span.cons/container.fail.cpp @@ -63,6 +63,10 @@ private: const T *data() const {return nullptr;} }; +template +std::span createImplicitSpan(container c) { + return {c}; // expected-error {{chosen constructor is explicit in copy-initialization}} +} int main(int, char**) { @@ -106,12 +110,14 @@ int main(int, char**) std::span< volatile int> s7{cv}; // expected-error {{no matching constructor for initialization of 'std::span'}} } -// statically sized +// explicit constructor necessary { - IsAContainer c; - std::span s1{c}; // expected-error {{no matching constructor for initialization of 'std::span'}} - } + IsAContainer c; + const IsAContainer cc; + createImplicitSpan(c); + createImplicitSpan(cc); + } return 0; } diff --git a/libcxx/test/std/containers/views/span.cons/container.pass.cpp b/libcxx/test/std/containers/views/span.cons/container.pass.cpp index d4758c6..af28cce 100644 --- a/libcxx/test/std/containers/views/span.cons/container.pass.cpp +++ b/libcxx/test/std/containers/views/span.cons/container.pass.cpp @@ -84,14 +84,32 @@ constexpr bool testConstexprSpan() return s1.data() == val.getV() && s1.size() == 1; } +template +constexpr bool testConstexprSpanStatic() +{ + constexpr IsAContainer val{}; + std::span s1{val}; + return s1.data() == val.getV() && s1.size() == 1; +} template void testRuntimeSpan() { IsAContainer val{}; const IsAContainer cVal; - std::span s1{val}; - std::span s2{cVal}; + std::span s1{val}; + std::span s2{cVal}; + assert(s1.data() == val.getV() && s1.size() == 1); + assert(s2.data() == cVal.getV() && s2.size() == 1); +} + +template +void testRuntimeSpanStatic() +{ + IsAContainer val{}; + const IsAContainer cVal; + std::span s1{val}; + std::span s2{cVal}; assert(s1.data() == val.getV() && s1.size() == 1); assert(s2.data() == cVal.getV() && s2.size() == 1); } @@ -105,12 +123,23 @@ int main(int, char**) static_assert(testConstexprSpan(), ""); static_assert(testConstexprSpan(), ""); + static_assert(testConstexprSpanStatic(), ""); + static_assert(testConstexprSpanStatic(), ""); + static_assert(testConstexprSpanStatic(), ""); + static_assert(testConstexprSpanStatic(), ""); + testRuntimeSpan(); testRuntimeSpan(); testRuntimeSpan(); testRuntimeSpan(); testRuntimeSpan(); + testRuntimeSpanStatic(); + testRuntimeSpanStatic(); + testRuntimeSpanStatic(); + testRuntimeSpanStatic(); + testRuntimeSpanStatic(); + checkCV(); return 0; diff --git a/libcxx/test/std/containers/views/span.cons/ptr_len.fail.cpp b/libcxx/test/std/containers/views/span.cons/ptr_len.fail.cpp index 05dbcf8..8ec3771 100644 --- a/libcxx/test/std/containers/views/span.cons/ptr_len.fail.cpp +++ b/libcxx/test/std/containers/views/span.cons/ptr_len.fail.cpp @@ -27,6 +27,11 @@ const int carr[] = {4,5,6}; volatile int varr[] = {7,8,9}; const volatile int cvarr[] = {1,3,5}; +template +std::span createImplicitSpan(T* ptr, size_t len) { + return {ptr, len}; // expected-error {{chosen constructor is explicit in copy-initialization}} +} + int main(int, char**) { // We can't check that the size doesn't match - because that's a runtime property @@ -60,5 +65,10 @@ int main(int, char**) std::span< volatile int,3> s7{cvarr, 3}; // expected-error {{no matching constructor for initialization of 'std::span'}} } +// explicit constructor necessary + { + createImplicitSpan(arr, 1); + } + return 0; } diff --git a/libcxx/test/std/containers/views/span.cons/ptr_ptr.fail.cpp b/libcxx/test/std/containers/views/span.cons/ptr_ptr.fail.cpp index 9c15ea5..73ef480 100644 --- a/libcxx/test/std/containers/views/span.cons/ptr_ptr.fail.cpp +++ b/libcxx/test/std/containers/views/span.cons/ptr_ptr.fail.cpp @@ -27,6 +27,11 @@ const int carr[] = {4,5,6}; volatile int varr[] = {7,8,9}; const volatile int cvarr[] = {1,3,5}; +template +std::span createImplicitSpan(T* first, T* last) { + return {first, last}; // expected-error {{chosen constructor is explicit in copy-initialization}} +} + int main(int, char**) { // We can't check that the size doesn't match - because that's a runtime property @@ -60,5 +65,10 @@ int main(int, char**) std::span< volatile int,3> s7{cvarr, cvarr + 3}; // expected-error {{no matching constructor for initialization of 'std::span'}} } + // explicit constructor necessary + { + createImplicitSpan(arr, arr + 1); + } + return 0; } diff --git a/libcxx/test/std/containers/views/span.cons/span.fail.cpp b/libcxx/test/std/containers/views/span.cons/span.fail.cpp index c303719..03ff2e6 100644 --- a/libcxx/test/std/containers/views/span.cons/span.fail.cpp +++ b/libcxx/test/std/containers/views/span.cons/span.fail.cpp @@ -24,6 +24,11 @@ #include "test_macros.h" +template +std::span createImplicitSpan(std::span s) { + return {s}; // expected-error {{chosen constructor is explicit in copy-initialization}} +} + void checkCV () { // std::span< int> sp; @@ -101,5 +106,10 @@ int main(int, char**) checkCV(); + // explicit constructor necessary + { + createImplicitSpan(sp); + } + return 0; } diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/span.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/span.version.pass.cpp new file mode 100644 index 0000000..c1a9f86 --- /dev/null +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/span.version.pass.cpp @@ -0,0 +1,52 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// WARNING: This test was generated by generate_feature_test_macro_components.py +// and should not be edited manually. + +// + +// Test the feature test macros defined by + +/* Constant Value + __cpp_lib_span 202002L [C++2a] +*/ + +#include +#include "test_macros.h" + +#if TEST_STD_VER < 14 + +# ifdef __cpp_lib_span +# error "__cpp_lib_span should not be defined before c++2a" +# endif + +#elif TEST_STD_VER == 14 + +# ifdef __cpp_lib_span +# error "__cpp_lib_span should not be defined before c++2a" +# endif + +#elif TEST_STD_VER == 17 + +# ifdef __cpp_lib_span +# error "__cpp_lib_span should not be defined before c++2a" +# endif + +#elif TEST_STD_VER > 17 + +# ifndef __cpp_lib_span +# error "__cpp_lib_span should be defined in c++2a" +# endif +# if __cpp_lib_span != 202002L +# error "__cpp_lib_span should have the value 202002L in c++2a" +# endif + +#endif // TEST_STD_VER > 17 + +int main(int, char**) { return 0; } diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp index 081f0fe..34e59e0 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp @@ -85,6 +85,7 @@ __cpp_lib_shared_ptr_arrays 201611L [C++17] __cpp_lib_shared_ptr_weak_type 201606L [C++17] __cpp_lib_shared_timed_mutex 201402L [C++14] + __cpp_lib_span 202002L [C++2a] __cpp_lib_string_udls 201304L [C++14] __cpp_lib_string_view 201606L [C++17] __cpp_lib_three_way_comparison 201711L [C++2a] @@ -391,6 +392,10 @@ # error "__cpp_lib_shared_timed_mutex should not be defined before c++14" # endif +# ifdef __cpp_lib_span +# error "__cpp_lib_span should not be defined before c++2a" +# endif + # ifdef __cpp_lib_string_udls # error "__cpp_lib_string_udls should not be defined before c++14" # endif @@ -784,6 +789,10 @@ # endif # endif +# ifdef __cpp_lib_span +# error "__cpp_lib_span should not be defined before c++2a" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++14" # endif @@ -1381,6 +1390,10 @@ # endif # endif +# ifdef __cpp_lib_span +# error "__cpp_lib_span should not be defined before c++2a" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++17" # endif @@ -2116,6 +2129,13 @@ # endif # endif +# ifndef __cpp_lib_span +# error "__cpp_lib_span should be defined in c++2a" +# endif +# if __cpp_lib_span != 202002L +# error "__cpp_lib_span should have the value 202002L in c++2a" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++2a" # endif diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index 96860c2..d19e358 100755 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -591,6 +591,12 @@ feature_test_macros = sorted([ add_version_header(x) for x in [ }, "headers": ["array"], }, + {"name": "__cpp_lib_span", + "values": { + "c++2a": int(202002), + }, + "headers": ["span"], + }, ]], key=lambda tc: tc["name"]) def get_std_dialects(): -- 2.7.4