diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 8e26bdcd860c1..e27b9433baeaa 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -308,6 +308,8 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_variant`` ``202106L`` ---------------------------------------------------------- ----------------- + ``__cpp_lib_view_interface`` ``202606L`` + ---------------------------------------------------------- ----------------- **C++23** ---------------------------------------------------------------------------- ``__cpp_lib_adaptor_iterator_pair_constructor`` ``202106L`` diff --git a/libcxx/docs/Status/Cxx29Papers.csv b/libcxx/docs/Status/Cxx29Papers.csv index a5d896f260657..733eab1a3b29e 100644 --- a/libcxx/docs/Status/Cxx29Papers.csv +++ b/libcxx/docs/Status/Cxx29Papers.csv @@ -4,7 +4,7 @@ "`P2414R12 `__","Pointer lifetime-end zap proposed solutions","2026-06 (Brno)","","","`#204392 `__","Voted as a Defect Report." "`P3319R6 `__","Add an ``iota`` object for ``simd`` (and more)","2026-06 (Brno)","","","`#204393 `__","" "`P3798R1 `__","The unexpected in ``std::expected``","2026-06 (Brno)","","","`#204394 `__","" -"`P3052R2 `__","``view_interface::at()``","2026-06 (Brno)","","","`#204395 `__","" +"`P3052R2 `__","``view_interface::at()``","2026-06 (Brno)","|Complete|,"23","`#204395 `__","" "`P4206R0 `__","Revert string support in ``std::constant_wrapper``","2026-06 (Brno)","","","`#203336 `__","To be applied as a Defect Report." "`P3395R6 `__","Fix encoding issues and add a formatter for ``std::error_code``","2026-06 (Brno)","","","`#204396 `__","" "`P3505R4 `__","Fix the default floating-point representation in ``std::format``","2026-06 (Brno)","","","`#204397 `__","To be applied as a Defect Report." diff --git a/libcxx/include/__ranges/view_interface.h b/libcxx/include/__ranges/view_interface.h index 37b2c9e2c1a75..efe263e895d16 100644 --- a/libcxx/include/__ranges/view_interface.h +++ b/libcxx/include/__ranges/view_interface.h @@ -15,6 +15,7 @@ #include <__concepts/same_as.h> #include <__config> #include <__iterator/concepts.h> +#include <__iterator/distance.h> #include <__iterator/iterator_traits.h> #include <__iterator/prev.h> #include <__memory/pointer_traits.h> @@ -25,6 +26,7 @@ #include <__type_traits/is_class.h> #include <__type_traits/make_unsigned.h> #include <__type_traits/remove_cv.h> +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -159,6 +161,26 @@ class view_interface { [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](range_difference_t<_RARange> __index) const { return ranges::begin(__derived())[__index]; } + + template + requires sized_range<_RARange> // freestanding-deleted + [[nodiscard]] constexpr decltype(auto) at(range_difference_t<_RARange> __index) { + if (__index < 0 || __index >= ranges::distance(__derived())) { + std::__throw_out_of_range( + "Precondition `0 <= __index < distance()` not satisfied. `.at(__index)` called with out-of-bounds index."); + } + return (*this)[__index]; + } + + template + requires sized_range<_RARange> // freestanding-deleted + [[nodiscard]] constexpr decltype(auto) at(range_difference_t<_RARange> __index) const { + if (__index < 0 || __index >= ranges::distance(__derived())) { + std::__throw_out_of_range( + "Precondition `0 <= __index < distance()` not satisfied. `.at(__index)` called with out-of-bounds index."); + } + return (*this)[__index]; + } }; } // namespace ranges diff --git a/libcxx/include/version b/libcxx/include/version index 7f2dc9e4b72ab..744eafd35b72a 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -289,6 +289,7 @@ __cpp_lib_unwrap_ref 201811L __cpp_lib_variant 202306L 202106L // C++20 202102L // C++17 +__cpp_lib_view_interface 202606L __cpp_lib_void_t 201411L */ @@ -487,6 +488,7 @@ __cpp_lib_void_t 201411L # define __cpp_lib_unwrap_ref 201811L # undef __cpp_lib_variant # define __cpp_lib_variant 202106L +# define __cpp_lib_view_interface 202606L #endif #if _LIBCPP_STD_VER >= 23 diff --git a/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp index a22c03f5d1a71..5d076211bd5b3 100644 --- a/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp +++ b/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp @@ -67,4 +67,9 @@ void test() { v[Diff{0}]; // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::as_const(v)[Diff{0}]; + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + v.at(Diff{0}); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(v).at(Diff{0}); } diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv index 073f698786117..f5a2c2451fcf9 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx23.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv @@ -11,6 +11,7 @@ algorithm iosfwd algorithm limits algorithm optional algorithm ratio +algorithm stdexcept algorithm tuple algorithm version any cstdint diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv index 1450a13e125ef..f5c27ff707840 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx26.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv @@ -11,6 +11,7 @@ algorithm iosfwd algorithm limits algorithm optional algorithm ratio +algorithm stdexcept algorithm tuple algorithm version any cstdint diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp index 670b0e664721a..f42c7ede894b5 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp @@ -80,6 +80,10 @@ # error "__cpp_lib_ranges_zip should not be defined before c++23" # endif +# ifdef __cpp_lib_view_interface +# error "__cpp_lib_view_interface should not be defined before c++20" +# endif + #elif TEST_STD_VER == 14 # ifdef __cpp_lib_default_template_type_for_algorithm_values @@ -142,6 +146,10 @@ # error "__cpp_lib_ranges_zip should not be defined before c++23" # endif +# ifdef __cpp_lib_view_interface +# error "__cpp_lib_view_interface should not be defined before c++20" +# endif + #elif TEST_STD_VER == 17 # ifdef __cpp_lib_default_template_type_for_algorithm_values @@ -204,6 +212,10 @@ # error "__cpp_lib_ranges_zip should not be defined before c++23" # endif +# ifdef __cpp_lib_view_interface +# error "__cpp_lib_view_interface should not be defined before c++20" +# endif + #elif TEST_STD_VER == 20 # ifdef __cpp_lib_default_template_type_for_algorithm_values @@ -269,6 +281,13 @@ # error "__cpp_lib_ranges_zip should not be defined before c++23" # endif +# ifndef __cpp_lib_view_interface +# error "__cpp_lib_view_interface should be defined in c++20" +# endif +# if __cpp_lib_view_interface != 202606L +# error "__cpp_lib_view_interface should have the value 202606L in c++20" +# endif + #elif TEST_STD_VER == 23 # ifdef __cpp_lib_default_template_type_for_algorithm_values @@ -385,6 +404,13 @@ # error "__cpp_lib_ranges_zip should have the value 202110L in c++23" # endif +# ifndef __cpp_lib_view_interface +# error "__cpp_lib_view_interface should be defined in c++23" +# endif +# if __cpp_lib_view_interface != 202606L +# error "__cpp_lib_view_interface should have the value 202606L in c++23" +# endif + #elif TEST_STD_VER > 23 # if !defined(_LIBCPP_VERSION) @@ -516,6 +542,13 @@ # error "__cpp_lib_ranges_zip should have the value 202110L in c++26" # endif +# ifndef __cpp_lib_view_interface +# error "__cpp_lib_view_interface should be defined in c++26" +# endif +# if __cpp_lib_view_interface != 202606L +# error "__cpp_lib_view_interface should have the value 202606L in c++26" +# endif + #endif // TEST_STD_VER > 23 // clang-format on 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 d9c78b73f7e23..5328d43d32cc8 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 @@ -944,6 +944,10 @@ # error "__cpp_lib_variant should not be defined before c++17" # endif +# ifdef __cpp_lib_view_interface +# error "__cpp_lib_view_interface should not be defined before c++20" +# endif + # ifdef __cpp_lib_void_t # error "__cpp_lib_void_t should not be defined before c++17" # endif @@ -1946,6 +1950,10 @@ # error "__cpp_lib_variant should not be defined before c++17" # endif +# ifdef __cpp_lib_view_interface +# error "__cpp_lib_view_interface should not be defined before c++20" +# endif + # ifdef __cpp_lib_void_t # error "__cpp_lib_void_t should not be defined before c++17" # endif @@ -3137,6 +3145,10 @@ # error "__cpp_lib_variant should have the value 202102L in c++17" # endif +# ifdef __cpp_lib_view_interface +# error "__cpp_lib_view_interface should not be defined before c++20" +# endif + # ifndef __cpp_lib_void_t # error "__cpp_lib_void_t should be defined in c++17" # endif @@ -4595,6 +4607,13 @@ # error "__cpp_lib_variant should have the value 202106L in c++20" # endif +# ifndef __cpp_lib_view_interface +# error "__cpp_lib_view_interface should be defined in c++20" +# endif +# if __cpp_lib_view_interface != 202606L +# error "__cpp_lib_view_interface should have the value 202606L in c++20" +# endif + # ifndef __cpp_lib_void_t # error "__cpp_lib_void_t should be defined in c++20" # endif @@ -6281,6 +6300,13 @@ # error "__cpp_lib_variant should have the value 202106L in c++23" # endif +# ifndef __cpp_lib_view_interface +# error "__cpp_lib_view_interface should be defined in c++23" +# endif +# if __cpp_lib_view_interface != 202606L +# error "__cpp_lib_view_interface should have the value 202606L in c++23" +# endif + # ifndef __cpp_lib_void_t # error "__cpp_lib_void_t should be defined in c++23" # endif @@ -8303,6 +8329,13 @@ # error "__cpp_lib_variant should have the value 202306L in c++26" # endif +# ifndef __cpp_lib_view_interface +# error "__cpp_lib_view_interface should be defined in c++26" +# endif +# if __cpp_lib_view_interface != 202606L +# error "__cpp_lib_view_interface should have the value 202606L in c++26" +# endif + # ifndef __cpp_lib_void_t # error "__cpp_lib_void_t should be defined in c++26" # endif diff --git a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp index bce13c38f2dab..23512ccc15517 100644 --- a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp +++ b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp @@ -21,17 +21,17 @@ #include "test_macros.h" #include "test_iterators.h" -template +template concept ValidViewInterfaceType = requires { typename std::ranges::view_interface; }; -struct Empty { }; +struct Empty {}; static_assert(!ValidViewInterfaceType); static_assert(!ValidViewInterfaceType); static_assert(!ValidViewInterfaceType); static_assert(!ValidViewInterfaceType); -static_assert(!ValidViewInterfaceType); -static_assert( ValidViewInterfaceType); +static_assert(!ValidViewInterfaceType); +static_assert(ValidViewInterfaceType); using InputIter = cpp20_input_iterator; @@ -50,8 +50,8 @@ struct SizedInputRange : std::ranges::view_interface { static_assert(std::ranges::sized_range); struct NotSizedSentinel { - using value_type = int; - using difference_type = std::ptrdiff_t; + using value_type = int; + using difference_type = std::ptrdiff_t; using iterator_concept = std::forward_iterator_tag; explicit NotSizedSentinel() = default; @@ -66,9 +66,7 @@ static_assert(std::forward_iterator); using ForwardIter = forward_iterator; // So that we conform to sized_sentinel_for. -constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) { - return base(x) - base(y); -} +constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) { return base(x) - base(y); } struct ForwardRange : std::ranges::view_interface { int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; @@ -78,19 +76,17 @@ struct ForwardRange : std::ranges::view_interface { static_assert(std::ranges::view); struct MoveOnlyForwardRange : std::ranges::view_interface { - int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; - MoveOnlyForwardRange(MoveOnlyForwardRange const&) = delete; - MoveOnlyForwardRange(MoveOnlyForwardRange &&) = default; - MoveOnlyForwardRange& operator=(MoveOnlyForwardRange &&) = default; - MoveOnlyForwardRange() = default; + int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; + MoveOnlyForwardRange(MoveOnlyForwardRange const&) = delete; + MoveOnlyForwardRange(MoveOnlyForwardRange&&) = default; + MoveOnlyForwardRange& operator=(MoveOnlyForwardRange&&) = default; + MoveOnlyForwardRange() = default; constexpr ForwardIter begin() const { return ForwardIter(const_cast(buff)); } constexpr ForwardIter end() const { return ForwardIter(const_cast(buff) + 8); } }; static_assert(std::ranges::view); -struct MI : std::ranges::view_interface, - std::ranges::view_interface { -}; +struct MI : std::ranges::view_interface, std::ranges::view_interface {}; static_assert(!std::ranges::view); struct EmptyIsTrue : std::ranges::view_interface { @@ -131,7 +127,7 @@ struct DataIsNull : std::ranges::view_interface { int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; constexpr ContIter begin() const { return ContIter(buff); } constexpr ContIter end() const { return ContIter(buff + 8); } - constexpr const int *data() const { return nullptr; } + constexpr const int* data() const { return nullptr; } }; static_assert(std::ranges::view); @@ -142,13 +138,21 @@ struct BoolConvertibleComparison : std::ranges::view_interface); -template -concept EmptyInvocable = requires (T const& obj) { obj.empty(); }; +template +concept EmptyInvocable = requires(T const& obj) { obj.empty(); }; -template -concept BoolOpInvocable = requires (T const& obj) { bool(obj); }; +template +concept BoolOpInvocable = requires(T const& obj) { bool(obj); }; constexpr bool testEmpty() { static_assert(!EmptyInvocable); // LWG 3715: `view_interface::empty` is overconstrained static_assert(EmptyInvocable); - static_assert( EmptyInvocable); + static_assert(EmptyInvocable); static_assert(!BoolOpInvocable); static_assert(BoolOpInvocable); - static_assert( BoolOpInvocable); + static_assert(BoolOpInvocable); SizedInputRange sizedInputRange; assert(!sizedInputRange.empty()); @@ -224,12 +228,12 @@ constexpr bool testEmpty() { return true; } -template -concept DataInvocable = requires (T const& obj) { obj.data(); }; +template +concept DataInvocable = requires(T const& obj) { obj.data(); }; constexpr bool testData() { static_assert(!DataInvocable); - static_assert( DataInvocable); + static_assert(DataInvocable); ContRange contiguous; assert(contiguous.data() == contiguous.buff); @@ -249,18 +253,18 @@ constexpr bool testData() { return true; } -template -concept SizeInvocable = requires (T const& obj) { obj.size(); }; +template +concept SizeInvocable = requires(T const& obj) { obj.size(); }; constexpr bool testSize() { static_assert(!SizeInvocable); static_assert(!SizeInvocable); - static_assert( SizeInvocable); + static_assert(SizeInvocable); // Test the test. static_assert(std::same_as() - std::declval()), std::ptrdiff_t>); using UnsignedSize = std::make_unsigned_t; - using SignedSize = std::common_type_t>; + using SignedSize = std::common_type_t>; ForwardRange forwardRange; assert(forwardRange.size() == 8); assert(static_cast(forwardRange).size() == 8); @@ -284,12 +288,12 @@ constexpr bool testSize() { return true; } -template -concept SubscriptInvocable = requires (T const& obj, std::size_t n) { obj[n]; }; +template +concept SubscriptInvocable = requires(T const& obj, std::size_t n) { obj[n]; }; constexpr bool testSubscript() { static_assert(!SubscriptInvocable); - static_assert( SubscriptInvocable); + static_assert(SubscriptInvocable); RARange randomAccess; assert(randomAccess[2] == 2); @@ -300,17 +304,57 @@ constexpr bool testSubscript() { return true; } -template -concept FrontInvocable = requires (T const& obj) { obj.front(); }; +template +concept AtInvocable = requires(T const& obj, std::size_t n) { obj.at(n); }; + +constexpr bool testAt() { + static_assert(!AtInvocable); + static_assert(AtInvocable); + + RARange randomAccess; + assert(randomAccess.at(2) == 2); + assert(static_cast(randomAccess).at(2) == 2); + randomAccess.at(2) = 3; + assert(randomAccess.at(2) == 3); + +#ifndef TEST_HAS_NO_EXCEPTIONS + if (!std::is_constant_evaluated()) { + try { + TEST_IGNORE_NODISCARD randomAccess.at(0); + assert(true); + } catch (std::out_of_range const&) { + // pass + assert(false); // This should not be reached because index 0 is valid. + } catch (...) { + assert(false); + } + + // Test that at() throws an exception when the index is out of range. + try { + TEST_IGNORE_NODISCARD randomAccess.at(10); + assert(false); + } catch (std::out_of_range const&) { + // pass + } catch (...) { + assert(false); + } + } +#endif + + return true; +} + +template +concept FrontInvocable = requires(T const& obj) { obj.front(); }; -template -concept BackInvocable = requires (T const& obj) { obj.back(); }; +template +concept BackInvocable = requires(T const& obj) { obj.back(); }; constexpr bool testFrontBack() { static_assert(!FrontInvocable); - static_assert( FrontInvocable); + static_assert(FrontInvocable); static_assert(!BackInvocable); - static_assert( BackInvocable); + static_assert(BackInvocable); ForwardRange forwardRange; assert(forwardRange.front() == 0); @@ -332,8 +376,10 @@ constexpr bool testFrontBack() { return true; } -struct V1 : std::ranges::view_interface { }; -struct V2 : std::ranges::view_interface { V1 base_; }; +struct V1 : std::ranges::view_interface {}; +struct V2 : std::ranges::view_interface { + V1 base_; +}; static_assert(sizeof(V2) == sizeof(V1)); int main(int, char**) { @@ -349,6 +395,9 @@ int main(int, char**) { testSubscript(); static_assert(testSubscript()); + testAt(); + static_assert(testAt()); + testFrontBack(); static_assert(testFrontBack()); diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index d764a1f677ba6..3934e101073a0 100644 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1522,6 +1522,13 @@ def add_version_header(tc): }, "headers": ["variant"], }, + { + "name": "__cpp_lib_view_interface", + "values": { + "c++20": 202606, + }, + "headers": ["ranges"], + }, { "name": "__cpp_lib_void_t", "values": {"c++17": 201411},