[libcxx-commits] [libcxx] dcdb07a - [libc++] Work around a Clang bug in transform_view, and regression-test.

Arthur O'Dwyer via libcxx-commits libcxx-commits at lists.llvm.org
Mon Dec 6 08:20:22 PST 2021


Author: Arthur O'Dwyer
Date: 2021-12-06T11:20:08-05:00
New Revision: dcdb07abce278a65e1a8df164ce9b942da6ebd23

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

LOG: [libc++] Work around a Clang bug in transform_view, and regression-test.

Clang trunk rejects the new test case, but this is a Clang bug
(PR47414, 47509, 50864, 44833).

```
In module 'std' imported from /Users/aodwyer/llvm-project/libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp:17:
/Users/aodwyer/llvm-project/build2/include/c++/v1/__ranges/transform_view.h:85:44: error: constraints not satisfied for alias template 'range_reference_t' [with _Rp = const NonConstView]
             regular_invocable<const _Fn&, range_reference_t<const _View>>
                                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/aodwyer/llvm-project/build2/include/c++/v1/__ranges/transform_view.h:416:25: note: in instantiation of template class 'std::ranges::transform_view<NonConstView, (lambda at /Users/aodwyer/llvm-project/libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp:73:71)>' requested here
      -> decltype(      transform_view(_VSTD::forward<_Range>(__range), _VSTD::forward<_Fn>(__f)))
                        ^
```

We can work around this by adding a layer of indirection: put the
problematic constraint into a named concept and Clang becomes more
amenable to SFINAE'ing instead of hard-erroring.

Drive-by simplify `range.transform/general.pass.cpp` to make it clearer
what it's actually testing in this area.

Differential Revision: https://reviews.llvm.org/D115116

Added: 
    

Modified: 
    libcxx/include/__ranges/transform_view.h
    libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 208a9a22694a7..0f53fbaa7e686 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -46,11 +46,15 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
+template<class _Fn, class _View>
+concept __regular_invocable_with_range_ref =
+  regular_invocable<_Fn, range_reference_t<_View>>;
+
 template<class _View, class _Fn>
 concept __transform_view_constraints =
-           view<_View> && is_object_v<_Fn> &&
-           regular_invocable<_Fn&, range_reference_t<_View>> &&
-           __referenceable<invoke_result_t<_Fn&, range_reference_t<_View>>>;
+  view<_View> && is_object_v<_Fn> &&
+  regular_invocable<_Fn&, range_reference_t<_View>> &&
+  __referenceable<invoke_result_t<_Fn&, range_reference_t<_View>>>;
 
 template<input_range _View, copy_constructible _Fn>
   requires __transform_view_constraints<_View, _Fn>
@@ -82,7 +86,7 @@ class transform_view : public view_interface<transform_view<_View, _Fn>> {
   _LIBCPP_HIDE_FROM_ABI
   constexpr __iterator<true> begin() const
     requires range<const _View> &&
-             regular_invocable<const _Fn&, range_reference_t<const _View>>
+             __regular_invocable_with_range_ref<const _Fn&, const _View>
   {
     return __iterator<true>(*this, ranges::begin(__base_));
   }
@@ -100,14 +104,14 @@ class transform_view : public view_interface<transform_view<_View, _Fn>> {
   _LIBCPP_HIDE_FROM_ABI
   constexpr __sentinel<true> end() const
     requires range<const _View> &&
-             regular_invocable<const _Fn&, range_reference_t<const _View>>
+             __regular_invocable_with_range_ref<const _Fn&, const _View>
   {
     return __sentinel<true>(ranges::end(__base_));
   }
   _LIBCPP_HIDE_FROM_ABI
   constexpr __iterator<true> end() const
     requires common_range<const _View> &&
-             regular_invocable<const _Fn&, range_reference_t<const _View>>
+             __regular_invocable_with_range_ref<const _Fn&, const _View>
   {
     return __iterator<true>(*this, ranges::end(__base_));
   }

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp
index 6dd80025289c8..6112cad0211c0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/general.pass.cpp
@@ -41,13 +41,6 @@ auto toUpper(R range) {
   return std::ranges::transform_view(range, [](char c) { return std::toupper(c); });
 }
 
-unsigned badRandom() { return 42; }
-
-template<std::ranges::range R, class Fn = std::plus<std::iter_value_t<R>>>
-auto withRandom(R&& range, Fn func = Fn()) {
-  return std::ranges::transform_view(range, std::bind_front(func, badRandom()));
-}
-
 template<class E1, class E2, size_t N, class Join = std::plus<E1>>
 auto joinArrays(E1 (&a)[N], E2 (&b)[N], Join join = Join()) {
   return std::ranges::transform_view(a, [&a, &b, join](auto& x) {
@@ -56,12 +49,30 @@ auto joinArrays(E1 (&a)[N], E2 (&b)[N], Join join = Join()) {
   });
 }
 
+struct NonConstView : std::ranges::view_base {
+  explicit NonConstView(int *b, int *e) : b_(b), e_(e) {}
+  const int *begin() { return b_; }  // deliberately non-const
+  const int *end() { return e_; }    // deliberately non-const
+  const int *b_;
+  const int *e_;
+};
+
 int main(int, char**) {
   {
-    std::vector vec = {1, 2, 3, 4};
-    auto sortOfRandom = withRandom(vec);
-    std::vector check = {43, 44, 45, 46};
-    assert(std::equal(sortOfRandom.begin(), sortOfRandom.end(), check.begin(), check.end()));
+    std::vector<int> vec = {1, 2, 3, 4};
+    auto transformed = std::ranges::transform_view(vec, [](int x) { return x + 42; });
+    int expected[] = {43, 44, 45, 46};
+    assert(std::equal(transformed.begin(), transformed.end(), expected, expected + 4));
+    const auto& ct = transformed;
+    assert(std::equal(ct.begin(), ct.end(), expected, expected + 4));
+  }
+
+  {
+    // Test a view type that is not const-iterable.
+    int a[] = {1, 2, 3, 4};
+    auto transformed = NonConstView(a, a + 4) | std::views::transform([](int x) { return x + 42; });
+    int expected[4] = {43, 44, 45, 46};
+    assert(std::equal(transformed.begin(), transformed.end(), expected, expected + 4));
   }
 
   {
@@ -69,7 +80,7 @@ int main(int, char**) {
     int b[4] = {4, 3, 2, 1};
     auto out = joinArrays(a, b);
     int check[4] = {5, 5, 5, 5};
-    assert(std::equal(out.begin(), out.end(), check));
+    assert(std::equal(out.begin(), out.end(), check, check + 4));
   }
 
   {


        


More information about the libcxx-commits mailing list