From 743cbb91499e138a63a398c6515667905f1b3be8 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 4 Nov 2013 01:48:18 +0000 Subject: [PATCH] Implement final resolution of DR1402: implicitly-declared move operators that would be deleted are still declared, but are ignored by overload resolution. Also, don't delete such members if a subobject has no corresponding move operation and a non-trivial copy. This causes us to implicitly declare move operations in more cases, but risks move-assigning virtual bases multiple times in some circumstances (a warning for that is to follow). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@193969 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclCXX.h | 60 +++---- lib/AST/ASTImporter.cpp | 3 - lib/AST/DeclCXX.cpp | 1 - lib/Sema/SemaDeclCXX.cpp | 169 ++---------------- lib/Sema/SemaExprCXX.cpp | 4 +- lib/Sema/SemaOverload.cpp | 17 +- lib/Serialization/ASTReaderDecl.cpp | 2 - lib/Serialization/ASTWriter.cpp | 2 - test/CXX/class.derived/class.abstract/p16.cpp | 4 +- test/CXX/drs/dr1xx.cpp | 2 +- test/CXX/special/class.copy/implicit-move.cpp | 92 +++++----- test/CXX/special/class.copy/p12-0x.cpp | 4 +- test/CXX/special/class.copy/p23-cxx11.cpp | 19 +- 13 files changed, 126 insertions(+), 253 deletions(-) diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 9a53b9e9fc..4e27dbd1ff 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -444,14 +444,6 @@ class CXXRecordDecl : public RecordDecl { /// const-qualified reference parameter or a non-reference parameter. bool HasDeclaredCopyAssignmentWithConstParam : 1; - /// \brief Whether an implicit move constructor was attempted to be declared - /// but would have been deleted. - bool FailedImplicitMoveConstructor : 1; - - /// \brief Whether an implicit move assignment operator was attempted to be - /// declared but would have been deleted. - bool FailedImplicitMoveAssignment : 1; - /// \brief Whether this class describes a C++ lambda. bool IsLambda : 1; @@ -773,12 +765,14 @@ public: /// \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(); + 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(); + return !hasUserDeclaredMoveAssignment() && hasMoveAssignment() && + !data().DefaultedMoveAssignmentIsDeleted; } /// \brief \c true if we know for sure that this class has an accessible /// destructor that is not deleted. @@ -870,28 +864,23 @@ public: needsImplicitMoveConstructor(); } - /// \brief Determine whether implicit move constructor generation for this - /// class has failed before. - bool hasFailedImplicitMoveConstructor() const { - return data().FailedImplicitMoveConstructor; - } - - /// \brief Set whether implicit move constructor generation for this class - /// has failed before. - void setFailedImplicitMoveConstructor(bool Failed = true) { - data().FailedImplicitMoveConstructor = Failed; + /// \brief Set that we attempted to declare an implicitly move + /// constructor, but overload resolution failed so we deleted it. + void setImplicitMoveConstructorIsDeleted() { + assert((data().DefaultedMoveConstructorIsDeleted || + needsOverloadResolutionForMoveConstructor()) && + "move constructor should not be deleted"); + data().DefaultedMoveConstructorIsDeleted = true; } /// \brief Determine whether this class should get an implicit move /// constructor or if any existing special member function inhibits this. bool needsImplicitMoveConstructor() const { - return !hasFailedImplicitMoveConstructor() && - !(data().DeclaredSpecialMembers & SMF_MoveConstructor) && + return !(data().DeclaredSpecialMembers & SMF_MoveConstructor) && !hasUserDeclaredCopyConstructor() && !hasUserDeclaredCopyAssignment() && !hasUserDeclaredMoveAssignment() && - !hasUserDeclaredDestructor() && - !data().DefaultedMoveConstructorIsDeleted; + !hasUserDeclaredDestructor(); } /// \brief Determine whether we need to eagerly declare a defaulted move @@ -947,29 +936,24 @@ public: needsImplicitMoveAssignment(); } - /// \brief Determine whether implicit move assignment generation for this - /// class has failed before. - bool hasFailedImplicitMoveAssignment() const { - return data().FailedImplicitMoveAssignment; - } - - /// \brief Set whether implicit move assignment generation for this class - /// has failed before. - void setFailedImplicitMoveAssignment(bool Failed = true) { - data().FailedImplicitMoveAssignment = Failed; + /// \brief Set that we attempted to declare an implicit move assignment + /// operator, but overload resolution failed so we deleted it. + void setImplicitMoveAssignmentIsDeleted() { + assert((data().DefaultedMoveAssignmentIsDeleted || + needsOverloadResolutionForMoveAssignment()) && + "move assignment should not be deleted"); + data().DefaultedMoveAssignmentIsDeleted = true; } /// \brief Determine whether this class should get an implicit move /// assignment operator or if any existing special member function inhibits /// this. bool needsImplicitMoveAssignment() const { - return !hasFailedImplicitMoveAssignment() && - !(data().DeclaredSpecialMembers & SMF_MoveAssignment) && + return !(data().DeclaredSpecialMembers & SMF_MoveAssignment) && !hasUserDeclaredCopyConstructor() && !hasUserDeclaredCopyAssignment() && !hasUserDeclaredMoveConstructor() && - !hasUserDeclaredDestructor() && - !data().DefaultedMoveAssignmentIsDeleted; + !hasUserDeclaredDestructor(); } /// \brief Determine whether we need to eagerly declare a move assignment diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 876af1e9e4..e16015b7c4 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -1983,9 +1983,6 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To, = FromData.HasDeclaredCopyConstructorWithConstParam; ToData.HasDeclaredCopyAssignmentWithConstParam = FromData.HasDeclaredCopyAssignmentWithConstParam; - ToData.FailedImplicitMoveConstructor - = FromData.FailedImplicitMoveConstructor; - ToData.FailedImplicitMoveAssignment = FromData.FailedImplicitMoveAssignment; ToData.IsLambda = FromData.IsLambda; SmallVector Bases; diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index ee25f65d49..b7f0fab397 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -71,7 +71,6 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) ImplicitCopyAssignmentHasConstParam(true), HasDeclaredCopyConstructorWithConstParam(false), HasDeclaredCopyAssignmentWithConstParam(false), - FailedImplicitMoveConstructor(false), FailedImplicitMoveAssignment(false), IsLambda(false), NumBases(0), NumVBases(0), Bases(), VBases(), Definition(D), FirstFriend() { } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index c8338353ea..006f6aa67b 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -9231,11 +9231,6 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) { ? SpecialMemberIsTrivial(CopyAssignment, CXXCopyAssignment) : ClassDecl->hasTrivialCopyAssignment()); - // C++11 [class.copy]p19: - // .... If the class definition does not explicitly declare a copy - // assignment operator, there is no user-declared move constructor, and - // there is no user-declared move assignment operator, a copy assignment - // operator is implicitly declared as defaulted. if (ShouldDeleteSpecialMember(CopyAssignment, CXXCopyAssignment)) SetDeclDeleted(CopyAssignment, ClassLoc); @@ -9572,120 +9567,13 @@ Sema::ComputeDefaultedMoveAssignmentExceptionSpec(CXXMethodDecl *MD) { return ExceptSpec; } -/// Determine whether the class type has any direct or indirect virtual base -/// classes which have a non-trivial move assignment operator. -static bool -hasVirtualBaseWithNonTrivialMoveAssignment(Sema &S, CXXRecordDecl *ClassDecl) { - for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(), - BaseEnd = ClassDecl->vbases_end(); - Base != BaseEnd; ++Base) { - CXXRecordDecl *BaseClass = - cast(Base->getType()->getAs()->getDecl()); - - // Try to declare the move assignment. If it would be deleted, then the - // class does not have a non-trivial move assignment. - if (BaseClass->needsImplicitMoveAssignment()) - S.DeclareImplicitMoveAssignment(BaseClass); - - if (BaseClass->hasNonTrivialMoveAssignment()) - return true; - } - - return false; -} - -/// Determine whether the given type either has a move constructor or is -/// trivially copyable. -static bool -hasMoveOrIsTriviallyCopyable(Sema &S, QualType Type, bool IsConstructor) { - Type = S.Context.getBaseElementType(Type); - - // FIXME: Technically, non-trivially-copyable non-class types, such as - // reference types, are supposed to return false here, but that appears - // to be a standard defect. - CXXRecordDecl *ClassDecl = Type->getAsCXXRecordDecl(); - if (!ClassDecl || !ClassDecl->getDefinition() || ClassDecl->isInvalidDecl()) - return true; - - if (Type.isTriviallyCopyableType(S.Context)) - return true; - - if (IsConstructor) { - // FIXME: Need this because otherwise hasMoveConstructor isn't guaranteed to - // give the right answer. - if (ClassDecl->needsImplicitMoveConstructor()) - S.DeclareImplicitMoveConstructor(ClassDecl); - return ClassDecl->hasMoveConstructor(); - } - - // FIXME: Need this because otherwise hasMoveAssignment isn't guaranteed to - // give the right answer. - if (ClassDecl->needsImplicitMoveAssignment()) - S.DeclareImplicitMoveAssignment(ClassDecl); - return ClassDecl->hasMoveAssignment(); -} - -/// Determine whether all non-static data members and direct or virtual bases -/// of class \p ClassDecl have either a move operation, or are trivially -/// copyable. -static bool subobjectsHaveMoveOrTrivialCopy(Sema &S, CXXRecordDecl *ClassDecl, - bool IsConstructor) { - for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), - BaseEnd = ClassDecl->bases_end(); - Base != BaseEnd; ++Base) { - if (Base->isVirtual()) - continue; - - if (!hasMoveOrIsTriviallyCopyable(S, Base->getType(), IsConstructor)) - return false; - } - - for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(), - BaseEnd = ClassDecl->vbases_end(); - Base != BaseEnd; ++Base) { - if (!hasMoveOrIsTriviallyCopyable(S, Base->getType(), IsConstructor)) - return false; - } - - for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(), - FieldEnd = ClassDecl->field_end(); - Field != FieldEnd; ++Field) { - if (!hasMoveOrIsTriviallyCopyable(S, Field->getType(), IsConstructor)) - return false; - } - - return true; -} - CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) { - // C++11 [class.copy]p20: - // If the definition of a class X does not explicitly declare a move - // assignment operator, one will be implicitly declared as defaulted - // if and only if: - // - // - [first 4 bullets] assert(ClassDecl->needsImplicitMoveAssignment()); DeclaringSpecialMember DSM(*this, ClassDecl, CXXMoveAssignment); if (DSM.isAlreadyBeingDeclared()) return 0; - // [Checked after we build the declaration] - // - the move assignment operator would not be implicitly defined as - // deleted, - - // [DR1402]: - // - X has no direct or indirect virtual base class with a non-trivial - // move assignment operator, and - // - each of X's non-static data members and direct or virtual base classes - // has a type that either has a move assignment operator or is trivially - // copyable. - if (hasVirtualBaseWithNonTrivialMoveAssignment(*this, ClassDecl) || - !subobjectsHaveMoveOrTrivialCopy(*this, ClassDecl,/*Constructor*/false)) { - ClassDecl->setFailedImplicitMoveAssignment(); - return 0; - } - // Note: The following rules are largely analoguous to the move // constructor rules. @@ -9729,18 +9617,9 @@ CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) { ? SpecialMemberIsTrivial(MoveAssignment, CXXMoveAssignment) : ClassDecl->hasTrivialMoveAssignment()); - // C++0x [class.copy]p9: - // If the definition of a class X does not explicitly declare a move - // assignment operator, one will be implicitly declared as defaulted if and - // only if: - // [...] - // - the move assignment operator would not be implicitly defined as - // deleted. if (ShouldDeleteSpecialMember(MoveAssignment, CXXMoveAssignment)) { - // Cache this result so that we don't try to generate this over and over - // on every lookup, leaking memory and wasting time. - ClassDecl->setFailedImplicitMoveAssignment(); - return 0; + ClassDecl->setImplicitMoveAssignmentIsDeleted(); + SetDeclDeleted(MoveAssignment, ClassLoc); } // Note that we have added this copy-assignment operator. @@ -9782,6 +9661,17 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation, // are assigned, in the order in which they were declared in the class // definition. + // FIXME: Issue a warning if our implicit move assignment operator will move + // from a virtual base more than once. For instance, given: + // + // struct A { A &operator=(A&&); }; + // struct B : virtual A {}; + // struct C : virtual A {}; + // struct D : B, C {}; + // + // If the move assignment operator of D is synthesized, we should warn, + // because the A vbase will be moved from multiple times. + // The statements that form the synthesized function body. SmallVector Statements; @@ -10071,11 +9961,6 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( ? SpecialMemberIsTrivial(CopyConstructor, CXXCopyConstructor) : ClassDecl->hasTrivialCopyConstructor()); - // C++11 [class.copy]p8: - // ... If the class definition does not explicitly declare a copy - // constructor, there is no user-declared move constructor, and there is no - // user-declared move assignment operator, a copy constructor is implicitly - // declared as defaulted. if (ShouldDeleteSpecialMember(CopyConstructor, CXXCopyConstructor)) SetDeclDeleted(CopyConstructor, ClassLoc); @@ -10195,29 +10080,12 @@ Sema::ComputeDefaultedMoveCtorExceptionSpec(CXXMethodDecl *MD) { CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor( CXXRecordDecl *ClassDecl) { - // C++11 [class.copy]p9: - // If the definition of a class X does not explicitly declare a move - // constructor, one will be implicitly declared as defaulted if and only if: - // - // - [first 4 bullets] assert(ClassDecl->needsImplicitMoveConstructor()); DeclaringSpecialMember DSM(*this, ClassDecl, CXXMoveConstructor); if (DSM.isAlreadyBeingDeclared()) return 0; - // [Checked after we build the declaration] - // - the move assignment operator would not be implicitly defined as - // deleted, - - // [DR1402]: - // - each of X's non-static data members and direct or virtual base classes - // has a type that either has a move constructor or is trivially copyable. - if (!subobjectsHaveMoveOrTrivialCopy(*this, ClassDecl, /*Constructor*/true)) { - ClassDecl->setFailedImplicitMoveConstructor(); - return 0; - } - QualType ClassType = Context.getTypeDeclType(ClassDecl); QualType ArgType = Context.getRValueReferenceType(ClassType); @@ -10260,16 +10128,9 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor( ? SpecialMemberIsTrivial(MoveConstructor, CXXMoveConstructor) : ClassDecl->hasTrivialMoveConstructor()); - // C++0x [class.copy]p9: - // If the definition of a class X does not explicitly declare a move - // constructor, one will be implicitly declared as defaulted if and only if: - // [...] - // - the move constructor would not be implicitly defined as deleted. if (ShouldDeleteSpecialMember(MoveConstructor, CXXMoveConstructor)) { - // Cache this result so that we don't try to generate this over and over - // on every lookup, leaking memory and wasting time. - ClassDecl->setFailedImplicitMoveConstructor(); - return 0; + ClassDecl->setImplicitMoveConstructorIsDeleted(); + SetDeclDeleted(MoveConstructor, ClassLoc); } // Note that we have declared this constructor. diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 323cd265e6..6f72d6598e 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -3602,8 +3602,8 @@ static bool evaluateTypeTrait(Sema &S, TypeTrait Kind, SourceLocation KWLoc, // is_trivially_constructible is defined as: // // is_constructible::value is true and the variable - // definition for is_constructible, as defined below, is known to call no - // operation that is not trivial. + // definition for is_constructible, as defined below, is known to call + // no operation that is not trivial. // // The predicate condition for a template specialization // is_constructible shall be satisfied if and only if the diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index c6bcf449b7..c0bd222947 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -5448,10 +5448,18 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, if (!CandidateSet.isNewCandidate(Function)) return; + // C++11 [class.copy]p11: [DR1402] + // A defaulted move constructor that is defined as deleted is ignored by + // overload resolution. + CXXConstructorDecl *Constructor = dyn_cast(Function); + if (Constructor && Constructor->isDefaulted() && Constructor->isDeleted() && + Constructor->isMoveConstructor()) + return; + // Overload resolution is always an unevaluated context. EnterExpressionEvaluationContext Unevaluated(*this, Sema::Unevaluated); - if (CXXConstructorDecl *Constructor = dyn_cast(Function)){ + if (Constructor) { // C++ [class.copy]p3: // A member function template is never instantiated to perform the copy // of a class object to an object of its class type. @@ -5626,6 +5634,13 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, if (!CandidateSet.isNewCandidate(Method)) return; + // C++11 [class.copy]p23: [DR1402] + // A defaulted move assignment operator that is defined as deleted is + // ignored by overload resolution. + if (Method->isDefaulted() && Method->isDeleted() && + Method->isMoveAssignmentOperator()) + return; + // Overload resolution is always an unevaluated context. EnterExpressionEvaluationContext Unevaluated(*this, Sema::Unevaluated); diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 5bcd5dca10..b8102d8417 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1208,8 +1208,6 @@ void ASTDeclReader::ReadCXXDefinitionData( Data.ImplicitCopyAssignmentHasConstParam = Record[Idx++]; Data.HasDeclaredCopyConstructorWithConstParam = Record[Idx++]; Data.HasDeclaredCopyAssignmentWithConstParam = Record[Idx++]; - Data.FailedImplicitMoveConstructor = Record[Idx++]; - Data.FailedImplicitMoveAssignment = Record[Idx++]; Data.NumBases = Record[Idx++]; if (Data.NumBases) diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 10957a2560..405488c357 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -5106,8 +5106,6 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec Record.push_back(Data.ImplicitCopyAssignmentHasConstParam); Record.push_back(Data.HasDeclaredCopyConstructorWithConstParam); Record.push_back(Data.HasDeclaredCopyAssignmentWithConstParam); - Record.push_back(Data.FailedImplicitMoveConstructor); - Record.push_back(Data.FailedImplicitMoveAssignment); // IsLambda bit is already saved. Record.push_back(Data.NumBases); diff --git a/test/CXX/class.derived/class.abstract/p16.cpp b/test/CXX/class.derived/class.abstract/p16.cpp index c237ed9044..2053218ca5 100644 --- a/test/CXX/class.derived/class.abstract/p16.cpp +++ b/test/CXX/class.derived/class.abstract/p16.cpp @@ -22,7 +22,7 @@ struct H; struct D { virtual E &operator=(const E &); // expected-note {{here}} virtual F &operator=(const F &); - virtual G &operator=(G&&); + virtual G &operator=(G&&); // expected-note {{here}} virtual H &operator=(H&&); // expected-note {{here}} friend struct F; @@ -34,8 +34,8 @@ private: struct E : D {}; // expected-error {{deleted function '~E' cannot override a non-deleted function}} \ // expected-error {{deleted function 'operator=' cannot override a non-deleted function}} struct F : D {}; -// No move ctor here, because it would be deleted. struct G : D {}; // expected-error {{deleted function '~G' cannot override a non-deleted function}} + // expected-error@-1 {{deleted function 'operator=' cannot override a non-deleted function}} struct H : D { H &operator=(H&&) = default; // expected-error {{deleted function 'operator=' cannot override a non-deleted function}} ~H(); diff --git a/test/CXX/drs/dr1xx.cpp b/test/CXX/drs/dr1xx.cpp index c67c51aa72..f8ea8d9b1a 100644 --- a/test/CXX/drs/dr1xx.cpp +++ b/test/CXX/drs/dr1xx.cpp @@ -71,7 +71,7 @@ namespace dr109 { // dr109: yes namespace dr111 { // dr111: dup 535 struct A { A(); A(volatile A&, int = 0); A(A&, const char * = "foo"); }; - struct B : A { B(); }; // expected-note {{would lose const qualifier}} expected-note {{requires 0 arguments}} + struct B : A { B(); }; // expected-note +{{would lose const qualifier}} expected-note {{requires 0 arguments}} const B b1; B b2(b1); // expected-error {{no matching constructor}} } diff --git a/test/CXX/special/class.copy/implicit-move.cpp b/test/CXX/special/class.copy/implicit-move.cpp index 1941da7e8a..5c3a6027cd 100644 --- a/test/CXX/special/class.copy/implicit-move.cpp +++ b/test/CXX/special/class.copy/implicit-move.cpp @@ -190,49 +190,59 @@ namespace DR1402 { NonTrivialMoveAssignVBase &operator=(NonTrivialMoveAssignVBase &&) = default; }; - // A non-movable, non-trivially-copyable class type as a subobject inhibits - // the declaration of a move operation. - struct NoMove1 { NonTrivialCopyCtor ntcc; }; // expected-note 2{{'const DR1402::NoMove1 &'}} - struct NoMove2 { NonTrivialCopyAssign ntcc; }; // expected-note 2{{'const DR1402::NoMove2 &'}} - struct NoMove3 : NonTrivialCopyCtor {}; // expected-note 2{{'const DR1402::NoMove3 &'}} - struct NoMove4 : NonTrivialCopyAssign {}; // expected-note 2{{'const DR1402::NoMove4 &'}} - struct NoMove5 : virtual NonTrivialCopyCtor {}; // expected-note 2{{'const DR1402::NoMove5 &'}} - struct NoMove6 : virtual NonTrivialCopyAssign {}; // expected-note 2{{'const DR1402::NoMove6 &'}} - struct NoMove7 : NonTrivialCopyCtorVBase {}; // expected-note 2{{'const DR1402::NoMove7 &'}} - struct NoMove8 : NonTrivialCopyAssignVBase {}; // expected-note 2{{'const DR1402::NoMove8 &'}} - - // A non-trivially-move-assignable virtual base class inhibits the declaration - // of a move assignment (which might move-assign the base class multiple - // times). + // DR1402: A non-movable, non-trivially-copyable class type as a subobject no + // longer inhibits the declaration of a move operation. + struct NoMove1 { NonTrivialCopyCtor ntcc; }; + struct NoMove2 { NonTrivialCopyAssign ntcc; }; + struct NoMove3 : NonTrivialCopyCtor {}; + struct NoMove4 : NonTrivialCopyAssign {}; + struct NoMove5 : virtual NonTrivialCopyCtor {}; + struct NoMove6 : virtual NonTrivialCopyAssign {}; + struct NoMove7 : NonTrivialCopyCtorVBase {}; + struct NoMove8 : NonTrivialCopyAssignVBase {}; + + // DR1402: A non-trivially-move-assignable virtual base class no longer + // inhibits the declaration of a move assignment (even though it might + // move-assign the base class multiple times). struct NoMove9 : NonTrivialMoveAssign {}; - struct NoMove10 : virtual NonTrivialMoveAssign {}; // expected-note {{'const DR1402::NoMove10 &'}} - struct NoMove11 : NonTrivialMoveAssignVBase {}; // expected-note {{'const DR1402::NoMove11 &'}} - - struct Test { - friend NoMove1::NoMove1(NoMove1 &&); // expected-error {{does not match}} - friend NoMove2::NoMove2(NoMove2 &&); // expected-error {{does not match}} - friend NoMove3::NoMove3(NoMove3 &&); // expected-error {{does not match}} - friend NoMove4::NoMove4(NoMove4 &&); // expected-error {{does not match}} - friend NoMove5::NoMove5(NoMove5 &&); // expected-error {{does not match}} - friend NoMove6::NoMove6(NoMove6 &&); // expected-error {{does not match}} - friend NoMove7::NoMove7(NoMove7 &&); // expected-error {{does not match}} - friend NoMove8::NoMove8(NoMove8 &&); // expected-error {{does not match}} - friend NoMove9::NoMove9(NoMove9 &&); - friend NoMove10::NoMove10(NoMove10 &&); - friend NoMove11::NoMove11(NoMove11 &&); - - friend NoMove1 &NoMove1::operator=(NoMove1 &&); // expected-error {{does not match}} - friend NoMove2 &NoMove2::operator=(NoMove2 &&); // expected-error {{does not match}} - friend NoMove3 &NoMove3::operator=(NoMove3 &&); // expected-error {{does not match}} - friend NoMove4 &NoMove4::operator=(NoMove4 &&); // expected-error {{does not match}} - friend NoMove5 &NoMove5::operator=(NoMove5 &&); // expected-error {{does not match}} - friend NoMove6 &NoMove6::operator=(NoMove6 &&); // expected-error {{does not match}} - friend NoMove7 &NoMove7::operator=(NoMove7 &&); // expected-error {{does not match}} - friend NoMove8 &NoMove8::operator=(NoMove8 &&); // expected-error {{does not match}} - friend NoMove9 &NoMove9::operator=(NoMove9 &&); - friend NoMove10 &NoMove10::operator=(NoMove10 &&); // expected-error {{does not match}} - friend NoMove11 &NoMove11::operator=(NoMove11 &&); // expected-error {{does not match}} + struct NoMove10 : virtual NonTrivialMoveAssign {}; + struct NoMove11 : NonTrivialMoveAssignVBase {}; + + template void test(T t) { + (void)T(static_cast(t)); // ok + t = static_cast(t); // ok + } + template void test(NoMove1); + template void test(NoMove2); + template void test(NoMove3); + template void test(NoMove4); + template void test(NoMove5); + template void test(NoMove6); + template void test(NoMove7); + template void test(NoMove8); + template void test(NoMove9); + template void test(NoMove10); + template void test(NoMove11); + + struct CopyOnly { + CopyOnly(const CopyOnly&); + CopyOnly &operator=(const CopyOnly&); }; + struct MoveOnly { + MoveOnly(MoveOnly&&); // expected-note {{user-declared move}} + MoveOnly &operator=(MoveOnly&&); + }; + template void test(CopyOnly); // ok, copies + template void test(MoveOnly); // ok, moves + struct CopyAndMove { // expected-note {{implicitly deleted}} + CopyOnly co; + MoveOnly mo; // expected-note {{deleted copy}} + }; + template void test(CopyAndMove); // ok, copies co, moves mo + void test2(CopyAndMove cm) { + (void)CopyAndMove(cm); // expected-error {{deleted}} + cm = cm; // expected-error {{deleted}} + } } namespace PR12625 { diff --git a/test/CXX/special/class.copy/p12-0x.cpp b/test/CXX/special/class.copy/p12-0x.cpp index 17b3191d1d..1b23b5a4b1 100644 --- a/test/CXX/special/class.copy/p12-0x.cpp +++ b/test/CXX/special/class.copy/p12-0x.cpp @@ -157,8 +157,8 @@ namespace TrivialityDependsOnImplicitDeletion { struct NoAccess { PrivateMove pm; - // NoAccess's move would be deleted, so is suppressed, - // so moves of it use PrivateMove's copy ctor, which is trivial. + // NoAccess's move is deleted, so moves of it use PrivateMove's copy ctor, + // which is trivial. }; static_assert(__is_trivially_constructible(NoAccess, const NoAccess &), ""); static_assert(__is_trivially_constructible(NoAccess, NoAccess &&), ""); diff --git a/test/CXX/special/class.copy/p23-cxx11.cpp b/test/CXX/special/class.copy/p23-cxx11.cpp index 90945c5803..c55d6c268a 100644 --- a/test/CXX/special/class.copy/p23-cxx11.cpp +++ b/test/CXX/special/class.copy/p23-cxx11.cpp @@ -9,7 +9,15 @@ template struct CopyAssign { template struct MoveAssign { static T t; void test() { - t = static_cast(t); // expected-error +{{deleted}} + // Overload resolution will ignore a defaulted, deleted move assignment, + // so check for it in a different way. + T &(T::*f)(T&&) = &T::operator=; // expected-error +{{deleted}} + } +}; +template struct MoveOrCopyAssign { + static T t; + void test() { + t = static_cast(t); // expected-error +{{copy assignment operator is implicitly deleted}} } }; @@ -89,29 +97,32 @@ struct D1 { AmbiguousCopyAssign a; // expected-note {{field 'a' has multiple copy}} }; struct D2 { - D2 &operator=(D2 &&) = default; // expected-note {{here}} + D2 &operator=(D2 &&) = default; // expected-note {{here}} expected-note {{copy assignment operator is implicitly deleted}} AmbiguousMoveAssign a; // expected-note {{field 'a' has multiple move}} }; struct D3 { DeletedCopyAssign a; // expected-note {{field 'a' has a deleted copy}} }; struct D4 { - D4 &operator=(D4 &&) = default; // expected-note {{here}} + D4 &operator=(D4 &&) = default; // expected-note {{here}} expected-note {{copy assignment operator is implicitly deleted}} DeletedMoveAssign a; // expected-note {{field 'a' has a deleted move}} }; struct D5 { InaccessibleCopyAssign a; // expected-note {{field 'a' has an inaccessible copy}} }; struct D6 { - D6 &operator=(D6 &&) = default; // expected-note {{here}} + D6 &operator=(D6 &&) = default; // expected-note {{here}} expected-note {{copy assignment operator is implicitly deleted}} InaccessibleMoveAssign a; // expected-note {{field 'a' has an inaccessible move}} }; template struct CopyAssign; // expected-note {{here}} template struct MoveAssign; // expected-note {{here}} +template struct MoveOrCopyAssign; // expected-note {{here}} template struct CopyAssign; // expected-note {{here}} template struct MoveAssign; // expected-note {{here}} +template struct MoveOrCopyAssign; // expected-note {{here}} template struct CopyAssign; // expected-note {{here}} template struct MoveAssign; // expected-note {{here}} +template struct MoveOrCopyAssign; // expected-note {{here}} // -- a direct or virtual base that cannot be copied/moved struct E1 : AmbiguousCopyAssign {}; // expected-note {{base class 'AmbiguousCopyAssign' has multiple copy}} -- 2.40.0