aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArthur O'Dwyer <arthur.j.odwyer@gmail.com>2021-12-15 22:10:34 -0500
committerArthur O'Dwyer <arthur.j.odwyer@gmail.com>2021-12-22 10:33:17 -0500
commit8ad364ad2123af98f24050417710f975b8816a90 (patch)
treee8b394e8c91a315266680c36368116c3f0285afe
parent9075009d1fd5f2bf9aa6c2f362d2993691a316b3 (diff)
downloadllvm-8ad364ad2123af98f24050417710f975b8816a90.zip
llvm-8ad364ad2123af98f24050417710f975b8816a90.tar.gz
llvm-8ad364ad2123af98f24050417710f975b8816a90.tar.bz2
[libc++] [ranges] Remove the static_assert from ranges::begin and ranges::end.
As discussed with ldionne. The problem with this static_assert is that it makes ranges::begin a pitfall for anyone ever to use inside a constraint or decltype. Many Ranges things, such as ranges::size, are specified as "Does X if X is well-formed, or else Y if Y is well-formed, or else `ranges::end(t) - ranges::begin(t)` if that is well-formed, or else..." And if there's a static_assert hidden inside `ranges::begin(t)`, then you get a hard error as soon as you ask the question -- even if the answer would have been "no, that's not well-formed"! Constraining on `requires { t + 0; }` or `requires { t + N; }` is verboten because of https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103700 . For ranges::begin, we can just decay to a pointer even in the incomplete-type case. For ranges::end, we can safely constrain on `sizeof(*t)`. Yes, this means that an array of incomplete type has a `ranges::begin` but no `ranges::end`... just like an unbounded array of complete type. This is a valid manifestation of IFNDR. All of the new libcxx/test/std/ cases are mandatory behavior, as far as I'm aware. Tests for the IFNDR cases in ranges::begin and ranges::end remain in `libcxx/test/libcxx/`. The similar tests for ranges::empty and ranges::data were simply wrong, AFAIK. Differential Revision: https://reviews.llvm.org/D115838
-rw-r--r--libcxx/include/__ranges/access.h32
-rw-r--r--libcxx/test/libcxx/ranges/range.access/begin.incomplete_type.sh.cpp74
-rw-r--r--libcxx/test/libcxx/ranges/range.access/end.incomplete_type.pass.cpp46
-rw-r--r--libcxx/test/libcxx/ranges/range.access/range.access.begin/incomplete.verify.cpp36
-rw-r--r--libcxx/test/libcxx/ranges/range.access/range.access.cbegin/incomplete.verify.cpp32
-rw-r--r--libcxx/test/libcxx/ranges/range.access/range.access.cend/incomplete.verify.cpp38
-rw-r--r--libcxx/test/libcxx/ranges/range.access/range.access.end/incomplete.verify.cpp38
-rw-r--r--libcxx/test/libcxx/ranges/range.access/range.prim/data.incomplete.verify.cpp56
-rw-r--r--libcxx/test/libcxx/ranges/range.access/range.prim/empty.incomplete.verify.cpp53
-rw-r--r--libcxx/test/std/ranges/range.access/range.access.begin/begin.pass.cpp8
-rw-r--r--libcxx/test/std/ranges/range.access/range.access.end/end.pass.cpp6
-rw-r--r--libcxx/test/std/ranges/range.access/range.prim/empty.pass.cpp11
-rw-r--r--libcxx/test/std/ranges/range.access/range.prim/size.pass.cpp11
13 files changed, 162 insertions, 279 deletions
diff --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h
index b0b89c0..d54b331 100644
--- a/libcxx/include/__ranges/access.h
+++ b/libcxx/include/__ranges/access.h
@@ -27,15 +27,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if !defined(_LIBCPP_HAS_NO_RANGES)
-// clang-format off
-
namespace ranges {
template <class _Tp>
concept __can_borrow =
is_lvalue_reference_v<_Tp> || enable_borrowed_range<remove_cvref_t<_Tp> >;
-
- template<class _Tp>
- concept __is_complete = requires { sizeof(_Tp); };
} // namespace ranges
// [range.access.begin]
@@ -61,15 +56,10 @@ namespace ranges::__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 {
- constexpr bool __complete = __is_complete<iter_value_t<_Tp> >;
- 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<remove_cv_t<_Tp>>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp& __t) const noexcept
+ {
+ return __t;
}
template <class _Tp>
@@ -127,14 +117,10 @@ namespace ranges::__end {
class __fn {
public:
template <class _Tp, size_t _Np>
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept {
- constexpr bool __complete = __is_complete<remove_cv_t<_Tp> >;
- 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) // Disallow incomplete element types.
+ {
+ return __t + _Np;
}
template <class _Tp>
@@ -209,8 +195,6 @@ namespace ranges::inline __cpo {
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.sh.cpp b/libcxx/test/libcxx/ranges/range.access/begin.incomplete_type.sh.cpp
new file mode 100644
index 0000000..0bf3599
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.access/begin.incomplete_type.sh.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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());
+}
+
+#endif // defined(TU1)
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
index 0000000..4b83ca8
--- /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 <ranges>
+#include <cassert>
+#include <type_traits>
+
+struct Incomplete;
+
+constexpr bool test()
+{
+ {
+ extern Incomplete bounded[10];
+ assert((!std::is_invocable_v<decltype(std::ranges::end), decltype((bounded))>));
+ assert((!std::is_invocable_v<decltype(std::ranges::cend), decltype((bounded))>));
+ assert((!std::is_invocable_v<decltype(std::ranges::end), decltype(std::as_const(bounded))>));
+ assert((!std::is_invocable_v<decltype(std::ranges::cend), decltype(std::as_const(bounded))>));
+ }
+ {
+ extern Incomplete unbounded[];
+ assert((!std::is_invocable_v<decltype(std::ranges::end), decltype((unbounded))>));
+ assert((!std::is_invocable_v<decltype(std::ranges::cend), decltype((unbounded))>));
+ assert((!std::is_invocable_v<decltype(std::ranges::end), decltype(std::as_const(unbounded))>));
+ assert((!std::is_invocable_v<decltype(std::ranges::cend), decltype(std::as_const(unbounded))>));
+ }
+
+ 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
index 4132252..0000000
--- 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 <ranges>
-
-#include <type_traits>
-
-using begin_t = decltype(std::ranges::begin);
-
-template <class T> void f() requires std::invocable<begin_t&, T> { }
-template <class T> void f() { }
-
-void test() {
- struct incomplete;
- f<incomplete(&)[]>();
- // expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}}
- f<incomplete(&)[10]>();
- // expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}}
- f<incomplete(&)[2][2]>();
- // 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<incomplete(&&)[10]>();
-}
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
index bd0ff5e..0000000
--- 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 <ranges>
-
-#include <type_traits>
-
-using cbegin_t = decltype(std::ranges::cbegin);
-
-template <class T> void f() requires std::invocable<cbegin_t&, T> { }
-template <class T> void f() { }
-
-void test() {
- struct incomplete;
- f<incomplete(&)[10]>();
- // 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<incomplete(&&)[10]>();
-}
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
index b4d6729..0000000
--- 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 <ranges>
-
-#include <type_traits>
-
-using cend_t = decltype(std::ranges::cend);
-
-template <class T> void f() requires std::invocable<cend_t&, T> { }
-template <class T> void f() { }
-
-void test() {
- struct incomplete;
- f<incomplete(&)[]>();
- // 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<incomplete(&)[10]>();
- // 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<incomplete(&)[2][2]>();
- // 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<incomplete(&&)[10]>();
-}
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
index ecfe1c4..0000000
--- 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 <ranges>
-
-#include <type_traits>
-
-using end_t = decltype(std::ranges::end);
-
-template <class T> void f() requires std::invocable<end_t&, T> { }
-template <class T> void f() { }
-
-void test() {
- struct incomplete;
- f<incomplete(&)[]>();
- // 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<incomplete(&)[10]>();
- // 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<incomplete(&)[2][2]>();
- // 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<incomplete(&&)[10]>();
-}
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
index f673280..0000000
--- 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 <ranges>
-
-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
index 3b5f79a5..0000000
--- 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 <ranges>
-
-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
index 3b712a1..05e6211 100644
--- 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 @@ using RangeCBeginT = decltype(std::ranges::cbegin)&;
static int globalBuff[8];
-struct Incomplete;
-
static_assert(!std::is_invocable_v<RangeBeginT, int (&&)[10]>);
static_assert( std::is_invocable_v<RangeBeginT, int (&)[10]>);
static_assert(!std::is_invocable_v<RangeBeginT, int (&&)[]>);
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<RangeCBeginT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[42]>);
+
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
index dd5da2e..1e4fc02 100644
--- 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<RangeEndT, int (&)[]>);
static_assert(!std::is_invocable_v<RangeEndT, int (&&)[10]>);
static_assert( std::is_invocable_v<RangeEndT, int (&)[10]>);
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeEndT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeEndT, Incomplete(&&)[42]>);
+static_assert(!std::is_invocable_v<RangeCEndT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeCEndT, Incomplete(&&)[42]>);
+
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
index 5a06fa8..9be523e 100644
--- 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<RangeEmptyT, int (&&)[1]>);
static_assert( std::is_invocable_v<RangeEmptyT, int (&)[1]>);
static_assert( std::is_invocable_v<RangeEmptyT, const int (&)[1]>);
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeEmptyT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeEmptyT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeEmptyT, Incomplete(&&)[]>);
+
+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<const Incomplete(&&)[42]>(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
index 1744974..92023cc 100644
--- 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<RangeSizeT, int[1]>);
static_assert( std::is_invocable_v<RangeSizeT, int (&&)[1]>);
static_assert( std::is_invocable_v<RangeSizeT, int (&)[1]>);
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeSizeT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeSizeT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeSizeT, Incomplete(&&)[]>);
+
+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<const Incomplete(&&)[42]>(array_of_incomplete)) == 42);
+
static_assert(std::semiregular<std::remove_cv_t<RangeSizeT>>);
struct SizeMember {