Yes, I'd very much like to get this into the upcoming release. On 21 August 2017 at 16:16, Hans Wennborg <h...@chromium.org> wrote:
> 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().NeedOverloadResolutionForCopyC > onstructor; > > data().NeedOverloadResolutionForMoveConstructor |= > > FieldRec->data().NeedOverloadResolutionForMoveC > onstructor; > > 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