<div dir="ltr">I forgot to say:<div><br></div><div>Based on a patch by Vassil Vassilev, which was based on a patch by <span style="font-size:12.8px">Bernd Schmidt, which was based on a patch by Reid Kleckner.</span></div></div><div class="gmail_extra"><br><div class="gmail_quote">On 8 August 2017 at 12:12, Richard Smith via cfe-commits <span dir="ltr"><<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: rsmith<br>
Date: Tue Aug 8 12:12:28 2017<br>
New Revision: 310401<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=310401&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project?rev=310401&view=rev</a><br>
Log:<br>
PR19668, PR23034: Fix handling of move constructors and deleted copy<br>
constructors when deciding whether classes should be passed indirectly.<br>
<br>
This fixes ABI differences between Clang and GCC:<br>
<br>
* Previously, Clang ignored the move constructor when making this<br>
determination. It now takes the move constructor into account, per<br>
<a href="https://github.com/itanium-cxx-abi/cxx-abi/pull/17" rel="noreferrer" target="_blank">https://github.com/itanium-<wbr>cxx-abi/cxx-abi/pull/17</a> (this change may<br>
seem recent, but the ABI change was agreed on the Itanium C++ ABI<br>
list a long time ago).<br>
<br>
* Previously, Clang's behavior when the copy constructor was deleted<br>
was unstable -- depending on whether the lazy declaration of the<br>
copy constructor had been triggered, you might get different behavior.<br>
We now eagerly declare the copy constructor whenever its deletedness<br>
is unclear, and ignore deleted copy/move constructors when looking for<br>
a trivial such constructor.<br>
<br>
This also fixes an ABI difference between Clang and MSVC:<br>
<br>
* If the copy constructor would be implicitly deleted (but has not been<br>
lazily declared yet), for instance because the class has an rvalue<br>
reference member, we would pass it directly. We now pass such a class<br>
indirectly, matching MSVC.<br>
<br>
Modified:<br>
cfe/trunk/include/clang/AST/<wbr>DeclCXX.h<br>
cfe/trunk/lib/AST/ASTImporter.<wbr>cpp<br>
cfe/trunk/lib/AST/DeclCXX.cpp<br>
cfe/trunk/lib/CodeGen/<wbr>CGCXXABI.cpp<br>
cfe/trunk/lib/CodeGen/<wbr>ItaniumCXXABI.cpp<br>
cfe/trunk/lib/CodeGen/<wbr>MicrosoftCXXABI.cpp<br>
cfe/trunk/lib/Sema/<wbr>SemaDeclCXX.cpp<br>
cfe/trunk/lib/Serialization/<wbr>ASTReaderDecl.cpp<br>
cfe/trunk/lib/Serialization/<wbr>ASTWriter.cpp<br>
cfe/trunk/test/CodeGenCXX/<wbr>uncopyable-args.cpp<br>
cfe/trunk/unittests/<wbr>ASTMatchers/<wbr>ASTMatchersNarrowingTest.cpp<br>
<br>
Modified: cfe/trunk/include/clang/AST/<wbr>DeclCXX.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=310401&r1=310400&r2=310401&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/include/<wbr>clang/AST/DeclCXX.h?rev=<wbr>310401&r1=310400&r2=310401&<wbr>view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/include/clang/AST/<wbr>DeclCXX.h (original)<br>
+++ cfe/trunk/include/clang/AST/<wbr>DeclCXX.h Tue Aug 8 12:12:28 2017<br>
@@ -375,6 +375,7 @@ class CXXRecordDecl : public RecordDecl<br>
/// \brief These flags are \c true if a defaulted corresponding special<br>
/// member can't be fully analyzed without performing overload resolution.<br>
/// @{<br>
+ unsigned NeedOverloadResolutionForCopyC<wbr>onstructor : 1;<br>
unsigned NeedOverloadResolutionForMoveC<wbr>onstructor : 1;<br>
unsigned NeedOverloadResolutionForMoveA<wbr>ssignment : 1;<br>
unsigned NeedOverloadResolutionForDestr<wbr>uctor : 1;<br>
@@ -383,6 +384,7 @@ class CXXRecordDecl : public RecordDecl<br>
/// \brief These flags are \c true if an implicit defaulted corresponding<br>
/// special member would be defined as deleted.<br>
/// @{<br>
+ unsigned DefaultedCopyConstructorIsDele<wbr>ted : 1;<br>
unsigned DefaultedMoveConstructorIsDele<wbr>ted : 1;<br>
unsigned DefaultedMoveAssignmentIsDelet<wbr>ed : 1;<br>
unsigned DefaultedDestructorIsDeleted : 1;<br>
@@ -415,6 +417,12 @@ class CXXRecordDecl : public RecordDecl<br>
/// constructor.<br>
unsigned HasDefaultedDefaultConstructor : 1;<br>
<br>
+ /// \brief True if this class can be passed in a non-address-preserving<br>
+ /// fashion (such as in registers) according to the C++ language rules.<br>
+ /// This does not imply anything about how the ABI in use will actually<br>
+ /// pass an object of this class.<br>
+ unsigned CanPassInRegisters : 1;<br>
+<br>
/// \brief True if a defaulted default constructor for this class would<br>
/// be constexpr.<br>
unsigned DefaultedDefaultConstructorIsC<wbr>onstexpr : 1;<br>
@@ -811,18 +819,50 @@ public:<br>
return data().FirstFriend.isValid();<br>
}<br>
<br>
+ /// \brief \c true if a defaulted copy constructor for this class would be<br>
+ /// deleted.<br>
+ bool defaultedCopyConstructorIsDele<wbr>ted() const {<br>
+ assert((!<wbr>needsOverloadResolutionForCopy<wbr>Constructor() ||<br>
+ (data().DeclaredSpecialMembers & SMF_CopyConstructor)) &&<br>
+ "this property has not yet been computed by Sema");<br>
+ return data().<wbr>DefaultedCopyConstructorIsDele<wbr>ted;<br>
+ }<br>
+<br>
+ /// \brief \c true if a defaulted move constructor for this class would be<br>
+ /// deleted.<br>
+ bool defaultedMoveConstructorIsDele<wbr>ted() const {<br>
+ assert((!<wbr>needsOverloadResolutionForMove<wbr>Constructor() ||<br>
+ (data().DeclaredSpecialMembers & SMF_MoveConstructor)) &&<br>
+ "this property has not yet been computed by Sema");<br>
+ return data().<wbr>DefaultedMoveConstructorIsDele<wbr>ted;<br>
+ }<br>
+<br>
+ /// \brief \c true if a defaulted destructor for this class would be deleted.<br>
+ bool defaultedDestructorIsDeleted() const {<br>
+ return !data().<wbr>DefaultedDestructorIsDeleted;<br>
+ }<br>
+<br>
+ /// \brief \c true if we know for sure that this class has a single,<br>
+ /// accessible, unambiguous copy constructor that is not deleted.<br>
+ bool hasSimpleCopyConstructor() const {<br>
+ return !<wbr>hasUserDeclaredCopyConstructor<wbr>() &&<br>
+ !data().<wbr>DefaultedCopyConstructorIsDele<wbr>ted;<br>
+ }<br>
+<br>
/// \brief \c true if we know for sure that this class has a single,<br>
/// accessible, unambiguous move constructor that is not deleted.<br>
bool hasSimpleMoveConstructor() const {<br>
return !<wbr>hasUserDeclaredMoveConstructor<wbr>() && hasMoveConstructor() &&<br>
!data().<wbr>DefaultedMoveConstructorIsDele<wbr>ted;<br>
}<br>
+<br>
/// \brief \c true if we know for sure that this class has a single,<br>
/// accessible, unambiguous move assignment operator that is not deleted.<br>
bool hasSimpleMoveAssignment() const {<br>
return !<wbr>hasUserDeclaredMoveAssignment(<wbr>) && hasMoveAssignment() &&<br>
!data().<wbr>DefaultedMoveAssignmentIsDelet<wbr>ed;<br>
}<br>
+<br>
/// \brief \c true if we know for sure that this class has an accessible<br>
/// destructor that is not deleted.<br>
bool hasSimpleDestructor() const {<br>
@@ -878,7 +918,16 @@ public:<br>
/// \brief Determine whether we need to eagerly declare a defaulted copy<br>
/// constructor for this class.<br>
bool needsOverloadResolutionForCopy<wbr>Constructor() const {<br>
- return data().HasMutableFields;<br>
+ // C++17 [class.copy.ctor]p6:<br>
+ // If the class definition declares a move constructor or move assignment<br>
+ // operator, the implicitly declared copy constructor is defined as<br>
+ // deleted.<br>
+ // In MSVC mode, sometimes a declared move assignment does not delete an<br>
+ // implicit copy constructor, so defer this choice to Sema.<br>
+ if (data().<wbr>UserDeclaredSpecialMembers &<br>
+ (SMF_MoveConstructor | SMF_MoveAssignment))<br>
+ return true;<br>
+ return data().<wbr>NeedOverloadResolutionForCopyC<wbr>onstructor;<br>
}<br>
<br>
/// \brief Determine whether an implicit copy constructor for this type<br>
@@ -919,7 +968,16 @@ public:<br>
needsImplicitMoveConstructor()<wbr>;<br>
}<br>
<br>
- /// \brief Set that we attempted to declare an implicitly move<br>
+ /// \brief Set that we attempted to declare an implicit copy<br>
+ /// constructor, but overload resolution failed so we deleted it.<br>
+ void setImplicitCopyConstructorIsDe<wbr>leted() {<br>
+ assert((data().<wbr>DefaultedCopyConstructorIsDele<wbr>ted ||<br>
+ needsOverloadResolutionForCopy<wbr>Constructor()) &&<br>
+ "Copy constructor should not be deleted");<br>
+ data().<wbr>DefaultedCopyConstructorIsDele<wbr>ted = true;<br>
+ }<br>
+<br>
+ /// \brief Set that we attempted to declare an implicit move<br>
/// constructor, but overload resolution failed so we deleted it.<br>
void setImplicitMoveConstructorIsDe<wbr>leted() {<br>
assert((data().<wbr>DefaultedMoveConstructorIsDele<wbr>ted ||<br>
@@ -1316,6 +1374,18 @@ public:<br>
return data().<wbr>HasIrrelevantDestructor;<br>
}<br>
<br>
+ /// \brief Determine whether this class has at least one trivial, non-deleted<br>
+ /// copy or move constructor.<br>
+ bool canPassInRegisters() const {<br>
+ return data().CanPassInRegisters;<br>
+ }<br>
+<br>
+ /// \brief Set that we can pass this RecordDecl in registers.<br>
+ // FIXME: This should be set as part of completeDefinition.<br>
+ void setCanPassInRegisters(bool CanPass) {<br>
+ data().CanPassInRegisters = CanPass;<br>
+ }<br>
+<br>
/// \brief Determine whether this class has a non-literal or/ volatile type<br>
/// non-static data member or base class.<br>
bool hasNonLiteralTypeFieldsOrBases<wbr>() const {<br>
<br>
Modified: cfe/trunk/lib/AST/ASTImporter.<wbr>cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTImporter.cpp?rev=310401&r1=310400&r2=310401&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/AST/<wbr>ASTImporter.cpp?rev=310401&r1=<wbr>310400&r2=310401&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/AST/ASTImporter.<wbr>cpp (original)<br>
+++ cfe/trunk/lib/AST/ASTImporter.<wbr>cpp Tue Aug 8 12:12:28 2017<br>
@@ -956,12 +956,16 @@ bool ASTNodeImporter::<wbr>ImportDefinition(R<br>
ToData.HasUninitializedFields = FromData.<wbr>HasUninitializedFields;<br>
ToData.HasInheritedConstructor = FromData.<wbr>HasInheritedConstructor;<br>
ToData.HasInheritedAssignment = FromData.<wbr>HasInheritedAssignment;<br>
+ ToData.<wbr>NeedOverloadResolutionForCopyC<wbr>onstructor<br>
+ = FromData.<wbr>NeedOverloadResolutionForCopyC<wbr>onstructor;<br>
ToData.<wbr>NeedOverloadResolutionForMoveC<wbr>onstructor<br>
= FromData.<wbr>NeedOverloadResolutionForMoveC<wbr>onstructor;<br>
ToData.<wbr>NeedOverloadResolutionForMoveA<wbr>ssignment<br>
= FromData.<wbr>NeedOverloadResolutionForMoveA<wbr>ssignment;<br>
ToData.<wbr>NeedOverloadResolutionForDestr<wbr>uctor<br>
= FromData.<wbr>NeedOverloadResolutionForDestr<wbr>uctor;<br>
+ ToData.<wbr>DefaultedCopyConstructorIsDele<wbr>ted<br>
+ = FromData.<wbr>DefaultedCopyConstructorIsDele<wbr>ted;<br>
ToData.<wbr>DefaultedMoveConstructorIsDele<wbr>ted<br>
= FromData.<wbr>DefaultedMoveConstructorIsDele<wbr>ted;<br>
ToData.<wbr>DefaultedMoveAssignmentIsDelet<wbr>ed<br>
@@ -973,6 +977,7 @@ bool ASTNodeImporter::<wbr>ImportDefinition(R<br>
= FromData.<wbr>HasConstexprNonCopyMoveConstru<wbr>ctor;<br>
ToData.<wbr>HasDefaultedDefaultConstructor<br>
= FromData.<wbr>HasDefaultedDefaultConstructor<wbr>;<br>
+ ToData.CanPassInRegisters = FromData.CanPassInRegisters;<br>
ToData.<wbr>DefaultedDefaultConstructorIsC<wbr>onstexpr<br>
= FromData.<wbr>DefaultedDefaultConstructorIsC<wbr>onstexpr;<br>
ToData.<wbr>HasConstexprDefaultConstructor<br>
<br>
Modified: cfe/trunk/lib/AST/DeclCXX.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=310401&r1=310400&r2=310401&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/AST/<wbr>DeclCXX.cpp?rev=310401&r1=<wbr>310400&r2=310401&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)<br>
+++ cfe/trunk/lib/AST/DeclCXX.cpp Tue Aug 8 12:12:28 2017<br>
@@ -55,15 +55,18 @@ CXXRecordDecl::DefinitionData:<wbr>:Definitio<br>
HasOnlyCMembers(true), HasInClassInitializer(false),<br>
HasUninitializedReferenceMembe<wbr>r(false), HasUninitializedFields(false),<br>
HasInheritedConstructor(false)<wbr>, HasInheritedAssignment(false),<br>
+ NeedOverloadResolutionForCopyC<wbr>onstructor(false),<br>
NeedOverloadResolutionForMoveC<wbr>onstructor(false),<br>
NeedOverloadResolutionForMoveA<wbr>ssignment(false),<br>
NeedOverloadResolutionForDestr<wbr>uctor(false),<br>
+ DefaultedCopyConstructorIsDele<wbr>ted(false),<br>
DefaultedMoveConstructorIsDele<wbr>ted(false),<br>
DefaultedMoveAssignmentIsDelet<wbr>ed(false),<br>
DefaultedDestructorIsDeleted(<wbr>false), HasTrivialSpecialMembers(SMF_<wbr>All),<br>
DeclaredNonTrivialSpecialMembe<wbr>rs(0), HasIrrelevantDestructor(true),<br>
HasConstexprNonCopyMoveConstru<wbr>ctor(false),<br>
HasDefaultedDefaultConstructor<wbr>(false),<br>
+ CanPassInRegisters(false),<br>
DefaultedDefaultConstructorIsC<wbr>onstexpr(true),<br>
HasConstexprDefaultConstructor<wbr>(false),<br>
HasNonLiteralTypeFieldsOrBases<wbr>(false), ComputedVisibleConversions(<wbr>false),<br>
@@ -352,8 +355,10 @@ CXXRecordDecl::setBases(<wbr>CXXBaseSpecifier<br>
setHasVolatileMember(true);<br>
<br>
// Keep track of the presence of mutable fields.<br>
- if (BaseClassDecl-><wbr>hasMutableFields())<br>
+ if (BaseClassDecl-><wbr>hasMutableFields()) {<br>
data().HasMutableFields = true;<br>
+ data().<wbr>NeedOverloadResolutionForCopyC<wbr>onstructor = true;<br>
+ }<br>
<br>
if (BaseClassDecl-><wbr>hasUninitializedReferenceMembe<wbr>r())<br>
data().<wbr>HasUninitializedReferenceMembe<wbr>r = true;<br>
@@ -406,6 +411,8 @@ void CXXRecordDecl::<wbr>addedClassSubobject(<br>
// -- a direct or virtual base class B that cannot be copied/moved [...]<br>
// -- a non-static data member of class type M (or array thereof)<br>
// that cannot be copied or moved [...]<br>
+ if (!Subobj-><wbr>hasSimpleCopyConstructor())<br>
+ data().<wbr>NeedOverloadResolutionForCopyC<wbr>onstructor = true;<br>
if (!Subobj-><wbr>hasSimpleMoveConstructor())<br>
data().<wbr>NeedOverloadResolutionForMoveC<wbr>onstructor = true;<br>
<br>
@@ -426,6 +433,7 @@ void CXXRecordDecl::<wbr>addedClassSubobject(<br>
// -- any non-static data member has a type with a destructor<br>
// that is deleted or inaccessible from the defaulted [ctor or dtor].<br>
if (!Subobj->hasSimpleDestructor(<wbr>)) {<br>
+ data().<wbr>NeedOverloadResolutionForCopyC<wbr>onstructor = true;<br>
data().<wbr>NeedOverloadResolutionForMoveC<wbr>onstructor = true;<br>
data().<wbr>NeedOverloadResolutionForDestr<wbr>uctor = true;<br>
}<br>
@@ -711,8 +719,10 @@ void CXXRecordDecl::addedMember(<wbr>Decl *D)<br>
data().IsStandardLayout = false;<br>
<br>
// Keep track of the presence of mutable fields.<br>
- if (Field->isMutable())<br>
+ if (Field->isMutable()) {<br>
data().HasMutableFields = true;<br>
+ data().<wbr>NeedOverloadResolutionForCopyC<wbr>onstructor = true;<br>
+ }<br>
<br>
// C++11 [class.union]p8, DR1460:<br>
// If X is a union, a non-static data member of X that is not an anonymous<br>
@@ -756,6 +766,12 @@ void CXXRecordDecl::addedMember(<wbr>Decl *D)<br>
// A standard-layout class is a class that:<br>
// -- has no non-static data members of type [...] reference,<br>
data().IsStandardLayout = false;<br>
+<br>
+ // C++1z [class.copy.ctor]p10:<br>
+ // A defaulted copy constructor for a class X is defined as deleted if X has:<br>
+ // -- a non-static data member of rvalue reference type<br>
+ if (T->isRValueReferenceType())<br>
+ data().<wbr>DefaultedCopyConstructorIsDele<wbr>ted = true;<br>
}<br>
<br>
if (!Field-><wbr>hasInClassInitializer() && !Field->isMutable()) {<br>
@@ -809,6 +825,10 @@ void CXXRecordDecl::addedMember(<wbr>Decl *D)<br>
// We may need to perform overload resolution to determine whether a<br>
// field can be moved if it's const or volatile qualified.<br>
if (T.getCVRQualifiers() & (Qualifiers::Const | Qualifiers::Volatile)) {<br>
+ // We need to care about 'const' for the copy constructor because an<br>
+ // implicit copy constructor might be declared with a non-const<br>
+ // parameter.<br>
+ data().<wbr>NeedOverloadResolutionForCopyC<wbr>onstructor = true;<br>
data().<wbr>NeedOverloadResolutionForMoveC<wbr>onstructor = true;<br>
data().<wbr>NeedOverloadResolutionForMoveA<wbr>ssignment = true;<br>
}<br>
@@ -819,6 +839,8 @@ void CXXRecordDecl::addedMember(<wbr>Decl *D)<br>
// -- X is a union-like class that has a variant member with a<br>
// non-trivial [corresponding special member]<br>
if (isUnion()) {<br>
+ if (FieldRec-><wbr>hasNonTrivialCopyConstructor()<wbr>)<br>
+ data().<wbr>DefaultedCopyConstructorIsDele<wbr>ted = true;<br>
if (FieldRec-><wbr>hasNonTrivialMoveConstructor()<wbr>)<br>
data().<wbr>DefaultedMoveConstructorIsDele<wbr>ted = true;<br>
if (FieldRec-><wbr>hasNonTrivialMoveAssignment())<br>
@@ -830,6 +852,8 @@ void CXXRecordDecl::addedMember(<wbr>Decl *D)<br>
// For an anonymous union member, our overload resolution will perform<br>
// overload resolution for its members.<br>
if (Field-><wbr>isAnonymousStructOrUnion()) {<br>
+ data().<wbr>NeedOverloadResolutionForCopyC<wbr>onstructor |=<br>
+ FieldRec->data().<wbr>NeedOverloadResolutionForCopyC<wbr>onstructor;<br>
data().<wbr>NeedOverloadResolutionForMoveC<wbr>onstructor |=<br>
FieldRec->data().<wbr>NeedOverloadResolutionForMoveC<wbr>onstructor;<br>
data().<wbr>NeedOverloadResolutionForMoveA<wbr>ssignment |=<br>
@@ -915,8 +939,10 @@ void CXXRecordDecl::addedMember(<wbr>Decl *D)<br>
}<br>
<br>
// Keep track of the presence of mutable fields.<br>
- if (FieldRec->hasMutableFields())<br>
+ if (FieldRec->hasMutableFields()) {<br>
data().HasMutableFields = true;<br>
+ data().<wbr>NeedOverloadResolutionForCopyC<wbr>onstructor = true;<br>
+ }<br>
<br>
// C++11 [class.copy]p13:<br>
// If the implicitly-defined constructor would satisfy the<br>
@@ -1450,7 +1476,7 @@ void CXXRecordDecl::<wbr>completeDefinition()<br>
<br>
void CXXRecordDecl::<wbr>completeDefinition(<wbr>CXXFinalOverriderMap *FinalOverriders) {<br>
RecordDecl::<wbr>completeDefinition();<br>
-<br>
+<br>
// If the class may be abstract (but hasn't been marked as such), check for<br>
// any pure final overriders.<br>
if (mayBeAbstract()) {<br>
<br>
Modified: cfe/trunk/lib/CodeGen/<wbr>CGCXXABI.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCXXABI.cpp?rev=310401&r1=310400&r2=310401&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/CodeGen/<wbr>CGCXXABI.cpp?rev=310401&r1=<wbr>310400&r2=310401&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/CodeGen/<wbr>CGCXXABI.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/<wbr>CGCXXABI.cpp Tue Aug 8 12:12:28 2017<br>
@@ -30,38 +30,9 @@ void CGCXXABI::ErrorUnsupportedABI(<wbr>CodeG<br>
}<br>
<br>
bool CGCXXABI::canCopyArgument(<wbr>const CXXRecordDecl *RD) const {<br>
- // If RD has a non-trivial move or copy constructor, we cannot copy the<br>
- // argument.<br>
- if (RD-><wbr>hasNonTrivialCopyConstructor() || RD-><wbr>hasNonTrivialMoveConstructor()<wbr>)<br>
- return false;<br>
-<br>
- // If RD has a non-trivial destructor, we cannot copy the argument.<br>
- if (RD->hasNonTrivialDestructor()<wbr>)<br>
- return false;<br>
-<br>
// We can only copy the argument if there exists at least one trivial,<br>
// non-deleted copy or move constructor.<br>
- // FIXME: This assumes that all lazily declared copy and move constructors are<br>
- // not deleted. This assumption might not be true in some corner cases.<br>
- bool CopyDeleted = false;<br>
- bool MoveDeleted = false;<br>
- for (const CXXConstructorDecl *CD : RD->ctors()) {<br>
- if (CD->isCopyConstructor() || CD->isMoveConstructor()) {<br>
- assert(CD->isTrivial());<br>
- // We had at least one undeleted trivial copy or move ctor. Return<br>
- // directly.<br>
- if (!CD->isDeleted())<br>
- return true;<br>
- if (CD->isCopyConstructor())<br>
- CopyDeleted = true;<br>
- else<br>
- MoveDeleted = true;<br>
- }<br>
- }<br>
-<br>
- // If all trivial copy and move constructors are deleted, we cannot copy the<br>
- // argument.<br>
- return !(CopyDeleted && MoveDeleted);<br>
+ return RD->canPassInRegisters();<br>
}<br>
<br>
llvm::Constant *CGCXXABI::<wbr>GetBogusMemberPointer(QualType T) {<br>
<br>
Modified: cfe/trunk/lib/CodeGen/<wbr>ItaniumCXXABI.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=310401&r1=310400&r2=310401&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/CodeGen/<wbr>ItaniumCXXABI.cpp?rev=310401&<wbr>r1=310400&r2=310401&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/CodeGen/<wbr>ItaniumCXXABI.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/<wbr>ItaniumCXXABI.cpp Tue Aug 8 12:12:28 2017<br>
@@ -63,11 +63,8 @@ public:<br>
bool classifyReturnType(<wbr>CGFunctionInfo &FI) const override;<br>
<br>
RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const override {<br>
- // Structures with either a non-trivial destructor or a non-trivial<br>
- // copy constructor are always indirect.<br>
- // FIXME: Use canCopyArgument() when it is fixed to handle lazily declared<br>
- // special members.<br>
- if (RD->hasNonTrivialDestructor() || RD-><wbr>hasNonTrivialCopyConstructor()<wbr>)<br>
+ // If C++ prohibits us from making a copy, pass by address.<br>
+ if (!canCopyArgument(RD))<br>
return RAA_Indirect;<br>
return RAA_Default;<br>
}<br>
@@ -1014,10 +1011,8 @@ bool ItaniumCXXABI::<wbr>classifyReturnType(C<br>
if (!RD)<br>
return false;<br>
<br>
- // Return indirectly if we have a non-trivial copy ctor or non-trivial dtor.<br>
- // FIXME: Use canCopyArgument() when it is fixed to handle lazily declared<br>
- // special members.<br>
- if (RD->hasNonTrivialDestructor() || RD-><wbr>hasNonTrivialCopyConstructor()<wbr>) {<br>
+ // If C++ prohibits us from making a copy, return by address.<br>
+ if (!canCopyArgument(RD)) {<br>
auto Align = CGM.getContext().<wbr>getTypeAlignInChars(FI.<wbr>getReturnType());<br>
FI.getReturnInfo() = ABIArgInfo::getIndirect(Align, /*ByVal=*/false);<br>
return true;<br>
<br>
Modified: cfe/trunk/lib/CodeGen/<wbr>MicrosoftCXXABI.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp?rev=310401&r1=310400&r2=310401&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/CodeGen/<wbr>MicrosoftCXXABI.cpp?rev=<wbr>310401&r1=310400&r2=310401&<wbr>view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/CodeGen/<wbr>MicrosoftCXXABI.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/<wbr>MicrosoftCXXABI.cpp Tue Aug 8 12:12:28 2017<br>
@@ -819,46 +819,44 @@ MicrosoftCXXABI::<wbr>getRecordArgABI(const C<br>
return RAA_Default;<br>
<br>
case llvm::Triple::x86_64:<br>
- // Win64 passes objects with non-trivial copy ctors indirectly.<br>
- if (RD-><wbr>hasNonTrivialCopyConstructor()<wbr>)<br>
- return RAA_Indirect;<br>
-<br>
- // If an object has a destructor, we'd really like to pass it indirectly<br>
+ // If a class has a destructor, we'd really like to pass it indirectly<br>
// because it allows us to elide copies. Unfortunately, MSVC makes that<br>
// impossible for small types, which it will pass in a single register or<br>
// stack slot. Most objects with dtors are large-ish, so handle that early.<br>
// We can't call out all large objects as being indirect because there are<br>
// multiple x64 calling conventions and the C++ ABI code shouldn't dictate<br>
// how we pass large POD types.<br>
+ //<br>
+ // Note: This permits small classes with nontrivial destructors to be<br>
+ // passed in registers, which is non-conforming.<br>
if (RD->hasNonTrivialDestructor() &&<br>
getContext().getTypeSize(RD-><wbr>getTypeForDecl()) > 64)<br>
return RAA_Indirect;<br>
<br>
- // If this is true, the implicit copy constructor that Sema would have<br>
- // created would not be deleted. FIXME: We should provide a more direct way<br>
- // for CodeGen to ask whether the constructor was deleted.<br>
- if (!RD-><wbr>hasUserDeclaredCopyConstructor<wbr>() &&<br>
- !RD-><wbr>hasUserDeclaredMoveConstructor<wbr>() &&<br>
- !RD-><wbr>needsOverloadResolutionForMove<wbr>Constructor() &&<br>
- !RD-><wbr>hasUserDeclaredMoveAssignment(<wbr>) &&<br>
- !RD-><wbr>needsOverloadResolutionForMove<wbr>Assignment())<br>
- return RAA_Default;<br>
-<br>
- // Otherwise, Sema should have created an implicit copy constructor if<br>
- // needed.<br>
- assert(!RD-><wbr>needsImplicitCopyConstructor()<wbr>);<br>
-<br>
- // We have to make sure the trivial copy constructor isn't deleted.<br>
- for (const CXXConstructorDecl *CD : RD->ctors()) {<br>
- if (CD->isCopyConstructor()) {<br>
- assert(CD->isTrivial());<br>
- // We had at least one undeleted trivial copy ctor. Return directly.<br>
- if (!CD->isDeleted())<br>
- return RAA_Default;<br>
+ // If a class has at least one non-deleted, trivial copy constructor, it<br>
+ // is passed according to the C ABI. Otherwise, it is passed indirectly.<br>
+ //<br>
+ // Note: This permits classes with non-trivial copy or move ctors to be<br>
+ // passed in registers, so long as they *also* have a trivial copy ctor,<br>
+ // which is non-conforming.<br>
+ if (RD-><wbr>needsImplicitCopyConstructor()<wbr>) {<br>
+ // If the copy ctor has not yet been declared, we can read its triviality<br>
+ // off the AST.<br>
+ if (!RD-><wbr>defaultedCopyConstructorIsDele<wbr>ted() &&<br>
+ RD->hasTrivialCopyConstructor(<wbr>))<br>
+ return RAA_Default;<br>
+ } else {<br>
+ // Otherwise, we need to find the copy constructor(s) and ask.<br>
+ for (const CXXConstructorDecl *CD : RD->ctors()) {<br>
+ if (CD->isCopyConstructor()) {<br>
+ // We had at least one nondeleted trivial copy ctor. Return directly.<br>
+ if (!CD->isDeleted() && CD->isTrivial())<br>
+ return RAA_Default;<br>
+ }<br>
}<br>
}<br>
<br>
- // The trivial copy constructor was deleted. Return indirectly.<br>
+ // We have no trivial, non-deleted copy constructor.<br>
return RAA_Indirect;<br>
}<br>
<br>
<br>
Modified: cfe/trunk/lib/Sema/<wbr>SemaDeclCXX.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=310401&r1=310400&r2=310401&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/Sema/<wbr>SemaDeclCXX.cpp?rev=310401&r1=<wbr>310400&r2=310401&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/Sema/<wbr>SemaDeclCXX.cpp (original)<br>
+++ cfe/trunk/lib/Sema/<wbr>SemaDeclCXX.cpp Tue Aug 8 12:12:28 2017<br>
@@ -5726,6 +5726,53 @@ static void DefineImplicitSpecialMember(<br>
}<br>
}<br>
<br>
+/// Determine whether a type is permitted to be passed or returned in<br>
+/// registers, per C++ [class.temporary]p3.<br>
+static bool computeCanPassInRegisters(Sema &S, CXXRecordDecl *D) {<br>
+ if (D->isDependentType() || D->isInvalidDecl())<br>
+ return false;<br>
+<br>
+ // Per C++ [class.temporary]p3, the relevant condition is:<br>
+ // each copy constructor, move constructor, and destructor of X is<br>
+ // either trivial or deleted, and X has at least one non-deleted copy<br>
+ // or move constructor<br>
+ bool HasNonDeletedCopyOrMove = false;<br>
+<br>
+ if (D-><wbr>needsImplicitCopyConstructor() &&<br>
+ !D-><wbr>defaultedCopyConstructorIsDele<wbr>ted()) {<br>
+ if (!D-><wbr>hasTrivialCopyConstructor())<br>
+ return false;<br>
+ HasNonDeletedCopyOrMove = true;<br>
+ }<br>
+<br>
+ if (S.getLangOpts().CPlusPlus11 && D-><wbr>needsImplicitMoveConstructor() &&<br>
+ !D-><wbr>defaultedMoveConstructorIsDele<wbr>ted()) {<br>
+ if (!D-><wbr>hasTrivialMoveConstructor())<br>
+ return false;<br>
+ HasNonDeletedCopyOrMove = true;<br>
+ }<br>
+<br>
+ if (D->needsImplicitDestructor() && !D-><wbr>defaultedDestructorIsDeleted() &&<br>
+ !D->hasTrivialDestructor())<br>
+ return false;<br>
+<br>
+ for (const CXXMethodDecl *MD : D->methods()) {<br>
+ if (MD->isDeleted())<br>
+ continue;<br>
+<br>
+ auto *CD = dyn_cast<CXXConstructorDecl>(<wbr>MD);<br>
+ if (CD && CD->isCopyOrMoveConstructor())<br>
+ HasNonDeletedCopyOrMove = true;<br>
+ else if (!isa<CXXDestructorDecl>(MD))<br>
+ continue;<br>
+<br>
+ if (!MD->isTrivial())<br>
+ return false;<br>
+ }<br>
+<br>
+ return HasNonDeletedCopyOrMove;<br>
+}<br>
+<br>
/// \brief Perform semantic checks on a class definition that has been<br>
/// completing, introducing implicitly-declared members, checking for<br>
/// abstract types, etc.<br>
@@ -5870,6 +5917,8 @@ void Sema::CheckCompletedCXXClass(<wbr>CXXRec<br>
}<br>
<br>
checkClassLevelDLLAttribute(<wbr>Record);<br>
+<br>
+ Record->setCanPassInRegisters(<wbr>computeCanPassInRegisters(*<wbr>this, Record));<br>
}<br>
<br>
/// Look up the special member function that would be called by a special<br>
@@ -7496,8 +7545,7 @@ void Sema::<wbr>ActOnFinishCXXMemberSpecifica<br>
reinterpret_cast<Decl**>(<wbr>FieldCollector->getCurFields()<wbr>),<br>
FieldCollector-><wbr>getCurNumFields()), LBrac, RBrac, AttrList);<br>
<br>
- CheckCompletedCXXClass(<br>
- dyn_cast_or_null<<wbr>CXXRecordDecl>(TagDecl));<br>
+ CheckCompletedCXXClass(dyn_<wbr>cast_or_null<CXXRecordDecl>(<wbr>TagDecl));<br>
}<br>
<br>
/// AddImplicitlyDeclaredMembersTo<wbr>Class - Adds any implicitly-declared<br>
@@ -11929,8 +11977,10 @@ CXXConstructorDecl *Sema::DeclareImplici<br>
Scope *S = getScopeForContext(ClassDecl);<br>
CheckImplicitSpecialMemberDecl<wbr>aration(S, CopyConstructor);<br>
<br>
- if (ShouldDeleteSpecialMember(<wbr>CopyConstructor, CXXCopyConstructor))<br>
+ if (ShouldDeleteSpecialMember(<wbr>CopyConstructor, CXXCopyConstructor)) {<br>
+ ClassDecl-><wbr>setImplicitCopyConstructorIsDe<wbr>leted();<br>
SetDeclDeleted(<wbr>CopyConstructor, ClassLoc);<br>
+ }<br>
<br>
if (S)<br>
PushOnScopeChains(<wbr>CopyConstructor, S, false);<br>
<br>
Modified: cfe/trunk/lib/Serialization/<wbr>ASTReaderDecl.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=310401&r1=310400&r2=310401&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/<wbr>Serialization/ASTReaderDecl.<wbr>cpp?rev=310401&r1=310400&r2=<wbr>310401&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/Serialization/<wbr>ASTReaderDecl.cpp (original)<br>
+++ cfe/trunk/lib/Serialization/<wbr>ASTReaderDecl.cpp Tue Aug 8 12:12:28 2017<br>
@@ -1559,9 +1559,11 @@ void ASTDeclReader::<wbr>ReadCXXDefinitionDat<br>
Data.HasUninitializedFields = Record.readInt();<br>
Data.HasInheritedConstructor = Record.readInt();<br>
Data.HasInheritedAssignment = Record.readInt();<br>
+ Data.<wbr>NeedOverloadResolutionForCopyC<wbr>onstructor = Record.readInt();<br>
Data.<wbr>NeedOverloadResolutionForMoveC<wbr>onstructor = Record.readInt();<br>
Data.<wbr>NeedOverloadResolutionForMoveA<wbr>ssignment = Record.readInt();<br>
Data.<wbr>NeedOverloadResolutionForDestr<wbr>uctor = Record.readInt();<br>
+ Data.<wbr>DefaultedCopyConstructorIsDele<wbr>ted = Record.readInt();<br>
Data.<wbr>DefaultedMoveConstructorIsDele<wbr>ted = Record.readInt();<br>
Data.<wbr>DefaultedMoveAssignmentIsDelet<wbr>ed = Record.readInt();<br>
Data.<wbr>DefaultedDestructorIsDeleted = Record.readInt();<br>
@@ -1570,6 +1572,7 @@ void ASTDeclReader::<wbr>ReadCXXDefinitionDat<br>
Data.HasIrrelevantDestructor = Record.readInt();<br>
Data.<wbr>HasConstexprNonCopyMoveConstru<wbr>ctor = Record.readInt();<br>
Data.<wbr>HasDefaultedDefaultConstructor = Record.readInt();<br>
+ Data.CanPassInRegisters = Record.readInt();<br>
Data.<wbr>DefaultedDefaultConstructorIsC<wbr>onstexpr = Record.readInt();<br>
Data.<wbr>HasConstexprDefaultConstructor = Record.readInt();<br>
Data.<wbr>HasNonLiteralTypeFieldsOrBases = Record.readInt();<br>
@@ -1697,9 +1700,11 @@ void ASTDeclReader::<wbr>MergeDefinitionData(<br>
MATCH_FIELD(<wbr>HasUninitializedFields)<br>
MATCH_FIELD(<wbr>HasInheritedConstructor)<br>
MATCH_FIELD(<wbr>HasInheritedAssignment)<br>
+ MATCH_FIELD(<wbr>NeedOverloadResolutionForCopyC<wbr>onstructor)<br>
MATCH_FIELD(<wbr>NeedOverloadResolutionForMoveC<wbr>onstructor)<br>
MATCH_FIELD(<wbr>NeedOverloadResolutionForMoveA<wbr>ssignment)<br>
MATCH_FIELD(<wbr>NeedOverloadResolutionForDestr<wbr>uctor)<br>
+ MATCH_FIELD(<wbr>DefaultedCopyConstructorIsDele<wbr>ted)<br>
MATCH_FIELD(<wbr>DefaultedMoveConstructorIsDele<wbr>ted)<br>
MATCH_FIELD(<wbr>DefaultedMoveAssignmentIsDelet<wbr>ed)<br>
MATCH_FIELD(<wbr>DefaultedDestructorIsDeleted)<br>
@@ -1708,6 +1713,7 @@ void ASTDeclReader::<wbr>MergeDefinitionData(<br>
MATCH_FIELD(<wbr>HasIrrelevantDestructor)<br>
OR_FIELD(<wbr>HasConstexprNonCopyMoveConstru<wbr>ctor)<br>
OR_FIELD(<wbr>HasDefaultedDefaultConstructor<wbr>)<br>
+ MATCH_FIELD(<wbr>CanPassInRegisters)<br>
MATCH_FIELD(<wbr>DefaultedDefaultConstructorIsC<wbr>onstexpr)<br>
OR_FIELD(<wbr>HasConstexprDefaultConstructor<wbr>)<br>
MATCH_FIELD(<wbr>HasNonLiteralTypeFieldsOrBases<wbr>)<br>
<br>
Modified: cfe/trunk/lib/Serialization/<wbr>ASTWriter.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriter.cpp?rev=310401&r1=310400&r2=310401&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/<wbr>Serialization/ASTWriter.cpp?<wbr>rev=310401&r1=310400&r2=<wbr>310401&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/Serialization/<wbr>ASTWriter.cpp (original)<br>
+++ cfe/trunk/lib/Serialization/<wbr>ASTWriter.cpp Tue Aug 8 12:12:28 2017<br>
@@ -5875,9 +5875,11 @@ void ASTRecordWriter::<wbr>AddCXXDefinitionDa<br>
Record->push_back(Data.<wbr>HasUninitializedFields);<br>
Record->push_back(Data.<wbr>HasInheritedConstructor);<br>
Record->push_back(Data.<wbr>HasInheritedAssignment);<br>
+ Record->push_back(Data.<wbr>NeedOverloadResolutionForCopyC<wbr>onstructor);<br>
Record->push_back(Data.<wbr>NeedOverloadResolutionForMoveC<wbr>onstructor);<br>
Record->push_back(Data.<wbr>NeedOverloadResolutionForMoveA<wbr>ssignment);<br>
Record->push_back(Data.<wbr>NeedOverloadResolutionForDestr<wbr>uctor);<br>
+ Record->push_back(Data.<wbr>DefaultedCopyConstructorIsDele<wbr>ted);<br>
Record->push_back(Data.<wbr>DefaultedMoveConstructorIsDele<wbr>ted);<br>
Record->push_back(Data.<wbr>DefaultedMoveAssignmentIsDelet<wbr>ed);<br>
Record->push_back(Data.<wbr>DefaultedDestructorIsDeleted);<br>
@@ -5886,6 +5888,7 @@ void ASTRecordWriter::<wbr>AddCXXDefinitionDa<br>
Record->push_back(Data.<wbr>HasIrrelevantDestructor);<br>
Record->push_back(Data.<wbr>HasConstexprNonCopyMoveConstru<wbr>ctor);<br>
Record->push_back(Data.<wbr>HasDefaultedDefaultConstructor<wbr>);<br>
+ Record->push_back(Data.<wbr>CanPassInRegisters);<br>
Record->push_back(Data.<wbr>DefaultedDefaultConstructorIsC<wbr>onstexpr);<br>
Record->push_back(Data.<wbr>HasConstexprDefaultConstructor<wbr>);<br>
Record->push_back(Data.<wbr>HasNonLiteralTypeFieldsOrBases<wbr>);<br>
<br>
Modified: cfe/trunk/test/CodeGenCXX/<wbr>uncopyable-args.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/uncopyable-args.cpp?rev=310401&r1=310400&r2=310401&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/test/<wbr>CodeGenCXX/uncopyable-args.<wbr>cpp?rev=310401&r1=310400&r2=<wbr>310401&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/test/CodeGenCXX/<wbr>uncopyable-args.cpp (original)<br>
+++ cfe/trunk/test/CodeGenCXX/<wbr>uncopyable-args.cpp Tue Aug 8 12:12:28 2017<br>
@@ -1,5 +1,6 @@<br>
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck %s<br>
-// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows-msvc -emit-llvm -o - %s | FileCheck %s -check-prefix=WIN64<br>
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows-msvc -emit-llvm -o - %s -fms-compatibility -fms-compatibility-version=18 | FileCheck %s -check-prefix=WIN64 -check-prefix=WIN64-18<br>
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows-msvc -emit-llvm -o - %s -fms-compatibility -fms-compatibility-version=19 | FileCheck %s -check-prefix=WIN64 -check-prefix=WIN64-19<br>
<br>
namespace trivial {<br>
// Trivial structs should be passed directly.<br>
@@ -52,12 +53,11 @@ void foo(A);<br>
void bar() {<br>
foo({});<br>
}<br>
-// FIXME: The copy ctor is implicitly deleted.<br>
-// CHECK-DISABLED-LABEL: define void @_ZN9move_ctor3barEv()<br>
-// CHECK-DISABLED: call void @_Z{{.*}}C1Ev(<br>
-// CHECK-DISABLED-NOT: call<br>
-// CHECK-DISABLED: call void @_ZN9move_ctor3fooENS_1AE(%"<wbr>struct.move_ctor::A"* %{{.*}})<br>
-// CHECK-DISABLED-LABEL: declare void @_ZN9move_ctor3fooENS_1AE(%"<wbr>struct.move_ctor::A"*)<br>
+// CHECK-LABEL: define void @_ZN9move_ctor3barEv()<br>
+// CHECK: call void @_Z{{.*}}C1Ev(<br>
+// CHECK-NOT: call<br>
+// CHECK: call void @_ZN9move_ctor3fooENS_1AE(%"<wbr>struct.move_ctor::A"* %{{.*}})<br>
+// CHECK-LABEL: declare void @_ZN9move_ctor3fooENS_1AE(%"<wbr>struct.move_ctor::A"*)<br>
<br>
// WIN64-LABEL: declare void @"\01?foo@move_ctor@@YAXUA@1@@<wbr>Z"(%"struct.move_ctor::A"*)<br>
}<br>
@@ -73,12 +73,11 @@ void foo(A);<br>
void bar() {<br>
foo({});<br>
}<br>
-// FIXME: The copy ctor is deleted.<br>
-// CHECK-DISABLED-LABEL: define void @_ZN11all_deleted3barEv()<br>
-// CHECK-DISABLED: call void @_Z{{.*}}C1Ev(<br>
-// CHECK-DISABLED-NOT: call<br>
-// CHECK-DISABLED: call void @_ZN11all_deleted3fooENS_1AE(%<wbr>"struct.all_deleted::A"* %{{.*}})<br>
-// CHECK-DISABLED-LABEL: declare void @_ZN11all_deleted3fooENS_1AE(%<wbr>"struct.all_deleted::A"*)<br>
+// CHECK-LABEL: define void @_ZN11all_deleted3barEv()<br>
+// CHECK: call void @_Z{{.*}}C1Ev(<br>
+// CHECK-NOT: call<br>
+// CHECK: call void @_ZN11all_deleted3fooENS_1AE(%<wbr>"struct.all_deleted::A"* %{{.*}})<br>
+// CHECK-LABEL: declare void @_ZN11all_deleted3fooENS_1AE(%<wbr>"struct.all_deleted::A"*)<br>
<br>
// WIN64-LABEL: declare void @"\01?foo@all_deleted@@YAXUA@1<wbr>@@Z"(%"struct.all_deleted::A"*<wbr>)<br>
}<br>
@@ -93,14 +92,15 @@ void foo(A);<br>
void bar() {<br>
foo({});<br>
}<br>
-// FIXME: The copy and move ctors are implicitly deleted.<br>
-// CHECK-DISABLED-LABEL: define void @_ZN18implicitly_<wbr>deleted3barEv()<br>
-// CHECK-DISABLED: call void @_Z{{.*}}C1Ev(<br>
-// CHECK-DISABLED-NOT: call<br>
-// CHECK-DISABLED: call void @_ZN18implicitly_<wbr>deleted3fooENS_1AE(%"struct.<wbr>implicitly_deleted::A"* %{{.*}})<br>
-// CHECK-DISABLED-LABEL: declare void @_ZN18implicitly_<wbr>deleted3fooENS_1AE(%"struct.<wbr>implicitly_deleted::A"*)<br>
-<br>
-// WIN64-LABEL: declare void @"\01?foo@implicitly_deleted@@<wbr>YAXUA@1@@Z"(%"struct.<wbr>implicitly_deleted::A"*)<br>
+// CHECK-LABEL: define void @_ZN18implicitly_<wbr>deleted3barEv()<br>
+// CHECK: call void @_Z{{.*}}C1Ev(<br>
+// CHECK-NOT: call<br>
+// CHECK: call void @_ZN18implicitly_<wbr>deleted3fooENS_1AE(%"struct.<wbr>implicitly_deleted::A"* %{{.*}})<br>
+// CHECK-LABEL: declare void @_ZN18implicitly_<wbr>deleted3fooENS_1AE(%"struct.<wbr>implicitly_deleted::A"*)<br>
+<br>
+// In MSVC 2013, the copy ctor is not deleted by a move assignment. In MSVC 2015, it is.<br>
+// WIN64-18-LABEL: declare void @"\01?foo@implicitly_deleted@@<wbr>YAXUA@1@@Z"(i64<br>
+// WIN64-19-LABEL: declare void @"\01?foo@implicitly_deleted@@<wbr>YAXUA@1@@Z"(%"struct.<wbr>implicitly_deleted::A"*)<br>
}<br>
<br>
namespace one_deleted {<br>
@@ -113,12 +113,11 @@ void foo(A);<br>
void bar() {<br>
foo({});<br>
}<br>
-// FIXME: The copy constructor is implicitly deleted.<br>
-// CHECK-DISABLED-LABEL: define void @_ZN11one_deleted3barEv()<br>
-// CHECK-DISABLED: call void @_Z{{.*}}C1Ev(<br>
-// CHECK-DISABLED-NOT: call<br>
-// CHECK-DISABLED: call void @_ZN11one_deleted3fooENS_1AE(%<wbr>"struct.one_deleted::A"* %{{.*}})<br>
-// CHECK-DISABLED-LABEL: declare void @_ZN11one_deleted3fooENS_1AE(%<wbr>"struct.one_deleted::A"*)<br>
+// CHECK-LABEL: define void @_ZN11one_deleted3barEv()<br>
+// CHECK: call void @_Z{{.*}}C1Ev(<br>
+// CHECK-NOT: call<br>
+// CHECK: call void @_ZN11one_deleted3fooENS_1AE(%<wbr>"struct.one_deleted::A"* %{{.*}})<br>
+// CHECK-LABEL: declare void @_ZN11one_deleted3fooENS_1AE(%<wbr>"struct.one_deleted::A"*)<br>
<br>
// WIN64-LABEL: declare void @"\01?foo@one_deleted@@YAXUA@1<wbr>@@Z"(%"struct.one_deleted::A"*<wbr>)<br>
}<br>
@@ -195,12 +194,10 @@ void foo(B);<br>
void bar() {<br>
foo({});<br>
}<br>
-// FIXME: This class has a non-trivial copy ctor and a trivial copy ctor. It's<br>
-// not clear whether we should pass by address or in registers.<br>
-// CHECK-DISABLED-LABEL: define void @_ZN14two_copy_ctors3barEv()<br>
-// CHECK-DISABLED: call void @_Z{{.*}}C1Ev(<br>
-// CHECK-DISABLED: call void @_ZN14two_copy_ctors3fooENS_<wbr>1BE(%"struct.two_copy_ctors::<wbr>B"* %{{.*}})<br>
-// CHECK-DISABLED-LABEL: declare void @_ZN14two_copy_ctors3fooENS_<wbr>1BE(%"struct.two_copy_ctors::<wbr>B"*)<br>
+// CHECK-LABEL: define void @_ZN14two_copy_ctors3barEv()<br>
+// CHECK: call void @_Z{{.*}}C1Ev(<br>
+// CHECK: call void @_ZN14two_copy_ctors3fooENS_<wbr>1BE(%"struct.two_copy_ctors::<wbr>B"* %{{.*}})<br>
+// CHECK-LABEL: declare void @_ZN14two_copy_ctors3fooENS_<wbr>1BE(%"struct.two_copy_ctors::<wbr>B"*)<br>
<br>
// WIN64-LABEL: declare void @"\01?foo@two_copy_ctors@@<wbr>YAXUB@1@@Z"(%"struct.two_copy_<wbr>ctors::B"*)<br>
}<br>
@@ -212,6 +209,7 @@ struct A {<br>
void *p;<br>
};<br>
void *foo(A a) { return a.p; }<br>
+// CHECK-LABEL: define i8* @_ZN15definition_only3fooENS_<wbr>1AE(%"struct.definition_only::<wbr>A"*<br>
// WIN64-LABEL: define i8* @"\01?foo@definition_only@@<wbr>YAPEAXUA@1@@Z"(%"struct.<wbr>definition_only::A"*<br>
}<br>
<br>
@@ -226,6 +224,7 @@ struct A {<br>
B b;<br>
};<br>
void *foo(A a) { return a.b.p; }<br>
+// CHECK-LABEL: define i8* @_ZN17deleted_by_<wbr>member3fooENS_1AE(%"struct.<wbr>deleted_by_member::A"*<br>
// WIN64-LABEL: define i8* @"\01?foo@deleted_by_member@@<wbr>YAPEAXUA@1@@Z"(%"struct.<wbr>deleted_by_member::A"*<br>
}<br>
<br>
@@ -239,6 +238,7 @@ struct A : B {<br>
A();<br>
};<br>
void *foo(A a) { return a.p; }<br>
+// CHECK-LABEL: define i8* @_ZN15deleted_by_base3fooENS_<wbr>1AE(%"struct.deleted_by_base::<wbr>A"*<br>
// WIN64-LABEL: define i8* @"\01?foo@deleted_by_base@@<wbr>YAPEAXUA@1@@Z"(%"struct.<wbr>deleted_by_base::A"*<br>
}<br>
<br>
@@ -253,6 +253,7 @@ struct A {<br>
B b;<br>
};<br>
void *foo(A a) { return a.b.p; }<br>
+// CHECK-LABEL: define i8* @_ZN22deleted_by_member_<wbr>copy3fooENS_1AE(%"struct.<wbr>deleted_by_member_copy::A"*<br>
// WIN64-LABEL: define i8* @"\01?foo@deleted_by_member_<wbr>copy@@YAPEAXUA@1@@Z"(%"struct.<wbr>deleted_by_member_copy::A"*<br>
}<br>
<br>
@@ -266,6 +267,7 @@ struct A : B {<br>
A();<br>
};<br>
void *foo(A a) { return a.p; }<br>
+// CHECK-LABEL: define i8* @_ZN20deleted_by_base_<wbr>copy3fooENS_1AE(%"struct.<wbr>deleted_by_base_copy::A"*<br>
// WIN64-LABEL: define i8* @"\01?foo@deleted_by_base_copy<wbr>@@YAPEAXUA@1@@Z"(%"struct.<wbr>deleted_by_base_copy::A"*<br>
}<br>
<br>
@@ -275,6 +277,75 @@ struct A {<br>
A(const A &o) = delete;<br>
void *p;<br>
};<br>
+// CHECK-LABEL: define i8* @_ZN15explicit_delete3fooENS_<wbr>1AE(%"struct.explicit_delete::<wbr>A"*<br>
// WIN64-LABEL: define i8* @"\01?foo@explicit_delete@@<wbr>YAPEAXUA@1@@Z"(%"struct.<wbr>explicit_delete::A"*<br>
void *foo(A a) { return a.p; }<br>
}<br>
+<br>
+namespace implicitly_deleted_copy_ctor {<br>
+struct A {<br>
+ // No move ctor due to copy assignment.<br>
+ A &operator=(const A&);<br>
+ // Deleted copy ctor due to rvalue ref member.<br>
+ int &&ref;<br>
+};<br>
+// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_<wbr>ctor3fooENS_1AE(%"struct.<wbr>implicitly_deleted_copy_ctor::<wbr>A"*<br>
+// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_<wbr>copy_ctor@@YAAEAHUA@1@@Z"(%"<wbr>struct.implicitly_deleted_<wbr>copy_ctor::A"*<br>
+int &foo(A a) { return a.ref; }<br>
+<br>
+struct B {<br>
+ // Passed direct: has non-deleted trivial copy ctor.<br>
+ B &operator=(const B&);<br>
+ int &ref;<br>
+};<br>
+int &foo(B b) { return b.ref; }<br>
+// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_<wbr>ctor3fooENS_1BE(i32*<br>
+// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_<wbr>copy_ctor@@YAAEAHUB@1@@Z"(i64<br>
+<br>
+struct X { X(const X&); };<br>
+struct Y { Y(const Y&) = default; };<br>
+<br>
+union C {<br>
+ C &operator=(const C&);<br>
+ // Passed indirect: copy ctor deleted due to variant member with nontrivial copy ctor.<br>
+ X x;<br>
+ int n;<br>
+};<br>
+int foo(C c) { return c.n; }<br>
+// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_<wbr>ctor3fooENS_1CE(%"union.<wbr>implicitly_deleted_copy_ctor::<wbr>C"*<br>
+// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_<wbr>copy_ctor@@YAHTC@1@@Z"(%"<wbr>union.implicitly_deleted_copy_<wbr>ctor::C"*<br>
+<br>
+struct D {<br>
+ D &operator=(const D&);<br>
+ // Passed indirect: copy ctor deleted due to variant member with nontrivial copy ctor.<br>
+ union {<br>
+ X x;<br>
+ int n;<br>
+ };<br>
+};<br>
+int foo(D d) { return d.n; }<br>
+// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_<wbr>ctor3fooENS_1DE(%"struct.<wbr>implicitly_deleted_copy_ctor::<wbr>D"*<br>
+// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_<wbr>copy_ctor@@YAHUD@1@@Z"(%"<wbr>struct.implicitly_deleted_<wbr>copy_ctor::D"*<br>
+<br>
+union E {<br>
+ // Passed direct: has non-deleted trivial copy ctor.<br>
+ E &operator=(const E&);<br>
+ Y y;<br>
+ int n;<br>
+};<br>
+int foo(E e) { return e.n; }<br>
+// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_<wbr>ctor3fooENS_1EE(i32<br>
+// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_<wbr>copy_ctor@@YAHTE@1@@Z"(i32<br>
+<br>
+struct F {<br>
+ // Passed direct: has non-deleted trivial copy ctor.<br>
+ F &operator=(const F&);<br>
+ union {<br>
+ Y y;<br>
+ int n;<br>
+ };<br>
+};<br>
+int foo(F f) { return f.n; }<br>
+// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_<wbr>ctor3fooENS_1FE(i32<br>
+// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_<wbr>copy_ctor@@YAHUF@1@@Z"(i32<br>
+}<br>
<br>
Modified: cfe/trunk/unittests/<wbr>ASTMatchers/<wbr>ASTMatchersNarrowingTest.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp?rev=310401&r1=310400&r2=310401&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/unittests/<wbr>ASTMatchers/<wbr>ASTMatchersNarrowingTest.cpp?<wbr>rev=310401&r1=310400&r2=<wbr>310401&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/unittests/<wbr>ASTMatchers/<wbr>ASTMatchersNarrowingTest.cpp (original)<br>
+++ cfe/trunk/unittests/<wbr>ASTMatchers/<wbr>ASTMatchersNarrowingTest.cpp Tue Aug 8 12:12:28 2017<br>
@@ -1108,26 +1108,35 @@ TEST(ConstructorDeclaration, IsExplicit)<br>
}<br>
<br>
TEST(ConstructorDeclaration, Kinds) {<br>
- EXPECT_TRUE(matches("struct S { S(); };",<br>
- cxxConstructorDecl(<wbr>isDefaultConstructor())));<br>
- EXPECT_TRUE(notMatches("struct S { S(); };",<br>
- cxxConstructorDecl(<wbr>isCopyConstructor())));<br>
- EXPECT_TRUE(notMatches("struct S { S(); };",<br>
- cxxConstructorDecl(<wbr>isMoveConstructor())));<br>
+ EXPECT_TRUE(matches(<br>
+ "struct S { S(); };",<br>
+ cxxConstructorDecl(<wbr>isDefaultConstructor(), unless(isImplicit()))));<br>
+ EXPECT_TRUE(notMatches(<br>
+ "struct S { S(); };",<br>
+ cxxConstructorDecl(<wbr>isCopyConstructor(), unless(isImplicit()))));<br>
+ EXPECT_TRUE(notMatches(<br>
+ "struct S { S(); };",<br>
+ cxxConstructorDecl(<wbr>isMoveConstructor(), unless(isImplicit()))));<br>
<br>
- EXPECT_TRUE(notMatches("struct S { S(const S&); };",<br>
- cxxConstructorDecl(<wbr>isDefaultConstructor())));<br>
- EXPECT_TRUE(matches("struct S { S(const S&); };",<br>
- cxxConstructorDecl(<wbr>isCopyConstructor())));<br>
- EXPECT_TRUE(notMatches("struct S { S(const S&); };",<br>
- cxxConstructorDecl(<wbr>isMoveConstructor())));<br>
+ EXPECT_TRUE(notMatches(<br>
+ "struct S { S(const S&); };",<br>
+ cxxConstructorDecl(<wbr>isDefaultConstructor(), unless(isImplicit()))));<br>
+ EXPECT_TRUE(matches(<br>
+ "struct S { S(const S&); };",<br>
+ cxxConstructorDecl(<wbr>isCopyConstructor(), unless(isImplicit()))));<br>
+ EXPECT_TRUE(notMatches(<br>
+ "struct S { S(const S&); };",<br>
+ cxxConstructorDecl(<wbr>isMoveConstructor(), unless(isImplicit()))));<br>
<br>
- EXPECT_TRUE(notMatches("struct S { S(S&&); };",<br>
- cxxConstructorDecl(<wbr>isDefaultConstructor())));<br>
- EXPECT_TRUE(notMatches("struct S { S(S&&); };",<br>
- cxxConstructorDecl(<wbr>isCopyConstructor())));<br>
- EXPECT_TRUE(matches("struct S { S(S&&); };",<br>
- cxxConstructorDecl(<wbr>isMoveConstructor())));<br>
+ EXPECT_TRUE(notMatches(<br>
+ "struct S { S(S&&); };",<br>
+ cxxConstructorDecl(<wbr>isDefaultConstructor(), unless(isImplicit()))));<br>
+ EXPECT_TRUE(notMatches(<br>
+ "struct S { S(S&&); };",<br>
+ cxxConstructorDecl(<wbr>isCopyConstructor(), unless(isImplicit()))));<br>
+ EXPECT_TRUE(matches(<br>
+ "struct S { S(S&&); };",<br>
+ cxxConstructorDecl(<wbr>isMoveConstructor(), unless(isImplicit()))));<br>
}<br>
<br>
TEST(ConstructorDeclaration, IsUserProvided) {<br>
<br>
<br>
______________________________<wbr>_________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/<wbr>mailman/listinfo/cfe-commits</a><br>
</blockquote></div><br></div>