diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 358889d8dbc37..3c7175c73f21e 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -506,6 +506,8 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_sstream_from_string_view`` ``202306L`` ---------------------------------------------------------- ----------------- + ``__cpp_lib_string_subview`` ``202506L`` + ---------------------------------------------------------- ----------------- ``__cpp_lib_string_view`` ``202403L`` ---------------------------------------------------------- ----------------- ``__cpp_lib_submdspan`` *unimplemented* diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index 509ead64ee525..b5dcfc7dfef31 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -40,6 +40,7 @@ Implemented Papers - P2321R2: ``zip`` (`Github `__) (The paper is partially implemented. ``zip_transform_view`` is implemented in this release) +- P3044R2: sub-``string_view`` from ``string`` (`Github `__) - P3168R2: Give ``std::optional`` Range Support (`Github `__) Improvements and New Features diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index f873d16808afe..9e1678f22c4be 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -129,7 +129,7 @@ "`P3179R9 `__","Parallel Range Algorithms","2025-06 (Sofia)","","","`#148137 `__","" "`P3709R2 `__","Reconsider parallel ``ranges::rotate_copy`` and ``ranges::reverse_copy``","2025-06 (Sofia)","","","`#148138 `__","" "`P3641R0 `__","Rename ``std::observable`` to ``std::observable_checkpoint``, and add a feature-test macro","2025-06 (Sofia)","","","`#148139 `__","" -"`P3044R2 `__","sub-``string_view`` from ``string``","2025-06 (Sofia)","","","`#148140 `__","" +"`P3044R2 `__","sub-``string_view`` from ``string``","2025-06 (Sofia)","|Complete|","22","`#148140 `__","" "`P2876R3 `__","Proposal to extend ``std::simd`` with more constructors and accessors","2025-06 (Sofia)","","","`#148143 `__","" "`P3480R6 `__","``std::simd`` is a range","2025-06 (Sofia)","","","`#148144 `__","" "`P2664R11 `__","Extend ``std::simd`` with permutation API","2025-06 (Sofia)","","","`#148145 `__","" diff --git a/libcxx/include/string b/libcxx/include/string index bbd7b98f112a6..1ad1b389c505e 100644 --- a/libcxx/include/string +++ b/libcxx/include/string @@ -280,6 +280,8 @@ public: basic_string substr(size_type pos = 0, size_type n = npos) const; // constexpr in C++20, removed in C++23 basic_string substr(size_type pos = 0, size_type n = npos) const&; // since C++23 constexpr basic_string substr(size_type pos = 0, size_type n = npos) &&; // since C++23 + constexpr basic_string_view subview(size_type pos = 0, + size_type n = npos) const; // since C++26 void swap(basic_string& str) noexcept(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value); // C++17, constexpr since C++20 @@ -1762,6 +1764,11 @@ public: return basic_string(std::move(*this), __pos, __n); } # endif +# if _LIBCPP_STD_VER >= 26 + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __self_view subview(size_type __pos = 0, size_type __n = npos) const { + return __self_view(*this).subview(__pos, __n); + } +# endif _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(basic_string& __str) # if _LIBCPP_STD_VER >= 14 diff --git a/libcxx/include/string_view b/libcxx/include/string_view index 983e5852015e0..5ecaa3de7deba 100644 --- a/libcxx/include/string_view +++ b/libcxx/include/string_view @@ -130,6 +130,8 @@ namespace std { size_type copy(charT* s, size_type n, size_type pos = 0) const; // constexpr in C++20 constexpr basic_string_view substr(size_type pos = 0, size_type n = npos) const; + constexpr basic_string_view subview(size_type pos = 0, + size_type n = npos) const; // freestanding-deleted, since C++26 constexpr int compare(basic_string_view s) const noexcept; constexpr int compare(size_type pos1, size_type n1, basic_string_view s) const; constexpr int compare(size_type pos1, size_type n1, @@ -465,6 +467,13 @@ public: : basic_string_view(__assume_valid(), data() + __pos, std::min(__n, size() - __pos)); } +# if _LIBCPP_STD_VER >= 26 + [[nodiscard]] + _LIBCPP_HIDE_FROM_ABI constexpr basic_string_view subview(size_type __pos = 0, size_type __n = npos) const { + return substr(__pos, __n); + } +# endif + _LIBCPP_CONSTEXPR_SINCE_CXX14 int compare(basic_string_view __sv) const _NOEXCEPT { size_type __rlen = std::min(size(), __sv.size()); int __retval = _Traits::compare(data(), __sv.data(), __rlen); diff --git a/libcxx/include/version b/libcxx/include/version index 16917a3bd9ddd..a132f0808aeb8 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -245,6 +245,7 @@ __cpp_lib_starts_ends_with 201711L __cpp_lib_string_contains 202011L __cpp_lib_string_resize_and_overwrite 202110L +__cpp_lib_string_subview 202506L __cpp_lib_string_udls 201304L __cpp_lib_string_view 202403L 201803L // C++20 @@ -599,6 +600,7 @@ __cpp_lib_void_t 201411L # define __cpp_lib_span_at 202311L # define __cpp_lib_span_initializer_list 202311L # define __cpp_lib_sstream_from_string_view 202306L +# define __cpp_lib_string_subview 202506L # undef __cpp_lib_string_view # define __cpp_lib_string_view 202403L // # define __cpp_lib_submdspan 202306L diff --git a/libcxx/test/libcxx/diagnostics/string.nodiscard.verify.cpp b/libcxx/test/libcxx/diagnostics/string.nodiscard.verify.cpp index 746ae633fff44..d421eaf5cb8f1 100644 --- a/libcxx/test/libcxx/diagnostics/string.nodiscard.verify.cpp +++ b/libcxx/test/libcxx/diagnostics/string.nodiscard.verify.cpp @@ -12,7 +12,12 @@ #include +#include "test_macros.h" + void test() { std::string string; string.empty(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +#if TEST_STD_VER >= 26 + string.subview(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +#endif } diff --git a/libcxx/test/libcxx/diagnostics/string_view.nodiscard.verify.cpp b/libcxx/test/libcxx/diagnostics/string_view.nodiscard.verify.cpp index b5548bc34fddc..e5b2258315fe4 100644 --- a/libcxx/test/libcxx/diagnostics/string_view.nodiscard.verify.cpp +++ b/libcxx/test/libcxx/diagnostics/string_view.nodiscard.verify.cpp @@ -12,7 +12,12 @@ #include +#include "test_macros.h" + void test() { std::string_view string_view; string_view.empty(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +#if TEST_STD_VER >= 26 + string_view.subview(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +#endif } diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp index 7236d5d7f2aca..147854eead2cc 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp @@ -60,6 +60,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifdef __cpp_lib_string_udls # error "__cpp_lib_string_udls should not be defined before c++14" # endif @@ -114,6 +118,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++14" # endif @@ -177,6 +185,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++17" # endif @@ -261,6 +273,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++20" # endif @@ -354,6 +370,10 @@ # error "__cpp_lib_string_resize_and_overwrite should have the value 202110L in c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++23" # endif @@ -456,6 +476,13 @@ # error "__cpp_lib_string_resize_and_overwrite should have the value 202110L in c++26" # endif +# ifndef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should be defined in c++26" +# endif +# if __cpp_lib_string_subview != 202506L +# error "__cpp_lib_string_subview should have the value 202506L in c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++26" # endif diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/string_view.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/string_view.version.compile.pass.cpp index c7bafb0bf059c..2c3716111102a 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/string_view.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/string_view.version.compile.pass.cpp @@ -40,6 +40,10 @@ # error "__cpp_lib_string_contains should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifdef __cpp_lib_string_view # error "__cpp_lib_string_view should not be defined before c++17" # endif @@ -66,6 +70,10 @@ # error "__cpp_lib_string_contains should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifdef __cpp_lib_string_view # error "__cpp_lib_string_view should not be defined before c++17" # endif @@ -92,6 +100,10 @@ # error "__cpp_lib_string_contains should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_view # error "__cpp_lib_string_view should be defined in c++17" # endif @@ -136,6 +148,10 @@ # error "__cpp_lib_string_contains should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_view # error "__cpp_lib_string_view should be defined in c++20" # endif @@ -183,6 +199,10 @@ # error "__cpp_lib_string_contains should have the value 202011L in c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_view # error "__cpp_lib_string_view should be defined in c++23" # endif @@ -239,6 +259,13 @@ # error "__cpp_lib_string_contains should have the value 202011L in c++26" # endif +# ifndef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should be defined in c++26" +# endif +# if __cpp_lib_string_subview != 202506L +# error "__cpp_lib_string_subview should have the value 202506L in c++26" +# endif + # ifndef __cpp_lib_string_view # error "__cpp_lib_string_view should be defined in c++26" # endif diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp index cde2f258b7732..6aa704a3ead3f 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -820,6 +820,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifdef __cpp_lib_string_udls # error "__cpp_lib_string_udls should not be defined before c++14" # endif @@ -1775,6 +1779,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++14" # endif @@ -2916,6 +2924,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++17" # endif @@ -4330,6 +4342,10 @@ # error "__cpp_lib_string_resize_and_overwrite should not be defined before c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++20" # endif @@ -5978,6 +5994,10 @@ # error "__cpp_lib_string_resize_and_overwrite should have the value 202110L in c++23" # endif +# ifdef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should not be defined before c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++23" # endif @@ -7935,6 +7955,13 @@ # error "__cpp_lib_string_resize_and_overwrite should have the value 202110L in c++26" # endif +# ifndef __cpp_lib_string_subview +# error "__cpp_lib_string_subview should be defined in c++26" +# endif +# if __cpp_lib_string_subview != 202506L +# error "__cpp_lib_string_subview should have the value 202506L in c++26" +# endif + # ifndef __cpp_lib_string_udls # error "__cpp_lib_string_udls should be defined in c++26" # endif diff --git a/libcxx/test/std/strings/basic.string/string.ops/string_substr/subview.pass.cpp b/libcxx/test/std/strings/basic.string/string.ops/string_substr/subview.pass.cpp new file mode 100644 index 0000000000000..3cbf5270faee6 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.ops/string_substr/subview.pass.cpp @@ -0,0 +1,123 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// + +// constexpr basic_string_view subview(size_type pos = 0, +// size_type n = npos) const; + +#include +#include +#include +#include +#include + +#include "constexpr_char_traits.h" +#include "make_string.h" +#include "min_allocator.h" +#include "test_allocator.h" +#include "test_macros.h" +#include "type_algorithms.h" + +#define CS(S) MAKE_CSTRING(CharT, S) + +template +constexpr void test() { + std::basic_string s{CS("Hello cruel world!"), AllocT{}}; + + { // With a default position and a character length. + + // Also check if subview() is a const-qualified. + assert(std::as_const(s).subview() == CS("Hello cruel world!")); + + // Check it the return type of subview() is correct. + std::same_as> decltype(auto) sv = s.subview(); + assert(sv == CS("Hello cruel world!")); + } + + { // Check with different position and length. + + // With a explict position and a character length. + assert(s.subview(6, 5) == CS("cruel")); + + // From the beginning of the string with a explicit character length. + assert(s.subview(0, 5) == CS("Hello")); + + // To the end of string with the default character length. + assert(s.subview(12) == CS("world!")); + + // From the beginning to the end of the string with explicit values. + assert(s.subview(0, s.size()) == CS("Hello cruel world!")); + } + + // Test if exceptions are thrown correctly. +#ifndef TEST_HAS_NO_EXCEPTIONS + if (!std::is_constant_evaluated()) { + { // With a position that is out of range. + try { + std::ignore = s.subview(s.size() + 1); + assert(false); + } catch ([[maybe_unused]] const std::out_of_range& ex) { + LIBCPP_ASSERT(std::string(ex.what()) == "string_view::substr"); + } catch (...) { + assert(false); + } + } + + { // With a position that is out of range and a 0 character length. + try { + std::ignore = s.subview(s.size() + 1, 0); + assert(false); + } catch ([[maybe_unused]] const std::out_of_range& ex) { + LIBCPP_ASSERT(std::string(ex.what()) == "string_view::substr"); + } catch (...) { + assert(false); + } + } + + { // With a position that is out of range and a some character length. + try { + std::ignore = s.subview(s.size() + 1, 1); + assert(false); + } catch ([[maybe_unused]] const std::out_of_range& ex) { + LIBCPP_ASSERT(std::string(ex.what()) == "string_view::substr"); + } catch (...) { + assert(false); + } + } + } +#endif +} + +template +constexpr void test() { + test, std::allocator>(); + test, min_allocator>(); + test, safe_allocator>(); + test, test_allocator>(); + + test, std::allocator>(); + test, min_allocator>(); + test, safe_allocator>(); + test, test_allocator>(); +} + +constexpr bool test() { + types::for_each(types::character_types(), [] { test(); }); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/strings/string.view/string.view.ops/subview.pass.cpp b/libcxx/test/std/strings/string.view/string.view.ops/subview.pass.cpp new file mode 100644 index 0000000000000..b781754c9ba96 --- /dev/null +++ b/libcxx/test/std/strings/string.view/string.view.ops/subview.pass.cpp @@ -0,0 +1,120 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// + +// constexpr basic_string_view subview(size_type pos = 0, +// size_type n = npos) const; // freestanding-deleted + +#include +#include +#include +#include + +#include "constexpr_char_traits.h" +#include "make_string.h" +#include "test_macros.h" +#include "type_algorithms.h" + +#define CS(S) MAKE_CSTRING(CharT, S) + +template +constexpr void test() { + std::basic_string_view sv{CS("Hello cruel world!")}; + + { // With a default position and a character length. + + // Check if subview() is a const-qualified. + assert(std::as_const(sv).subview() == CS("Hello cruel world!")); + + // Check if the return type of subview() is correct. + std::same_as> decltype(auto) subsv = sv.subview(); + assert(subsv == CS("Hello cruel world!")); + } + + { // Check with different position and length. + + // With a explict position and a character length. + assert(sv.subview(6, 5) == CS("cruel")); + + // From the beginning of the string with a explicit character length. + assert(sv.subview(0, 5) == CS("Hello")); + + // To the end of string with the default character length. + assert(sv.subview(12) == CS("world!")); + + // From the beginning to the end of the string with explicit values. + assert(sv.subview(0, sv.size()) == CS("Hello cruel world!")); + } + + // Test if exceptions are thrown correctly. +#ifndef TEST_HAS_NO_EXCEPTIONS + if (!std::is_constant_evaluated()) { + { // With a position that is out of range. + try { + std::ignore = sv.subview(sv.size() + 1); + assert(false); + } catch ([[maybe_unused]] const std::out_of_range& ex) { + LIBCPP_ASSERT(std::string(ex.what()) == "string_view::substr"); + } catch (...) { + assert(false); + } + } + + { // With a position that is out of range and a 0 character length. + try { + std::ignore = sv.subview(sv.size() + 1, 0); + assert(false); + } catch ([[maybe_unused]] const std::out_of_range& ex) { + LIBCPP_ASSERT(std::string(ex.what()) == "string_view::substr"); + } catch (...) { + assert(false); + } + } + + { // With a position that is out of range and a some character length. + try { + std::ignore = sv.subview(sv.size() + 1, 1); + assert(false); + } catch ([[maybe_unused]] const std::out_of_range& ex) { + LIBCPP_ASSERT(std::string(ex.what()) == "string_view::substr"); + } catch (...) { + assert(false); + } + } + } +#endif +} + +template +constexpr void test() { + test>(); + test>(); + test>(); + test>(); + + test>(); + test>(); + test>(); + test>(); +} + +constexpr bool test() { + types::for_each(types::character_types(), [] { test(); }); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index c1e579c775746..5d469d4914b0b 100644 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1334,6 +1334,11 @@ def add_version_header(tc): "values": {"c++23": 202110}, "headers": ["string"], }, + { + "name": "__cpp_lib_string_subview", + "values": {"c++26": 202506}, + "headers": ["string", "string_view"], + }, { "name": "__cpp_lib_string_udls", "values": {"c++14": 201304},