diff --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv --- a/libcxx/docs/Status/Cxx2bPapers.csv +++ b/libcxx/docs/Status/Cxx2bPapers.csv @@ -19,7 +19,7 @@ "`P1518R2 `__","LWG","Stop overconstraining allocators in container deduction guides","June 2021","|Complete|","13.0" "`P1659R3 `__","LWG","starts_with and ends_with","June 2021","","" "`P1951R1 `__","LWG","Default Arguments for pair Forwarding Constructor","June 2021","|Complete|","14.0" -"`P1989R2 `__","LWG","Range constructor for std::string_view","June 2021","","" +"`P1989R2 `__","LWG","Range constructor for std::string_view","June 2021","|Complete|","14.0" "`P2136R3 `__","LWG","invoke_r","June 2021","","" "`P2166R1 `__","LWG","A Proposal to Prohibit std::basic_string and std::basic_string_view construction from nullptr","June 2021","|Complete|","13.0" "","","","","","" diff --git a/libcxx/include/filesystem b/libcxx/include/filesystem --- a/libcxx/include/filesystem +++ b/libcxx/include/filesystem @@ -1033,7 +1033,7 @@ auto __p_root_name = __p.__root_name(); auto __p_root_name_size = __p_root_name.size(); if (__p.is_absolute() || - (!__p_root_name.empty() && __p_root_name != root_name())) { + (!__p_root_name.empty() && __p_root_name != __string_view(root_name().__pn_))) { __pn_ = __p.__pn_; return *this; } diff --git a/libcxx/include/string_view b/libcxx/include/string_view --- a/libcxx/include/string_view +++ b/libcxx/include/string_view @@ -87,6 +87,8 @@ constexpr basic_string_view(const charT* str, size_type len); template constexpr basic_string_view(It begin, End end); // C++20 + template + constexpr basic_string_view(Range&& r); // C++23 // 7.4, basic_string_view iterator support constexpr const_iterator begin() const noexcept; @@ -171,6 +173,8 @@ // basic_string_view deduction guides template basic_string_view(It, End) -> basic_string_view>; // C++20 + template + basic_string_view(Range&&) -> basic_string_view>; // C++23 // 7.11, Hash support template struct hash; @@ -193,8 +197,11 @@ #include <__config> #include <__debug> +#include <__ranges/concepts.h> +#include <__ranges/data.h> #include <__ranges/enable_borrowed_range.h> #include <__ranges/enable_view.h> +#include <__ranges/size.h> #include <__string> #include #include @@ -202,6 +209,7 @@ #include #include #include +#include #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -288,6 +296,25 @@ } #endif +#if _LIBCPP_STD_VER > 20 && !defined(_LIBCPP_HAS_NO_RANGES) + template + requires ( + !is_same_v, basic_string_view> && + ranges::contiguous_range<_Range> && + ranges::sized_range<_Range> && + is_same_v, _CharT> && + !is_convertible_v<_Range, const _CharT*> && + (!requires(remove_cvref_t<_Range>& d) { + d.operator _VSTD::basic_string_view<_CharT, _Traits>(); + }) && + (!requires { + typename remove_reference_t<_Range>::traits_type; + } || is_same_v::traits_type, _Traits>) + ) + constexpr _LIBCPP_HIDE_FROM_ABI + basic_string_view(_Range&& __r) : __data(ranges::data(__r)), __size(ranges::size(__r)) {} +#endif + _LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY basic_string_view(const _CharT* __s) : __data(__s), __size(_VSTD::__char_traits_length_checked<_Traits>(__s)) {} @@ -695,6 +722,12 @@ basic_string_view(_It, _End) -> basic_string_view>; #endif + +#if _LIBCPP_STD_VER > 20 && !defined(_LIBCPP_HAS_NO_RANGES) +template + basic_string_view(_Range) -> basic_string_view>; +#endif + // [string.view.comparison] // operator == template @@ -706,7 +739,9 @@ return __lhs.compare(__rhs) == 0; } -template +// The dummy default template parameters are used to work around a MSVC issue with mangling, see VSO-409326 for details. +// This applies to the other sufficient overloads below for the other comparison operators. +template _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY bool operator==(basic_string_view<_CharT, _Traits> __lhs, typename common_type >::type __rhs) _NOEXCEPT @@ -715,7 +750,7 @@ return __lhs.compare(__rhs) == 0; } -template +template _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY bool operator==(typename common_type >::type __lhs, basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT @@ -735,7 +770,7 @@ return __lhs.compare(__rhs) != 0; } -template +template _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY bool operator!=(basic_string_view<_CharT, _Traits> __lhs, typename common_type >::type __rhs) _NOEXCEPT @@ -745,7 +780,7 @@ return __lhs.compare(__rhs) != 0; } -template +template _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY bool operator!=(typename common_type >::type __lhs, basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT @@ -764,7 +799,7 @@ return __lhs.compare(__rhs) < 0; } -template +template _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY bool operator<(basic_string_view<_CharT, _Traits> __lhs, typename common_type >::type __rhs) _NOEXCEPT @@ -772,7 +807,7 @@ return __lhs.compare(__rhs) < 0; } -template +template _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY bool operator<(typename common_type >::type __lhs, basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT @@ -789,7 +824,7 @@ return __lhs.compare(__rhs) > 0; } -template +template _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY bool operator>(basic_string_view<_CharT, _Traits> __lhs, typename common_type >::type __rhs) _NOEXCEPT @@ -797,7 +832,7 @@ return __lhs.compare(__rhs) > 0; } -template +template _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY bool operator>(typename common_type >::type __lhs, basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT @@ -814,7 +849,7 @@ return __lhs.compare(__rhs) <= 0; } -template +template _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY bool operator<=(basic_string_view<_CharT, _Traits> __lhs, typename common_type >::type __rhs) _NOEXCEPT @@ -822,7 +857,7 @@ return __lhs.compare(__rhs) <= 0; } -template +template _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY bool operator<=(typename common_type >::type __lhs, basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT @@ -840,7 +875,7 @@ } -template +template _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY bool operator>=(basic_string_view<_CharT, _Traits> __lhs, typename common_type >::type __rhs) _NOEXCEPT @@ -848,7 +883,7 @@ return __lhs.compare(__rhs) >= 0; } -template +template _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY bool operator>=(typename common_type >::type __lhs, basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT diff --git a/libcxx/test/std/strings/string.view/string.view.cons/from_range.pass.cpp b/libcxx/test/std/strings/string.view/string.view.cons/from_range.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/string.view/string.view.cons/from_range.pass.cpp @@ -0,0 +1,199 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// + +// template +// constexpr basic_string_view(Range&& range); + +#include +#include +#include +#include +#include +#include +#include + +#include "constexpr_char_traits.h" +#include "make_string.h" +#include "test_iterators.h" +#include "test_range.h" + +template +constexpr void test() { + auto data = MAKE_STRING_VIEW(CharT, "test"); + std::array arr; + for(int i = 0; i < 4; ++i) { + arr[i] = data[i]; + } + auto sv = std::basic_string_view(arr); + + ASSERT_SAME_TYPE(decltype(sv), std::basic_string_view); + assert(sv.size() == arr.size()); + assert(sv.data() == arr.data()); +} + +constexpr bool test() { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + test(); + test(); + test(); + + { + struct NonConstConversionOperator { + const char* data_ = "test"; + constexpr const char* begin() const { return data_; } + constexpr const char* end() const { return data_ + 4; } + constexpr operator std::basic_string_view() { return "NonConstConversionOp"; } + }; + + NonConstConversionOperator nc; + std::string_view sv = nc; + assert(sv == "NonConstConversionOp"); + static_assert(!std::is_constructible_v); // conversion operator is non-const + } + + { + struct ConstConversionOperator { + const char* data_ = "test"; + constexpr const char* begin() const { return data_; } + constexpr const char* end() const { return data_ + 4; } + constexpr operator std::basic_string_view() const { return "ConstConversionOp"; } + }; + ConstConversionOperator cv; + std::basic_string_view sv = cv; + assert(sv == "ConstConversionOp"); + } + + struct DeletedConversionOperator { + const char* data_ = "test"; + constexpr const char* begin() const { return data_; } + constexpr const char* end() const { return data_ + 4; } + operator std::basic_string_view() = delete; + }; + + struct DeletedConstConversionOperator { + const char* data_ = "test"; + constexpr const char* begin() const { return data_; } + constexpr const char* end() const { return data_ + 4; } + operator std::basic_string_view() const = delete; + }; + + static_assert(std::is_constructible_v); + static_assert(std::is_constructible_v); + static_assert(std::is_constructible_v); + static_assert(std::is_constructible_v); + + // Test that we're not trying to use the type's conversion operator to string_view in the constructor. + { + const DeletedConversionOperator d; + std::basic_string_view csv = d; + assert(csv == "test"); + } + + { + DeletedConstConversionOperator dc; + std::basic_string_view sv = dc; + assert(sv == "test"); + } + + return true; +} + +static_assert(std::is_constructible_v&>); +static_assert(std::is_constructible_v&>); +static_assert(std::is_constructible_v&&>); +static_assert(std::is_constructible_v&&>); + +using SizedButNotContiguousRange = std::ranges::subrange>; +static_assert(!std::ranges::contiguous_range); +static_assert(std::ranges::sized_range); +static_assert(!std::is_constructible_v); + +using ContiguousButNotSizedRange = std::ranges::subrange, sentinel_wrapper, std::ranges::subrange_kind::unsized>; +static_assert(std::ranges::contiguous_range); +static_assert(!std::ranges::sized_range); +static_assert(!std::is_constructible_v); + +static_assert(!std::is_constructible_v>); // different CharT + +struct WithStringViewConversionOperator { + char* begin() const; + char* end() const; + operator std::string_view() const { return {}; } +}; + +static_assert(std::is_constructible_v); // lvalue +static_assert(std::is_constructible_v); // const lvalue +static_assert(std::is_constructible_v); // rvalue + +template +struct WithTraitsType { + typename CharTraits::char_type* begin() const; + typename CharTraits::char_type* end() const; + using traits_type = CharTraits; +}; + +using CCT = constexpr_char_traits; +static_assert(std::is_constructible_v>>); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS +static_assert(std::is_constructible_v>>); +#endif +static_assert(std::is_constructible_v, WithTraitsType>); +static_assert(!std::is_constructible_v>); // wrong traits type +#ifndef TEST_HAS_NO_WIDE_CHARACTERS +static_assert(!std::is_constructible_v>>); // wrong traits type +#endif + +#ifndef TEST_HAS_NO_EXCEPTIONS +void test_throwing() { + struct ThrowingData { + char* begin() const { return nullptr; } + char* end() const { return nullptr; } + char* data() const { throw 42; return nullptr; } + }; + try { + ThrowingData x; + (void) std::string_view(x); + assert(false); + } catch (int i) { + assert(i == 42); + } + + struct ThrowingSize { + char* begin() const { return nullptr; } + char* end() const { return nullptr; } + size_t size() const { throw 42; return 0; } + }; + try { + ThrowingSize x; + (void) std::string_view(x); + assert(false); + } catch (int i) { + assert(i == 42); + } +} +#endif + +int main(int, char**) { + test(); + static_assert(test()); +#ifndef TEST_HAS_NO_EXCEPTIONS + test_throwing(); +#endif + + return 0; +} + diff --git a/libcxx/test/std/strings/string.view/string.view.cons/deduct.pass.cpp b/libcxx/test/std/strings/string.view/string.view.deduct/iterator_sentinel.pass.cpp rename from libcxx/test/std/strings/string.view/string.view.cons/deduct.pass.cpp rename to libcxx/test/std/strings/string.view/string.view.deduct/iterator_sentinel.pass.cpp diff --git a/libcxx/test/std/strings/string.view/string.view.deduct/range.pass.cpp b/libcxx/test/std/strings/string.view/string.view.deduct/range.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/string.view/string.view.deduct/range.pass.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-no-concepts + +// + +// template +// basic_string_view(Range&&) -> basic_string_view>; // C++23 + +#include +#include + +#include "make_string.h" +#include "test_iterators.h" +#include "test_macros.h" + +template +void test() { + auto val = MAKE_STRING(CharT, "test"); + auto sv = std::basic_string_view(val); + ASSERT_SAME_TYPE(decltype(sv), std::basic_string_view); + assert(sv.size() == val.size()); + assert(sv.data() == val.data()); +} + +void test() { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + test(); + test(); + test(); + test(); + + struct Widget { + const char16_t *data_ = u"foo"; + contiguous_iterator begin() const { return contiguous_iterator(data_); } + contiguous_iterator end() const { return contiguous_iterator(data_ + 3); } + }; + std::basic_string_view bsv = Widget(); + ASSERT_SAME_TYPE(decltype(bsv), std::basic_string_view); +} + +int main(int, char**) { + test(); + + return 0; +} +