Index: lib/CodeGen/CGCXXABI.cpp =================================================================== --- lib/CodeGen/CGCXXABI.cpp +++ lib/CodeGen/CGCXXABI.cpp @@ -30,6 +30,9 @@ } bool CGCXXABI::canCopyArgument(const CXXRecordDecl *RD) const { + // See also Sema::ShouldDeleteSpecialMember. These two functions + // should be kept consistent. + // If RD has a non-trivial move or copy constructor, we cannot copy the // argument. if (RD->hasNonTrivialCopyConstructor() || RD->hasNonTrivialMoveConstructor()) @@ -41,10 +44,7 @@ // 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; + bool CopyOrMoveDeleted = false; for (const CXXConstructorDecl *CD : RD->ctors()) { if (CD->isCopyConstructor() || CD->isMoveConstructor()) { assert(CD->isTrivial()); @@ -52,16 +52,25 @@ // directly. if (!CD->isDeleted()) return true; - if (CD->isCopyConstructor()) - CopyDeleted = true; - else - MoveDeleted = true; + CopyOrMoveDeleted = true; } } + // If a move constructor or move assignment operator was declared, the + // default copy constructors are implicitly deleted, except in one case + // related to compatibility with MSVC pre-2015. + if (RD->hasUserDeclaredMoveConstructor()) + return false; + if (RD->hasUserDeclaredMoveAssignment()) { + const LangOptions &opts = CGM.getLangOpts(); + bool DeletesOnlyMatchingCopy = + opts.MSVCCompat && !opts.isCompatibleWithMSVC(LangOptions::MSVC2015); + if (!DeletesOnlyMatchingCopy) + return false; + } // If all trivial copy and move constructors are deleted, we cannot copy the // argument. - return !(CopyDeleted && MoveDeleted); + return !CopyOrMoveDeleted; } llvm::Constant *CGCXXABI::GetBogusMemberPointer(QualType T) { Index: lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- lib/CodeGen/ItaniumCXXABI.cpp +++ lib/CodeGen/ItaniumCXXABI.cpp @@ -63,11 +63,8 @@ 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; } @@ -988,10 +985,8 @@ 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; Index: test/CodeGenCXX/uncopyable-args.cpp =================================================================== --- test/CodeGenCXX/uncopyable-args.cpp +++ test/CodeGenCXX/uncopyable-args.cpp @@ -52,12 +52,11 @@ 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 +72,11 @@ 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,12 +91,11 @@ 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"*) +// 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"*) // WIN64-LABEL: declare void @"\01?foo@implicitly_deleted@@YAXUA@1@@Z"(%"struct.implicitly_deleted::A"*) } @@ -113,12 +110,11 @@ 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 +191,10 @@ 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"*) }