[libc++] No longer support ranges::begin(x) when x is an array of incomplete type.
var-const points out that `ranges::begin` is (non-normatively but explicitly) always supposed to return a `std::input_or_output_iterator`, and `Incomplete*` is not a `std::input_or_output_iterator` because it has no `operator++`. Therefore, we should never return `Incomplete*` from `ranges::begin(x)`, even when `x` is `Incomplete(&)[]`. Instead, just SFINAE away. Differential Revision: https://reviews.llvm.org/D118963
This commit is contained in:
parent
c67c9cfe3f
commit
cc1d02ba2d
|
@ -59,10 +59,17 @@ namespace __begin {
|
|||
|
||||
struct __fn {
|
||||
template <class _Tp>
|
||||
requires is_array_v<remove_cv_t<_Tp>>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp& __t) const noexcept
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[]) const noexcept
|
||||
requires (sizeof(_Tp) != 0) // Disallow incomplete element types.
|
||||
{
|
||||
return __t;
|
||||
return __t + 0;
|
||||
}
|
||||
|
||||
template <class _Tp, size_t _Np>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept
|
||||
requires (sizeof(_Tp) != 0) // Disallow incomplete element types.
|
||||
{
|
||||
return __t + 0;
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
|
@ -127,7 +134,7 @@ namespace __end {
|
|||
public:
|
||||
template <class _Tp, size_t _Np>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept
|
||||
requires (sizeof(*__t) != 0) // Disallow incomplete element types.
|
||||
requires (sizeof(_Tp) != 0) // Disallow incomplete element types.
|
||||
{
|
||||
return __t + _Np;
|
||||
}
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// RUN: %{cxx} %{flags} %{compile_flags} -c %s -o %t.tu1.o -DTU1
|
||||
// RUN: %{cxx} %{flags} %{compile_flags} -c %s -o %t.tu2.o -DTU2
|
||||
// RUN: %{cxx} %t.tu1.o %t.tu2.o %{flags} %{link_flags} -o %t.exe
|
||||
// RUN: %{exec} %t.exe
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
|
||||
// Test the libc++-specific behavior that we handle the IFNDR case for ranges::begin
|
||||
// by returning the beginning of the array-of-incomplete-type.
|
||||
// Use two translation units so that `Incomplete` really is never completed
|
||||
// at any point within TU2, but the array `bounded` is still given a definition
|
||||
// (in TU1) to avoid an "undefined reference" error from the linker.
|
||||
// All of the actually interesting stuff takes place within TU2.
|
||||
|
||||
#include <ranges>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
#if defined(TU1)
|
||||
|
||||
struct Incomplete {};
|
||||
Incomplete bounded[10];
|
||||
Incomplete unbounded[10];
|
||||
|
||||
#else // defined(TU1)
|
||||
|
||||
struct Incomplete;
|
||||
|
||||
constexpr bool test()
|
||||
{
|
||||
{
|
||||
extern Incomplete bounded[10];
|
||||
assert(std::ranges::begin(bounded) == bounded);
|
||||
assert(std::ranges::cbegin(bounded) == bounded);
|
||||
assert(std::ranges::begin(std::as_const(bounded)) == bounded);
|
||||
assert(std::ranges::cbegin(std::as_const(bounded)) == bounded);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::begin(bounded)), Incomplete*);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(bounded)), const Incomplete*);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::begin(std::as_const(bounded))), const Incomplete*);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(std::as_const(bounded))), const Incomplete*);
|
||||
}
|
||||
{
|
||||
extern Incomplete unbounded[];
|
||||
assert(std::ranges::begin(unbounded) == unbounded);
|
||||
assert(std::ranges::cbegin(unbounded) == unbounded);
|
||||
assert(std::ranges::begin(std::as_const(unbounded)) == unbounded);
|
||||
assert(std::ranges::cbegin(std::as_const(unbounded)) == unbounded);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::begin(unbounded)), Incomplete*);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(unbounded)), const Incomplete*);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::begin(std::as_const(unbounded))), const Incomplete*);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(std::as_const(unbounded))), const Incomplete*);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
test();
|
||||
static_assert(test());
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // defined(TU1)
|
|
@ -31,9 +31,26 @@ static_assert( std::is_invocable_v<RangeBeginT, int (&)[]>);
|
|||
|
||||
struct Incomplete;
|
||||
static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[]>);
|
||||
static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[42]>);
|
||||
static_assert(!std::is_invocable_v<RangeBeginT, const Incomplete(&&)[]>);
|
||||
static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[]>);
|
||||
static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[42]>);
|
||||
static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete(&&)[]>);
|
||||
|
||||
static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[10]>);
|
||||
static_assert(!std::is_invocable_v<RangeBeginT, const Incomplete(&&)[10]>);
|
||||
static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[10]>);
|
||||
static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete(&&)[10]>);
|
||||
|
||||
// This case is IFNDR; we handle it SFINAE-friendly.
|
||||
LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, Incomplete(&)[]>);
|
||||
LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, const Incomplete(&)[]>);
|
||||
LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, Incomplete(&)[]>);
|
||||
LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, const Incomplete(&)[]>);
|
||||
|
||||
// This case is IFNDR; we handle it SFINAE-friendly.
|
||||
LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, Incomplete(&)[10]>);
|
||||
LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, const Incomplete(&)[10]>);
|
||||
LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, Incomplete(&)[10]>);
|
||||
LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, const Incomplete(&)[10]>);
|
||||
|
||||
struct BeginMember {
|
||||
int x;
|
||||
|
|
Loading…
Reference in a new issue