[libc++] Implement P1989R2: range constructor for string_view
authorJoe Loser <joeloser93@gmail.com>
Wed, 3 Nov 2021 22:45:04 +0000 (18:45 -0400)
committerJoe Loser <joeloser93@gmail.com>
Thu, 2 Dec 2021 04:16:36 +0000 (23:16 -0500)
Implement P1989R2 which adds a range constructor for `string_view`.

Adjust `operator/=` in `path` to avoid atomic constraints caching issue
getting provoked from this PR.

Add defaulted template argument to `string_view`'s "sufficient
overloads" to avoid mangling issues in `clang-cl` builds. It is a
MSVC mangling bug that this works around.

Differential Revision: https://reviews.llvm.org/D113161

libcxx/docs/Status/Cxx2bPapers.csv
libcxx/include/filesystem
libcxx/include/string_view
libcxx/test/std/strings/string.view/string.view.cons/from_range.pass.cpp [new file with mode: 0644]
libcxx/test/std/strings/string.view/string.view.deduct/iterator_sentinel.pass.cpp [moved from libcxx/test/std/strings/string.view/string.view.cons/deduct.pass.cpp with 100% similarity]
libcxx/test/std/strings/string.view/string.view.deduct/range.pass.cpp [new file with mode: 0644]

index ae756c3..8bb2a72 100644 (file)
@@ -19,7 +19,7 @@
 "`P1518R2 <https://wg21.link/P1518R2>`__","LWG","Stop overconstraining allocators in container deduction guides","June 2021","|Complete|","13.0"
 "`P1659R3 <https://wg21.link/P1659R3>`__","LWG","starts_with and ends_with","June 2021","",""
 "`P1951R1 <https://wg21.link/P1951R1>`__","LWG","Default Arguments for pair Forwarding Constructor","June 2021","|Complete|","14.0"
-"`P1989R2 <https://wg21.link/P1989R2>`__","LWG","Range constructor for std::string_view","June 2021","",""
+"`P1989R2 <https://wg21.link/P1989R2>`__","LWG","Range constructor for std::string_view","June 2021","|Complete|","14.0"
 "`P2136R3 <https://wg21.link/P2136R3>`__","LWG","invoke_r","June 2021","",""
 "`P2166R1 <https://wg21.link/P2166R1>`__","LWG","A Proposal to Prohibit std::basic_string and std::basic_string_view construction from nullptr","June 2021","|Complete|","13.0"
 "","","","","",""
index c688ab7..39e8ca2 100644 (file)
@@ -1033,7 +1033,7 @@ public:
     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;
     }
index 373b394..a5f85e8 100644 (file)
@@ -87,6 +87,8 @@ namespace std {
       constexpr basic_string_view(const charT* str, size_type len);
       template <class It, class End>
       constexpr basic_string_view(It begin, End end); // C++20
+      template <class Range>
+      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 @@ namespace std {
   // basic_string_view deduction guides
   template<class It, class End>
     basic_string_view(It, End) -> basic_string_view<iter_value_t<It>>; // C++20
+  template<class Range>
+    basic_string_view(Range&&) -> basic_string_view<ranges::range_value_t<Range>>; // C++23
 
   // 7.11, Hash support
   template <class T> struct hash;
@@ -193,8 +197,11 @@ namespace std {
 
 #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 <algorithm>
 #include <compare>
@@ -202,6 +209,7 @@ namespace std {
 #include <iterator>
 #include <limits>
 #include <stdexcept>
+#include <type_traits>
 #include <version>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -288,6 +296,25 @@ public:
     }
 #endif
 
+#if _LIBCPP_STD_VER > 20 && !defined(_LIBCPP_HAS_NO_RANGES)
+    template <class _Range>
+      requires (
+        !is_same_v<remove_cvref_t<_Range>, basic_string_view> &&
+        ranges::contiguous_range<_Range> &&
+        ranges::sized_range<_Range> &&
+        is_same_v<ranges::range_value_t<_Range>, _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<typename remove_reference_t<_Range>::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 @@ template <contiguous_iterator _It, sized_sentinel_for<_It> _End>
   basic_string_view(_It, _End) -> basic_string_view<iter_value_t<_It>>;
 #endif
 
+
+#if _LIBCPP_STD_VER > 20 && !defined(_LIBCPP_HAS_NO_RANGES)
+template <ranges::contiguous_range _Range>
+  basic_string_view(_Range) -> basic_string_view<ranges::range_value_t<_Range>>;
+#endif
+
 // [string.view.comparison]
 // operator ==
 template<class _CharT, class _Traits>
@@ -706,7 +739,9 @@ bool operator==(basic_string_view<_CharT, _Traits> __lhs,
     return __lhs.compare(__rhs) == 0;
 }
 
-template<class _CharT, class _Traits>
+// 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<class _CharT, class _Traits, int = 1>
 _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
 bool operator==(basic_string_view<_CharT, _Traits> __lhs,
                 typename common_type<basic_string_view<_CharT, _Traits> >::type __rhs) _NOEXCEPT
@@ -715,7 +750,7 @@ bool operator==(basic_string_view<_CharT, _Traits> __lhs,
     return __lhs.compare(__rhs) == 0;
 }
 
-template<class _CharT, class _Traits>
+template<class _CharT, class _Traits, int = 2>
 _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
 bool operator==(typename common_type<basic_string_view<_CharT, _Traits> >::type __lhs,
                 basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT
@@ -735,7 +770,7 @@ bool operator!=(basic_string_view<_CharT, _Traits> __lhs, basic_string_view<_Cha
     return __lhs.compare(__rhs) != 0;
 }
 
-template<class _CharT, class _Traits>
+template<class _CharT, class _Traits, int = 1>
 _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
 bool operator!=(basic_string_view<_CharT, _Traits> __lhs,
                 typename common_type<basic_string_view<_CharT, _Traits> >::type __rhs) _NOEXCEPT
@@ -745,7 +780,7 @@ bool operator!=(basic_string_view<_CharT, _Traits> __lhs,
     return __lhs.compare(__rhs) != 0;
 }
 
-template<class _CharT, class _Traits>
+template<class _CharT, class _Traits, int = 2>
 _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
 bool operator!=(typename common_type<basic_string_view<_CharT, _Traits> >::type __lhs,
                 basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT
@@ -764,7 +799,7 @@ bool operator<(basic_string_view<_CharT, _Traits> __lhs, basic_string_view<_Char
     return __lhs.compare(__rhs) < 0;
 }
 
-template<class _CharT, class _Traits>
+template<class _CharT, class _Traits, int = 1>
 _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
 bool operator<(basic_string_view<_CharT, _Traits> __lhs,
                 typename common_type<basic_string_view<_CharT, _Traits> >::type __rhs) _NOEXCEPT
@@ -772,7 +807,7 @@ bool operator<(basic_string_view<_CharT, _Traits> __lhs,
     return __lhs.compare(__rhs) < 0;
 }
 
-template<class _CharT, class _Traits>
+template<class _CharT, class _Traits, int = 2>
 _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
 bool operator<(typename common_type<basic_string_view<_CharT, _Traits> >::type __lhs,
                 basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT
@@ -789,7 +824,7 @@ bool operator> (basic_string_view<_CharT, _Traits> __lhs, basic_string_view<_Cha
     return __lhs.compare(__rhs) > 0;
 }
 
-template<class _CharT, class _Traits>
+template<class _CharT, class _Traits, int = 1>
 _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
 bool operator>(basic_string_view<_CharT, _Traits> __lhs,
                 typename common_type<basic_string_view<_CharT, _Traits> >::type __rhs) _NOEXCEPT
@@ -797,7 +832,7 @@ bool operator>(basic_string_view<_CharT, _Traits> __lhs,
     return __lhs.compare(__rhs) > 0;
 }
 
-template<class _CharT, class _Traits>
+template<class _CharT, class _Traits, int = 2>
 _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
 bool operator>(typename common_type<basic_string_view<_CharT, _Traits> >::type __lhs,
                 basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT
@@ -814,7 +849,7 @@ bool operator<=(basic_string_view<_CharT, _Traits> __lhs, basic_string_view<_Cha
     return __lhs.compare(__rhs) <= 0;
 }
 
-template<class _CharT, class _Traits>
+template<class _CharT, class _Traits, int = 1>
 _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
 bool operator<=(basic_string_view<_CharT, _Traits> __lhs,
                 typename common_type<basic_string_view<_CharT, _Traits> >::type __rhs) _NOEXCEPT
@@ -822,7 +857,7 @@ bool operator<=(basic_string_view<_CharT, _Traits> __lhs,
     return __lhs.compare(__rhs) <= 0;
 }
 
-template<class _CharT, class _Traits>
+template<class _CharT, class _Traits, int = 2>
 _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
 bool operator<=(typename common_type<basic_string_view<_CharT, _Traits> >::type __lhs,
                 basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT
@@ -840,7 +875,7 @@ bool operator>=(basic_string_view<_CharT, _Traits> __lhs, basic_string_view<_Cha
 }
 
 
-template<class _CharT, class _Traits>
+template<class _CharT, class _Traits, int = 1>
 _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
 bool operator>=(basic_string_view<_CharT, _Traits> __lhs,
                 typename common_type<basic_string_view<_CharT, _Traits> >::type __rhs) _NOEXCEPT
@@ -848,7 +883,7 @@ bool operator>=(basic_string_view<_CharT, _Traits> __lhs,
     return __lhs.compare(__rhs) >= 0;
 }
 
-template<class _CharT, class _Traits>
+template<class _CharT, class _Traits, int = 2>
 _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
 bool operator>=(typename common_type<basic_string_view<_CharT, _Traits> >::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 (file)
index 0000000..e342e25
--- /dev/null
@@ -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
+
+// <string_view>
+
+//  template <class Range>
+//  constexpr basic_string_view(Range&& range);
+
+#include <string_view>
+#include <array>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <type_traits>
+#include <vector>
+
+#include "constexpr_char_traits.h"
+#include "make_string.h"
+#include "test_iterators.h"
+#include "test_range.h"
+
+template<class CharT>
+constexpr void test() {
+  auto data = MAKE_STRING_VIEW(CharT, "test");
+  std::array<CharT, 4> arr;
+  for(int i = 0; i < 4; ++i) {
+    arr[i] = data[i];
+  }
+  auto sv = std::basic_string_view<CharT>(arr);
+
+  ASSERT_SAME_TYPE(decltype(sv), std::basic_string_view<CharT>);
+  assert(sv.size() == arr.size());
+  assert(sv.data() == arr.data());
+}
+
+constexpr bool test() {
+  test<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>();
+#endif
+  test<char8_t>();
+  test<char16_t>();
+  test<char32_t>();
+
+  {
+    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<char>() { return "NonConstConversionOp"; }
+    };
+
+    NonConstConversionOperator nc;
+    std::string_view sv = nc;
+    assert(sv == "NonConstConversionOp");
+    static_assert(!std::is_constructible_v<std::string_view,
+                                           const NonConstConversionOperator&>); // 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<char>() const { return "ConstConversionOp"; }
+    };
+    ConstConversionOperator cv;
+    std::basic_string_view<char> 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<char>() = 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<char>() const = delete;
+  };
+
+  static_assert(std::is_constructible_v<std::string_view, DeletedConversionOperator>);
+  static_assert(std::is_constructible_v<std::string_view, const DeletedConversionOperator>);
+  static_assert(std::is_constructible_v<std::string_view, DeletedConstConversionOperator>);
+  static_assert(std::is_constructible_v<std::string_view, const DeletedConstConversionOperator>);
+
+  // 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<char> csv = d;
+    assert(csv == "test");
+  }
+
+  {
+    DeletedConstConversionOperator dc;
+    std::basic_string_view<char> sv = dc;
+    assert(sv == "test");
+  }
+
+  return true;
+}
+
+static_assert(std::is_constructible_v<std::string_view, std::vector<char>&>);
+static_assert(std::is_constructible_v<std::string_view, const std::vector<char>&>);
+static_assert(std::is_constructible_v<std::string_view, std::vector<char>&&>);
+static_assert(std::is_constructible_v<std::string_view, const std::vector<char>&&>);
+
+using SizedButNotContiguousRange = std::ranges::subrange<random_access_iterator<char*>>;
+static_assert(!std::ranges::contiguous_range<SizedButNotContiguousRange>);
+static_assert(std::ranges::sized_range<SizedButNotContiguousRange>);
+static_assert(!std::is_constructible_v<std::string_view, SizedButNotContiguousRange>);
+
+using ContiguousButNotSizedRange = std::ranges::subrange<contiguous_iterator<char*>, sentinel_wrapper<char*>, std::ranges::subrange_kind::unsized>;
+static_assert(std::ranges::contiguous_range<ContiguousButNotSizedRange>);
+static_assert(!std::ranges::sized_range<ContiguousButNotSizedRange>);
+static_assert(!std::is_constructible_v<std::string_view, ContiguousButNotSizedRange>);
+
+static_assert(!std::is_constructible_v<std::string_view, std::vector<char16_t>>); // different CharT
+
+struct WithStringViewConversionOperator {
+  char* begin() const;
+  char* end() const;
+  operator std::string_view() const { return {}; }
+};
+
+static_assert(std::is_constructible_v<std::string_view, WithStringViewConversionOperator>); // lvalue
+static_assert(std::is_constructible_v<std::string_view, const WithStringViewConversionOperator&>); // const lvalue
+static_assert(std::is_constructible_v<std::string_view, WithStringViewConversionOperator&&>); // rvalue
+
+template <class CharTraits>
+struct WithTraitsType {
+  typename CharTraits::char_type* begin() const;
+  typename CharTraits::char_type* end() const;
+  using traits_type = CharTraits;
+};
+
+using CCT = constexpr_char_traits<char>;
+static_assert(std::is_constructible_v<std::string_view, WithTraitsType<std::char_traits<char>>>);
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(std::is_constructible_v<std::wstring_view, WithTraitsType<std::char_traits<wchar_t>>>);
+#endif
+static_assert(std::is_constructible_v<std::basic_string_view<char, CCT>, WithTraitsType<CCT>>);
+static_assert(!std::is_constructible_v<std::string_view, WithTraitsType<CCT>>);  // wrong traits type
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::is_constructible_v<std::wstring_view, WithTraitsType<std::char_traits<char>>>);  // 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.deduct/range.pass.cpp b/libcxx/test/std/strings/string.view/string.view.deduct/range.pass.cpp
new file mode 100644 (file)
index 0000000..d8858db
--- /dev/null
@@ -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
+
+// <string_view>
+
+//  template<class Range>
+//    basic_string_view(Range&&) -> basic_string_view<ranges::range_value_t<Range>>; // C++23
+
+#include <string_view>
+#include <cassert>
+
+#include "make_string.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+
+template<class CharT>
+void test() {
+  auto val = MAKE_STRING(CharT, "test");
+  auto sv = std::basic_string_view(val);
+  ASSERT_SAME_TYPE(decltype(sv), std::basic_string_view<CharT>);
+  assert(sv.size() == val.size());
+  assert(sv.data() == val.data());
+}
+
+void test() {
+  test<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>();
+#endif
+  test<char8_t>();
+  test<char16_t>();
+  test<char32_t>();
+  test<char>();
+
+  struct Widget {
+    const char16_t *data_ = u"foo";
+    contiguous_iterator<const char16_t*> begin() const { return contiguous_iterator<const char16_t*>(data_); }
+    contiguous_iterator<const char16_t*> end() const { return contiguous_iterator<const char16_t*>(data_ + 3); }
+  };
+  std::basic_string_view bsv = Widget();
+  ASSERT_SAME_TYPE(decltype(bsv), std::basic_string_view<char16_t>);
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}
+