This is an archive of the discontinued LLVM Phabricator instance.

[libc++] Fix std::copy and std::move for ranges with potentially overlapping tail padding
ClosedPublic

Authored by philnik on Jun 1 2023, 5:33 PM.

Diff Detail

Event Timeline

philnik created this revision.Jun 1 2023, 5:33 PM
Herald added a project: Restricted Project. · View Herald TranscriptJun 1 2023, 5:33 PM
philnik requested review of this revision.Jun 1 2023, 5:33 PM
Herald added a project: Restricted Project. · View Herald TranscriptJun 1 2023, 5:33 PM
Herald added a reviewer: Restricted Project. · View Herald Transcript
ldionne requested changes to this revision.Jun 2 2023, 11:59 AM
ldionne added inline comments.
libcxx/include/__string/constexpr_c_functions.h
136

I think we are fine with __constexpr_memmove, but the confusion here comes from the fact that we don't have a great way of conveying what the __count is: is it a number of elements or a number of bytes? This is also a problem we have in our other functions like __constexpr_memcmp_equal and others.

One suggestion would be to introduce types like this:

enum class _NumberOfElements : size_t {};
enum class _NumberOfBytes : size_t {};

And then we could take the appropriate parameter in __constexpr_memmove. The benefit is that callers would now explicitly pass the value so there's no ambiguity:

__constexpr_memmove(dest, src, _NumberOfElements(n)) // works
__constexpr_memmove(dest, src, _NumberOfBytes(n)) // doesn't work
__constexpr_memmove(dest, src, n) // doesn't work

We could apply the same thing to the other functions. If we do this, perhaps we should start with a refactoring of the existing functions?

139

Can we replace __char_traits_move by this?

147

This is the case where __is_ptr_in_range(dst, dstend, src) is true. This must be implemented using a normal forward for-loop:

|-------------------------------------------------|
                         ^ src                    ^ srcend
        ^ dst                         ^ dstend

This is the case where __is_ptr_in_range(src, srcend, dst) is true. This must be implemented by looping backwards from srcend to src and writing from dstend to dst.

|-------------------------------------------------|
        ^ src                    ^ srcend
                    ^ dst                         ^ dstend

Right now I think you have the logic the wrong way around. This also needs a test.

The logic should be

std::__is_pointer_in_range(__src, __src + __count, __dest)
libcxx/include/__type_traits/datasizeof.h
23–24
32–33

This fails for builtin types. Needs a test!

libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
1

What about copy_n, copy_backward and move_backward?

50–56

Let's enclose this in {} to make it clear we're testing a separate property. (everywhere)

This revision now requires changes to proceed.Jun 2 2023, 11:59 AM
philnik updated this revision to Diff 529454.Jun 7 2023, 3:36 PM
philnik marked 7 inline comments as done.

Address comments

philnik updated this revision to Diff 529736.Jun 8 2023, 3:01 PM

Try to fix CI

I think this LGTM but I'd like to see again once the rebase issues have been resolved.

ldionne accepted this revision.Jun 13 2023, 8:27 AM
ldionne added inline comments.
libcxx/include/__utility/is_pointer_in_range.h
36–37

This is never true since you're passing _Tp*. is_function<_Tp> was correct, I think.

Edit: Actually the overload won't match if passed a function, so let's remove this altogether. You can probably remove some includes too.

36–37

If T is a ptr to member, this function doesn't match so I don't think it's too useful to have this static_assert at all: https://godbolt.org/z/8z8KPn9e5

libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp
51

Since this test case and the one below it are not dependent on any template params, let's move them out of this function. It confused me at first. Probably applies to the other test files as well.

This revision is now accepted and ready to land.Jun 13 2023, 8:27 AM
philnik updated this revision to Diff 531018.Jun 13 2023, 11:50 AM
philnik marked 3 inline comments as done.

Address comments

philnik updated this revision to Diff 531049.Jun 13 2023, 1:31 PM

Fix formatting

philnik updated this revision to Diff 531382.Jun 14 2023, 9:30 AM

Try to fix CI

philnik updated this revision to Diff 531919.Jun 15 2023, 3:23 PM

Try to fix CI

philnik updated this revision to Diff 536301.Jun 30 2023, 9:54 AM

Try to fix CI

This revision was landed with ongoing or failed builds.Jun 30 2023, 1:48 PM
This revision was automatically updated to reflect the committed changes.
alexfh added a subscriber: alexfh.Jul 4 2023, 1:07 PM
alexfh added inline comments.
libcxx/include/__string/constexpr_c_functions.h
149

Some of our (and third-party) code started triggering an error related to this line after this commit. It turns out, std::vector<T>::erase() doesn't compile now, if T is move-only: https://gcc.godbolt.org/z/Txnc66M97

Is this intended?

ldionne added inline comments.Jul 4 2023, 1:26 PM
libcxx/include/__string/constexpr_c_functions.h
149

Hmm, thanks for the heads up. vector<T>::erase(...) should only require that T is move assignable. In fact, it looks like this is being triggered in the std::move algorithm -- we probably don't have tests with move-only types in std::move, which is pretty embarrassing. @philnik it looks like we need to check additional constraints before calling __constexpr_memmove. Using std::move(__src[__count - 1]) here wouldn't be an option since the semantics wouldn't match those of memmove IMO.

alexfh added inline comments.Jul 4 2023, 5:02 PM
libcxx/include/__string/constexpr_c_functions.h
149

If this is not intended, I'd ask for a revert, since the impact of this is rather large.

hans added a subscriber: hans.Jul 5 2023, 9:57 AM
hans added inline comments.Jul 6 2023, 1:09 AM
libcxx/include/__string/constexpr_c_functions.h
149

+1 this is breaking us (Chromium) also.

ldionne added inline comments.Jul 6 2023, 7:49 AM
libcxx/include/__string/constexpr_c_functions.h
149

See D154613. This patch is old enough that we'd like to avoid reverting, especially since we have a clear understanding of the issue.