Boost C++ Libraries: Ticket #11835: boost::has_trivial_copy is incorrect on Clang https://svn.boost.org/trac10/ticket/11835 <p> Currently, <code>boost::has_trivial_copy&lt;T&gt;::value</code> is false when <code>T</code> has a trivial copy constructor, but is uncopyable. For example, the following type, despite having a trivial copy constructor, will report false: </p> <pre class="wiki">struct T { T(T const&amp;) = delete; } </pre><p> The offending code seems to be: </p> <pre class="wiki">template &lt;typename T&gt; struct has_trivial_copy_impl { #ifdef BOOST_HAS_TRIVIAL_COPY # ifdef __clang__ BOOST_STATIC_CONSTANT(bool, value = BOOST_HAS_TRIVIAL_COPY(T) &amp;&amp; boost::is_copy_constructible&lt;T&gt;::value); # else BOOST_STATIC_CONSTANT(bool, value = BOOST_HAS_TRIVIAL_COPY(T)); # endif #else BOOST_STATIC_CONSTANT(bool, value = (::boost::type_traits::ice_and&lt; ::boost::is_pod&lt;T&gt;::value, ::boost::type_traits::ice_not&lt; ::boost::is_volatile&lt;T&gt;::value &gt;::value &gt;::value)); #endif }; </pre><p> This fix was applied for Clang for the following ticket: </p> <p> <a class="ext-link" href="https://svn.boost.org/trac/boost/ticket/10389"><span class="icon">​</span>https://svn.boost.org/trac/boost/ticket/10389</a> </p> <p> The fix actually broke the behaviour of <code>boost::has_trivial_copy</code> in Clang, to make it match the broken behaviour in GCC 4.8 and VC++. The bug was actually in Boost.Container, not Boost.<a class="missing wiki">TypeTraits</a>, but that bug has now been fixed (Boost.Container no longer depends on Boost.<a class="missing wiki">TypeTraits</a>). GCC actually fixed its intrinsic <code>__has_trivial_copy</code> in GCC 4.9, so <code>boost::has_trivial_copy</code> works correctly in the latest GCC versions. It's still broken on VC++ because of its faulty implementation of <code>__has_trivial_copy</code>, though it seems probable that there is no possible workaround. </p> <p> But I digress. <code>__has_trivial_copy</code> works on Clang, but the "fix" in Boost breaks <code>boost::has_trivial_copy</code>. Note that <code>boost::has_trivial_assign</code>, <code>boost::has_trivial_move_constructor</code> and <code>boost::has_trivial_move_assign</code> appear to be okay. </p> <p> And one final thought: <code>boost::has_trivial_copy</code> is not really useful for anything except implementing higher-level type-traits like <code>std::is_trivially_copyable</code> (which essentially says whether a type can be safely <code>memcpy</code>'d -- a copy optimization). <code>std::has_trivial_copy_constructor</code> does not exist, probably for this reason. In my opinion, it would be better for <code>boost::has_trivial_copy</code> to not exist in the public API, instead replacing it with a <code>boost::is_trivially_copyable</code>. </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/11835 Trac 1.4.3 John Maddock Wed, 09 Dec 2015 18:15:03 GMT <link>https://svn.boost.org/trac10/ticket/11835#comment:1 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/11835#comment:1</guid> <description> <p> Hold on a second - type T in your example is not IMO trivially copyable (because it's not copyable at all). I realise some interpretations of the std will result in std::has_trivial_copy returning true for this type, but that seems to be bonkers to me. It has always been (deliberate) Boost.<a class="missing wiki">TypeTraits</a> behaviour for such types to be non-trivial. </p> <p> What am I missing? </p> </description> <category>Ticket</category> </item> <item> <author>joseph.thomson@…</author> <pubDate>Wed, 09 Dec 2015 20:00:50 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/11835#comment:2 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/11835#comment:2</guid> <description> <p> As far as I can see, the standard unambiguously states that deleted copy constructors (and other similar functions) are trivial. I quote from the C++14 draft: </p> <blockquote class="citation"> <p> [class.copy] </p> <p> A copy/move constructor for class X is trivial if it is not <strong>user-provided</strong>, its parameter-type-list is &gt; equivalent to the parameter-type-list of an implicit declaration, and if </p> <ul><li>class X has no virtual functions and no virtual base classes, and </li><li>class X has no non-static data members of volatile-qualified type, and </li><li>the constructor selected to copy/move each direct base class subobject is trivial, and </li><li>for each non-static data member of X that is of class type (or array thereof), the constructor &gt; selected to copy/move that member is trivial; </li></ul><p> otherwise the copy/move constructor is non-trivial. </p> </blockquote> <p> The key term here is "user-provided". Are deleted functions user-provided? </p> <blockquote class="citation"> <p> [dcl.fct.def.default] </p> <p> A function is user-provided if it is user-declared and <strong>not explicitly</strong> defaulted or <strong>deleted</strong> on its first declaration. </p> </blockquote> <p> There is no way around it: <code>T</code> has a trivial copy constructor. </p> <p> But how about <code>T</code> being trivially copyable? </p> <blockquote class="citation"> <p> [class] </p> <p> A trivially copyable class is a class that </p> <ul><li>has no non-trivial copy constructors, </li><li>has no non-trivial move constructors, </li><li>has no non-trivial copy assignment operators, </li><li>has no non-trivial move assignment operators, and </li><li>has a trivial destructor. </li></ul></blockquote> <p> We have already established that the copy constructor is trivial. The other functions aren't even user-defined, so they must be trivial too. The unavoidable conclusion is that <code>T</code> is trivially copyable. For completeness, here is the definition of <code>std::is_trivially_copyable</code>: </p> <blockquote class="citation"> <p> [meta.unary.prop] </p> <p> Template: <code>template &lt;class T&gt; struct is_trivially_copyable;</code> </p> <p> Condition: <code>T</code> is a <strong>trivially copyable</strong> type </p> </blockquote> <p> In my opinion, it does make sense, seeing as <code>is_trivially_copyable</code> simply states whether the object can be safely <code>memcpy</code>'d. Perhaps it's a badly named trait, but you can't deny what the standard says. An object doesn't need to have a copy constructor or assignment operator to be <code>memcpy</code>'d. Besides, what would the requirement be if they were needed? One or the other, or both? Seems better just to separate these concepts out into <code>std::is_trivially_copy_constructible</code> and <code>std::is_trivially_copy_assignable</code>, which is indeed what they did. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>John Maddock</dc:creator> <pubDate>Sat, 12 Dec 2015 18:31:39 GMT</pubDate> <title>status changed; resolution set https://svn.boost.org/trac10/ticket/11835#comment:3 https://svn.boost.org/trac10/ticket/11835#comment:3 <ul> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">wontfix</span> </li> </ul> <p> The point here is that has_trivial_copy is a Boost-specific trait so we can do what we want, and much more importantly: defining non-copyable types as trivially copyable drives a coach and horses through type safety, and leads to downright nasty and pernicious bugs. The original bug report you referenced is one such - types that are moveable but not copyable should never, ever be memcpy'd. If they are, then bad things typically happen. I may add a note to the docs to this effect, but the more I think about this the less inclined I am to make any change to current behaviour. </p> Ticket joseph.thomson@… Sat, 12 Dec 2015 21:05:31 GMT <link>https://svn.boost.org/trac10/ticket/11835#comment:4 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/11835#comment:4</guid> <description> <p> Okay, I did some more searching, and apparently there is a proposal to require at least one special function to not be deleted in order to classify as trivially copyable: </p> <p> <a class="ext-link" href="http://stackoverflow.com/questions/29759441/is-a-class-with-deleted-copy-constructor-trivially-copyable"><span class="icon">​</span>http://stackoverflow.com/questions/29759441/is-a-class-with-deleted-copy-constructor-trivially-copyable</a> </p> <p> This is because classes which use special OS-level resources, like atomics, mutexes and condition variables, should not be <code>memcpy</code>-able: </p> <p> <a class="ext-link" href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4460.html"><span class="icon">​</span>http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4460.html</a> </p> <p> Note that this still does not mean that a trivially copyable type requires a copy constructor; for example, a class with only a non-deleted move assignment operator may still be trivially copyable. </p> <p> However, <code>boost::is_trivially_copyable</code> does not exist; this ticket is about <code>boost::has_trivial_copy</code>. You mention that the referenced bug was caused by the unsafe standard-compliant behaviour of GCC's <code>__has_trivial_copy</code> intrinsic; in fact, as I now realise, the bug is still there! Your preferred version of <code>has_trivial_copy</code> is essentially equivalent to <code>std::is_trivially_copy_constructible</code>, and <code>is_trivially_copy_constructible</code> is <em>not</em> a sufficient condition for <code>memcpy</code>-ability; the sufficient condition is <code>is_trivially_copyable</code>. A trivially copy constructible class might not be safe to <code>memcpy</code>, as it may have other non-trivial special functions (it may be unlikely that they would do anything unsafe, but it's possible). </p> <p> This is actually a problem with your documentation. You should probably add your own version of <code>is_trivially_copyable</code> and tell people to check that if they want to <code>memcpy</code> things. And Boost's concept of "trivial" doesn't <strong>have</strong> to match the C++ standard. You can invent your own definition. However, you should probably apply it consistently. </p> <p> First, the behaviour of <code>boost::has_trivial_copy</code> still varies between compilers. Specifically, GCC reports that deleted copy constructors are trivial. </p> <p> Secondly, the behaviours of <code>has_trivial_copy_assign</code>, <code>has_trivial_move_constructor</code> and <code>has_trivial_move_assign</code> do not match <code>has_trivial_copy</code>, on both GCC and Clang. </p> <p> However, once you have fixed these two issues, you have a problem if you want to implement <code>is_trivially_copyable</code> in terms of these other traits. As the proposed amendment mentions, defining deleted special functions as non-trivial means that this type becomes non-trivial: </p> <pre class="wiki">struct X { const int val; }; </pre><p> Thus, your implementation of <code>is_trivially_copyable</code> would say that <code>X</code> cannot be safely <code>memcpy</code>'d. This is a problem, no? </p> <p> In conclusion: </p> <ul><li><code>has_trivial_copy</code> and its related traits do not adhere to your definition of "trivial" on some compilers. </li><li>The documentation for these traits says that they can be used to check whether <code>memcpy</code> is safe to use; this is incorrect, as <code>is_trivially_copyable</code> is the required condition. </li><li>Your preferred definition of "trivial" is too conservative, and will result in some obviously <code>memcpy</code>-able types reporting false for <code>is_trivially_copyable</code>. </li><li>The current standard definition of <code>is_trivially_copyable</code> is potentially dangerous when types use certain OS-level resources; a pending amendment to the standard fixes this. </li></ul><p> Perhaps this is a whole other issue in itself. Tell me if you want me to raise a separate ticket, and I will. </p> </description> <category>Ticket</category> </item> <item> <author>joseph.thomson@…</author> <pubDate>Sat, 12 Dec 2015 21:35:19 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/11835#comment:5 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/11835#comment:5</guid> <description> <p> p.s. Ignore the bit where I said <em>"(it may be unlikely that they would do anything unsafe, but it's possible)"</em>; I'm just confusing myself. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>John Maddock</dc:creator> <pubDate>Sun, 13 Dec 2015 12:03:32 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/11835#comment:6 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/11835#comment:6</guid> <description> <blockquote class="citation"> <blockquote> <p> has_trivial_copy and its related traits do not adhere to your definition of "trivial" on some compilers. </p> </blockquote> </blockquote> <p> I just checked them all with gcc 4.6.4, 4.7.4, 4.8.4, 4.9.2, 5.1.0, plus clang and Intel and they all do the correct thing - which is to say they all regard deleted operators as non-trivial. This is in 1.60 beta BTW. Also verified our tests check this. Note that all these traits have been completely rewritten for the next release. </p> <blockquote class="citation"> <p> The documentation for these traits says that they can be used to check whether memcpy is safe to use; this is incorrect, as is_trivially_copyable is the required condition. </p> </blockquote> <p> We have never provided is_trivially_copyable, if we did it would likely be composed from these traits. Plus I believe you have this backwards - is_trivially_copy_constructible is the std trait - and there is a precondition on that trait that the type be copy-constructible, from C++14: </p> <p> "For a referenceable type T, the same result as is_trivially_constructible&lt;T, const T&amp;&gt;::value, otherwise false." </p> <p> and then is_trivially_constructible says: </p> <p> "is_constructible&lt;T, Args...&gt;::value is true and the variable definition for is_constructible, as defined below, is known to call no operation that is not trivial ( 3.9, 12)." </p> <p> Which would rule out types with deleted copy-constructors from being trivially-copyable. </p> <blockquote class="citation"> <p> Your preferred definition of "trivial" is too conservative, and will result in some obviously memcpy-able types reporting false for is_trivially_copyable. </p> </blockquote> <p> Such as? Note that has_trivial_copy (which I accept is badly named) refers only to copy-constructors. In your example: </p> <pre class="wiki">struct X { const int val; }; </pre><p> If you care at all about const-correctness (and you should) then such a type really is non-trivial, or more specifically: </p> <pre class="wiki">default construct trivial copy-construct trivial move-construct trivial destruct trivial copy-assign not trivial or allowed move-assign not trivial or allowed </pre><p> Which I believe is what 1.60 has implemented. </p> <blockquote class="citation"> <p> The current standard definition of is_trivially_copyable is potentially dangerous when types use certain OS-level resources; a pending amendment to the standard fixes this. </p> </blockquote> <p> Right, IMO code should use traits which pertain to the actual operation that will be performed - for example boost::has_trivial_copy is closest to std::is_trivially_copy_constructible and both should return false for non-copyable types and avoid that nasty (and very real) black hole. </p> <p> One final point: has_trivial_copy and friends were developed for C++03 (and later included in TR1 standard). There's a lot of code out there which relies on has_trivial_copy implies safe-to-memcpy instead of copy construct. That code should absolutely not be broken just because C++11 has introduced new features: ie deleted functions. </p> </description> <category>Ticket</category> </item> <item> <author>joseph.thomson@…</author> <pubDate>Sun, 13 Dec 2015 15:36:17 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/11835#comment:7 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/11835#comment:7</guid> <description> <blockquote class="citation"> <p> I just checked them all with gcc 4.6.4, 4.7.4, 4.8.4, 4.9.2, 5.1.0, plus clang and Intel and they all do the correct thing - which is to say they all regard deleted operators as non-trivial. This is in 1.60 beta BTW. Also verified our tests check this. Note that all these traits have been completely rewritten for the next release. </p> </blockquote> <p> That's good! </p> <blockquote class="citation"> <p> One final point: has_trivial_copy and friends were developed for C++03 (and later included in TR1 standard). There's a lot of code out there which relies on has_trivial_copy implies safe-to-memcpy instead of copy construct. That code should absolutely not be broken just because C++11 has introduced new features: ie deleted functions. </p> </blockquote> <p> I agree. It's a shame that Boost.<a class="missing wiki">TypeTraits</a>' definition of "trivial" special functions doesn't match the language specification, but not breaking existing code is more important technically correct naming. Have you considered providing aliases (e.g. <code>boost::is_copy_constructible</code>) that match the standard library? </p> <blockquote class="citation"> <p> We have never provided is_trivially_copyable, if we did it would likely be composed from these traits. Plus I believe you have this backwards - is_trivially_copy_constructible is the std trait - and there is a precondition on that trait that the type be copy-constructible, from C++14: </p> <p> "For a referenceable type T, the same result as is_trivially_constructible&lt;T, const T&amp;&gt;::value, otherwise false." </p> <p> and then is_trivially_constructible says: </p> <p> "is_constructible&lt;T, Args...&gt;::value is true and the variable definition for is_constructible, as defined below, is known to call no operation that is not trivial ( 3.9, 12)." Which would rule out types with deleted copy-constructors from being trivially-copyable. </p> </blockquote> <p> Classes with deleted copy constructors <em>can</em> be trivially copyable (as I will demonstrate). However, the issue I was trying to raise here was that trivially copy constructible types are <em>not necessarily</em> trivially copyable, so it is potentially undefined behaviour to <code>memcpy</code> them. </p> <p> I think you may be confusing three different concepts: </p> <p> <strong>triviality of special functions</strong> </p> <p> [class.copy] </p> <blockquote class="citation"> <p> A copy/move constructor for class X is trivial if it is not user-provided, its parameter-type-list is equivalent to the parameter-type-list of an implicit declaration, and if </p> <ul><li>class X has no virtual functions (10.3) and no virtual base classes (10.1), and </li><li>class X has no non-static data members of volatile-qualified type, and </li><li>the constructor selected to copy/move each direct base class subobject is trivial, and </li><li>for each non-static data member of X that is of class type (or array thereof), the constructor selected to copy/move that member is trivial; </li></ul><p> otherwise the copy/move constructor is non-trivial. </p> </blockquote> <blockquote class="citation"> <p> A copy/move assignment operator for class X is trivial if it is not user-provided, its parameter-type-list is equivalent to the parameter-type-list of an implicit declaration, and if </p> <ul><li>class X has no virtual functions (10.3) and no virtual base classes (10.1), and </li><li>class X has no non-static data members of volatile-qualified type, and </li><li>the assignment operator selected to copy/move each direct base class subobject is trivial, and </li><li>for each non-static data member of X that is of class type (or array thereof), the assignment operator selected to copy/move that member is trivial; </li></ul><p> otherwise the copy/move assignment operator is non-trivial. </p> </blockquote> <blockquote class="citation"> <p> A destructor is trivial if it is not user-provided and if: </p> <ul><li>the destructor is not virtual, </li><li>all of the direct base classes of its class have trivial destructors, and </li><li>for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor. </li></ul><p> Otherwise, the destructor is non-trivial. </p> </blockquote> <p> [dcl.fct.def.default] </p> <blockquote class="citation"> <p> A function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. </p> </blockquote> <p> This all means that deleted special functions <em>can</em> be trivial. It doesn't matter how <code>boost::has_trivial_copy</code> behaves (you can make it do whatever you like); this is the standard's definition of what constitutes a trivial special function, and it is essential to when interpreting the standard's definition of "trivially copyable". </p> <p> <strong>trivial copyability of classes</strong> </p> <p> [class] </p> <blockquote class="citation"> <p> A trivially copyable class is a class that: </p> <ul><li>has no non-trivial copy constructors (12.8), </li><li>has no non-trivial move constructors (12.8), </li><li>has no non-trivial copy assignment operators (13.5.3, 12.8), </li><li>has no non-trivial move assignment operators (13.5.3, 12.8), and </li><li>has a trivial destructor (12.4). </li></ul></blockquote> <p> Since deleted special function <em>can</em> be trivial, according to the standard, this means that this function <em>is</em> trivially copyable, however counter-intuitive it may be: </p> <pre class="wiki">struct X { X(X const&amp;) = delete; X&amp; operator=(X const&amp;) = delete; X(X&amp;&amp;) = delete; X&amp; operator=(X&amp;&amp;) = delete; }; </pre><p> However, a proposal has been submitted to change the definition of "trivially copyable", because under the current definition, classes like <code>std::atomic</code> and <code>std::mutex</code> are trivially copyable. </p> <p> <a class="ext-link" href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1734"><span class="icon">​</span>http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1734</a> </p> <p> This proposal suggests that "trivially copyable" classes have to have at least one non-deleted copy/move special functions (and that the destructor be non-deleted): </p> <blockquote class="citation"> <p> A trivially copyable class is a class: </p> <ul><li>where each copy constructor, move constructor, copy assignment operator, and move assignment operator (12.8, 13.5.3) is either deleted or trivial, </li><li>that has at least one non-deleted copy constructor, move constructor, copy assignment operator, or move assignment operator, and </li><li>that has a trivial, non-deleted destructor (12.4). </li></ul></blockquote> <p> This would make the aforementioned class no longer trivially copyable. However, a trivially copyable class does <em>not</em> necessarily require a non-deleted copy constructor; any non-deleted special function will fulfil the requirements. Thus, this class would still be trivially copyable: </p> <pre class="wiki">struct X { const int val; }; </pre><p> I took this example from a C++ standards committee document: </p> <p> <a class="ext-link" href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4460.html"><span class="icon">​</span>http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4460.html</a> </p> <p> So in their words: </p> <blockquote class="citation"> <p> So, why don't we just say that a deleted special member function is non-trivial? Alas, that would mean the following type becomes non-trivial: </p> <pre class="wiki">struct X { const int val; }; </pre><p> X has a deleted copy assignment operator. That doesn't mean that X is not trivially copyable. And X has been trivial since the dawn of time, so we must be careful not to break such expectations. </p> </blockquote> <p> <strong>trivial constructibility/assignability of classes</strong> </p> <p> You already quoted the standard, so I don't have to. </p> <p> For a class to be trivially copy constructible, it must be <strong>copy constructible</strong>, and the copy constructor must be <strong>trivial</strong>. Since you cannot copy construct an object using a <em>deleted</em> copy constructor, I agree that a class with a deleted copy constructor is <em>not</em> trivially copy constructible. However, copy constructibility is <em>not</em> a requirement of trivial copyability; the presence of a trivial copy constructor <em>is</em> (even a deleted one). Even under the proposed amendment, as long as there is at least one other non-deleted copy/move function, a class does <em>not</em> have to be copy constructible to be trivially copyable. </p> <p> <strong>In conclusion...</strong> </p> <p> In other words, both trivially copyable and trivially copy constructible classes <em>must</em> have trivial copy constructors (deleted copy constructors <em>may</em> be trivial), but only trivially copy constructible classes <em>must</em> be copy constructible. </p> <p> But the most important thing is this: </p> <p> [intro.object] </p> <blockquote class="citation"> <p> An object of trivially copyable or standard-layout type (3.9) shall occupy contiguous bytes of storage. </p> </blockquote> <p> [basic.types] </p> <blockquote class="citation"> <p> For any trivially copyable type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject, if the underlying bytes (1.7) making up obj1 are copied into obj2,43 obj2 shall subsequently hold the same value as obj1. </p> <pre class="wiki">T* t1p; T* t2p; // provided that t2p points to an initialized object ... std::memcpy(t1p, t2p, sizeof(T)); // at this point, every subobject of trivially copyable type in *t1p contains // the same value as the corresponding subobject in *t2p </pre></blockquote> <p> Thus, using <code>memcpy</code> to copy a non-trivially copyable class would be undefined behaviour. Since trivially copy constructible classes are not necessarily trivially copyable, relying on <code>boost::has_trivial_copy</code> to tell you whether it's safe to use <code>memcpy</code> could lead to undefined behaviour. This is why I say that your documentation needs to be changed (and potentially, <code>boost::is_trivially_copyable</code> added). </p> <p> Note that you could implement a <code>boost::is_trivially_copyable</code> that requires all copy/move special functions to be non-deleted. While this would be overly-conservative (e.g. the aforementioned <code>X</code> class would register as non-trivially copyable), it would at least be sufficient to check for <code>memcpy</code>-ability. </p> </description> <category>Ticket</category> </item> </channel> </rss>