From aea11161bc7db9f21babdf095ae757b78dafba11 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Tue, 22 Nov 2016 00:21:43 +0000 Subject: [PATCH] Sema, CodeGen: Ensure that an implicit copy ctor is available more often under the Microsoft C++ ABI. This is needed because whether the constructor is deleted can control whether we pass structs by value directly. To fix this properly we probably want a more direct way for CodeGen to ask whether the constructor was deleted. Fixes PR31049. Differential Revision: https://reviews.llvm.org/D26822 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@287600 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/MicrosoftCXXABI.cpp | 25 +++++++----- lib/Sema/SemaDeclCXX.cpp | 11 ++++++ test/CodeGenCXX/uncopyable-args.cpp | 61 +++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 9 deletions(-) diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 2722985b2e..60940fa8fd 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -830,25 +830,32 @@ MicrosoftCXXABI::getRecordArgABI(const CXXRecordDecl *RD) const { getContext().getTypeSize(RD->getTypeForDecl()) > 64) return RAA_Indirect; - // We have a trivial copy constructor or no copy constructors, but we have - // to make sure it isn't deleted. - bool CopyDeleted = false; + // 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; - CopyDeleted = true; } } // The trivial copy constructor was deleted. Return indirectly. - if (CopyDeleted) - return RAA_Indirect; - - // There were no copy ctors. Return in RAX. - return RAA_Default; + return RAA_Indirect; } llvm_unreachable("invalid enum"); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 82da95e9a6..7d208a43ad 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -7396,6 +7396,17 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { if (ClassDecl->needsOverloadResolutionForCopyConstructor() || ClassDecl->hasInheritedConstructor()) DeclareImplicitCopyConstructor(ClassDecl); + // For the MS ABI we need to know whether the copy ctor is deleted. A + // prerequisite for deleting the implicit copy ctor is that the class has a + // move ctor or move assignment that is either user-declared or whose + // semantics are inherited from a subobject. FIXME: We should provide a more + // direct way for CodeGen to ask whether the constructor was deleted. + else if (Context.getTargetInfo().getCXXABI().isMicrosoft() && + (ClassDecl->hasUserDeclaredMoveConstructor() || + ClassDecl->needsOverloadResolutionForMoveConstructor() || + ClassDecl->hasUserDeclaredMoveAssignment() || + ClassDecl->needsOverloadResolutionForMoveAssignment())) + DeclareImplicitCopyConstructor(ClassDecl); } if (getLangOpts().CPlusPlus11 && ClassDecl->needsImplicitMoveConstructor()) { diff --git a/test/CodeGenCXX/uncopyable-args.cpp b/test/CodeGenCXX/uncopyable-args.cpp index c1d284a741..9cd57dd01c 100644 --- a/test/CodeGenCXX/uncopyable-args.cpp +++ b/test/CodeGenCXX/uncopyable-args.cpp @@ -204,3 +204,64 @@ void bar() { // WIN64-LABEL: declare void @"\01?foo@two_copy_ctors@@YAXUB@1@@Z"(%"struct.two_copy_ctors::B"*) } + +namespace definition_only { +struct A { + A(); + A(A &&o); + void *p; +}; +void *foo(A a) { return a.p; } +// WIN64-LABEL: define i8* @"\01?foo@definition_only@@YAPEAXUA@1@@Z"{{.*}} +// WIN64-NEXT: : +// WIN64-NEXT: getelementptr +// WIN64-NEXT: load +} + +namespace deleted_by_member { +struct B { + B(); + B(B &&o); + void *p; +}; +struct A { + A(); + B b; +}; +void *foo(A a) { return a.b.p; } +// WIN64-LABEL: define i8* @"\01?foo@deleted_by_member@@YAPEAXUA@1@@Z"{{.*}} +// WIN64-NEXT: : +// WIN64-NEXT: getelementptr +// WIN64-NEXT: getelementptr +// WIN64-NEXT: load +} + +namespace deleted_by_base { +struct B { + B(); + B(B &&o); + void *p; +}; +struct A : B { + A(); +}; +void *foo(A a) { return a.p; } +// WIN64-LABEL: define i8* @"\01?foo@deleted_by_base@@YAPEAXUA@1@@Z"{{.*}} +// WIN64-NEXT: : +// WIN64-NEXT: bitcast +// WIN64-NEXT: getelementptr +// WIN64-NEXT: load +} + +namespace explicit_delete { +struct A { + A(); + A(const A &o) = delete; + void *p; +}; +// WIN64-LABEL: define i8* @"\01?foo@explicit_delete@@YAPEAXUA@1@@Z"{{.*}} +// WIN64-NEXT: : +// WIN64-NEXT: getelementptr +// WIN64-NEXT: load +void *foo(A a) { return a.p; } +} -- 2.40.0