Implement a little bit of cleanup and a lot more of the base work

behind implicit moves. We now correctly identify move constructors and
assignment operators and update bits on the record correctly. Generation
of implicit moves (declarations or definitions) is not yet supported.

llvm-svn: 132080
This commit is contained in:
Alexis Hunt 2011-05-25 20:50:04 +00:00
parent 9a26f0f260
commit fcaeae4929
7 changed files with 204 additions and 88 deletions

View File

@ -1546,6 +1546,13 @@ public:
/// which declarations were built.
static unsigned NumImplicitCopyConstructorsDeclared;
/// \brief The number of implicitly-declared move constructors.
static unsigned NumImplicitMoveConstructors;
/// \brief The number of implicitly-declared move constructors for
/// which declarations were built.
static unsigned NumImplicitMoveConstructorsDeclared;
/// \brief The number of implicitly-declared copy assignment operators.
static unsigned NumImplicitCopyAssignmentOperators;
@ -1553,6 +1560,13 @@ public:
/// which declarations were built.
static unsigned NumImplicitCopyAssignmentOperatorsDeclared;
/// \brief The number of implicitly-declared move assignment operators.
static unsigned NumImplicitMoveAssignmentOperators;
/// \brief The number of implicitly-declared move assignment operators for
/// which declarations were built.
static unsigned NumImplicitMoveAssignmentOperatorsDeclared;
/// \brief The number of implicitly-declared destructors.
static unsigned NumImplicitDestructors;

View File

@ -278,10 +278,18 @@ class CXXRecordDecl : public RecordDecl {
/// user-declared copy constructor.
bool UserDeclaredCopyConstructor : 1;
/// UserDeclareMoveConstructor - True when this class has a
/// user-declared move constructor.
bool UserDeclaredMoveConstructor : 1;
/// UserDeclaredCopyAssignment - True when this class has a
/// user-declared copy assignment operator.
bool UserDeclaredCopyAssignment : 1;
/// UserDeclareMoveAssignment - True when this class has a
/// user-declared move assignment.
bool UserDeclaredMoveAssignment : 1;
/// UserDeclaredDestructor - True when this class has a
/// user-declared destructor.
bool UserDeclaredDestructor : 1;
@ -448,9 +456,15 @@ class CXXRecordDecl : public RecordDecl {
/// \brief Whether we have already declared the copy constructor.
bool DeclaredCopyConstructor : 1;
/// \brief Whether we have already declared the move constructor.
bool DeclaredMoveConstructor : 1;
/// \brief Whether we have already declared the copy-assignment operator.
bool DeclaredCopyAssignment : 1;
/// \brief Whether we have already declared the move-assignment operator.
bool DeclaredMoveAssignment : 1;
/// \brief Whether we have already declared a destructor within the class.
bool DeclaredDestructor : 1;
@ -695,11 +709,13 @@ public:
/// hasConstCopyConstructor - Determines whether this class has a
/// copy constructor that accepts a const-qualified argument.
bool hasConstCopyConstructor(const ASTContext &Context) const;
bool hasConstCopyConstructor() const;
/// getCopyConstructor - Returns the copy constructor for this class
CXXConstructorDecl *getCopyConstructor(const ASTContext &Context,
unsigned TypeQuals) const;
CXXConstructorDecl *getCopyConstructor(unsigned TypeQuals) const;
/// getMoveConstructor - Returns the move constructor for this class
CXXConstructorDecl *getMoveConstructor() const;
/// \brief Retrieve the copy-assignment operator for this class, if available.
///
@ -712,6 +728,10 @@ public:
/// \returns The copy-assignment operator that can be invoked, or NULL if
/// a unique copy-assignment operator could not be found.
CXXMethodDecl *getCopyAssignmentOperator(bool ArgIsConst) const;
/// getMoveAssignmentOperator - Returns the move assignment operator for this
/// class
CXXMethodDecl *getMoveAssignmentOperator() const;
/// hasUserDeclaredConstructor - Whether this class has any
/// user-declared constructors. When true, a default constructor
@ -740,7 +760,27 @@ public:
bool hasDeclaredCopyConstructor() const {
return data().DeclaredCopyConstructor;
}
/// hasUserDeclaredMoveOperation - Whether this class has a user-
/// declared move constructor or assignment operator. When false, a
/// move constructor and assignment operator may be implicitly declared.
bool hasUserDeclaredMoveOperation() const {
return data().UserDeclaredMoveConstructor ||
data().UserDeclaredMoveAssignment;
}
/// \brief Determine whether this class has had a move constructor
/// declared by the user.
bool hasUserDeclaredMoveConstructor() const {
return data().UserDeclaredMoveConstructor;
}
/// \brief Determine whether this class has had a move constructor
/// declared.
bool hasDeclaredMoveConstructor() const {
return data().DeclaredMoveConstructor;
}
/// hasUserDeclaredCopyAssignment - Whether this class has a
/// user-declared copy assignment operator. When false, a copy
/// assigment operator will be implicitly declared.
@ -755,7 +795,19 @@ public:
bool hasDeclaredCopyAssignment() const {
return data().DeclaredCopyAssignment;
}
/// \brief Determine whether this class has had a move assignment
/// declared by the user.
bool hasUserDeclaredMoveAssignment() const {
return data().UserDeclaredMoveAssignment;
}
/// hasDeclaredMoveAssignment - Whether this class has a
/// declared move assignment operator.
bool hasDeclaredMoveAssignment() const {
return data().DeclaredMoveAssignment;
}
/// hasUserDeclaredDestructor - Whether this class has a
/// user-declared destructor. When false, a destructor will be
/// implicitly declared.
@ -1225,6 +1277,9 @@ public:
/// \brief Determine whether this is a copy-assignment operator, regardless
/// of whether it was declared implicitly or explicitly.
bool isCopyAssignmentOperator() const;
/// \brief Determine whether this is a move assignment operator.
bool isMoveAssignmentOperator() const;
const CXXMethodDecl *getCanonicalDecl() const {
return cast<CXXMethodDecl>(FunctionDecl::getCanonicalDecl());

View File

@ -1118,7 +1118,9 @@ public:
CXXDefaultConstructor = 0,
CXXCopyConstructor = 1,
CXXCopyAssignment = 2,
CXXDestructor = 3
CXXDestructor = 3,
CXXMoveConstructor = 4,
CXXMoveAssignment = 5
};
bool CheckNontrivialField(FieldDecl *FD);
void DiagnoseNontrivial(const RecordType* Record, CXXSpecialMember mem);

View File

@ -39,8 +39,12 @@ unsigned ASTContext::NumImplicitDefaultConstructors;
unsigned ASTContext::NumImplicitDefaultConstructorsDeclared;
unsigned ASTContext::NumImplicitCopyConstructors;
unsigned ASTContext::NumImplicitCopyConstructorsDeclared;
unsigned ASTContext::NumImplicitMoveConstructors;
unsigned ASTContext::NumImplicitMoveConstructorsDeclared;
unsigned ASTContext::NumImplicitCopyAssignmentOperators;
unsigned ASTContext::NumImplicitCopyAssignmentOperatorsDeclared;
unsigned ASTContext::NumImplicitMoveAssignmentOperators;
unsigned ASTContext::NumImplicitMoveAssignmentOperatorsDeclared;
unsigned ASTContext::NumImplicitDestructors;
unsigned ASTContext::NumImplicitDestructorsDeclared;
@ -318,9 +322,17 @@ void ASTContext::PrintStats() const {
fprintf(stderr, " %u/%u implicit copy constructors created\n",
NumImplicitCopyConstructorsDeclared,
NumImplicitCopyConstructors);
if (getLangOptions().CPlusPlus)
fprintf(stderr, " %u/%u implicit move constructors created\n",
NumImplicitMoveConstructorsDeclared,
NumImplicitMoveConstructors);
fprintf(stderr, " %u/%u implicit copy assignment operators created\n",
NumImplicitCopyAssignmentOperatorsDeclared,
NumImplicitCopyAssignmentOperators);
if (getLangOptions().CPlusPlus)
fprintf(stderr, " %u/%u implicit move assignment operators created\n",
NumImplicitMoveAssignmentOperatorsDeclared,
NumImplicitMoveAssignmentOperators);
fprintf(stderr, " %u/%u implicit destructors created\n",
NumImplicitDestructorsDeclared, NumImplicitDestructors);
@ -3717,7 +3729,7 @@ bool ASTContext::BlockRequiresCopying(QualType Ty) const {
if (getLangOptions().CPlusPlus) {
if (const RecordType *RT = Ty->getAs<RecordType>()) {
CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
return RD->hasConstCopyConstructor(*this);
return RD->hasConstCopyConstructor();
}
}

View File

@ -29,7 +29,8 @@ using namespace clang;
CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
: UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false),
UserDeclaredCopyAssignment(false), UserDeclaredDestructor(false),
UserDeclaredMoveConstructor(false), UserDeclaredCopyAssignment(false),
UserDeclaredMoveAssignment(false), UserDeclaredDestructor(false),
Aggregate(true), PlainOldData(true), Empty(true), Polymorphic(false),
Abstract(false), IsStandardLayout(true), HasNoNonEmptyBases(true),
HasPrivateFields(false), HasProtectedFields(false), HasPublicFields(false),
@ -39,7 +40,8 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
HasTrivialMoveAssignment(true), HasTrivialDestructor(true),
HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false),
UserProvidedDefaultConstructor(false), DeclaredDefaultConstructor(false),
DeclaredCopyConstructor(false), DeclaredCopyAssignment(false),
DeclaredCopyConstructor(false), DeclaredMoveConstructor(false),
DeclaredCopyAssignment(false), DeclaredMoveAssignment(false),
DeclaredDestructor(false), NumBases(0), NumVBases(0), Bases(), VBases(),
Definition(D), FirstFriend(0) {
}
@ -268,8 +270,8 @@ bool CXXRecordDecl::hasAnyDependentBases() const {
return !forallBases(SawBase, 0);
}
bool CXXRecordDecl::hasConstCopyConstructor(const ASTContext &Context) const {
return getCopyConstructor(Context, Qualifiers::Const) != 0;
bool CXXRecordDecl::hasConstCopyConstructor() const {
return getCopyConstructor(Qualifiers::Const) != 0;
}
bool CXXRecordDecl::isTriviallyCopyable() const {
@ -312,8 +314,8 @@ GetBestOverloadCandidateSimple(
return Cands[Best].first;
}
CXXConstructorDecl *CXXRecordDecl::getCopyConstructor(const ASTContext &Context,
unsigned TypeQuals) const{
CXXConstructorDecl *CXXRecordDecl::getCopyConstructor(unsigned TypeQuals) const{
ASTContext &Context = getASTContext();
QualType ClassType
= Context.getTypeDeclType(const_cast<CXXRecordDecl*>(this));
DeclarationName ConstructorName
@ -343,6 +345,14 @@ CXXConstructorDecl *CXXRecordDecl::getCopyConstructor(const ASTContext &Context,
GetBestOverloadCandidateSimple(Found));
}
CXXConstructorDecl *CXXRecordDecl::getMoveConstructor() const {
for (ctor_iterator I = ctor_begin(), E = ctor_end(); I != E; ++I)
if (I->isMoveConstructor())
return *I;
return 0;
}
CXXMethodDecl *CXXRecordDecl::getCopyAssignmentOperator(bool ArgIsConst) const {
ASTContext &Context = getASTContext();
QualType Class = Context.getTypeDeclType(const_cast<CXXRecordDecl *>(this));
@ -393,6 +403,14 @@ CXXMethodDecl *CXXRecordDecl::getCopyAssignmentOperator(bool ArgIsConst) const {
return GetBestOverloadCandidateSimple(Found);
}
CXXMethodDecl *CXXRecordDecl::getMoveAssignmentOperator() const {
for (method_iterator I = method_begin(), E = method_end(); I != E; ++I)
if (I->isMoveAssignmentOperator())
return *I;
return 0;
}
void CXXRecordDecl::markedVirtualFunctionPure() {
// C++ [class.abstract]p2:
// A class is abstract if it has at least one pure virtual function.
@ -459,33 +477,32 @@ void CXXRecordDecl::addedMember(Decl *D) {
if (ASTMutationListener *L = getASTMutationListener())
L->AddedCXXImplicitMember(data().Definition, D);
// If this is a special member function, note that it was added and then
// return early.
if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
// If this is the implicit default constructor, note that we have now
// declared it.
if (Constructor->isDefaultConstructor()) {
if (Constructor->isDefaultConstructor())
data().DeclaredDefaultConstructor = true;
}
// If this is the implicit copy constructor, note that we have now
// declared it.
else if (Constructor->isCopyConstructor())
data().DeclaredCopyConstructor = true;
else if (Constructor->isMoveConstructor())
data().DeclaredMoveConstructor = true;
else
goto NotASpecialMember;
return;
}
if (isa<CXXDestructorDecl>(D)) {
} else if (isa<CXXDestructorDecl>(D)) {
data().DeclaredDestructor = true;
return;
}
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
// If this is the implicit copy constructor, note that we have now
// declared it.
// FIXME: Move constructors
if (Method->getOverloadedOperator() == OO_Equal)
} else if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
if (Method->isCopyAssignmentOperator())
data().DeclaredCopyAssignment = true;
else if (Method->isMoveAssignmentOperator())
data().DeclaredMoveAssignment = true;
else
goto NotASpecialMember;
return;
}
NotASpecialMember:;
// Any other implicit declarations are handled like normal declarations.
}
@ -524,6 +541,9 @@ void CXXRecordDecl::addedMember(Decl *D) {
UserProvided = true;
}
} else if (Constructor->isMoveConstructor()) {
data().UserDeclaredMoveConstructor = true;
data().DeclaredMoveConstructor = true;
// C++0x [class.copy]p13:
// A copy/move constructor for class X is trivial if it is not
// user-provided [...]
@ -579,61 +599,42 @@ void CXXRecordDecl::addedMember(Decl *D) {
// Handle (user-declared) member functions.
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
if (Method->getOverloadedOperator() == OO_Equal) {
// We're interested specifically in copy assignment operators.
const FunctionProtoType *FnType
= Method->getType()->getAs<FunctionProtoType>();
assert(FnType && "Overloaded operator has no proto function type.");
assert(FnType->getNumArgs() == 1 && !FnType->isVariadic());
// Copy assignment operators must be non-templates.
if (Method->getPrimaryTemplate() || FunTmpl)
return;
ASTContext &Context = getASTContext();
QualType ClassType = Context.getCanonicalType(Context.getTypeDeclType(
const_cast<CXXRecordDecl*>(this)));
bool isRValueRefArg = false;
QualType ArgType = FnType->getArgType(0);
if (const LValueReferenceType *Ref =
ArgType->getAs<LValueReferenceType>()) {
ArgType = Ref->getPointeeType();
} else if (const RValueReferenceType *Ref =
ArgType->getAs<RValueReferenceType>()) {
ArgType = Ref->getPointeeType();
isRValueRefArg = true;
}
if (!Context.hasSameUnqualifiedType(ClassType, ArgType))
return;
if (Method->isCopyAssignmentOperator()) {
// C++ [class]p4:
// A POD-struct is an aggregate class that [...] has no user-defined
// copy assignment operator [...].
// This is the C++03 bit only.
data().PlainOldData = false;
if (!isRValueRefArg) {
// This is a copy assignment operator.
// This is a copy assignment operator.
// Suppress the implicit declaration of a copy constructor.
data().UserDeclaredCopyAssignment = true;
data().DeclaredCopyAssignment = true;
// Suppress the implicit declaration of a copy constructor.
data().UserDeclaredCopyAssignment = true;
data().DeclaredCopyAssignment = true;
// C++0x [class.copy]p27:
// A copy/move assignment operator for class X is trivial if it is
// neither user-provided nor deleted [...]
if (Method->isUserProvided())
data().HasTrivialCopyAssignment = false;
} else {
// This is a move assignment operator.
// C++0x [class.copy]p27:
// A copy/move assignment operator for class X is trivial if it is
// neither user-provided nor deleted [...]
if (Method->isUserProvided())
data().HasTrivialCopyAssignment = false;
// C++0x [class.copy]p27:
// A copy/move assignment operator for class X is trivial if it is
// neither user-provided nor deleted [...]
if (Method->isUserProvided())
data().HasTrivialMoveAssignment = false;
}
return;
}
if (Method->isMoveAssignmentOperator()) {
// This is an extension in C++03 mode, but we'll keep consistency by
// taking a move assignment operator to induce non-POD-ness
data().PlainOldData = false;
// This is a move assignment operator.
data().UserDeclaredMoveAssignment = true;
data().DeclaredMoveAssignment = true;
// C++0x [class.copy]p27:
// A copy/move assignment operator for class X is trivial if it is
// neither user-provided nor deleted [...]
if (Method->isUserProvided())
data().HasTrivialMoveAssignment = false;
}
// Keep the list of conversion functions up-to-date.
@ -1170,14 +1171,13 @@ bool CXXMethodDecl::isUsualDeallocationFunction() const {
}
bool CXXMethodDecl::isCopyAssignmentOperator() const {
// C++0x [class.copy]p19:
// C++0x [class.copy]p17:
// A user-declared copy assignment operator X::operator= is a non-static
// non-template member function of class X with exactly one parameter of
// type X, X&, const X&, volatile X& or const volatile X&.
if (/*operator=*/getOverloadedOperator() != OO_Equal ||
/*non-static*/ isStatic() ||
/*non-template*/getPrimaryTemplate() || getDescribedFunctionTemplate() ||
/*exactly one parameter*/getNumParams() != 1)
/*non-template*/getPrimaryTemplate() || getDescribedFunctionTemplate())
return false;
QualType ParamType = getParamDecl(0)->getType();
@ -1190,6 +1190,26 @@ bool CXXMethodDecl::isCopyAssignmentOperator() const {
return Context.hasSameUnqualifiedType(ClassType, ParamType);
}
bool CXXMethodDecl::isMoveAssignmentOperator() const {
// C++0x [class.copy]p19:
// A user-declared move assignment operator X::operator= is a non-static
// non-template member function of class X with exactly one parameter of type
// X&&, const X&&, volatile X&&, or const volatile X&&.
if (getOverloadedOperator() != OO_Equal || isStatic() ||
getPrimaryTemplate() || getDescribedFunctionTemplate())
return false;
QualType ParamType = getParamDecl(0)->getType();
if (!isa<RValueReferenceType>(ParamType))
return false;
ParamType = ParamType->getPointeeType();
ASTContext &Context = getASTContext();
QualType ClassType
= Context.getCanonicalType(Context.getTypeDeclType(getParent()));
return Context.hasSameUnqualifiedType(ClassType, ParamType);
}
void CXXMethodDecl::addOverriddenMethod(const CXXMethodDecl *MD) {
assert(MD->isCanonicalDecl() && "Method is not canonical!");
assert(!MD->getParent()->isDependentContext() &&

View File

@ -7736,7 +7736,15 @@ void Sema::DiagnoseNontrivial(const RecordType* T, CXXSpecialMember member) {
case CXXCopyConstructor:
if (RD->hasUserDeclaredCopyConstructor()) {
SourceLocation CtorLoc =
RD->getCopyConstructor(Context, 0)->getLocation();
RD->getCopyConstructor(0)->getLocation();
Diag(CtorLoc, diag::note_nontrivial_user_defined) << QT << member;
return;
}
break;
case CXXMoveConstructor:
if (RD->hasUserDeclaredMoveConstructor()) {
SourceLocation CtorLoc = RD->getMoveConstructor()->getLocation();
Diag(CtorLoc, diag::note_nontrivial_user_defined) << QT << member;
return;
}
@ -7752,6 +7760,14 @@ void Sema::DiagnoseNontrivial(const RecordType* T, CXXSpecialMember member) {
}
break;
case CXXMoveAssignment:
if (RD->hasUserDeclaredMoveAssignment()) {
SourceLocation AssignLoc = RD->getMoveAssignmentOperator()->getLocation();
Diag(AssignLoc, diag::note_nontrivial_user_defined) << QT << member;
return;
}
break;
case CXXDestructor:
if (RD->hasUserDeclaredDestructor()) {
SourceLocation DtorLoc = LookupDestructor(RD)->getLocation();

View File

@ -7069,8 +7069,7 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) {
if (!BaseClassDecl->hasDeclaredCopyConstructor())
DeclareImplicitCopyConstructor(BaseClassDecl);
HasConstCopyConstructor
= BaseClassDecl->hasConstCopyConstructor(Context);
HasConstCopyConstructor = BaseClassDecl->hasConstCopyConstructor();
}
for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(),
@ -7082,8 +7081,7 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) {
if (!BaseClassDecl->hasDeclaredCopyConstructor())
DeclareImplicitCopyConstructor(BaseClassDecl);
HasConstCopyConstructor
= BaseClassDecl->hasConstCopyConstructor(Context);
HasConstCopyConstructor= BaseClassDecl->hasConstCopyConstructor();
}
// -- for all the nonstatic data members of X that are of a
@ -7101,8 +7099,7 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) {
if (!FieldClassDecl->hasDeclaredCopyConstructor())
DeclareImplicitCopyConstructor(FieldClassDecl);
HasConstCopyConstructor
= FieldClassDecl->hasConstCopyConstructor(Context);
HasConstCopyConstructor = FieldClassDecl->hasConstCopyConstructor();
}
}
// Otherwise, the implicitly declared copy constructor will have
@ -7129,7 +7126,7 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) {
DeclareImplicitCopyConstructor(BaseClassDecl);
if (CXXConstructorDecl *CopyConstructor
= BaseClassDecl->getCopyConstructor(Context, Quals))
= BaseClassDecl->getCopyConstructor(Quals))
ExceptSpec.CalledDecl(CopyConstructor);
}
for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(),
@ -7142,7 +7139,7 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) {
DeclareImplicitCopyConstructor(BaseClassDecl);
if (CXXConstructorDecl *CopyConstructor
= BaseClassDecl->getCopyConstructor(Context, Quals))
= BaseClassDecl->getCopyConstructor(Quals))
ExceptSpec.CalledDecl(CopyConstructor);
}
for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(),
@ -7157,7 +7154,7 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) {
DeclareImplicitCopyConstructor(FieldClassDecl);
if (CXXConstructorDecl *CopyConstructor
= FieldClassDecl->getCopyConstructor(Context, Quals))
= FieldClassDecl->getCopyConstructor(Quals))
ExceptSpec.CalledDecl(CopyConstructor);
}
}