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