LLVM Bugzilla is read-only and represents the historical archive of all LLVM issues filled before November 26, 2021. Use github to submit LLVM bugs

Bug 28527 - std::copy() calls memmove on nontrivially-copyable type
Summary: std::copy() calls memmove on nontrivially-copyable type
Status: CONFIRMED
Alias: None
Product: libc++
Classification: Unclassified
Component: All Bugs (show other bugs)
Version: 3.8
Hardware: PC Windows NT
: P normal
Assignee: Marshall Clow (home)
URL:
Keywords:
: 45103 (view as bug list)
Depends on:
Blocks:
 
Reported: 2016-07-12 16:49 PDT by Aaron Ballman
Modified: 2020-03-05 21:02 PST (History)
5 users (show)

See Also:
Fixed By Commit(s):


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Aaron Ballman 2016-07-12 16:49:29 PDT
Consider the following snippet:

#include <algorithm>

int main() {
  volatile int *v1[100];
  volatile int *v2[100];

  std::copy(v1, v1 + 100, v2);
}

This results in a call to memmove() rather than copying individual volatile elements of the array (https://godbolt.org/g/b0fv3R). This should be handled a bit more kindly, since volatile-qualified types are not trivially-copyable types ([basic.types]p9 states in part: "Cv-unqualified scalar types, trivially copyable class types (Clause 9), arrays of such types, and nonvolatile const-qualified versions of these types (3.9.3) are collectively called trivially copyable types.").
Comment 1 Marshall Clow (home) 2016-07-12 16:54:27 PDT
The problem is that this is true:
     static_assert(std::is_trivially_copy_assignable<volatile int>::value, "" );
Comment 2 Eli Friedman 2016-07-12 18:35:39 PDT
"volatile int*" is a pointer to a volatile type, but it isn't itself a volatile type. I think you meant the following?


#include <algorithm>

int main() {
  volatile int v1[100];
  volatile int v2[100];

  std::copy(v1, v1 + 100, v2);
}

Gives:

error: cannot initialize a parameter of type 'void *' with an lvalue of type 'volatile int *'
Comment 3 Aaron Ballman 2016-07-13 10:14:58 PDT
(In reply to comment #2)
> "volatile int*" is a pointer to a volatile type, but it isn't itself a
> volatile type. I think you meant the following?
> 
> 
> #include <algorithm>
> 
> int main() {
>   volatile int v1[100];
>   volatile int v2[100];
> 
>   std::copy(v1, v1 + 100, v2);
> }
> 
> Gives:
> 
> error: cannot initialize a parameter of type 'void *' with an lvalue of type
> 'volatile int *'

No, I meant 'volatile int *' -- a pointer to volatile ints. Those cannot be copied via memcpy() or memmove() under as-if because volatile int is not a trivially copyable type.
Comment 4 Eli Friedman 2016-07-13 12:26:58 PDT
(In reply to comment #3)
> No, I meant 'volatile int *' -- a pointer to volatile ints. Those cannot be
> copied via memcpy() or memmove() under as-if because volatile int is not a
> trivially copyable type.

Your original testcase passes a volatile int** to std::copy... which can be copied with memcpy (there aren't any volatile objects involved).
Comment 5 Aaron Ballman 2016-07-13 12:30:37 PDT
(In reply to comment #4)
> (In reply to comment #3)
> > No, I meant 'volatile int *' -- a pointer to volatile ints. Those cannot be
> > copied via memcpy() or memmove() under as-if because volatile int is not a
> > trivially copyable type.
> 
> Your original testcase passes a volatile int** to std::copy... which can be
> copied with memcpy (there aren't any volatile objects involved).

Ugh, my brain some days. :-( You're absolutely right, this does pass a volatile int **.

However, when I correct my test case to not be wrong, I still get misbehavior.

https://godbolt.org/g/tC0y7b

In file included from /tmp/gcc-explorer-compiler116613-82-1qhm7jl/example.cpp:1:
/opt/clang+llvm-3.8.0-x86_64-linux-gnu-ubuntu-14.04/bin/../include/c++/v1/algorithm:1748:24: error: cannot initialize a parameter of type 'void *' with an lvalue of type 'volatile int *'
_VSTD::memmove(__result, __first, __n * sizeof(_Up));
^~~~~~~~
/opt/clang+llvm-3.8.0-x86_64-linux-gnu-ubuntu-14.04/bin/../include/c++/v1/algorithm:1757:19: note: in instantiation of function template specialization 'std::__1::__copy<volatile int, volatile int>' requested here
return _VSTD::__copy(__unwrap_iter(__first), __unwrap_iter(__last), __unwrap_iter(__result));
^
7 : note: in instantiation of function template specialization 'std::__1::copy<volatile int *, volatile int *>' requested here
std::copy(v1, v1 + 100, v2);
^
/usr/include/string.h:50:29: note: passing argument to parameter '__dest' here
extern void *memmove (void *__dest, const void *__src, size_t __n)
^
1 error generated.
Compilation failed
Comment 6 Marshall Clow (home) 2017-01-03 11:17:47 PST
I get the same behavior (compiler error) on gcc, but not on MSVC.
Comment 7 Richard Smith 2017-04-10 14:21:59 PDT
(Responding to Marshall's question from IRC)

Per http://wg21.link/cwg2094, clang is correct to consider volatile types and types with volatile subobjects as being trivially copyable. Perhaps we need a separate trait to determine if copying an object is really equivalent to memcpy (trivially copyable *and* has no volatile subobjects). =(
Comment 8 Marshall Clow (home) 2017-04-11 19:30:32 PDT
> Perhaps we need a separate trait to determine if copying an object is really equivalent to memcpy.

If we do that, then we can get rid of "is_trivially_copyable/is_trivially_copy_constructible/etc", because that's pretty much all it they're used for.
Comment 9 Marshall Clow (home) 2020-03-05 21:02:23 PST
*** Bug 45103 has been marked as a duplicate of this bug. ***