[libcxx-commits] [libcxx] 8ad364a - [libc++] [ranges] Remove the static_assert from ranges::begin and ranges::end.

Arthur O'Dwyer via libcxx-commits libcxx-commits at lists.llvm.org
Wed Dec 22 07:37:21 PST 2021


Author: Arthur O'Dwyer
Date: 2021-12-22T10:33:17-05:00
New Revision: 8ad364ad2123af98f24050417710f975b8816a90

URL: https://github.com/llvm/llvm-project/commit/8ad364ad2123af98f24050417710f975b8816a90
DIFF: https://github.com/llvm/llvm-project/commit/8ad364ad2123af98f24050417710f975b8816a90.diff

LOG: [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

Added: 
    libcxx/test/libcxx/ranges/range.access/begin.incomplete_type.sh.cpp
    libcxx/test/libcxx/ranges/range.access/end.incomplete_type.pass.cpp

Modified: 
    libcxx/include/__ranges/access.h
    libcxx/test/std/ranges/range.access/range.access.begin/begin.pass.cpp
    libcxx/test/std/ranges/range.access/range.access.end/end.pass.cpp
    libcxx/test/std/ranges/range.access/range.prim/empty.pass.cpp
    libcxx/test/std/ranges/range.access/range.prim/size.pass.cpp

Removed: 
    libcxx/test/libcxx/ranges/range.access/range.access.begin/incomplete.verify.cpp
    libcxx/test/libcxx/ranges/range.access/range.access.cbegin/incomplete.verify.cpp
    libcxx/test/libcxx/ranges/range.access/range.access.cend/incomplete.verify.cpp
    libcxx/test/libcxx/ranges/range.access/range.access.end/incomplete.verify.cpp
    libcxx/test/libcxx/ranges/range.access/range.prim/data.incomplete.verify.cpp
    libcxx/test/libcxx/ranges/range.access/range.prim/empty.incomplete.verify.cpp


################################################################################
diff  --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h
index b0b89c0eeea3a..d54b3314e4359 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 0000000000000..0bf359900145e
--- /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 0000000000000..4b83ca8945a31
--- /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 41322529792e2..0000000000000
--- 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 bd0ff5e85dd20..0000000000000
--- 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 b4d6729c9547b..0000000000000
--- 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 ecfe1c4e5a5a2..0000000000000
--- 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 f67328084c6b0..0000000000000
--- 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 3b5f79a5bdf5a..0000000000000
--- 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 3b712a1cc1377..05e6211f7a541 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 dd5da2ebf5944..1e4fc0213fc4f 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 5a06fa88348a2..9be523eb42b7b 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 17449744cd515..92023cc6a2873 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 {


        


More information about the libcxx-commits mailing list