This is an archive of the discontinued LLVM Phabricator instance.

[libc++] Simplify span specializations
AbandonedPublic

Authored by jloser on Sep 8 2021, 5:36 PM.

Details

Reviewers
ldionne
Quuxplusone
Mordante
zoecarver
cjdb
Group Reviewers
Restricted Project
Summary

span has a partial class template specialization for when the extent
is dynamic. This leads to a lot of repeated code (for type aliases,
constraints on constructors, etc.) which is not ideal. It makes for more
of a maintenance burden as more things are added to span such as
current work I'm doing with P1394.

Refactor span to be just one class template and handle the few differences
between static and dynamic extents with appropriate constraints and
if constexpr where needed. Only store the extent data member when the extent
is dynamic. This ensures no additional storage overhead when the span has a
static extent.

Diff Detail

Event Timeline

jloser requested review of this revision.Sep 8 2021, 5:36 PM
jloser created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptSep 8 2021, 5:36 PM
Herald added a reviewer: Restricted Project. · View Herald Transcript

When I wrote span, I tried to do it this way, and I found that it led to much more complicated code.
Maybe span has changed enough that this is no longer true - dunno.

Stuff like this became much easier:

return span<ElementType, see below>(
data() + Offset, Count != dynamic_extent ? Count : size() - Offset);

Remarks: The second template argument of the returned span type is:
Count != dynamic_extent ? Count
: (Extent != dynamic_extent ? Extent - Offset
: dynamic_extent)

jloser updated this revision to Diff 371497.Sep 8 2021, 9:06 PM

Try to fix CI

curdeius added inline comments.
libcxx/include/span
320

I don't think it changes anything, but I'd like to have a confirmation here from anyone knowing better the standardese. Does it change anything to have explicitly defaulted destructor vs. implicitly defaulted one?

440–441

To avoid an ABI break in the case of static extent, you suppose that [[no_unique_address]] will be honoured by the compiler, right?
Because otherwise, the size of span<T, N> (N != dynamic_extent) would change.
Unless I'm mistaken, MSVC doesn't honour this attribute unless forced (cf. https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch/#msvc-extensions-and-abi).
Not sure whether that's a problem though.

libcxx/test/std/containers/views/span.sub/subspan.fail.cpp
50

Where did the array is too large message came from before? Why doesn't it show up with this change?

jloser updated this revision to Diff 371577.Sep 9 2021, 6:38 AM

Use base class instead of [[no_unique_address]] to avoid potential ABI break on MSVC

libcxx/include/span
320

This isn't strictly required to be uncommented in this patch. I added tests for the destructor behavior the other day in D109286 and noted the implicitly defaulted one is sufficient. I just didn't love having it commented out here, but I don't feel strongly at all.

If someone (@Quuxplusone @ldionne or others) suspect this would be an issue, we can do something about it.

440–441

Right. We can't have an ABI break here and that was my intent with use of [[no_unique_address]]. The blog post you linked and the GitHub issue it mentions has me a bit worried.

With that being said, I'm going to just use a base class to avoid an ABI break. I would prefer to use [[no_unique_address]], but playing things safe is my preference here.

I will note that we unconditionally use [[no_unique_address]] in several of ranges things that are C++20-only; it's slightly different than span though as span has been shipping for a few years now.

jloser added inline comments.Sep 9 2021, 7:06 AM
libcxx/test/std/containers/views/span.sub/subspan.fail.cpp
50

In the previous code, an error diagnostic of

Line 232: array is too large (18446744073709551614 elements)

would have been given in checking this constructor.

In the new code, I'm not entirely sure why we don't get that additional constructor error in the list of errors diagnostics. Note that the static_assert still fires for the "Offset + count out of range in span::subspan()". I poked around a bit, but couldn't get it to trigger the additional constructor error about the array size. Do you or others have an idea why?

Quuxplusone requested changes to this revision.Sep 9 2021, 2:49 PM
Quuxplusone added inline comments.
libcxx/include/span
440–441

Maybe I'm missing something, but isn't the base-class approach super obviously an ABI break? The old libc++ code stored "pointer, then size" and this PR's code stores "size, then pointer".

Also, storing members in a dependent base class means a lot more this-> noise in member functions.

If we're just worried about the difficulty of dealing with two very similar copies of the same code, how about we just move the specializations into __span/static_extent.h and __span/dynamic_extent.h, and then try to minimize the diff between them?

Or, do nothing. I'm still unclear on what is the benefit here or how this relates to enable_if or whatever was being talked about earlier.

This revision now requires changes to proceed.Sep 9 2021, 2:49 PM
jloser added inline comments.Sep 9 2021, 3:28 PM
libcxx/include/span
440–441

No, you're right regarding the ABI break. Having the base class means we'll change the layout which is an obvious ABI break and we can't do that.

My initial worry was indeed in dealing with two very similar copies of the code. I'm not thrilled with how similar the two specializations are, but I think I'll implement P1394 and see what the divergence looks like then and revisit this. We could conditionally define the macro for no_unique_address to workaround MSVC to avoid the ABI break there and go that approach.

In any case, I'm going to abandon this review for now and think about this some more - may revisit after P1394.

jloser abandoned this revision.Sep 9 2021, 3:28 PM