]> granicus.if.org Git - clang/commitdiff
Sema, CodeGen: Ensure that an implicit copy ctor is available more often under the...
authorPeter Collingbourne <peter@pcc.me.uk>
Tue, 22 Nov 2016 00:21:43 +0000 (00:21 +0000)
committerPeter Collingbourne <peter@pcc.me.uk>
Tue, 22 Nov 2016 00:21:43 +0000 (00:21 +0000)
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
lib/Sema/SemaDeclCXX.cpp
test/CodeGenCXX/uncopyable-args.cpp

index 2722985b2e5872d3b1202cc8a246632e40285746..60940fa8fdeb4271efa0106f4e2ce82981f8a425 100644 (file)
@@ -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");
index 82da95e9a67b8160d1950a61569260feb12d375d..7d208a43ada17325ebfdc26749937c8d96aee32e 100644 (file)
@@ -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()) {
index c1d284a74185b192bfbdf0ada79a915f8a5802ad..9cd57dd01c280700fd6465078e79589c41ac829c 100644 (file)
@@ -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; }
+}