[llvm-branch-commits] [libcxx] 9f9ea70 - [libc++] No longer support ranges::begin(x) when x is an array of incomplete type.

Arthur O'Dwyer via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Feb 4 13:13:17 PST 2022


Author: Arthur O'Dwyer
Date: 2022-02-04T16:12:35-05:00
New Revision: 9f9ea707d0c6f2839a52a7045a35ea3356d851be

URL: https://github.com/llvm/llvm-project/commit/9f9ea707d0c6f2839a52a7045a35ea3356d851be
DIFF: https://github.com/llvm/llvm-project/commit/9f9ea707d0c6f2839a52a7045a35ea3356d851be.diff

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

(cherry picked from commit cc1d02ba2d177e0d31561934337f5412167f6954)

Added: 
    

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

Removed: 
    libcxx/test/libcxx/ranges/range.access/begin.incomplete_type.sh.cpp


################################################################################
diff  --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h
index 67c6c57bd81e4..07a92d7834755 100644
--- a/libcxx/include/__ranges/access.h
+++ b/libcxx/include/__ranges/access.h
@@ -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;
     }

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
deleted file mode 100644
index cc27789c04f3d..0000000000000
--- a/libcxx/test/libcxx/ranges/range.access/begin.incomplete_type.sh.cpp
+++ /dev/null
@@ -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)

diff  --git a/libcxx/test/std/ranges/range.access/begin.pass.cpp b/libcxx/test/std/ranges/range.access/begin.pass.cpp
index 07b1a3b89a7f1..7014681ccabf9 100644
--- a/libcxx/test/std/ranges/range.access/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/begin.pass.cpp
@@ -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;


        


More information about the llvm-branch-commits mailing list