diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2013-11-04 01:48:18 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2013-11-04 01:48:18 +0000 |
commit | 8b86f2d4010e1c5415ff561945d621858ca81c88 (patch) | |
tree | e52e0ef0c7432c3eab40f2d6a176a16e6082a9d7 /clang/lib/Sema/SemaDeclCXX.cpp | |
parent | 7fc270a1718ca1b654a51d83f1bdaebe747954dd (diff) | |
download | bcm5719-llvm-8b86f2d4010e1c5415ff561945d621858ca81c88.tar.gz bcm5719-llvm-8b86f2d4010e1c5415ff561945d621858ca81c88.zip |
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).
llvm-svn: 193969
Diffstat (limited to 'clang/lib/Sema/SemaDeclCXX.cpp')
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 169 |
1 files changed, 15 insertions, 154 deletions
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c8338353ea9..006f6aa67bf 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/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<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->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<Stmt*, 8> 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. |