Commit e817c23f by Jonathan Wakely

libstdc++: Issues with range access CPOs (P2091R0)

This changes how arrays of unknown bound and/or incomplete element type
are handled.

	* include/bits/range_access.h (ranges::begin): Reject array of
	incomplete type.
	(ranges::end, ranges::size): Require arrays to be bounded.
	(ranges::data): Require lvalue or borrowed_range.
	(ranges::iterator_t): Remove constraint.
	* testsuite/std/ranges/access/begin.cc: Do not check array of
	incomplete type.
	* testsuite/std/ranges/access/begin_neg.cc: New test.
	* testsuite/std/ranges/access/end_neg.cc: Adjust expected error.
	* testsuite/std/ranges/access/size_neg.cc: Adjust expected error.
	* testsuite/std/ranges/access/ssize.cc: Do not check array of
	incomplete type.
parent 4be779f5
2020-02-20 Jonathan Wakely <jwakely@redhat.com> 2020-02-20 Jonathan Wakely <jwakely@redhat.com>
* include/bits/range_access.h (ranges::begin): Reject array of
incomplete type.
(ranges::end, ranges::size): Require arrays to be bounded.
(ranges::data): Require lvalue or borrowed_range.
(ranges::iterator_t): Remove constraint.
* testsuite/std/ranges/access/begin.cc: Do not check array of
incomplete type.
* testsuite/std/ranges/access/begin_neg.cc: New test.
* testsuite/std/ranges/access/end_neg.cc: Adjust expected error.
* testsuite/std/ranges/access/size_neg.cc: Adjust expected error.
* testsuite/std/ranges/access/ssize.cc: Do not check array of
incomplete type.
* include/std/system_error (error_category::operator<=>) * include/std/system_error (error_category::operator<=>)
(operator<=>(const error_code&, const error_code&)) (operator<=>(const error_code&, const error_code&))
(operator<=>(const error_condition&, const error_condition&)): Define (operator<=>(const error_condition&, const error_condition&)): Define
......
...@@ -382,8 +382,8 @@ namespace ranges ...@@ -382,8 +382,8 @@ namespace ranges
{ __decay_copy(__t.begin()) } -> input_or_output_iterator; { __decay_copy(__t.begin()) } -> input_or_output_iterator;
}; };
template<typename _Tp> void begin(_Tp&&) = delete; void begin(auto&) = delete;
template<typename _Tp> void begin(initializer_list<_Tp>&&) = delete; void begin(const auto&) = delete;
template<typename _Tp> template<typename _Tp>
concept __adl_begin = __class_or_enum<remove_reference_t<_Tp>> concept __adl_begin = __class_or_enum<remove_reference_t<_Tp>>
...@@ -417,7 +417,9 @@ namespace ranges ...@@ -417,7 +417,9 @@ namespace ranges
if constexpr (is_array_v<remove_reference_t<_Tp>>) if constexpr (is_array_v<remove_reference_t<_Tp>>)
{ {
static_assert(is_lvalue_reference_v<_Tp>); static_assert(is_lvalue_reference_v<_Tp>);
return __t; 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>) else if constexpr (__member_begin<_Tp>)
return __t.begin(); return __t.begin();
...@@ -433,8 +435,8 @@ namespace ranges ...@@ -433,8 +435,8 @@ namespace ranges
-> sentinel_for<decltype(_Begin{}(std::forward<_Tp>(__t)))>; -> sentinel_for<decltype(_Begin{}(std::forward<_Tp>(__t)))>;
}; };
template<typename _Tp> void end(_Tp&&) = delete; void end(auto&) = delete;
template<typename _Tp> void end(initializer_list<_Tp>&&) = delete; void end(const auto&) = delete;
template<typename _Tp> template<typename _Tp>
concept __adl_end = __class_or_enum<remove_reference_t<_Tp>> concept __adl_end = __class_or_enum<remove_reference_t<_Tp>>
...@@ -451,7 +453,7 @@ namespace ranges ...@@ -451,7 +453,7 @@ namespace ranges
static constexpr bool static constexpr bool
_S_noexcept() _S_noexcept()
{ {
if constexpr (is_array_v<remove_reference_t<_Tp>>) if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
return true; return true;
else if constexpr (__member_end<_Tp>) else if constexpr (__member_end<_Tp>)
return noexcept(__decay_copy(std::declval<_Tp&>().end())); return noexcept(__decay_copy(std::declval<_Tp&>().end()));
...@@ -461,15 +463,14 @@ namespace ranges ...@@ -461,15 +463,14 @@ namespace ranges
public: public:
template<__maybe_borrowed_range _Tp> template<__maybe_borrowed_range _Tp>
requires is_array_v<remove_reference_t<_Tp>> || __member_end<_Tp> requires is_bounded_array_v<remove_reference_t<_Tp>> || __member_end<_Tp>
|| __adl_end<_Tp> || __adl_end<_Tp>
constexpr auto 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>>) if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
{ {
static_assert(is_lvalue_reference_v<_Tp>); static_assert(is_lvalue_reference_v<_Tp>);
static_assert(is_bounded_array_v<remove_reference_t<_Tp>>);
return __t + extent_v<remove_reference_t<_Tp>>; return __t + extent_v<remove_reference_t<_Tp>>;
} }
else if constexpr (__member_end<_Tp>) else if constexpr (__member_end<_Tp>)
...@@ -519,7 +520,8 @@ namespace ranges ...@@ -519,7 +520,8 @@ namespace ranges
{ __decay_copy(__t.rbegin()) } -> input_or_output_iterator; { __decay_copy(__t.rbegin()) } -> input_or_output_iterator;
}; };
template<typename _Tp> void rbegin(_Tp&&) = delete; void rbegin(auto&) = delete;
void rbegin(const auto&) = delete;
template<typename _Tp> template<typename _Tp>
concept __adl_rbegin = __class_or_enum<remove_reference_t<_Tp>> concept __adl_rbegin = __class_or_enum<remove_reference_t<_Tp>>
...@@ -582,7 +584,8 @@ namespace ranges ...@@ -582,7 +584,8 @@ namespace ranges
-> sentinel_for<decltype(_RBegin{}(__t))>; -> sentinel_for<decltype(_RBegin{}(__t))>;
}; };
template<typename _Tp> void rend(_Tp&&) = delete; void rend(auto&) = delete;
void rend(const auto&) = delete;
template<typename _Tp> template<typename _Tp>
concept __adl_rend = __class_or_enum<remove_reference_t<_Tp>> concept __adl_rend = __class_or_enum<remove_reference_t<_Tp>>
...@@ -664,7 +667,8 @@ namespace ranges ...@@ -664,7 +667,8 @@ namespace ranges
-> __detail::__is_integer_like; -> __detail::__is_integer_like;
}; };
template<typename _Tp> void size(_Tp&&) = delete; void size(auto&) = delete;
void size(const auto&) = delete;
template<typename _Tp> template<typename _Tp>
concept __adl_size = __class_or_enum<remove_reference_t<_Tp>> concept __adl_size = __class_or_enum<remove_reference_t<_Tp>>
...@@ -691,7 +695,7 @@ namespace ranges ...@@ -691,7 +695,7 @@ namespace ranges
static constexpr bool static constexpr bool
_S_noexcept() _S_noexcept()
{ {
if constexpr (is_array_v<remove_reference_t<_Tp>>) if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
return true; return true;
else if constexpr (__member_size<_Tp>) else if constexpr (__member_size<_Tp>)
return noexcept(__decay_copy(std::declval<_Tp>().size())); return noexcept(__decay_copy(std::declval<_Tp>().size()));
...@@ -704,14 +708,13 @@ namespace ranges ...@@ -704,14 +708,13 @@ namespace ranges
public: public:
template<typename _Tp> template<typename _Tp>
requires is_array_v<remove_reference_t<_Tp>> requires is_bounded_array_v<remove_reference_t<_Tp>>
|| __member_size<_Tp> || __adl_size<_Tp> || __sentinel_size<_Tp> || __member_size<_Tp> || __adl_size<_Tp> || __sentinel_size<_Tp>
constexpr auto constexpr auto
operator()(_Tp&& __e) const noexcept(_S_noexcept<_Tp>()) operator()(_Tp&& __e) const noexcept(_S_noexcept<_Tp>())
{ {
if constexpr (is_array_v<remove_reference_t<_Tp>>) if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
{ {
static_assert(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>) else if constexpr (__member_size<_Tp>)
...@@ -826,7 +829,8 @@ namespace ranges ...@@ -826,7 +829,8 @@ namespace ranges
} }
public: public:
template<typename _Tp> requires __member_data<_Tp> || __begin_data<_Tp> template<__maybe_borrowed_range _Tp>
requires __member_data<_Tp> || __begin_data<_Tp>
constexpr auto constexpr auto
operator()(_Tp&& __e) const noexcept(_S_noexcept<_Tp>()) operator()(_Tp&& __e) const noexcept(_S_noexcept<_Tp>())
{ {
...@@ -881,8 +885,8 @@ namespace ranges ...@@ -881,8 +885,8 @@ namespace ranges
concept borrowed_range concept borrowed_range
= range<_Tp> && __detail::__maybe_borrowed_range<_Tp>; = range<_Tp> && __detail::__maybe_borrowed_range<_Tp>;
template<range _Range> template<typename _Tp>
using iterator_t = decltype(ranges::begin(std::declval<_Range&>())); using iterator_t = decltype(ranges::begin(std::declval<_Tp&>()));
template<range _Range> template<range _Range>
using sentinel_t = decltype(ranges::end(std::declval<_Range&>())); using sentinel_t = decltype(ranges::end(std::declval<_Range&>()));
......
...@@ -36,10 +36,10 @@ test01() ...@@ -36,10 +36,10 @@ test01()
constexpr long b[2] = { }; constexpr long b[2] = { };
static_assert( std::ranges::begin(b) == (b + 0) ); static_assert( std::ranges::begin(b) == (b + 0) );
struct Incomplete; struct X { };
using A = Incomplete[]; // unbounded array of incomplete type using A = X[]; // unbounded array
extern A& f(); extern A& f();
static_assert( same_as<decltype(std::ranges::begin(f())), Incomplete*> ); static_assert( same_as<decltype(std::ranges::begin(f())), X*> );
} }
void void
......
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++2a" }
// { dg-do compile { target c++2a } }
#include <ranges>
auto
test01()
{
using A = int[2];
extern A&& f(); // rvalue of type that doesn't satisfy borrowed_range
return std::ranges::begin(f()); // { dg-error "no match" }
}
struct incomplete;
extern incomplete array[2];
auto
test02()
{
return std::ranges::begin(array); // { dg-error "here" }
}
// { dg-error "incomplete type" "" { target *-*-* } 0 }
...@@ -25,9 +25,8 @@ extern int unbounded[]; ...@@ -25,9 +25,8 @@ extern int unbounded[];
auto auto
test01() test01()
{ {
return std::ranges::end(unbounded); // { dg-error "here" } return std::ranges::end(unbounded); // { dg-error "no match" }
} }
// { dg-error "static assertion failed" "" { target *-*-* } 0 }
struct incomplete; struct incomplete;
extern incomplete array[2]; extern incomplete array[2];
...@@ -38,5 +37,3 @@ test02() ...@@ -38,5 +37,3 @@ test02()
return std::ranges::end(array); // { dg-error "here" } return std::ranges::end(array); // { dg-error "here" }
} }
// { dg-error "incomplete type" "" { target *-*-* } 0 } // { dg-error "incomplete type" "" { target *-*-* } 0 }
...@@ -25,6 +25,5 @@ extern int unbounded[]; ...@@ -25,6 +25,5 @@ extern int unbounded[];
auto auto
test01() test01()
{ {
return std::ranges::size(unbounded); // { dg-error "here" } return std::ranges::size(unbounded); // { dg-error "no match" }
} }
// { dg-error "static assertion failed" "" { target *-*-* } 0 }
...@@ -36,11 +36,6 @@ test01() ...@@ -36,11 +36,6 @@ test01()
static_assert( std::same_as<decltype(std::ranges::ssize(a2)), ptrdiff_t> ); static_assert( std::same_as<decltype(std::ranges::ssize(a2)), ptrdiff_t> );
VERIFY( std::ranges::ssize(a2) == 2); VERIFY( std::ranges::ssize(a2) == 2);
static_assert( noexcept(std::ranges::ssize(a2)) ); static_assert( noexcept(std::ranges::ssize(a2)) );
struct Incomplete;
using A = Incomplete[2]; // bounded array of incomplete type
extern A& f();
static_assert( std::same_as<decltype(std::ranges::ssize(f())), ptrdiff_t> );
} }
void void
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment