fix __simple_view concept in std::ranges
For reference, the definition in the standard is here:
http://eel.is/c++draft/ranges#range.utility.helpers
Changes:
libcxx/include/__ranges/concepts.h
Differential D116808
[libc++] fix __simple_view concept in std::ranges huixie90 on Jan 7 2022, 6:41 AM. Authored by
Details
fix __simple_view concept in std::ranges For reference, the definition in the standard is here: Changes: libcxx/include/__ranges/concepts.h
Diff Detail
Event TimelineComment Actions Please undo your formatting changes, so that the code change is easier to find in the diff (and also because the old formatting was strictly better). I actually don't see what your proposed difference is right now at all — it looks identical? Comment Actions Please add a regression test to test/libcxx/ranges/range.utility.helpers/simple_view.compile.pass Comment Actions I am not sure about the process and I am happy to undo the formatting change. But I followed the instructions of the contribution guide and used the "arc" tool for the review and the tool suggested me to lint the code in this way and automatically applied the formatting fix. the actual change is the last bit is changed from iterator_t -> sentinel_t anyway. should I surround the code clang-format off with clang-format on after revert the formatting? Comment Actions Just use --nolint with arc. Don't add //clang-format. Almost no file in libc++ is conforming to the clang-format rules. It is not the most useful in libc++, because we have much weird stuff which clang-format just doesn't format properly.
Comment Actions Ah, I see; thanks @philnik! This looks like a real bugfix then. Thankfully, it looks like we have test coverage for this case — so this PR also needs to update some existing tests, but might not need to add many (if any) new tests. OTOH, it looks like __simple_view is used inside take_view, drop_view, and join_view, and none of those tests are failing. This indicates missing test coverage for those view adaptors.
Comment Actions Updating D116808: fix __simple_view concept in std::ranges revert formatting and add unit test Comment Actions Please also add test coverage in take_view, drop_view, and join_view, which @Quuxplusone basically requested, unless I'm mistaken.
Comment Actions @philnik, @Quuxplusone Anyways, I found an interesting behaviour change after the update Comment Actions The tests are in libcxx/test/std/ranges/range.adaptors/{range.take, range.drop, range.join.view}. Comment Actions
(Slightly simplified: https://godbolt.org/z/8MnoGzWaM ) Yep, that looks like a libc++ bug that's getting fixed here, so please also add a regression test for that, in the take_view tests. $ git grep views::take ../libcxx/test/ ../libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp://static_assert(test(std::views::take, a, 10)); ../libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp://static_assert(test(std::views::take_while, a, [](int x){ return x < 10; })); $ git grep ranges::take_view ../libcxx/test/ | wc -l 55
Comment Actions
The buggy libc++ behaviour is actually better than the correct (by correct I mean same as standard) one. (in my opinion) Looking at the specification constexpr auto begin() requires (!simple-view<V>) { if constexpr (sized_range<V>) { if constexpr (random_access_range<V>) { return ranges::begin(base_); } else { auto sz = range_difference_t<V>(size()); return counted_iterator(ranges::begin(base_), sz); } } else { return counted_iterator(ranges::begin(base_), count_); } } constexpr auto begin() const requires range<const V> { if constexpr (sized_range<const V>) { if constexpr (random_access_range<const V>) { return ranges::begin(base_); } else { auto sz = range_difference_t<const V>(size()); return counted_iterator(ranges::begin(base_), sz); } } else { return counted_iterator(ranges::begin(base_), count_); } } Both const and non-const overloads say, hey, if the base_ is random_access_range and sized_range, we can directly use base_'s iterator without creating the counted_iterator. For non-const overload of begin, it has requires (!simple-view<V>). What I think it means is that, hey, no matter base_ is const or not, they return the same type of iterators so we don't need the non-const overload begin here, because the const version should do the same thing. But in my contrived example, V is simple-view, and sized_range <V> is true and size_range<const V> is false. so the const overload of begin goes to the fallback path. If the non-const overload of begin exists (as in the libc++), it would select the better path Comment Actions That being said, I think the correct fix is to change the standard to be constexpr auto begin() requires (!(simple-view<V> && size_range<const V> && random_access_range<const V>)) { so that it can actually return int* in this case Comment Actions @huixie90: FYI, I brought this idea up in the #ranges channel on Slack; we'll see what the experts say. :) https://cpplang.slack.com/archives/C4Q3A3XB8/p1641579035002100 Comment Actions Updating D116808: fix __simple_view concept in std::ranges added unit test for take_view Comment Actions I added tests for take_view as it has observable behaviour change. For other views, I can't seem to find observable things. As simple-view concept is really about removing the non-const overload if it is same as the const overload. The only thing I am thinking of testing is that if simple-view<BaseView> is satisfied, we can check if it only has one overload of begin. I tried different things But compilers don't all agree and in one case, it even triggers clang to ICE Comment Actions Updating D116808: fix __simple_view concept in std::ranges add unit tests for drop_view and join_view Comment Actions Updating D116808: fix __simple_view concept in std::ranges update take test, no need to instantiate the class Comment Actions Updating D116808: fix __simple_view concept in std::ranges remove unnamed namespace to work around -Wundefined-internal
Comment Actions
I could do but I don't think it is necessary.
Sure I can do. I put comments before the test points just because all other test points do the same thing. but happy to move
Iterators are private defined inside the view. How can you spell it out without using decltype?
The reason I don't create an instance is that I think I did create an instance at beginning and some configuration in the CI failed and complained that some of my member function is not defined. what about { using TakeNonSimpleView = std::ranges::take_view<NonCommonSimpleView>; ASSERT_SAME_TYPE(decltype(std::declval<TakeNonSimpleView&>().begin()), std::counted_iterator<int*>); ASSERT_SAME_TYPE(decltype(std::declval<TakeNonSimpleView const&>().begin()), std::counted_iterator<int*>); } Comment Actions @Quuxplusone { bool called = false; auto dv = std::ranges::drop_view<SimpleView>(SimpleView{&called}, 4); assert(std::as_const(dv).begin() == nullptr); assert(!called); assert(dv.begin() == nullptr); assert(called); // this is going to fail because according to the standard the non-const overload of begin doesn't exist if `base` is `simple-view`. so `called` will still be false here }
Comment Actions Updating D116808: fix __simple_view concept in std::ranges addressed issues in the code review. updated the tests Comment Actions
I added a false then true case test, but without adding size const overload. Instead, I simply made it not satisfying simple view concept. (which is the concept I really want to test)
Comment Actions @huixie90 Thanks for the fix. Since this already has some people involved, I won't chime in more than to clarify a few things: Don't worry -- it's indeed very misleading. This is a frequent issue for contributors, and we should make it so that arc doesn't try to run lint by default on libcxx/ and libcxxabi/. But I don't know how to make that happen (which also points out to another problem, which is the high barrier to LLVM's infrastructure). Indeed, I have local patches I'm slowly finishing up in the middle of everything else. They add views::take, views::drop and views::join. Those views are tested though, just not their range adaptor objects. Comment Actions What would be the next step here? I think I'd need someone says "ship it" before I can continue. Comment Actions
Comment Actions Updating D116808: fix __simple_view concept in std::ranges addressed issues in the review Comment Actions @Quuxplusone I applied the suggestions and see if it compiles fine.
Comment Actions Please consider starting your commit message (the short title line) with [libc++]. You don't *have* to, but it's customary for us to do it.
Comment Actions @ldionne I don't seem to have access to push to llvm. and I get this error when I try to push Comment Actions If you need someone to commit this on your behalf, please provide Author Name <email@domain> for attribution and we'll do it. Comment Actions Thanks! Here it is: Author: Hui Xie Generally how many patches one needs to do before they can apply for the commit access? Comment Actions Thanks, committed. I don't think there's any hard rule, but 4-5 patches is probably good, and it would be worth requesting access if you plan on contributing more to the project in the future. (Also, side note but I wonder how/whether this is going to change if we more to GitHub PRs.) |
If you want to you can also give clang format a nudge by adding // where you want a line break
That said I do not know whether this is usually accepted here