From dcdb07abce278a65e1a8df164ce9b942da6ebd23 Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Sun, 5 Dec 2021 10:08:05 -0500 Subject: [PATCH] [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> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /Users/aodwyer/llvm-project/build2/include/c++/v1/__ranges/transform_view.h:416:25: note: in instantiation of template class 'std::ranges::transform_view' 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 --- libcxx/include/__ranges/transform_view.h | 16 ++++++---- .../range.transform/general.pass.cpp | 35 ++++++++++++++-------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h index 208a9a2..0f53fba 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 +concept __regular_invocable_with_range_ref = + regular_invocable<_Fn, range_reference_t<_View>>; + template concept __transform_view_constraints = - view<_View> && is_object_v<_Fn> && - regular_invocable<_Fn&, range_reference_t<_View>> && - __referenceable>>; + view<_View> && is_object_v<_Fn> && + regular_invocable<_Fn&, range_reference_t<_View>> && + __referenceable>>; template requires __transform_view_constraints<_View, _Fn> @@ -82,7 +86,7 @@ public: _LIBCPP_HIDE_FROM_ABI constexpr __iterator begin() const requires range && - regular_invocable> + __regular_invocable_with_range_ref { return __iterator(*this, ranges::begin(__base_)); } @@ -100,14 +104,14 @@ public: _LIBCPP_HIDE_FROM_ABI constexpr __sentinel end() const requires range && - regular_invocable> + __regular_invocable_with_range_ref { return __sentinel(ranges::end(__base_)); } _LIBCPP_HIDE_FROM_ABI constexpr __iterator end() const requires common_range && - regular_invocable> + __regular_invocable_with_range_ref { return __iterator(*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 6dd8002..6112cad 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>> -auto withRandom(R&& range, Fn func = Fn()) { - return std::ranges::transform_view(range, std::bind_front(func, badRandom())); -} - template> 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 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)); } { -- 2.7.4