This is an archive of the discontinued LLVM Phabricator instance.

[libc++] Uses operator<=> in string_view
ClosedPublic

Authored by Mordante on Jul 21 2022, 11:36 AM.

Details

Reviewers
ldionne
jloser
philnik
Group Reviewers
Restricted Project
Commits
rG3818b4df1e10: [libc++] Uses operator<=> in string_view
Summary

Implements:

  • LWG3432 Missing requirement for comparison_category

Implements part of:

  • P1614R2 The Mothership has Landed

Diff Detail

Event Timeline

Mordante created this revision.Jul 21 2022, 11:36 AM
Herald added a project: Restricted Project. · View Herald TranscriptJul 21 2022, 11:36 AM
Mordante requested review of this revision.Jul 21 2022, 11:36 AM
Herald added a project: Restricted Project. · View Herald TranscriptJul 21 2022, 11:36 AM
Herald added a reviewer: Restricted Project. · View Herald Transcript
Mordante added inline comments.Jul 21 2022, 11:40 AM
libcxx/docs/Status/SpaceshipProjects.csv
36

I noticed the link was a review after making my changes, the same or string below.

libcxx/include/__string/char_traits.h
196

These all can be using, this will be done in a NFC follow up.

libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char/types.pass.cpp
17

These all can be using, this will be done in a NFC follow up.

27

All these test can be a compile.pass.cpp like char32_t, this will be done in a NFC follow up.

Mordante updated this revision to Diff 447125.Jul 24 2022, 5:43 AM

CI fixes.

jloser added a subscriber: jloser.Jul 24 2022, 10:28 AM
jloser added inline comments.
libcxx/include/string_view
796

We currently repeat this pattern of code three times, which is about when I'd start to look to refactor it into a common detail function -- what do you think?

This will eventually (soon) be needed for operator<=> for std::string I believe, right?

Either way, from a QoI perspective, what do you think about checking the type of comparison_category to make sure it is one of partial_ordering, weak_ordering, or strong_ordering and not some bogus type, like void for example? This would be a bug from the user's perspective and likely IFNDR from the standards perspective, but it could be an easy QoI improvement I think.

802

We may want to copy the comment from above to indicate the int = 1 and int = 2 dummy non-type template parameters are to work around an MSVC mangling issue as noted above in the operator==. It says it applies to the other sufficient overloads below for comparison ops, but I wouldn't be opposed to a comment here for spaceship too. Thoughts?

805

Nit: can use common_type_t instead - here and elsewhere.

philnik added inline comments.
libcxx/include/string_view
803–810

What breaks if you drop the other two overloads? The compiler should automatically generate the operator<=>(common_type_t<basic_string_view>, basic_string_view) IIUC. Or is that just for operator==?

805

I think this should actually be type_identity_t. You could theoretically have something like

struct CharT {
  // all the stuff to make it char-like
};

struct CharTT {
  template <class Traits>
  CharTT(std::basic_string_view<CharT, Traits>);
 };

template <class Traits>
struct std::common_type<std::basic_string_view<CharT, Traits>, std::basic_string_view<CharT, Traits>> {
  using type = CharTT;
};

right? That would result in a hard error.

Thanks for the comments! I'll have a look at them.

libcxx/include/string_view
796

We currently repeat this pattern of code three times, which is about when I'd start to look to refactor it into a common detail function -- what do you think?

I feel these functions are small enough to keep the duplicates.

This will eventually (soon) be needed for operator<=> for std::string I believe, right?

No std::string will call std::string_view's operator<=>. I already have a patch for that.
So there will be no additional duplicates.

Either way, from a QoI perspective, what do you think about checking the type of comparison_category to make sure it is one of partial_ordering, weak_ordering, or strong_ordering and not some bogus type, like void for example? This would be a bug from the user's perspective and likely IFNDR from the standards perspective, but it could be an easy QoI improvement I think.

Good point! Interestingly P1614 "The Mothership has Landed" has no restrictions on R, that's why I didn't add it.
However per [string.view.comparison]/4

Mandates: R denotes a comparison category type ([cmp.categories]).

That was changed by https://wg21.link/lwg3432

(Note that we should be careful with adding non-Standard restrictions. The original wording allowed an int by adding a restriction libc++ would become non-conforming. Instead when we want to add restrictions we should file an LWG issue to make it mandated.)

802

I didn't do it since the 5 other operators also didn't have it. But I was a bit tempted to do it, since I was a bit surprised myself.

805

Good point.

Mordante updated this revision to Diff 447357.Jul 25 2022, 8:34 AM

Addresses review comments.

Mordante edited the summary of this revision. (Show Details)Jul 25 2022, 8:36 AM
Mordante marked 4 inline comments as done.Jul 25 2022, 8:43 AM
Mordante added inline comments.
libcxx/include/string_view
803–810

One of the two can be removed, when I removed the seconds several tests started to fail.
I didn't do a deep investigation.

805

I'm not entirely clear what you mean, can you elaborate?

philnik added inline comments.Jul 25 2022, 11:16 AM
libcxx/include/string_view
805

You are allowed to specialize std::common_type for types that depend on at least one user-defined type. That means that you could theoretically specialize it for the identity and return a different type. (std::common_type<T> forwards to std::common_type<T, T>) I think you actually want to type_identity_t<basic_string_view<...>> instead, because that guarantees that you get basic_string_view<...> back.

jloser added inline comments.Jul 25 2022, 8:29 PM
libcxx/include/string_view
805

type_identity_t does work for creating the non-deduced context which means that only one argument would participate in template argument deduction and then the other one would get implicitly converted to the deduced type. Note this is the "sufficient overloads" wording in the standard for string_view's comparison operators.

However, I'm not convinced yet that our implementation is broken for the reason you claim @philnik. Can you add a test to show it's a problem on top-of-tree with the current comparison/relative operators? Note that whatever we do for operator<=>, we should do the same for the comparison/relative operators (==, !=, <, etc.). Currently, we use common_type there and not type_identity_t.

In particular, take a look at the current tests for the sufficient overloads at libcxx/test/std/strings/string.view/string.view.comparison/equal.pass.cpp and friends.

philnik added inline comments.Jul 26 2022, 1:43 AM
libcxx/include/string_view
805

Here is a reproducer.

ldionne added inline comments.
libcxx/include/string_view
805

I think @philnik's right. We should add a test and use __type_identity_t -- regardless, __type_identity_t makes more sense in that context, IDK why the code was written with common_type<T>::type in the first place (does anybody know?).

I would tackle that as a separate patch since this is a fix to the existing code.

libcxx/test/support/constexpr_char_traits.h
27
jloser added inline comments.Jul 26 2022, 10:07 AM
libcxx/include/string_view
805

Nice test! It does look to be a bug that we should fix on top-of-tree in the sufficient overload cases. Good find.

Re your question, @ldionne, I don't know. When I added the range constructor for std::string_view late last year or so, I recall having to reason about the sufficient overloads due to the mangling issue. At the time, I justified to myself that common_type_t was OK as it was currently written (it's been like this for a while), but I didn't consider the fact that users could specialize common_type.

Mordante marked 2 inline comments as done.Aug 1 2022, 9:01 AM

All review comments relevant to this review have been resolved. The open issues will be done in followup patches.

libcxx/include/string_view
805

Agreed I'll look at this issue in a separate patch.

Mordante updated this revision to Diff 449037.Aug 1 2022, 9:02 AM

Address review comment.

avogelsgesang added inline comments.
libcxx/include/string_view
39

operator< was also removed

libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char32_t/types.compile.pass.cpp
24

what is this include used for?

libcxx/test/std/strings/string.view/string.view.comparison/comparison.pass.cpp
63

factor out

auto expected = i == j ? Ordering::equivalent : i < j ? Ordering::less : Ordering::greater;

This condition seems to be duplicated for all of the following asserts

Or maybe even

auto expected = Ordering{i <=> j};
ldionne accepted this revision as: ldionne.Aug 2 2022, 9:16 AM

LGTM, but please wait for other reviewers to be satisfied too.

My understanding is that there will be a follow-up change to switch from common_type_t to __type_identity_t, as we discussed during live review just now.

libcxx/include/string_view
802–803

This looks really strange to me, I'd suggest using else {...}.

libcxx/test/std/strings/string.view/string.view.comparison/comparison.verify.cpp
25 ↗(On Diff #449037)

It would be nice to add a non .verify.cpp test with char_traits that explicitly specify a comparison_category that is weak_ordering.

jloser accepted this revision.Aug 2 2022, 1:16 PM

Still looks good from my perspective.

libcxx/include/string_view
805

@Mordante to clarify, the plan is to stick with common_type_t for both the existing comparison operators and the new operator<=> and the bug of not using type_identity_t (__type_identity_t I guess since we backport string_view to older standard modes) will be fixed in both in a follow-up? If so, I'm OK with that.

Mordante marked 5 inline comments as done.Aug 3 2022, 9:22 AM
Mordante added inline comments.
libcxx/include/string_view
39

Good catch!

805

Yes I prefer to do both in the same commit.

libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char32_t/types.compile.pass.cpp
24

This is a generic test file which is used in most test, like __config in our include files.
In this case it's required for TEST_STD_VER

libcxx/test/std/strings/string.view/string.view.comparison/comparison.pass.cpp
63

For unit tests I prefer to keep things "simple" and sometimes a bit more verbose than really needed.
That makes it easier to understand what's being tested.

For example i <=> j is strong_ordering which will change the value depending on the type of Ordering.

Mordante updated this revision to Diff 449685.Aug 3 2022, 9:23 AM
Mordante marked 3 inline comments as done.

Rebased to test CI and addresses review comments.

Mordante updated this revision to Diff 449692.Aug 3 2022, 9:45 AM

Fixes CI.

philnik accepted this revision.Aug 4 2022, 9:36 AM

LGTM % nit.

libcxx/include/string_view
801
This revision is now accepted and ready to land.Aug 4 2022, 9:36 AM
Mordante marked an inline comment as done.Aug 4 2022, 9:57 AM
Mordante added inline comments.
libcxx/include/string_view
801

Nice catch!

This revision was automatically updated to reflect the committed changes.
Mordante marked an inline comment as done.
Mordante marked an inline comment as done.Aug 4 2022, 10:56 AM
Mordante marked 6 inline comments as done.Aug 6 2022, 5:35 AM
Mordante added inline comments.
libcxx/include/string_view
805

D131322 switches commont_type_t to type_identity_t for both the spaceship operator as the existing operators.

mumbleskates added inline comments.
libcxx/test/support/test_comparisons.h
141–142

For posterity, is there a strong reason this nuance was added? As I understand it, the standard mandates that strong_ordering::equal == strong_ordering::equivalent must hold true and the equal name only exists as a historical alias of the same value.

Mordante added inline comments.Aug 8 2022, 10:04 AM
libcxx/test/support/test_comparisons.h
141–142

Yes since I missed that detail in the Standard ;-) I had a look and they are indeed the same, thanks for asking the question! I'll make a patch to simplify this.

Mordante marked an inline comment as done.Aug 8 2022, 10:59 AM
Mordante added inline comments.
libcxx/test/support/test_comparisons.h
141–142

Fixed in D131419.

mumbleskates added inline comments.Aug 10 2022, 8:32 PM
libcxx/test/std/strings/string.view/string.view.comparison/comparison.pass.cpp
127–130

while looking at asserting the returned ordering type inside testOrder() in test_comparisons.h, i found that the only tests in the entire suite that fail are these right here, because test() above immediately discards the char_traits after the assertions it makes with type T on lines 74 and 75. Only those two lines are effectively different; after that, the basic_string and basic_string_view types are reconstructed from the extracted CharT and, for the same CharT, are completely identical.

is this intentional? it seems like it isn't, because every three-way comparison tested after AssertOrderReturn<Ordering, T>(); on line 75 actually produces a std::strong_ordering no matter what.

The good news is that the string view types do indeed seem to return the correct ordering, as it's tested on the first 2 lines, but it seems like this might be unintentionally missed surface area and i can't be sure on my own.

You can see this yourself, by replacing all the ?: expressions with i and j with i <=> j and adding ASSERT_SAME_TYPE(decltype(t1 <=> t2), Order); inside testOrder() and it will pass.