It was missing the cast to bool in bool(__t.empty()).
It was wrongly using std::forward in some places.
Details
- Reviewers
ldionne var-const Mordante philnik • Quuxplusone - Group Reviewers
Restricted Project - Commits
- rGa2a9a5c7d3d9: [libc++] [ranges] Fix bugs in ranges::empty().
Diff Detail
Event Timeline
libcxx/include/__ranges/empty.h | ||
---|---|---|
59–60 | Note the sneaky requires-clause hiding in among the "write it three times" business. template<class _Tp> concept __forward_iterable = forward_iterator<decltype(ranges::begin(declval<_Tp&>()))>; template <__forward_iterable _Tp> _LIBCPP_HIDE_FROM_ABI static constexpr auto __go(_Tp&& __t, __priority_tag<0>) noexcept(noexcept(bool(ranges::begin(__t) == ranges::end(__t)))) -> decltype( bool(ranges::begin(__t) == ranges::end(__t))) { return bool(ranges::begin(__t) == ranges::end(__t)); } but I'm ambivalent on whether that's better. |
I don't especially like the fact that we're moving away from requires clauses and back to straight SFINAE. Can you check what effect this has on diagnostics?
libcxx/include/__ranges/empty.h | ||
---|---|---|
67 | Old: 32 lines; new: 8 lines. I would call the new version "frustratingly opaque" but the old version "annoyingly verbose." |
libcxx/include/__ranges/empty.h | ||
---|---|---|
67 | Honestly, the output after is pretty terrible. It doesn't show anything at all, so it's basically impossible to know what's going wrong. I don't think we want to use this new technique in our ranges implementation. I would rather we revert to the more verbose and arguably not so great technique we had before, where we have to manually exclude other overloads. |
I added a late-breaking value-category-related test case to the ranges::empty test; this fixes that too.
Thanks for fixing those bugs. We talked about it offline and I wasn't able to come up with a significantly better solution than the explicit mutual exclusion of concepts we had before, however I'm still not really comfortable moving to this 100% SFINAE based approach due to diagnostics concerns. If we do it here, one could argue that we should do it everywhere, cause it *is* simpler from a writing-the-code perspective. But I don't want to encourage that general direction.
So IMO we should fix the bugs you're fixing without changing the approach we had, i.e. the somewhat clunky helper concepts we used to define (and which is consistent with the approach taken in the rest of ranges).
Review comments.
Update with my proposed new-and-improved way of getting reasonable diagnostics when the SFINAE fails.
Guess what? We had a test/libcxx/ test that was supposed to test these diagnostics, but it was testing ranges::begin instead of ranges::empty.
New-and-improved CPO design coming to all the other ranges.access CPOs, in a PR depending on this one that I'll upload shortly.
Does removing the old parent revision make buildkite happier about applying the patch?
Can you please:
- Fix the bug(s) you are fixing without using a new CPO design
- Open a different review that proposes a new CPO design so we can discuss it separately
Scale back to just the bugfixes I'm aware of; isolate the refactoring into a separate (and still to-be-written) PR.
libcxx/test/std/ranges/range.access/empty.pass.cpp | ||
---|---|---|
157–164 | Why the == false and == true? Seems weird. |
Accepting-as-libc++ to land this. (@ldionne is out; but his "requested changes" applied to the previous version of this PR, which did a lot of rewriting in addition to the bugfixes. I believe scaling it back to just the bugfixes addresses those comments sufficiently.)
libcxx/test/std/ranges/range.access/empty.pass.cpp | ||
---|---|---|
157–164 | As mentioned in the other review, these tests don't really verify return types at all, so here I infer that the writer was trying to sort of hint at "...and empty(e) should return bool(true), not just anything that happens to be truthy." Of course the compiler doesn't care what you're hinting at, so this isn't very useful as a test. ;) I think I'll remove these == trues and change == false to !, but in a separate NFC patch. |
Note the sneaky requires-clause hiding in among the "write it three times" business.
I'd be willing to pull this out into a helper concept, e.g.
but I'm ambivalent on whether that's better.
I'm also unclear on how to observe the difference between this constraint and a simple forward_range<_Tp&>. @tcanens perhaps?