diff --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h --- a/libcxx/include/__ranges/access.h +++ b/libcxx/include/__ranges/access.h @@ -27,15 +27,10 @@ #if !defined(_LIBCPP_HAS_NO_RANGES) -// clang-format off - namespace ranges { template concept __can_borrow = is_lvalue_reference_v<_Tp> || enable_borrowed_range >; - - template - concept __is_complete = requires { sizeof(_Tp); }; } // namespace ranges // [range.access.begin] @@ -61,15 +56,10 @@ struct __fn { template - requires is_array_v> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp& __t) const noexcept { - constexpr bool __complete = __is_complete >; - if constexpr (__complete) { // used to disable cryptic diagnostic - return __t + 0; - } - else { - static_assert(__complete, "`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."); - } + requires is_array_v> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp& __t) const noexcept + { + return __t; } template @@ -127,14 +117,10 @@ class __fn { public: template - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept { - constexpr bool __complete = __is_complete >; - if constexpr (__complete) { // used to disable cryptic diagnostic - return __t + _Np; - } - else { - static_assert(__complete, "`std::ranges::end` is SFINAE-unfriendly on arrays of an incomplete type."); - } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept + requires (sizeof(*__t) != 0) + { + return __t + _Np; } template @@ -209,8 +195,6 @@ inline constexpr auto cend = __cend::__fn{}; } // namespace ranges::__cpo -// clang-format off - #endif // !defined(_LIBCPP_HAS_NO_RANGES) _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/libcxx/ranges/range.access/begin.incomplete.type.pass.cpp b/libcxx/test/libcxx/ranges/range.access/begin.incomplete.type.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.access/begin.incomplete.type.pass.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// 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. + +#include +#include + +#include "test_macros.h" + +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()); +} diff --git a/libcxx/test/libcxx/ranges/range.access/end.incomplete.type.pass.cpp b/libcxx/test/libcxx/ranges/range.access/end.incomplete.type.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.access/end.incomplete.type.pass.cpp @@ -0,0 +1,46 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// 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::end +// by being SFINAE-friendly. + +#include +#include +#include + +struct Incomplete; + +constexpr bool test() +{ + { + extern Incomplete bounded[10]; + assert((!std::is_invocable_v)); + assert((!std::is_invocable_v)); + assert((!std::is_invocable_v)); + assert((!std::is_invocable_v)); + } + { + extern Incomplete unbounded[]; + assert((!std::is_invocable_v)); + assert((!std::is_invocable_v)); + assert((!std::is_invocable_v)); + assert((!std::is_invocable_v)); + } + + return true; +} + +int main(int, char**) +{ + test(); + static_assert(test()); +} diff --git a/libcxx/test/libcxx/ranges/range.access/range.access.begin/incomplete.verify.cpp b/libcxx/test/libcxx/ranges/range.access/range.access.begin/incomplete.verify.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/ranges/range.access/range.access.begin/incomplete.verify.cpp +++ /dev/null @@ -1,36 +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 -// -//===----------------------------------------------------------------------===// - -// 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 provide a better diagnostic when calling -// std::ranges::begin on an array of incomplete type. - -#include - -#include - -using begin_t = decltype(std::ranges::begin); - -template void f() requires std::invocable { } -template void f() { } - -void test() { - struct incomplete; - f(); - // expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}} - f(); - // expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}} - f(); - // expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}} - - // This is okay because calling `std::ranges::begin` on any rvalue is ill-formed. - f(); -} diff --git a/libcxx/test/libcxx/ranges/range.access/range.access.cbegin/incomplete.verify.cpp b/libcxx/test/libcxx/ranges/range.access/range.access.cbegin/incomplete.verify.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/ranges/range.access/range.access.cbegin/incomplete.verify.cpp +++ /dev/null @@ -1,32 +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 -// -//===----------------------------------------------------------------------===// - -// 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 provide a better diagnostic when calling -// std::ranges::cbegin on an array of incomplete type. - -#include - -#include - -using cbegin_t = decltype(std::ranges::cbegin); - -template void f() requires std::invocable { } -template void f() { } - -void test() { - struct incomplete; - f(); - // expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}} - - // This is okay because calling `std::ranges::end` on any rvalue is ill-formed. - f(); -} diff --git a/libcxx/test/libcxx/ranges/range.access/range.access.cend/incomplete.verify.cpp b/libcxx/test/libcxx/ranges/range.access/range.access.cend/incomplete.verify.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/ranges/range.access/range.access.cend/incomplete.verify.cpp +++ /dev/null @@ -1,38 +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 -// -//===----------------------------------------------------------------------===// - -// 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 provide a better diagnostic when calling -// std::ranges::cend on an array of incomplete type. - -#include - -#include - -using cend_t = decltype(std::ranges::cend); - -template void f() requires std::invocable { } -template void f() { } - -void test() { - struct incomplete; - f(); - // expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}} - // expected-error@*:* {{"`std::ranges::end` is SFINAE-unfriendly on arrays of an incomplete type."}} - f(); - // expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}} - // expected-error@*:* {{"`std::ranges::end` is SFINAE-unfriendly on arrays of an incomplete type."}} - f(); - // expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}} - - // This is okay because calling `std::ranges::end` on any rvalue is ill-formed. - f(); -} diff --git a/libcxx/test/libcxx/ranges/range.access/range.access.end/incomplete.verify.cpp b/libcxx/test/libcxx/ranges/range.access/range.access.end/incomplete.verify.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/ranges/range.access/range.access.end/incomplete.verify.cpp +++ /dev/null @@ -1,38 +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 -// -//===----------------------------------------------------------------------===// - -// 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 provide a better diagnostic when calling -// std::ranges::end on an array of incomplete type. - -#include - -#include - -using end_t = decltype(std::ranges::end); - -template void f() requires std::invocable { } -template void f() { } - -void test() { - struct incomplete; - f(); - // expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}} - // expected-error@*:* {{"`std::ranges::end` is SFINAE-unfriendly on arrays of an incomplete type."}} - f(); - // expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}} - // expected-error@*:* {{"`std::ranges::end` is SFINAE-unfriendly on arrays of an incomplete type."}} - f(); - // expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}} - - // This is okay because calling `std::ranges::end` on any rvalue is ill-formed. - f(); -} diff --git a/libcxx/test/libcxx/ranges/range.access/range.prim/data.incomplete.verify.cpp b/libcxx/test/libcxx/ranges/range.access/range.prim/data.incomplete.verify.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/ranges/range.access/range.prim/data.incomplete.verify.cpp +++ /dev/null @@ -1,56 +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 -// -//===----------------------------------------------------------------------===// - -// 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 provide a better diagnostic when calling -// std::ranges::data on an array of incomplete type. - -#include - -struct Incomplete; - -void f(Incomplete arr[]) { - // expected-error@*:* {{is SFINAE-unfriendly on arrays of an incomplete type.}} - // expected-error@*:* {{no matching function for call}} - std::ranges::data(arr); -} - -void f(Incomplete(&arr)[]) { - // expected-error@*:* {{is SFINAE-unfriendly on arrays of an incomplete type.}} - // expected-error@*:* {{no matching function for call}} - std::ranges::data(arr); -} - -void f(Incomplete(&&arr)[]) { - // expected-error@*:* {{is SFINAE-unfriendly on arrays of an incomplete type.}} - // expected-error@*:* {{no matching function for call}} - std::ranges::data(arr); -} - -void f2(Incomplete arr[2]) { - // expected-error@*:* {{no matching function for call}} - std::ranges::data(arr); -} - -void f(Incomplete(&arr)[2]) { - // expected-error@*:* {{no matching function for call}} - std::ranges::data(arr); -} - -void f(Incomplete(&&arr)[2]) { - // expected-error@*:* {{no matching function for call}} - std::ranges::data(arr); -} - -void f(Incomplete(&arr)[2][2]) { - // expected-error@*:* {{no matching function for call}} - std::ranges::data(arr); -} diff --git a/libcxx/test/libcxx/ranges/range.access/range.prim/empty.incomplete.verify.cpp b/libcxx/test/libcxx/ranges/range.access/range.prim/empty.incomplete.verify.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/ranges/range.access/range.prim/empty.incomplete.verify.cpp +++ /dev/null @@ -1,53 +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 -// -//===----------------------------------------------------------------------===// - -// 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 provide a better diagnostic when calling -// std::ranges::empty on an array of incomplete type. - -#include - -struct Incomplete; - -void f(Incomplete arr[]) { - // expected-error@*:* {{is SFINAE-unfriendly on arrays of an incomplete type.}} - // expected-error@*:* {{call to deleted function call operator in type}} - // expected-error@*:* {{attempt to use a deleted function}} - std::ranges::begin(arr); -} - -void f(Incomplete(&arr)[]) { - // expected-error@*:* {{is SFINAE-unfriendly on arrays of an incomplete type.}} - std::ranges::begin(arr); -} - -void f(Incomplete(&&arr)[]) { - // expected-error@*:* {{is SFINAE-unfriendly on arrays of an incomplete type.}} - std::ranges::begin(arr); -} - -void f2(Incomplete arr[2]) { - // expected-error@*:* {{call to deleted function call operator in type}} - // expected-error@*:* {{attempt to use a deleted function}} - std::ranges::begin(arr); -} - -void f(Incomplete(&arr)[2]) { - std::ranges::begin(arr); -} - -void f(Incomplete(&&arr)[2]) { - std::ranges::begin(arr); -} - -void f(Incomplete(&arr)[2][2]) { - std::ranges::begin(arr); -} diff --git a/libcxx/test/std/ranges/range.access/range.access.begin/begin.pass.cpp b/libcxx/test/std/ranges/range.access/range.access.begin/begin.pass.cpp --- a/libcxx/test/std/ranges/range.access/range.access.begin/begin.pass.cpp +++ b/libcxx/test/std/ranges/range.access/range.access.begin/begin.pass.cpp @@ -23,13 +23,17 @@ static int globalBuff[8]; -struct Incomplete; - static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); +struct Incomplete; +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + struct BeginMember { int x; constexpr const int *begin() const { return &x; } diff --git a/libcxx/test/std/ranges/range.access/range.access.end/end.pass.cpp b/libcxx/test/std/ranges/range.access/range.access.end/end.pass.cpp --- a/libcxx/test/std/ranges/range.access/range.access.end/end.pass.cpp +++ b/libcxx/test/std/ranges/range.access/range.access.end/end.pass.cpp @@ -28,6 +28,12 @@ static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); +struct Incomplete; +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + struct EndMember { int x; constexpr const int *begin() const { return nullptr; } diff --git a/libcxx/test/std/ranges/range.access/range.prim/empty.pass.cpp b/libcxx/test/std/ranges/range.access/range.prim/empty.pass.cpp --- a/libcxx/test/std/ranges/range.access/range.prim/empty.pass.cpp +++ b/libcxx/test/std/ranges/range.access/range.prim/empty.pass.cpp @@ -30,6 +30,17 @@ static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); +struct Incomplete; +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +extern Incomplete array_of_incomplete[42]; +static_assert(!std::ranges::empty(array_of_incomplete)); +static_assert(!std::ranges::empty(std::move(array_of_incomplete))); +static_assert(!std::ranges::empty(std::as_const(array_of_incomplete))); +static_assert(!std::ranges::empty(static_cast(array_of_incomplete))); + struct NonConstSizeAndEmpty { int size(); bool empty(); diff --git a/libcxx/test/std/ranges/range.access/range.prim/size.pass.cpp b/libcxx/test/std/ranges/range.access/range.prim/size.pass.cpp --- a/libcxx/test/std/ranges/range.access/range.prim/size.pass.cpp +++ b/libcxx/test/std/ranges/range.access/range.prim/size.pass.cpp @@ -25,6 +25,17 @@ static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); +struct Incomplete; +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +extern Incomplete array_of_incomplete[42]; +static_assert(std::ranges::size(array_of_incomplete) == 42); +static_assert(std::ranges::size(std::move(array_of_incomplete)) == 42); +static_assert(std::ranges::size(std::as_const(array_of_incomplete)) == 42); +static_assert(std::ranges::size(static_cast(array_of_incomplete)) == 42); + static_assert(std::semiregular>); struct SizeMember {