This is an archive of the discontinued LLVM Phabricator instance.

[libc++] Enable the optimized _IsSame on GCC as well as Clang.
ClosedPublic

Authored by Quuxplusone on Dec 4 2021, 9:14 AM.

Details

Summary
However, there's a problem on both GCC and Clang: they can't mangle
`__is_same(T,U)` if it appears anywhere that affects mangling. That's
a hard error. And it turns out that GCC puts dependent return types
into the mangling more aggressively than Clang, so for GCC's benefit
we need to avoid using raw `_IsSame` in the return type of
`swap(tuple&, tuple&)`. Therefore, make `__all` into a named type
instead of an alias.

Diff Detail

Event Timeline

Quuxplusone requested review of this revision.Dec 4 2021, 9:14 AM
Quuxplusone created this revision.
Herald added 1 blocking reviewer(s): Restricted Project. · View Herald TranscriptDec 4 2021, 9:14 AM
Quuxplusone edited the summary of this revision. (Show Details)
However, there's a problem on both GCC and Clang: they can't mangle
`__is_same(T,U)` if it appears anywhere that affects mangling. That's
a hard error. And it turns out that GCC puts dependent return types
into the mangling more aggressively than Clang, so we need to avoid
using raw `_IsSame` (or type aliases such as `__all` that expand into
`_IsSame`) in one return type, for GCC's benefit.
ldionne requested changes to this revision.Dec 6 2021, 7:31 AM
ldionne added inline comments.
libcxx/include/tuple
1120 ↗(On Diff #391883)

Instead, I really feel like we could redefine __all to be:

template <bool ..._Pred>
struct __all : _IsSame<__all_dummy<_Pred...>, __all_dummy<((void)_Pred, true)...>> { };

After all, we will generally instantiate __all with several types, however we probably won't instantiate __all itself several times. What I'm trying to say is that since __all is variadic, we will probably instantiate it some factor less than the number of types that we're passing into it, so it might be reasonable to define it as a struct despite the additional costs. That would fix our mangling problem without needing a definition for __all_are_swappable, which IMO is kind of weird.

libcxx/include/type_traits
552–553

Do we support any compilers where this is false? I don't think so -- we could probably just drop the #if altogether.

This revision now requires changes to proceed.Dec 6 2021, 7:31 AM
Quuxplusone added inline comments.Dec 6 2021, 8:06 AM
libcxx/include/tuple
1120 ↗(On Diff #391883)

That would fix the mangling problem for __all specifically, but that's an O(n) patch for an O(n^2) problem (doesn't fix things that aren't __all), whereas what I've done here is the O(1) patch for the O(n^2) problem.
Personally I'd rather keep the O(1) paper-over patch for now, and then if we see this Clang/GCC incompatibility issue coming up repeatedly, we think about how to generalize it then. I suspect the issue won't come up much at all, because C++>=20 stuff will all use requires instead of return-type SFINAE.

libcxx/include/type_traits
552–553

It appeared to me that MSVC doesn't support __has_builtin at all, so it ends up nerfed to false by default.
However, I'm OK with dropping the #if and seeing what happens. Someone can always put it back later.

Quuxplusone edited the summary of this revision. (Show Details)

Remove the #if entirely.

ldionne added inline comments.Dec 6 2021, 8:13 AM
libcxx/include/tuple
1120 ↗(On Diff #391883)

The problem I have with fixing it locally is that we'll eventually hit the same problem if we use __all in another place. It seems cleaner to me to fix __all and add a comment explaining the GCC bug. Then, file a bug with GCC and reference it in the comment. That way, we pessimize __all but we reduce the likelihood of hitting the bug again to the maximum, and eventually we'll remove the pessimization when the GCC bug is fixed.

libcxx/include/type_traits
552–553

AFAICT we have CI for all our supported platforms. If CI is happy without #if, I think that means we're good. If not, then let's keep the #if.

jloser added a subscriber: jloser.Dec 6 2021, 8:39 AM
jloser added inline comments.
libcxx/include/type_traits
571

Any appetite for adding a libcxx test for this behavior? It is a "private" type trait, but useful property in helping compile times, so I think a simple test doesn't hurt.

941

Why move this below the // is_class comment? It's a standalone helper type unrelated to is_class AFAICT, right? I recommend moving it above the // is_class comment. Maybe even add the // helper class: comment above the struct __two {char __lx[2];};.

Alternatively, revert the diff entirely as it's not needed for this patch. :)

Quuxplusone added inline comments.Dec 6 2021, 9:28 AM
libcxx/include/type_traits
941

is_class is the first one who needs it, although yeah this was path-dependent and I should just move it below the old // helper class: comment. (Not sure how it wound up above it in the first place, and haven't looked.)

Quuxplusone edited the summary of this revision. (Show Details)

Updated with @ldionne's suggestion. Let's see if buildkite is happy with this; if so, then I'm ready to land it and forget it. :)

ldionne accepted this revision.Dec 15 2021, 2:33 PM

LGTM with comments.

libcxx/include/type_traits
564–565

I would remove that part of the comment -- I don't see what it adds.

Also, I would document that this _IsSame can't be used anywhere that affects mangling in order to save some cycles if someone encounters the same issue in the future.

583–585

I'm going to be really nitpicky and ask that you make this change in a separate NFC commit, no review needed. Just to keep reviews clean.

This revision is now accepted and ready to land.Dec 15 2021, 2:33 PM
Quuxplusone marked 5 inline comments as done.Dec 15 2021, 4:27 PM
Quuxplusone added inline comments.
libcxx/include/type_traits
564–565

I added the latter part of the comment because it wasn't obvious to me at first glance. The alternative (and the first thing I happened to think of) is that on those platforms we define _IsSame<T,U> as an alias for is_same<T,U> and just have to beware of using _IsSame anywhere that the difference would be observable.
However, I'll move that into the commit message; and move the "affects mangling" bit (partly) out of the commit message into the comment.

583–585

Done!