diff options
-rw-r--r-- | libstdc++-v3/include/bits/iterator_concepts.h | 25 | ||||
-rw-r--r-- | libstdc++-v3/include/bits/ranges_base.h | 165 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/std/ranges/access/cdata.cc | 40 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/std/ranges/access/data.cc | 39 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/std/ranges/access/empty.cc | 24 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/std/ranges/access/size.cc | 20 |
6 files changed, 188 insertions, 125 deletions
diff --git a/libstdc++-v3/include/bits/iterator_concepts.h b/libstdc++-v3/include/bits/iterator_concepts.h index 8f97a5d..f4e94a6 100644 --- a/libstdc++-v3/include/bits/iterator_concepts.h +++ b/libstdc++-v3/include/bits/iterator_concepts.h @@ -925,8 +925,11 @@ namespace ranges struct default_sentinel_t { }; inline constexpr default_sentinel_t default_sentinel{}; - namespace __detail + // This is the namespace for [range.access] CPOs. + namespace ranges::__cust_access { + using std::__detail::__class_or_enum; + template<typename _Tp> constexpr decay_t<_Tp> __decay_copy(_Tp&& __t) @@ -936,9 +939,11 @@ namespace ranges template<typename _Tp> concept __member_begin = requires(_Tp& __t) { - { __detail::__decay_copy(__t.begin()) } -> input_or_output_iterator; + { __cust_access::__decay_copy(__t.begin()) } + -> input_or_output_iterator; }; + // Poison pills so that unqualified lookup doesn't find std::begin. void begin(auto&) = delete; void begin(const auto&) = delete; @@ -946,7 +951,8 @@ namespace ranges concept __adl_begin = __class_or_enum<remove_reference_t<_Tp>> && requires(_Tp& __t) { - { __detail::__decay_copy(begin(__t)) } -> input_or_output_iterator; + { __cust_access::__decay_copy(begin(__t)) } + -> input_or_output_iterator; }; // Simplified version of std::ranges::begin that only supports lvalues, @@ -954,24 +960,23 @@ namespace ranges template<typename _Tp> requires is_array_v<_Tp> || __member_begin<_Tp&> || __adl_begin<_Tp&> auto - __ranges_begin(_Tp& __t) + __begin(_Tp& __t) { if constexpr (is_array_v<_Tp>) - { - static_assert(sizeof(remove_all_extents_t<_Tp>) != 0, - "not array of incomplete type"); - return __t + 0; - } + return __t + 0; else if constexpr (__member_begin<_Tp&>) return __t.begin(); else return begin(__t); } + } // namespace ranges::__cust_access + namespace __detail + { // Implementation of std::ranges::iterator_t, without using ranges::begin. template<typename _Tp> using __range_iter_t - = decltype(__detail::__ranges_begin(std::declval<_Tp&>())); + = decltype(ranges::__cust_access::__begin(std::declval<_Tp&>())); } // namespace __detail diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h index 32d664f..17a421a 100644 --- a/libstdc++-v3/include/bits/ranges_base.h +++ b/libstdc++-v3/include/bits/ranges_base.h @@ -89,10 +89,6 @@ namespace ranges namespace __cust_access { using std::ranges::__detail::__maybe_borrowed_range; - using std::__detail::__class_or_enum; - using std::__detail::__decay_copy; - using std::__detail::__member_begin; - using std::__detail::__adl_begin; struct _Begin { @@ -114,13 +110,11 @@ namespace ranges requires is_array_v<remove_reference_t<_Tp>> || __member_begin<_Tp> || __adl_begin<_Tp> constexpr auto - operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp>()) + operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp&>()) { if constexpr (is_array_v<remove_reference_t<_Tp>>) { static_assert(is_lvalue_reference_v<_Tp>); - using _Up = remove_all_extents_t<remove_reference_t<_Tp>>; - static_assert(sizeof(_Up) != 0, "not array of incomplete type"); return __t + 0; } else if constexpr (__member_begin<_Tp>) @@ -137,6 +131,7 @@ namespace ranges -> sentinel_for<decltype(_Begin{}(std::forward<_Tp>(__t)))>; }; + // Poison pills so that unqualified lookup doesn't find std::end. void end(auto&) = delete; void end(const auto&) = delete; @@ -165,10 +160,10 @@ namespace ranges public: template<__maybe_borrowed_range _Tp> - requires is_bounded_array_v<remove_reference_t<_Tp>> || __member_end<_Tp> - || __adl_end<_Tp> + requires is_bounded_array_v<remove_reference_t<_Tp>> + || __member_end<_Tp> || __adl_end<_Tp> constexpr auto - operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp>()) + operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp&>()) { if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>) { @@ -182,12 +177,15 @@ namespace ranges } }; - template<typename _Tp> + // If _To is an lvalue-reference, return const _Tp&, otherwise const _Tp&&. + template<typename _To, typename _Tp> constexpr decltype(auto) - __as_const(_Tp&& __t) noexcept + __as_const(_Tp& __t) noexcept { - if constexpr (is_lvalue_reference_v<_Tp>) - return static_cast<const remove_reference_t<_Tp>&>(__t); + static_assert(std::is_same_v<_To&, _Tp&>); + + if constexpr (is_lvalue_reference_v<_To>) + return const_cast<const _Tp&>(__t); else return static_cast<const _Tp&&>(__t); } @@ -197,10 +195,10 @@ namespace ranges template<typename _Tp> constexpr auto operator()(_Tp&& __e) const - noexcept(noexcept(_Begin{}(__cust_access::__as_const((_Tp&&)__e)))) - requires requires { _Begin{}(__cust_access::__as_const((_Tp&&)__e)); } + noexcept(noexcept(_Begin{}(__cust_access::__as_const<_Tp>(__e)))) + requires requires { _Begin{}(__cust_access::__as_const<_Tp>(__e)); } { - return _Begin{}(__cust_access::__as_const(std::forward<_Tp>(__e))); + return _Begin{}(__cust_access::__as_const<_Tp>(__e)); } }; @@ -209,10 +207,10 @@ namespace ranges template<typename _Tp> constexpr auto operator()(_Tp&& __e) const - noexcept(noexcept(_End{}(__cust_access::__as_const((_Tp&&)__e)))) - requires requires { _End{}(__cust_access::__as_const((_Tp&&)__e)); } + noexcept(noexcept(_End{}(__cust_access::__as_const<_Tp>(__e)))) + requires requires { _End{}(__cust_access::__as_const<_Tp>(__e)); } { - return _End{}(__cust_access::__as_const(std::forward<_Tp>(__e))); + return _End{}(__cust_access::__as_const<_Tp>(__e)); } }; @@ -268,7 +266,7 @@ namespace ranges requires __member_rbegin<_Tp> || __adl_rbegin<_Tp> || __reversable<_Tp> constexpr auto operator()(_Tp&& __t) const - noexcept(_S_noexcept<_Tp>()) + noexcept(_S_noexcept<_Tp&>()) { if constexpr (__member_rbegin<_Tp>) return __t.rbegin(); @@ -326,7 +324,7 @@ namespace ranges requires __member_rend<_Tp> || __adl_rend<_Tp> || __reversable<_Tp> constexpr auto operator()(_Tp&& __t) const - noexcept(_S_noexcept<_Tp>()) + noexcept(_S_noexcept<_Tp&>()) { if constexpr (__member_rend<_Tp>) return __t.rend(); @@ -342,10 +340,10 @@ namespace ranges template<typename _Tp> constexpr auto operator()(_Tp&& __e) const - noexcept(noexcept(_RBegin{}(__cust_access::__as_const((_Tp&&)__e)))) - requires requires { _RBegin{}(__cust_access::__as_const((_Tp&&)__e)); } + noexcept(noexcept(_RBegin{}(__cust_access::__as_const<_Tp>(__e)))) + requires requires { _RBegin{}(__cust_access::__as_const<_Tp>(__e)); } { - return _RBegin{}(__cust_access::__as_const(std::forward<_Tp>(__e))); + return _RBegin{}(__cust_access::__as_const<_Tp>(__e)); } }; @@ -354,19 +352,18 @@ namespace ranges template<typename _Tp> constexpr auto operator()(_Tp&& __e) const - noexcept(noexcept(_REnd{}(__cust_access::__as_const((_Tp&&)__e)))) - requires requires { _REnd{}(__cust_access::__as_const((_Tp&&)__e)); } + noexcept(noexcept(_REnd{}(__cust_access::__as_const<_Tp>(__e)))) + requires requires { _REnd{}(__cust_access::__as_const<_Tp>(__e)); } { - return _REnd{}(__cust_access::__as_const(std::forward<_Tp>(__e))); + return _REnd{}(__cust_access::__as_const<_Tp>(__e)); } }; template<typename _Tp> concept __member_size = !disable_sized_range<remove_cvref_t<_Tp>> - && requires(_Tp&& __t) + && requires(_Tp& __t) { - { __decay_copy(std::forward<_Tp>(__t).size()) } - -> __detail::__is_integer_like; + { __decay_copy(__t.size()) } -> __detail::__is_integer_like; }; void size(auto&) = delete; @@ -375,19 +372,19 @@ namespace ranges template<typename _Tp> concept __adl_size = __class_or_enum<remove_reference_t<_Tp>> && !disable_sized_range<remove_cvref_t<_Tp>> - && requires(_Tp&& __t) + && requires(_Tp& __t) { - { __decay_copy(size(std::forward<_Tp>(__t))) } - -> __detail::__is_integer_like; + { __decay_copy(size(__t)) } -> __detail::__is_integer_like; }; template<typename _Tp> - concept __sentinel_size = requires(_Tp&& __t) + concept __sentinel_size = requires(_Tp& __t) { - { _Begin{}(std::forward<_Tp>(__t)) } -> forward_iterator; + { _Begin{}(__t) } -> forward_iterator; + + { _End{}(__t) } -> sized_sentinel_for<decltype(_Begin{}(__t))>; - { _End{}(std::forward<_Tp>(__t)) } - -> sized_sentinel_for<decltype(_Begin{}(std::forward<_Tp>(__t)))>; + __detail::__to_unsigned_like(_End{}(__t) - _Begin{}(__t)); }; struct _Size @@ -400,12 +397,12 @@ namespace ranges if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>) return true; else if constexpr (__member_size<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp>().size())); + return noexcept(__decay_copy(std::declval<_Tp&>().size())); else if constexpr (__adl_size<_Tp>) - return noexcept(__decay_copy(size(std::declval<_Tp>()))); + return noexcept(__decay_copy(size(std::declval<_Tp&>()))); else if constexpr (__sentinel_size<_Tp>) - return noexcept(_End{}(std::declval<_Tp>()) - - _Begin{}(std::declval<_Tp>())); + return noexcept(_End{}(std::declval<_Tp&>()) + - _Begin{}(std::declval<_Tp&>())); } public: @@ -413,39 +410,30 @@ namespace ranges requires is_bounded_array_v<remove_reference_t<_Tp>> || __member_size<_Tp> || __adl_size<_Tp> || __sentinel_size<_Tp> constexpr auto - operator()(_Tp&& __e) const noexcept(_S_noexcept<_Tp>()) + operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp&>()) { if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>) - { - return extent_v<remove_reference_t<_Tp>>; - } + return extent_v<remove_reference_t<_Tp>>; else if constexpr (__member_size<_Tp>) - return std::forward<_Tp>(__e).size(); + return __t.size(); else if constexpr (__adl_size<_Tp>) - return size(std::forward<_Tp>(__e)); + return size(__t); else if constexpr (__sentinel_size<_Tp>) - return __detail::__to_unsigned_like( - _End{}(std::forward<_Tp>(__e)) - - _Begin{}(std::forward<_Tp>(__e))); + return __detail::__to_unsigned_like(_End{}(__t) - _Begin{}(__t)); } }; struct _SSize { template<typename _Tp> - requires requires (_Tp&& __e) - { - _Begin{}(std::forward<_Tp>(__e)); - _Size{}(std::forward<_Tp>(__e)); - } + requires requires (_Tp& __t) { _Size{}(__t); } constexpr auto - operator()(_Tp&& __e) const - noexcept(noexcept(_Size{}(std::forward<_Tp>(__e)))) + operator()(_Tp&& __t) const noexcept(noexcept(_Size{}(__t))) { - using __iter_type = decltype(_Begin{}(std::forward<_Tp>(__e))); + using __iter_type = decltype(_Begin{}(__t)); using __diff_type = iter_difference_t<__iter_type>; using __gnu_cxx::__int_traits; - auto __size = _Size{}(std::forward<_Tp>(__e)); + auto __size = _Size{}(__t); if constexpr (integral<__diff_type>) { if constexpr (__int_traits<__diff_type>::__digits @@ -457,19 +445,17 @@ namespace ranges }; template<typename _Tp> - concept __member_empty = requires(_Tp&& __t) - { bool(std::forward<_Tp>(__t).empty()); }; + concept __member_empty = requires(_Tp& __t) { bool(__t.empty()); }; template<typename _Tp> - concept __size0_empty = requires(_Tp&& __t) - { _Size{}(std::forward<_Tp>(__t)) == 0; }; + concept __size0_empty = requires(_Tp& __t) { _Size{}(__t) == 0; }; template<typename _Tp> - concept __eq_iter_empty = requires(_Tp&& __t) + concept __eq_iter_empty = requires(_Tp& __t) { - { _Begin{}(std::forward<_Tp>(__t)) } -> forward_iterator; - bool(_Begin{}(std::forward<_Tp>(__t)) - == _End{}(std::forward<_Tp>(__t))); + { _Begin{}(__t) } -> forward_iterator; + + bool(_Begin{}(__t) == _End{}(__t)); }; struct _Empty @@ -480,28 +466,27 @@ namespace ranges _S_noexcept() { if constexpr (__member_empty<_Tp>) - return noexcept(std::declval<_Tp>().empty()); + return noexcept(std::declval<_Tp&>().empty()); else if constexpr (__size0_empty<_Tp>) - return noexcept(_Size{}(std::declval<_Tp>()) == 0); + return noexcept(_Size{}(std::declval<_Tp&>()) == 0); else - return noexcept(bool(_Begin{}(std::declval<_Tp>()) - == _End{}(std::declval<_Tp>()))); + return noexcept(bool(_Begin{}(std::declval<_Tp&>()) + == _End{}(std::declval<_Tp&>()))); } public: template<typename _Tp> requires __member_empty<_Tp> || __size0_empty<_Tp> - || __eq_iter_empty<_Tp> + || __eq_iter_empty<_Tp> constexpr bool - operator()(_Tp&& __e) const noexcept(_S_noexcept<_Tp>()) + operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp&>()) { if constexpr (__member_empty<_Tp>) - return bool(std::forward<_Tp>(__e).empty()); + return bool(__t.empty()); else if constexpr (__size0_empty<_Tp>) - return _Size{}(std::forward<_Tp>(__e)) == 0; + return _Size{}(__t) == 0; else - return bool(_Begin{}(std::forward<_Tp>(__e)) - == _End{}(std::forward<_Tp>(__e))); + return bool(_Begin{}(__t) == _End{}(__t)); } }; @@ -510,12 +495,12 @@ namespace ranges && is_object_v<remove_pointer_t<_Tp>>; template<typename _Tp> - concept __member_data = is_lvalue_reference_v<_Tp> - && requires(_Tp __t) { { __t.data() } -> __pointer_to_object; }; + concept __member_data + = requires(_Tp& __t) { { __t.data() } -> __pointer_to_object; }; template<typename _Tp> - concept __begin_data = requires(_Tp&& __t) - { { _Begin{}(std::forward<_Tp>(__t)) } -> contiguous_iterator; }; + concept __begin_data = requires(_Tp& __t) + { { _Begin{}(__t) } -> contiguous_iterator; }; struct _Data { @@ -525,21 +510,21 @@ namespace ranges _S_noexcept() { if constexpr (__member_data<_Tp>) - return noexcept(__decay_copy(std::declval<_Tp>().data())); + return noexcept(__decay_copy(std::declval<_Tp&>().data())); else - return noexcept(_Begin{}(std::declval<_Tp>())); + return noexcept(_Begin{}(std::declval<_Tp&>())); } public: template<__maybe_borrowed_range _Tp> requires __member_data<_Tp> || __begin_data<_Tp> constexpr auto - operator()(_Tp&& __e) const noexcept(_S_noexcept<_Tp>()) + operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp>()) { if constexpr (__member_data<_Tp>) - return __e.data(); + return __t.data(); else - return std::to_address(_Begin{}(std::forward<_Tp>(__e))); + return std::to_address(_Begin{}(__t)); } }; @@ -548,10 +533,10 @@ namespace ranges template<typename _Tp> constexpr auto operator()(_Tp&& __e) const - noexcept(noexcept(_Data{}(__cust_access::__as_const((_Tp&&)__e)))) - requires requires { _Data{}(__cust_access::__as_const((_Tp&&)__e)); } + noexcept(noexcept(_Data{}(__cust_access::__as_const<_Tp>(__e)))) + requires requires { _Data{}(__cust_access::__as_const<_Tp>(__e)); } { - return _Data{}(__cust_access::__as_const(std::forward<_Tp>(__e))); + return _Data{}(__cust_access::__as_const<_Tp>(__e)); } }; diff --git a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc index bea27cc..2dfb683 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc @@ -21,6 +21,10 @@ #include <ranges> #include <testsuite_hooks.h> +template<typename T> + concept has_cdata + = requires (T&& t) { std::ranges::cdata(std::forward<T>(t)); }; + void test01() { @@ -31,12 +35,18 @@ test01() int* data() { return &j; } const R* data() const noexcept { return nullptr; } }; + static_assert( has_cdata<R&> ); + static_assert( has_cdata<const R&> ); R r; const R& c = r; VERIFY( std::ranges::cdata(r) == (R*)nullptr ); static_assert( noexcept(std::ranges::cdata(r)) ); VERIFY( std::ranges::cdata(c) == (R*)nullptr ); static_assert( noexcept(std::ranges::cdata(c)) ); + + // not lvalues and not borrowed ranges + static_assert( !has_cdata<R> ); + static_assert( !has_cdata<const R> ); } void @@ -44,28 +54,36 @@ test02() { int a[] = { 0, 1 }; VERIFY( std::ranges::cdata(a) == a + 0 ); + + static_assert( has_cdata<int(&)[2]> ); + static_assert( !has_cdata<int(&&)[2]> ); } -struct R +struct R3 { - long l = 0; + static inline int i = 0; + static inline long l = 0; - int* data() const { return nullptr; } - friend long* begin(R&& r); // this function is not defined - friend const long* begin(const R& r) { return &r.l; } - friend const short* begin(const R&&); // not defined + int* data() &; // this function is not defined + friend long* begin(R3&& r); // not defined + friend const long* begin(const R3& r) { return &r.l; } + friend const short* begin(const R3&&); // not defined }; -// This is a lie, ranges::begin(R&&) returns a dangling iterator. -template<> constexpr bool std::ranges::enable_borrowed_range<R> = true; +template<> constexpr bool std::ranges::enable_borrowed_range<R3> = true; void test03() { - R r; - const R& c = r; + static_assert( has_cdata<R3&> ); + static_assert( has_cdata<R3> ); // borrowed range + static_assert( has_cdata<const R3&> ); + static_assert( has_cdata<const R3> ); // borrowed range + + R3 r; + const R3& c = r; VERIFY( std::ranges::cdata(r) == std::ranges::data(c) ); - VERIFY( std::ranges::cdata(std::move(r)) == std::ranges::begin(c) ); + VERIFY( std::ranges::cdata(std::move(r)) == std::ranges::data(c) ); VERIFY( std::ranges::cdata(std::move(c)) == std::ranges::begin(c) ); } diff --git a/libstdc++-v3/testsuite/std/ranges/access/data.cc b/libstdc++-v3/testsuite/std/ranges/access/data.cc index 40d3027..237bbcc 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/data.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/data.cc @@ -22,6 +22,10 @@ #include <testsuite_hooks.h> #include <testsuite_iterators.h> +template<typename T> + concept has_data + = requires (T&& t) { std::ranges::data(std::forward<T>(t)); }; + void test01() { @@ -32,12 +36,18 @@ test01() int* data() { return &j; } const R* data() const noexcept { return nullptr; } }; + static_assert( has_data<R&> ); + static_assert( has_data<const R&> ); R r; const R& c = r; VERIFY( std::ranges::data(r) == &r.j ); static_assert( !noexcept(std::ranges::data(r)) ); VERIFY( std::ranges::data(c) == (R*)nullptr ); static_assert( noexcept(std::ranges::data(c)) ); + + // not lvalues and not borrowed ranges + static_assert( !has_data<R> ); + static_assert( !has_data<const R> ); } @@ -49,31 +59,42 @@ test02() __gnu_test::test_range<int, __gnu_test::contiguous_iterator_wrapper> r(a); VERIFY( std::ranges::data(r) == std::to_address(std::ranges::begin(r)) ); + + static_assert( has_data<int(&)[2]> ); + static_assert( has_data<decltype(r)&> ); + static_assert( !has_data<int(&&)[2]> ); + static_assert( !has_data<decltype(r)&&> ); } struct R3 { - long l = 0; + static inline int i; + static inline long l; - int* data() const { return nullptr; } - friend long* begin(R3& r) { return &r.l; } - friend const long* begin(const R3& r) { return &r.l + 1; } + int* data() & { return &i; } + friend long* begin(const R3& r) { return &l; } + friend const short* begin(const R3&&); // not defined }; -// N.B. this is a lie, begin on an R3 rvalue will return a dangling pointer. template<> constexpr bool std::ranges::enable_borrowed_range<R3> = true; void test03() { + static_assert( has_data<R3&> ); + static_assert( has_data<R3> ); // borrowed range + static_assert( has_data<const R3&> ); + static_assert( has_data<const R3> ); // borrowed range + R3 r; const R3& c = r; - // r.data() can only be used on an lvalue, but ranges::begin(R3&&) is OK - // because R3 satisfies ranges::borrowed_range. - VERIFY( std::ranges::data(std::move(r)) == std::to_address(std::ranges::begin(std::move(r))) ); - VERIFY( std::ranges::data(std::move(c)) == std::to_address(std::ranges::begin(std::move(c))) ); + // PR libstdc++/100824 + // ranges::data should treat the subexpression as an lvalue + VERIFY( std::ranges::data(std::move(r)) == &R3::i ); + VERIFY( std::ranges::data(std::move(c)) == &R3::l ); } + int main() { diff --git a/libstdc++-v3/testsuite/std/ranges/access/empty.cc b/libstdc++-v3/testsuite/std/ranges/access/empty.cc index 1cddffa..9044dd1 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/empty.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/empty.cc @@ -35,7 +35,9 @@ test01() constexpr R r; static_assert( !std::ranges::empty(r) ); static_assert( same_as<decltype(std::ranges::empty(r)), bool> ); - static_assert( std::ranges::empty(std::move(r)) ); + // PR libstdc++/100824 + // ranges::empty should treat the subexpression as an lvalue + static_assert( !std::ranges::empty(std::move(r)) ); static_assert( same_as<decltype(std::ranges::empty(std::move(r))), bool> ); } @@ -68,9 +70,29 @@ test02() VERIFY( !std::ranges::empty(so) ); } +void +test03() +{ + // PR libstdc++/100824 + // ranges::empty should treat the subexpression as an lvalue + + struct R + { + constexpr bool empty() & { return true; } + }; + static_assert( std::ranges::empty(R{}) ); + + struct R2 + { + constexpr unsigned size() & { return 0; } + }; + static_assert( std::ranges::empty(R2{}) ); +} + int main() { test01(); test02(); + test03(); } diff --git a/libstdc++-v3/testsuite/std/ranges/access/size.cc b/libstdc++-v3/testsuite/std/ranges/access/size.cc index 7293085..f25a1cb 100644 --- a/libstdc++-v3/testsuite/std/ranges/access/size.cc +++ b/libstdc++-v3/testsuite/std/ranges/access/size.cc @@ -76,12 +76,14 @@ test03() const R3& c = r; VERIFY( std::ranges::size(r) == 1 ); static_assert( noexcept(std::ranges::size(r)) ); - VERIFY( std::ranges::size(std::move(r)) == 3U ); - static_assert( !noexcept(std::ranges::size(std::move(r))) ); + // PR libstdc++/100824 + // ranges::size should treat the subexpression as an lvalue + VERIFY( std::ranges::size(std::move(r)) == 1 ); + static_assert( noexcept(std::ranges::size(std::move(r))) ); VERIFY( std::ranges::size(c) == 2L ); static_assert( !noexcept(std::ranges::size(c)) ); - VERIFY( std::ranges::size(std::move(c)) == 4UL ); - static_assert( noexcept(std::ranges::size(std::move(c))) ); + VERIFY( std::ranges::size(std::move(c)) == 2L ); + static_assert( !noexcept(std::ranges::size(std::move(c))) ); } void @@ -109,6 +111,15 @@ test05() VERIFY( std::ranges::size(r) == 1 ); } +void +test06() +{ + // PR libstdc++/100824 + // ranges::size should treat the subexpression as an lvalue + struct R { constexpr int size() & { return 42; } }; + static_assert( std::ranges::size(R{}) == 42 ); +} + int main() { @@ -117,4 +128,5 @@ main() test03(); test04(); test05(); + test06(); } |