r310983 - PR19668, PR23034: Fix handling of move constructors and deleted copy
Alex L via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 20 06:15:39 PDT 2017
On 16 August 2017 at 02:49, Richard Smith via cfe-commits <
cfe-commits at 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;
>
Hi Richard,
This commit (and this line in particular) broke unions with record members
that have volatile members in C++ 03 and below. They are now rejected by
Clang. I filed https://bugs.llvm.org/show_bug.cgi?id=34683. Could you
please take a look?
Thanks,
Alex
> 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 at move_ctor@@YAXUA at 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 at all_deleted@@YAXUA at 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 at implicitly_deleted@@YAXUA at 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 at implicitly_deleted@@YAXUA at 1
> @@Z"(i64
> +// WIN64-19-LABEL: declare void @"\01?foo at implicitly_deleted@@YAXUA at 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 at one_deleted@@YAXUA at 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 at two_copy_ctors@@YAXUB at 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 at definition_only@@YAPEAXUA at 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 at deleted_by_member@@YAPEAXUA at 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 at deleted_by_base@@YAPEAXUA at 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 at deleted_by_member_copy@@YAPEAXUA at 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 at deleted_by_base_copy@@YAPEAXUA at 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 at explicit_delete@@YAPEAXUA at 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 at implicitly_deleted_copy_ctor@
> @YAAEAHUA at 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 at implicitly_deleted_copy_ctor@
> @YAAEAHUB at 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 at implicitly_deleted_copy_ctor@
> @YAHTC at 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 at implicitly_deleted_copy_ctor@
> @YAHUD at 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 at implicitly_deleted_copy_ctor@
> @YAHTE at 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 at implicitly_deleted_copy_ctor@
> @YAHUF at 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 at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20170920/d75015a4/attachment-0001.html>
More information about the cfe-commits
mailing list