This is an archive of the discontinued LLVM Phabricator instance.

[libcxx][ranges] Add range.subrange.
ClosedPublic

Authored by zoecarver on May 6 2021, 10:15 AM.

Details

Summary

Basically the title.

Diff Detail

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
zoecarver requested review of this revision.May 6 2021, 10:15 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 6 2021, 10:15 AM
Herald added a reviewer: Restricted Project. · View Herald Transcript
cjdb added inline comments.May 6 2021, 10:16 AM
libcxx/include/type_traits
2311–2314 ↗(On Diff #343449)

I wanna poach this for D101922. WDYT?

For now I only wanted to comment on the unsigned conversion. ATM I don't have time for a proper review.

libcxx/include/type_traits
2311–2314 ↗(On Diff #343449)

I would like _LIBCPP_INLINE_VISIBILITY and I dislike the _like suffix. Of course if we remove the _like it duplicates __to_unsigned defined in <charconv>. I start to feel it makes sense to make a header to do some of these signed to unsigned conversions. (I also use them for <format> but there I already require <charconv>. WDYT? (Note <charconv> is available in C++11 as an extension.)
I wouldn't mind to put up a review to make this change.

zoecarver planned changes to this revision.May 6 2021, 4:39 PM
cjdb added inline comments.May 6 2021, 10:20 PM
libcxx/include/type_traits
2311–2314 ↗(On Diff #343449)

This is already in main. I'm not sure why it's showing up in the diff.

Mordante added inline comments.May 9 2021, 5:10 AM
libcxx/include/type_traits
2311–2314 ↗(On Diff #343449)

I see thanks. Any objection to renaming it to __to_unsigned? Since <charconv> already depends on <type_traits> I'll make a patch to let <charconv> reuse this function.

This is ready for review now.

libcxx/include/type_traits
2311–2314 ↗(On Diff #343449)

I see thanks. Any objection to renaming it to __to_unsigned? Since <charconv> already depends on <type_traits> I'll make a patch to let <charconv> reuse this function.

That would be great, thanks @Mordante.

Fix tests/rebase fallout.

zoecarver updated this revision to Diff 344557.May 11 2021, 2:02 PM

Make default ctorable and add a test.

Mordante requested changes to this revision.May 12 2021, 9:57 AM

Please validate the visibility for all types
template classes -> _LIBCPP_TEMPLATE_VIS
enums -> _LIBCPP_ENUM_VIS
inline function -> _LIBCPP_INLINE_VISIBILITY
For now I only looked at the subrange header. Will look at the test at another time.

libcxx/include/__ranges/subrange.h
41

Please __uglify t.
Minor nit, since you use _Range instead of _Tp, how do you feel about using __r instead of __t?

51

The Standard uses not-same-as instead of !same_as.

template<class T, class U>
  concept not-same-as =                         // exposition only
    !same_­as<remove_cvref_t<T>, remove_cvref_t<U>>;

Is there a reason to deviate?

64

Just curious, why these names instead of _Tp, _Up, and _Vp?

71

Why not using a bool as underlying type? The Standard uses enum class subrange_kind : bool { unsized, sized }; .

120

Another case of !same_as vs not-same-as. When using __not_same_as this concept can be used instead of class like in the Standard.
Also look at the other !same_as in this class.

160

Please use NODISCARD_EXT.

162

Can you rename this to __make_unsigned_t?

195

Interesting, hopefully __n will never be std::numeric_limits<iter_difference_t<_Iter>>::min()

libcxx/include/type_traits
2311–2314 ↗(On Diff #343449)

I've put up D102332.

This revision now requires changes to proceed.May 12 2021, 9:57 AM

inline function -> _LIBCPP_INLINE_VISIBILITY

I thought this was only for internal functions, because public functions have stable pre/post conditions, is that not correct?

libcxx/include/__ranges/subrange.h
41

Good idea about using __r. I should have documented this more clearly, but I'm going to "properly" add these concepts in a future patch. I just didn't realize they were a prerequisite until too late here, so I just copied them in.

64

I think it's a bit clearer, then you don't have to map T to the caller and try to figure out which argument it is, you can just say, "oh that's supposed to be pair-like."

162

I think this comment got moved. What do you mean?

zoecarver updated this revision to Diff 344990.May 12 2021, 5:21 PM

Apply Mark's comments.

inline function -> _LIBCPP_INLINE_VISIBILITY

I thought this was only for internal functions, because public functions have stable pre/post conditions, is that not correct?

AFAIK it's done for all inline functions. (I wouldn't be surprised if there are more functions not having the marker.) If you, for example, look at the <bit> header all functions have this marker. Both the public and internal functions.

Of course it would be nice to have a CI job to catch the issues ;-)

libcxx/include/__ranges/concepts.h
72 ↗(On Diff #344990)

Why not in the namespace?

libcxx/include/__ranges/subrange.h
64

Thanks for the information.

162

I had another look and this time a make-unsigned-like-t doesn't mean exposition only type but make_unsigned_t depending on conditions. So it seems your code is correct.

AFAIK it's done for all inline functions. (I wouldn't be surprised if there are more functions not having the marker.) If you, for example, look at the <bit> header all functions have this marker. Both the public and internal functions.

_LIBCPP_INLINE_VISIBILITY is synonymous for _LIBCPP_HIDE_FROM_ABI, right? (Side-note: are we trying to move to using the latter?) Anyway, I think it's just used to inline things so they're hidden from the ABI (i.e., the function goes away so there's nothing to get baked into the ABI). However, we don't want or need public functions to be hidden from the ABI. In fact, we probably don't want them to be hidden from the ABI, so that we can still have the benefits of ABI stability, and reduced code-size.

libcxx/include/__ranges/concepts.h
72 ↗(On Diff #344990)

I figured this is a more general utility. Not just for ranges.

But this makes me think: it would probably be better to move this to <concepts>, actually.

cjdb requested changes to this revision.May 18 2021, 11:51 AM

I'll review the tests later today, but I anticipate some of my feedback might shape the existing tests.
I'd also appreciate if we can split this test set into multiple files, similar to how test/std/containers/sequences/vector/ is broken down.

libcxx/include/__ranges/concepts.h
72 ↗(On Diff #344990)

Yes, please move to <concepts>.

libcxx/include/__ranges/subrange.h
49–52

Checking that the comment isn't the opposite of what the code wants is a bit convoluted. I suggest making a named concept out of this bit to improve readability.

57

I think the standard-provided comment is useful in improving readability.

116–120

I believe you also need an overload for when __store_size == false.

135

You'll probably need to guard on !__store_size && sized_range<R>, since it's possible for both to be true, thus creating an ambiguity. Please add a test for this very case.

142

If we already have a make_unsigned_like_t, please use that. Otherwise, carry on.

144

Standard uses braces for this one.

147

Should be __not_same_as.

155

Regardless of how the _LIBCPP_NODISCARD_EXT discussion plays out, this one should be [[nodiscard]].

172

Regardless of how the _LIBCPP_NODISCARD_EXT discussion plays out, this one should be [[nodiscard]].

179

Regardless of how the _LIBCPP_NODISCARD_EXT discussion plays out, this one should be [[nodiscard]].

184

Regardless of how the _LIBCPP_NODISCARD_EXT discussion plays out, this one should be [[nodiscard]].

This revision now requires changes to proceed.May 18 2021, 11:51 AM
tcanens added inline comments.
libcxx/include/__ranges/subrange.h
144

Standard uses braces for this one.

For ranges wording, wherever braces have behavior different from parens, the use of braces is almost certainly a defect. P2367R0 is expected to fix all such occurrences. https://github.com/cplusplus/draft/issues/4593 deals with the rest.

147

Who came up with the idea to have a exposition-only concept named not-same-as that's not not same_as?

zoecarver marked 11 inline comments as done.May 20 2021, 4:29 PM
zoecarver added inline comments.
libcxx/include/__ranges/subrange.h
116–120

Other than the one on L112?

135

Hmm, I think you're right. So that would be the case where !sized_sentinel_for but it had a size member or something?

142

I don't see one.

144

Is there an observable difference/do we care? If there's no difference I try to use parens, because that shows there's no difference (if I use braces, a reader might ask "is there a reason we have to use braces here?").

zoecarver marked 2 inline comments as done.May 20 2021, 4:30 PM
zoecarver added inline comments.
libcxx/include/__ranges/subrange.h
147

This is the most annoying thing...

zoecarver marked an inline comment as done.May 20 2021, 4:57 PM
zoecarver added inline comments.
libcxx/include/__ranges/subrange.h
135

For posterity: this is actually the opposite of what we need to guard on, we need to guard on __store_size && sized_range<R>.

Because for this overload:

constexpr subrange(R&& r) requires (!StoreSize || sized_­range<R>);

We do

  • subrange(r, size) when StoreSize
  • subrange(begin, end) when !StoreSize

However, it was just easier to write it as two overloads where we do

  • subrange(begin, end) when !StoreSize
  • subrange(r, size) when sized_range

The problem is that didn't catch the case (as I said above) where both sized_range and !StoreSize are true, in which case it would be ambiguous. I fixed it and added DifferentSentienlWithSizeMember as a regression test.

zoecarver updated this revision to Diff 346894.May 20 2021, 4:58 PM

Address review comments.

zoecarver marked an inline comment as done.May 20 2021, 4:59 PM

I think we're getting close. There still seem to be some open review comments and some comments marked as done, which aren't done. I added comment to them, be please verify whether I didn't overlook any. I still wonder about the visibility macros, but let's discuss that on Discord.

libcxx/include/__ranges/subrange.h
41

Can you address this issue?

147

I agree, I also dislike this idea. Can you use the __not_as_as?

184

Agreed, these 3 require [[nodiscard]] according to the Standard. (No idea why this empty() doesn't need it.)

libcxx/test/std/ranges/range.utility/range.subrange/access.pass.cpp
13

Is this msvc work-around still required. IIRC it was due to some __ugly_macro_conflicts.
Likewise for the other newly added unit tests.

libcxx/test/std/ranges/range.utility/range.subrange/ctad.compile.pass.cpp
27

Please add some linebreaks to keep this readable. Same for the other places with these overly long lines. IIRC our current maximum column width is 120.

libcxx/test/std/ranges/range.utility/range.subrange/ctor.pass.cpp
31

Nice comment!

zoecarver marked 6 inline comments as done.May 25 2021, 3:32 PM
zoecarver added inline comments.
libcxx/test/std/ranges/range.utility/range.subrange/ctor.pass.cpp
31

Thanks :)

zoecarver updated this revision to Diff 347809.May 25 2021, 3:33 PM
  • Rebase.
  • Address all comments.
Mordante accepted this revision as: Mordante.May 26 2021, 8:37 AM

LGTM, except for the [[nodiscard]] on empty(). Please make sure the build passes CI before committing.

libcxx/include/__ranges/subrange.h
160

This one shouldn't be [[nodiscard]] according to the Standard.

Sorry for the slow reply. Thanks @Mordante.

@cjdb how's this look now?

libcxx/include/__ranges/subrange.h
160

I think we decided that members named "empty" are sufficiently vexing because users something think it will empty the container (or in this case sub range).

zoecarver updated this revision to Diff 349129.Jun 1 2021, 4:32 PM

Rebase.

Note: I'm going to land this when the tests pass.

Mordante added inline comments.Jun 2 2021, 10:14 AM
libcxx/include/__ranges/subrange.h
160

Oke wasn't aware we decided that. But I agree calling size() and discarding the result is most likely a bug.

ldionne requested changes to this revision.Jun 2 2021, 1:11 PM

General note for the tests - can we split tests so that we test one overload (as documented in the Standard) per file? This is what we do elsewhere and it makes it easier to make sure we're testing everything correctly. If a function is only a very tiny derivative of another one, then it's OK to test both in one shot, of course.

libcxx/include/__ranges/subrange.h
46

This is *so* weird - I would have expected something like tuple_size<_Tp>::value == 2 instead. But leave as-is, that's how the Standard does it.

64

For a static like that, I'd use _StoreSize instead.

Re: using _Iter instead of _Tp, I strongly agree -- using _Iter is a lot clearer than what the Standard uses.

65

__begin_? (same for other member variables)

74

Here, we're going to store the size if _Kind == subrange_kind::sized even if _Sent is a sized sentinel for _Iter. We should not be storing it when _Sent is a sized sentinel.

97–99

Instead, you could use this->member when referring to a member in the dependent base, and _Base::__store_size when referring to the static. IMO that's easier to follow and more idiomatic, WDYT?

142

I'm surprised they didn't make this _Iter begin() && instead to make sure we can only call this on a xvalue.

148

See, I'm very curious to understand why the Standard didn't mark this one as [[nodiscard]]. It can't be an oversight, since they marked a bunch of methods around it [[nodiscard]]. Just curious to understand what the motivation was.

libcxx/test/std/ranges/range.utility/range.subrange/access.pass.cpp
88

testPrimitives?

libcxx/test/std/ranges/range.utility/range.subrange/ctad.compile.pass.cpp
21

namespace ranges = std::ranges or something like that, but we frown upon importing names.

This revision now requires changes to proceed.Jun 2 2021, 1:11 PM
zoecarver added inline comments.Jun 2 2021, 2:02 PM
libcxx/include/__ranges/subrange.h
64

The style I try to use is that variables are always __snake_case or camelCase (regardless of staticness) and the former at least seems to be the existing style. This way, _TitleCase or TitleCase is reserved for types (also matches current style and is my personal preference).

65

In recent commits, at least, I tend not to suffix with an underscore. Historically in libc++ we seem to do both. If you feel strongly, let's add some docs/style guid and I'll fix it.

80

Nit: no constexpr.

97–99

This is all personal preference, but the way I see it, there's no confusion as to what __begin is referring to and it's unconditionally a member. If someone went looking for the definition, they'd find this which would lead them to it.

In fact, there might be an argument that saying this->member adds confusion, because someone might say "why do we need to use this->" or try to remove that.

For me it really comes down to simplicity/brevity. Writing __begin is simply fewer characters.

142

This is odd and does not seem to be inline with most views.

Quuxplusone added inline comments.Jun 2 2021, 8:38 PM
libcxx/include/__ranges/subrange.h
49–50

Check whether these should be ADL or std::get, and then (if the ADL is intentional) let's please figure out a way to document that in the code long-term.
I'm almost amenable to making a macro like #define _LIBCPP_ADL(x) x just to tag intentional ADLs. That'd be a bigger change than this PR, obviously. For this PR, if the ADL is intentional, I think it'd suffice to just leave a comment // intentional ADL.

65

FWIW, I feel strongly: libc++ should follow libc++ style, which is "data members get underscore-suffixed."

97–99

FWIW, I agree with @ldionne: using-declarations (and their effect on name lookup) are significantly more confusing for the reader/maintainer than the plain old this->__member_ expression syntax we're all familiar with.

142

Tangential, but yeah: it wouldn't make sense to require the caller to write std::move(myRange).begin(), because then how would you ever get at the end-sentinel? You couldn't write again std::move(myRange).end(), because that would trigger all your static-analyzer's "double-moved-from" checks.

183

this->__size_ += _VSTD::__to_unsigned_like(-__n);
(The ADL-proofing of __to_unsigned_like actually doesn't matter because the lookup of operator- will be ADL anyway; but let's be consistent in our "ADL-proof by default" style.)
Ditto line 190 below.

Orthogonally: Is the whole point of __store_size_ just to keep track of whether __size_ exists? Could we just do

if constexpr (requires { this->__size_; })
    this->__size_ += ...;

instead, throughout? Or would that not-work for some technical reason?

208–209

Formatting nit: the -> belongs on the second line. Lines 199-200 above do it right AFAIC.

libcxx/test/std/ranges/range.utility/range.subrange/access.pass.cpp
58

Definitely just write out std::ptrdiff_t in the like two places it appears.

73

Formatting nit: friend constexpr

libcxx/test/std/ranges/range.utility/range.subrange/ctad.compile.pass.cpp
21

(moving my comment from the previous file after seeing Louis's)
Looking through this test file, I don't think skipping the std:: on lines 89–163 is really buying you anything. Strongly consider just writing out std::ranges::whatever everywhere it needs to be. (The benefit is greppability.)

27

One way to clean this up is to introduce an array of int a and a typedef FI:

int a[10];
using FI = forward_iterator<int*>;
static_assert(std::same_as<decltype(subrange(FI(a), FI(a))),
    subrange<FI, FI, std::ranges::subrange_kind::unsized>>);
static_assert(std::same_as<decltype(subrange(FI(a), FI(a), 0)),
    subrange<FI, FI, std::ranges::subrange_kind::sized>>);
static_assert(std::same_as<decltype(subrange(a, a)),
    subrange<int*, int*, std::ranges::subrange_kind::unsized>>);
static_assert(std::same_as<decltype(subrange(a, a, 0)),
    subrange<int*, int*, std::ranges::subrange_kind::sized>>);
static_assert(std::same_as<decltype(subrange(a, nullptr)),
    subrange<int*, std::nullptr_t, std::ranges::subrange_kind::unsized>>);
static_assert(std::same_as<decltype(subrange(a, nullptr, 0)),
    subrange<int*, std::nullptr_t, std::ranges::subrange_kind::sized>>);
libcxx/test/std/ranges/range.utility/range.subrange/ctor.pass.cpp
48

base_

83

value_ (but actually, I think you should just call this base_ — see? the underscore has a purpose! :))

127–128

I think there is not. (Also you wrote unsized for unsigned)

164

s/enl/nel/g
Also s/Foward/Forward/g below, and probably some others; do a typo-checking pass on this file.

libcxx/test/std/ranges/range.utility/range.subrange/general.compile.pass.cpp
23

Formatting nit: class... Args with a space.
(Also, as above, please don't namespace ranges =; you never even use it in this test file!)

ldionne added inline comments.Jun 3 2021, 12:51 PM
libcxx/include/__ranges/subrange.h
64

Ack.

65

@zoecarver If you haven't been using underscores lately, then it's simply an oversight in the code review. libc++ has been using trailing underscores for members forever, this isn't something new. We could definitely benefit from documenting the coding style we want, however fixing the issue in this review shouldn't be gated on that.

97–99

Arthur's comment is specifically why I requested that change.

zoecarver marked 31 inline comments as done.Jun 4 2021, 11:08 AM
zoecarver added inline comments.
libcxx/include/__ranges/subrange.h
49–50

I've been informed that these are not ADL. Fixed.

49–52

I think it's important to make sure this is the same as the standard naming. Luckily the standard changed this for us.

65

OK I'll update these. Also, I'll add to my list of things I probably won't ever find time for: configure the clang tidy rule for this.

116–120

@cjdb Ping.

148

I'm just guessing here, but maybe LWG didn't think that empty was sufficiently vexing. As I've already discussed, it makes sense to mark next/prev nodiscard because there are multiple APIs that do different things there, and begin needs to be nodiscard because it's moving the iterator. Empty doesn't have anything "special" (and reason to mark it nodiscard) other than a confusing name.

183

Any reason for this-> over _Base::?

Or would that not-work for some technical reason?

Pretty sure there's a clang bug that doesn't allow us to use inline-requires like this.

libcxx/test/std/ranges/range.utility/range.subrange/ctad.compile.pass.cpp
27

Did something similar, it's a bit better now.

libcxx/test/std/ranges/range.utility/range.subrange/ctor.pass.cpp
127–128

Removed. Thanks.

zoecarver marked 6 inline comments as done.Jun 4 2021, 11:10 AM
zoecarver added inline comments.
libcxx/test/std/ranges/range.utility/range.subrange/ctor.pass.cpp
164

I'm bad at spotting these, so if you see others let me know and I'll fix them.

zoecarver updated this revision to Diff 349933.Jun 4 2021, 11:42 AM

Apply review comments.

ldionne accepted this revision.Jun 7 2021, 1:50 PM

LGTM with my comments.

libcxx/include/__ranges/subrange.h
164

Can we add tests that check that those methods are [[nodiscard]], since those are mandated by the standard?

183

Any reason for this-> over _Base::?

Nope, either way's fine as far as I'm concerned.

(I'd like @cjdb to take a final look at this and give the green light if he's fine with it, since he had a previous requires-changes)

Quuxplusone added inline comments.Jun 8 2021, 5:29 AM
libcxx/include/__ranges/subrange.h
183

I strongly prefer this->__size_. Don't use weird syntax unless there's a technical reason for it.

cjdb accepted this revision.Jun 8 2021, 9:48 AM

Thanks for working hard on this!

libcxx/include/__ranges/subrange.h
97–99

I'm with Zoe on this one. We have an underscore-suffix-for-members convention, so it should be clear that __member_ is shorthand for this->__member_, and _Base::__member_ is for the base's member. Whether or not it's an instance member or a static member feels largely irrelevant to me?

116–120

Ah, yes, I missed that, sorry.

144

Yes. Tim's GH link has all the relevant info.

This revision is now accepted and ready to land.Jun 8 2021, 9:48 AM
cjdb requested changes to this revision.Jun 10 2021, 8:58 AM

Please update according to the recent changes made by P2325.

This revision now requires changes to proceed.Jun 10 2021, 8:58 AM

Apply review comments.

Will land when bots are green.

The above update:

  • Split tests into multiple files.
  • Rebased onto D102468 and applied changes from Barry's paper.
This revision was not accepted when it landed; it landed in state Needs Review.Jun 11 2021, 9:34 AM
This revision was automatically updated to reflect the committed changes.