// -- any direct or virtual base class [...] has a type with a destructor
// that is deleted or inaccessible
if (!(CSM == Sema::CXXDefaultConstructor &&
- Field && Field->hasInClassInitializer())) {
- Sema::SpecialMemberOverloadResult *SMOR = lookupIn(Class);
- CXXMethodDecl *Member = SMOR->getMethod();
- if (shouldDeleteForSubobjectCall(Subobj, SMOR, false))
- return true;
-
- // FIXME: CWG 1402 moves these bullets elsewhere.
- if (CSM == Sema::CXXMoveConstructor) {
- CXXConstructorDecl *Ctor = cast<CXXConstructorDecl>(Member);
- // -- for the move constructor, a [...] direct or virtual base class with
- // a type that does not have a move constructor and is not trivially
- // copyable.
- if (!Ctor->isMoveConstructor() && !Class->isTriviallyCopyable())
- return true;
- } else if (CSM == Sema::CXXMoveAssignment) {
- // -- for the move assignment operator, a direct base class with a type
- // that does not have a move assignment operator and is not trivially
- // copyable.
- if (!Member->isMoveAssignmentOperator() && !Class->isTriviallyCopyable())
- return true;
- }
- }
+ Field && Field->hasInClassInitializer()) &&
+ shouldDeleteForSubobjectCall(Subobj, lookupIn(Class), false))
+ return true;
// C++11 [class.ctor]p5, C++11 [class.copy]p11:
// -- any direct or virtual base class or non-static data member has a
/// Check whether we should delete a special member function due to the class
/// having a particular direct or virtual base class.
bool SpecialMemberDeletionInfo::shouldDeleteForBase(CXXBaseSpecifier *Base) {
- // C++11 [class.copy]p23:
- // -- for the move assignment operator, any direct or indirect virtual
- // base class.
- if (CSM == Sema::CXXMoveAssignment && Base->isVirtual()) {
- if (Diagnose)
- S.Diag(Base->getLocStart(), diag::note_deleted_move_assign_virtual_base)
- << MD->getParent() << Base->getType();
- return true;
- }
-
- if (shouldDeleteForClassSubobject(Base->getType()->getAsCXXRecordDecl(),
- Base))
- return true;
-
- return false;
+ CXXRecordDecl *BaseClass = Base->getType()->getAsCXXRecordDecl();
+ return shouldDeleteForClassSubobject(BaseClass, Base);
}
/// Check whether we should delete a special member function due to the class
(!getLangOpts().MicrosoftMode || CSM == CXXCopyConstructor)) {
if (!Diagnose) return true;
UserDeclaredMove = RD->getMoveConstructor();
+ assert(UserDeclaredMove);
} else if (RD->hasUserDeclaredMoveAssignment() &&
(!getLangOpts().MicrosoftMode || CSM == CXXCopyAssignment)) {
if (!Diagnose) return true;
UserDeclaredMove = RD->getMoveAssignmentOperator();
+ assert(UserDeclaredMove);
}
if (UserDeclaredMove) {
DeclareImplicitCopyAssignment(ClassDecl);
}
- if (getLangOpts().CPlusPlus0x && ClassDecl->needsImplicitMoveAssignment()){
+ if (getLangOpts().CPlusPlus0x && ClassDecl->needsImplicitMoveAssignment()) {
++ASTContext::NumImplicitMoveAssignmentOperators;
// Likewise for the move assignment operator.
while (F.hasNext()) {
NamedDecl *D = F.next();
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D))
- if (Copying ? Method->isCopyAssignmentOperator() :
- Method->isMoveAssignmentOperator())
+ if (Method->isCopyAssignmentOperator() ||
+ (!Copying && Method->isMoveAssignmentOperator()))
continue;
F.erase();
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 the class has both a trivial move assignment and a non-trivial move
+ // assignment, hasTrivialMoveAssignment() is false.
+ if (BaseClass->hasDeclaredMoveAssignment() &&
+ !BaseClass->hasTrivialMoveAssignment())
+ 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)
+ return true;
+
+ if (Type.isTriviallyCopyableType(S.Context))
+ return true;
+
+ if (IsConstructor) {
+ if (ClassDecl->needsImplicitMoveConstructor())
+ S.DeclareImplicitMoveConstructor(ClassDecl);
+ return ClassDecl->hasDeclaredMoveConstructor();
+ }
+
+ if (ClassDecl->needsImplicitMoveAssignment())
+ S.DeclareImplicitMoveAssignment(ClassDecl);
+ return ClassDecl->hasDeclaredMoveAssignment();
+}
+
+/// 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());
+
+ // [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.
// ASTs.
Expr *This = ActOnCXXThis(Loc).takeAs<Expr>();
assert(This && "Reference to this cannot fail!");
-
+
// Assign base classes.
bool Invalid = false;
for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(),
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());
+
+ // [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;
+ }
+
ImplicitExceptionSpecification Spec(
ComputeDefaultedMoveCtorExceptionSpec(ClassDecl));
void test_contains_rref() {
(ContainsRValueRef(ContainsRValueRef()));
}
+
+
+namespace DR1402 {
+ struct NonTrivialCopyCtor {
+ NonTrivialCopyCtor(const NonTrivialCopyCtor &);
+ };
+ struct NonTrivialCopyAssign {
+ NonTrivialCopyAssign &operator=(const NonTrivialCopyAssign &);
+ };
+
+ struct NonTrivialCopyCtorVBase : virtual NonTrivialCopyCtor {
+ NonTrivialCopyCtorVBase(NonTrivialCopyCtorVBase &&);
+ NonTrivialCopyCtorVBase &operator=(NonTrivialCopyCtorVBase &&) = default;
+ };
+ struct NonTrivialCopyAssignVBase : virtual NonTrivialCopyAssign {
+ NonTrivialCopyAssignVBase(NonTrivialCopyAssignVBase &&);
+ NonTrivialCopyAssignVBase &operator=(NonTrivialCopyAssignVBase &&) = default;
+ };
+
+ struct NonTrivialMoveAssign {
+ NonTrivialMoveAssign(NonTrivialMoveAssign&&);
+ NonTrivialMoveAssign &operator=(NonTrivialMoveAssign &&);
+ };
+ struct NonTrivialMoveAssignVBase : virtual NonTrivialMoveAssign {
+ NonTrivialMoveAssignVBase(NonTrivialMoveAssignVBase &&);
+ 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{{'DR1402::NoMove7 &'}}
+ struct NoMove8 : NonTrivialCopyAssignVBase {}; // expected-note 2{{'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).
+ struct NoMove9 : NonTrivialMoveAssign {};
+ struct NoMove10 : virtual NonTrivialMoveAssign {}; // expected-note {{'DR1402::NoMove10 &'}}
+ struct NoMove11 : NonTrivialMoveAssignVBase {}; // expected-note {{'DR1402::NoMove11 &'}}
+
+ struct Test {
+ friend NoMove1::NoMove1(NoMove1 &&); // expected-error {{no matching function}}
+ friend NoMove2::NoMove2(NoMove2 &&); // expected-error {{no matching function}}
+ friend NoMove3::NoMove3(NoMove3 &&); // expected-error {{no matching function}}
+ friend NoMove4::NoMove4(NoMove4 &&); // expected-error {{no matching function}}
+ friend NoMove5::NoMove5(NoMove5 &&); // expected-error {{no matching function}}
+ friend NoMove6::NoMove6(NoMove6 &&); // expected-error {{no matching function}}
+ friend NoMove7::NoMove7(NoMove7 &&); // expected-error {{no matching function}}
+ friend NoMove8::NoMove8(NoMove8 &&); // expected-error {{no matching function}}
+ friend NoMove9::NoMove9(NoMove9 &&);
+ friend NoMove10::NoMove10(NoMove10 &&);
+ friend NoMove11::NoMove11(NoMove11 &&);
+
+ friend NoMove1 &NoMove1::operator=(NoMove1 &&); // expected-error {{no matching function}}
+ friend NoMove2 &NoMove2::operator=(NoMove2 &&); // expected-error {{no matching function}}
+ friend NoMove3 &NoMove3::operator=(NoMove3 &&); // expected-error {{no matching function}}
+ friend NoMove4 &NoMove4::operator=(NoMove4 &&); // expected-error {{no matching function}}
+ friend NoMove5 &NoMove5::operator=(NoMove5 &&); // expected-error {{no matching function}}
+ friend NoMove6 &NoMove6::operator=(NoMove6 &&); // expected-error {{no matching function}}
+ friend NoMove7 &NoMove7::operator=(NoMove7 &&); // expected-error {{no matching function}}
+ friend NoMove8 &NoMove8::operator=(NoMove8 &&); // expected-error {{no matching function}}
+ friend NoMove9 &NoMove9::operator=(NoMove9 &&);
+ friend NoMove10 &NoMove10::operator=(NoMove10 &&); // expected-error {{no matching function}}
+ friend NoMove11 &NoMove11::operator=(NoMove11 &&); // expected-error {{no matching function}}
+ };
+}