-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[libc++][ranges] Fix ranges::to
with ADL-only begin
/end
#119161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
When implementing the resolution of LWG4016, the `ranges::for_each` call to reduce inclusion dependency. However, the inlining was not always correct, because builtin-in range-`for` may just stop and fail when a deleted member `begin`/`end` function is encountered, while `ranges` CPOs continue to consider ADL-found `begin`/`end`. As a result, we should still use `ranges::begin`/`ranges::end`.
@llvm/pr-subscribers-libcxx Author: A. Jiang (frederick-vs-ja) ChangesWhen implementing the resolution of LWG4016, the As a result, we should still use Follows-up #113103. Fixes #119133. Full diff: https://github.com/llvm/llvm-project/pull/119161.diff 2 Files Affected:
diff --git a/libcxx/include/__ranges/to.h b/libcxx/include/__ranges/to.h
index c937b0656de87d..360c33ee8f2083 100644
--- a/libcxx/include/__ranges/to.h
+++ b/libcxx/include/__ranges/to.h
@@ -109,8 +109,11 @@ template <class _Container, input_range _Range, class... _Args>
__result.reserve(static_cast<range_size_t<_Container>>(ranges::size(__range)));
}
- for (auto&& __ref : __range) {
- using _Ref = decltype(__ref);
+ auto __iter = ranges::begin(__range);
+ auto __sent = ranges::end(__range);
+ for (; __iter != __sent; ++__iter) {
+ auto&& __ref = *__iter;
+ using _Ref = decltype(__ref);
if constexpr (requires { __result.emplace_back(std::declval<_Ref>()); }) {
__result.emplace_back(std::forward<_Ref>(__ref));
} else if constexpr (requires { __result.push_back(std::declval<_Ref>()); }) {
diff --git a/libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp b/libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp
index a983745fd636e8..26c7af6d88479b 100644
--- a/libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp
+++ b/libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp
@@ -550,11 +550,30 @@ constexpr void test_recursive() {
assert(std::ranges::to<C4>(in_owning_view) == result);
}
+struct adl_only_range {
+ static constexpr int numbers[2]{42, 1729};
+
+ void begin() const = delete;
+ void end() const = delete;
+
+ friend constexpr const int* begin(const adl_only_range&) { return std::ranges::begin(numbers); }
+ friend constexpr const int* end(const adl_only_range&) { return std::ranges::end(numbers); }
+};
+
+constexpr void test_lwg4016_regression() {
+ using Cont = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::PushBack, true>;
+
+ std::ranges::contiguous_range auto r = adl_only_range{};
+ auto v = r | std::ranges::to<Cont>();
+ assert(std::ranges::equal(v, adl_only_range::numbers));
+}
+
constexpr bool test() {
test_constraints();
test_ctr_choice_order();
test_lwg_3785();
test_recursive();
+ test_lwg4016_regression();
return true;
}
|
libcxx/include/__ranges/to.h
Outdated
for (auto&& __ref : __range) { | ||
using _Ref = decltype(__ref); | ||
auto __iter = ranges::begin(__range); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest we use ranges::for_each
instead, that seems cleaner and I don't think saving an include dependency is worth the added code complexity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also wonder whether this would improve interactions with deque
iterators.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched to use ranges::for_each
. I wonder whether range-for on ref_view{__range}
suffices.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is somehow bad, as encountered in #113103 (comment).
When implementing the resolution of LWG4016, the
ranges::for_each
call was omitted to reduce inclusion dependency. However, the inlining was not always correct, because builtin-in range-for
may just stop and fail when a deleted memberbegin
/end
function is encountered, whileranges
CPOs continue to consider ADL-foundbegin
/end
.As a result, we should still use
ranges::begin
/ranges::end
.Follows-up #113103. Fixes #119133.