Lazily declare copy-assignment operators.

llvm-svn: 107521
This commit is contained in:
Douglas Gregor 2010-07-02 21:50:04 +00:00
parent 3b1657b777
commit 330b9cff74
8 changed files with 181 additions and 85 deletions

View File

@ -1327,6 +1327,13 @@ public:
// Statistics
//===--------------------------------------------------------------------===//
/// \brief The number of implicitly-declared copy assignment operators.
static unsigned NumImplicitCopyAssignmentOperators;
/// \brief The number of implicitly-declared copy assignment operators for
/// which declarations were built.
static unsigned NumImplicitCopyAssignmentOperatorsDeclared;
/// \brief The number of implicitly-declared destructors.
static unsigned NumImplicitDestructors;

View File

@ -319,6 +319,9 @@ class CXXRecordDecl : public RecordDecl {
/// already computed and are available.
bool ComputedVisibleConversions : 1;
/// \brief Whether we have already declared the copy-assignment operator.
bool DeclaredCopyAssignment : 1;
/// \brief Whether we have already declared a destructor within the class.
bool DeclaredDestructor : 1;
@ -542,12 +545,6 @@ public:
CXXConstructorDecl *getCopyConstructor(ASTContext &Context,
unsigned TypeQuals) const;
/// hasConstCopyAssignment - Determines whether this class has a
/// copy assignment operator that accepts a const-qualified argument.
/// It returns its decl in MD if found.
bool hasConstCopyAssignment(ASTContext &Context,
const CXXMethodDecl *&MD) const;
/// \brief Retrieve the copy-assignment operator for this class, if available.
///
/// This routine attempts to find the copy-assignment operator for this
@ -581,7 +578,7 @@ public:
/// addedAssignmentOperator - Notify the class that another assignment
/// operator has been added. This routine helps maintain information about the
/// class based on which operators have been added.
/// class based on which operators have been added.
void addedAssignmentOperator(ASTContext &Context, CXXMethodDecl *OpDecl);
/// hasUserDeclaredCopyAssignment - Whether this class has a
@ -591,6 +588,20 @@ public:
return data().UserDeclaredCopyAssignment;
}
/// \brief Determine whether this class has had its copy assignment operator
/// declared, either via the user or via an implicit declaration.
///
/// This value is used for lazy creation of copy assignment operators.
bool hasDeclaredCopyAssignment() const {
return data().DeclaredCopyAssignment;
}
/// \brief Note whether this class has already had its copy assignment
/// operator declared.
void setDeclaredCopyAssignment(bool DCA) {
data().DeclaredCopyAssignment = DCA;
}
/// hasUserDeclaredDestructor - Whether this class has a
/// user-declared destructor. When false, a destructor will be
/// implicitly declared.

View File

@ -31,6 +31,8 @@
using namespace clang;
unsigned ASTContext::NumImplicitCopyAssignmentOperators;
unsigned ASTContext::NumImplicitCopyAssignmentOperatorsDeclared;
unsigned ASTContext::NumImplicitDestructors;
unsigned ASTContext::NumImplicitDestructorsDeclared;
@ -257,6 +259,9 @@ void ASTContext::PrintStats() const {
fprintf(stderr, "Total bytes = %d\n", int(TotalBytes));
// Implicit special member functions.
fprintf(stderr, " %u/%u implicit copy assignment operators created\n",
NumImplicitCopyAssignmentOperatorsDeclared,
NumImplicitCopyAssignmentOperators);
fprintf(stderr, " %u/%u implicit destructors created\n",
NumImplicitDestructorsDeclared, NumImplicitDestructors);

View File

@ -32,7 +32,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
Abstract(false), HasTrivialConstructor(true),
HasTrivialCopyConstructor(true), HasTrivialCopyAssignment(true),
HasTrivialDestructor(true), ComputedVisibleConversions(false),
DeclaredDestructor(false),
DeclaredCopyAssignment(false), DeclaredDestructor(false),
Bases(0), NumBases(0), VBases(0), NumVBases(0),
Definition(D), FirstFriend(0) {
}
@ -219,53 +219,6 @@ CXXConstructorDecl *CXXRecordDecl::getCopyConstructor(ASTContext &Context,
GetBestOverloadCandidateSimple(Found));
}
bool CXXRecordDecl::hasConstCopyAssignment(ASTContext &Context,
const CXXMethodDecl *& MD) const {
QualType ClassType = Context.getCanonicalType(Context.getTypeDeclType(
const_cast<CXXRecordDecl*>(this)));
DeclarationName OpName =Context.DeclarationNames.getCXXOperatorName(OO_Equal);
DeclContext::lookup_const_iterator Op, OpEnd;
for (llvm::tie(Op, OpEnd) = this->lookup(OpName);
Op != OpEnd; ++Op) {
// C++ [class.copy]p9:
// A user-declared copy assignment 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&.
const CXXMethodDecl* Method = dyn_cast<CXXMethodDecl>(*Op);
if (!Method)
continue;
if (Method->isStatic())
continue;
if (Method->getPrimaryTemplate())
continue;
const FunctionProtoType *FnType =
Method->getType()->getAs<FunctionProtoType>();
assert(FnType && "Overloaded operator has no prototype.");
// Don't assert on this; an invalid decl might have been left in the AST.
if (FnType->getNumArgs() != 1 || FnType->isVariadic())
continue;
bool AcceptsConst = true;
QualType ArgType = FnType->getArgType(0);
if (const LValueReferenceType *Ref = ArgType->getAs<LValueReferenceType>()) {
ArgType = Ref->getPointeeType();
// Is it a non-const lvalue reference?
if (!ArgType.isConstQualified())
AcceptsConst = false;
}
if (!Context.hasSameUnqualifiedType(ArgType, ClassType))
continue;
MD = Method;
// We have a single argument of type cv X or cv X&, i.e. we've found the
// copy assignment operator. Return whether it accepts const arguments.
return AcceptsConst;
}
assert(isInvalidDecl() &&
"No copy assignment operator declared in valid code.");
return false;
}
CXXMethodDecl *CXXRecordDecl::getCopyAssignmentOperator(bool ArgIsConst) const {
ASTContext &Context = getASTContext();
QualType Class = Context.getTypeDeclType(const_cast<CXXRecordDecl *>(this));
@ -378,7 +331,8 @@ void CXXRecordDecl::addedAssignmentOperator(ASTContext &Context,
// Suppress the implicit declaration of a copy constructor.
data().UserDeclaredCopyAssignment = true;
data().DeclaredCopyAssignment = true;
// C++ [class.copy]p11:
// A copy assignment operator is trivial if it is implicitly declared.
// FIXME: C++0x: don't do this for "= default" copy operators.

View File

@ -668,6 +668,7 @@ void PCHDeclReader::VisitCXXRecordDecl(CXXRecordDecl *D) {
Data.HasTrivialCopyAssignment = Record[Idx++];
Data.HasTrivialDestructor = Record[Idx++];
Data.ComputedVisibleConversions = Record[Idx++];
Data.DeclaredCopyAssignment = Record[Idx++];
Data.DeclaredDestructor = Record[Idx++];
// setBases() is unsuitable since it may try to iterate the bases of an

View File

@ -664,6 +664,7 @@ void PCHDeclWriter::VisitCXXRecordDecl(CXXRecordDecl *D) {
Record.push_back(Data.HasTrivialCopyAssignment);
Record.push_back(Data.HasTrivialDestructor);
Record.push_back(Data.ComputedVisibleConversions);
Record.push_back(Data.DeclaredCopyAssignment);
Record.push_back(Data.DeclaredDestructor);
Record.push_back(D->getNumBases());

View File

@ -2659,8 +2659,16 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) {
if (!ClassDecl->hasUserDeclaredCopyConstructor())
DeclareImplicitCopyConstructor(ClassDecl);
if (!ClassDecl->hasUserDeclaredCopyAssignment())
DeclareImplicitCopyAssignment(ClassDecl);
if (!ClassDecl->hasUserDeclaredCopyAssignment()) {
++ASTContext::NumImplicitCopyAssignmentOperators;
// If we have a dynamic class, then the copy assignment operator may be
// virtual, so we have to declare it immediately. This ensures that, e.g.,
// it shows up in the right place in the vtable and that we diagnose
// problems with the implicit exception specification.
if (ClassDecl->isDynamicClass())
DeclareImplicitCopyAssignment(ClassDecl);
}
if (!ClassDecl->hasUserDeclaredDestructor()) {
++ASTContext::NumImplicitDestructors;
@ -4547,6 +4555,58 @@ BuildSingleCopyAssign(Sema &S, SourceLocation Loc, QualType T,
Loc, move(Copy));
}
/// \brief Determine whether the given class has a copy assignment operator
/// that accepts a const-qualified argument.
static bool hasConstCopyAssignment(Sema &S, const CXXRecordDecl *CClass) {
CXXRecordDecl *Class = const_cast<CXXRecordDecl *>(CClass);
if (!Class->hasDeclaredCopyAssignment())
S.DeclareImplicitCopyAssignment(Class);
QualType ClassType = S.Context.getCanonicalType(S.Context.getTypeDeclType(Class));
DeclarationName OpName
= S.Context.DeclarationNames.getCXXOperatorName(OO_Equal);
DeclContext::lookup_const_iterator Op, OpEnd;
for (llvm::tie(Op, OpEnd) = Class->lookup(OpName); Op != OpEnd; ++Op) {
// C++ [class.copy]p9:
// A user-declared copy assignment 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&.
const CXXMethodDecl* Method = dyn_cast<CXXMethodDecl>(*Op);
if (!Method)
continue;
if (Method->isStatic())
continue;
if (Method->getPrimaryTemplate())
continue;
const FunctionProtoType *FnType =
Method->getType()->getAs<FunctionProtoType>();
assert(FnType && "Overloaded operator has no prototype.");
// Don't assert on this; an invalid decl might have been left in the AST.
if (FnType->getNumArgs() != 1 || FnType->isVariadic())
continue;
bool AcceptsConst = true;
QualType ArgType = FnType->getArgType(0);
if (const LValueReferenceType *Ref = ArgType->getAs<LValueReferenceType>()){
ArgType = Ref->getPointeeType();
// Is it a non-const lvalue reference?
if (!ArgType.isConstQualified())
AcceptsConst = false;
}
if (!S.Context.hasSameUnqualifiedType(ArgType, ClassType))
continue;
// We have a single argument of type cv X or cv X&, i.e. we've found the
// copy assignment operator. Return whether it accepts const arguments.
return AcceptsConst;
}
assert(Class->isInvalidDecl() &&
"No copy assignment operator declared in valid code.");
return false;
}
CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
// Note: The following rules are largely analoguous to the copy
// constructor rules. Note that virtual bases are not taken into account
@ -4574,9 +4634,7 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
"Cannot generate implicit members for class with dependent bases.");
const CXXRecordDecl *BaseClassDecl
= cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl());
const CXXMethodDecl *MD = 0;
HasConstCopyAssignment = BaseClassDecl->hasConstCopyAssignment(Context,
MD);
HasConstCopyAssignment = hasConstCopyAssignment(*this, BaseClassDecl);
}
// -- for all the nonstatic data members of X that are of a class
@ -4591,9 +4649,7 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
if (const RecordType *FieldClassType = FieldType->getAs<RecordType>()) {
const CXXRecordDecl *FieldClassDecl
= cast<CXXRecordDecl>(FieldClassType->getDecl());
const CXXMethodDecl *MD = 0;
HasConstCopyAssignment
= FieldClassDecl->hasConstCopyAssignment(Context, MD);
HasConstCopyAssignment = hasConstCopyAssignment(*this, FieldClassDecl);
}
}
@ -4614,8 +4670,12 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(),
BaseEnd = ClassDecl->bases_end();
Base != BaseEnd; ++Base) {
const CXXRecordDecl *BaseClassDecl
CXXRecordDecl *BaseClassDecl
= cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl());
if (!BaseClassDecl->hasDeclaredCopyAssignment())
DeclareImplicitCopyAssignment(BaseClassDecl);
if (CXXMethodDecl *CopyAssign
= BaseClassDecl->getCopyAssignmentOperator(HasConstCopyAssignment))
ExceptSpec.CalledDecl(CopyAssign);
@ -4626,8 +4686,12 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
++Field) {
QualType FieldType = Context.getBaseElementType((*Field)->getType());
if (const RecordType *FieldClassType = FieldType->getAs<RecordType>()) {
const CXXRecordDecl *FieldClassDecl
CXXRecordDecl *FieldClassDecl
= cast<CXXRecordDecl>(FieldClassType->getDecl());
if (!FieldClassDecl->hasDeclaredCopyAssignment())
DeclareImplicitCopyAssignment(FieldClassDecl);
if (CXXMethodDecl *CopyAssign
= FieldClassDecl->getCopyAssignmentOperator(HasConstCopyAssignment))
ExceptSpec.CalledDecl(CopyAssign);
@ -4663,12 +4727,13 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
VarDecl::None, 0);
CopyAssignment->setParams(&FromParam, 1);
// Don't call addedAssignmentOperator. The class does not need to know about
// the implicitly-declared copy assignment operator.
// Note that we have added this copy-assignment operator.
ClassDecl->setDeclaredCopyAssignment(true);
++ASTContext::NumImplicitCopyAssignmentOperatorsDeclared;
if (Scope *S = getScopeForContext(ClassDecl))
PushOnScopeChains(CopyAssignment, S, true);
else
ClassDecl->addDecl(CopyAssignment);
PushOnScopeChains(CopyAssignment, S, false);
ClassDecl->addDecl(CopyAssignment);
AddOverriddenMethods(ClassDecl, CopyAssignment);
return CopyAssignment;

View File

@ -464,12 +464,65 @@ static bool CanDeclareSpecialMemberFunction(ASTContext &Context,
}
void Sema::ForceDeclarationOfImplicitMembers(CXXRecordDecl *Class) {
// If the copy assignment operator has not yet been declared, do so now.
if (CanDeclareSpecialMemberFunction(Context, Class) &&
!Class->hasDeclaredCopyAssignment())
DeclareImplicitCopyAssignment(Class);
// If the destructor has not yet been declared, do so now.
if (CanDeclareSpecialMemberFunction(Context, Class) &&
!Class->hasDeclaredDestructor())
DeclareImplicitDestructor(Class);
}
/// \brief Determine whether this is the name of an implicitly-declared
/// special member function.
static bool isImplicitlyDeclaredMemberFunctionName(DeclarationName Name) {
switch (Name.getNameKind()) {
case DeclarationName::CXXDestructorName:
return true;
case DeclarationName::CXXOperatorName:
return Name.getCXXOverloadedOperator() == OO_Equal;
default:
break;
}
return false;
}
/// \brief If there are any implicit member functions with the given name
/// that need to be declared in the given declaration context, do so.
static void DeclareImplicitMemberFunctionsWithName(Sema &S,
DeclarationName Name,
const DeclContext *DC) {
if (!DC)
return;
switch (Name.getNameKind()) {
case DeclarationName::CXXDestructorName:
if (const CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(DC))
if (Record->getDefinition() && !Record->hasDeclaredDestructor() &&
CanDeclareSpecialMemberFunction(S.Context, Record))
S.DeclareImplicitDestructor(const_cast<CXXRecordDecl *>(Record));
break;
case DeclarationName::CXXOperatorName:
if (Name.getCXXOverloadedOperator() != OO_Equal)
break;
if (const CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(DC))
if (Record->getDefinition() && !Record->hasDeclaredCopyAssignment() &&
CanDeclareSpecialMemberFunction(S.Context, Record))
S.DeclareImplicitCopyAssignment(const_cast<CXXRecordDecl *>(Record));
break;
default:
break;
}
}
// Adds all qualifying matches for a name within a decl context to the
// given lookup result. Returns true if any matches were found.
@ -477,20 +530,8 @@ static bool LookupDirect(Sema &S, LookupResult &R, const DeclContext *DC) {
bool Found = false;
// Lazily declare C++ special member functions.
if (S.getLangOptions().CPlusPlus) {
switch (R.getLookupName().getNameKind()) {
case DeclarationName::CXXDestructorName:
if (const CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(DC))
if (Record->getDefinition() && !Record->hasDeclaredDestructor() &&
CanDeclareSpecialMemberFunction(S.Context, Record))
S.DeclareImplicitDestructor(const_cast<CXXRecordDecl *>(Record));
break;
default:
break;
}
}
if (S.getLangOptions().CPlusPlus)
DeclareImplicitMemberFunctionsWithName(S, R.getLookupName(), DC);
// Perform lookup into this declaration context.
DeclContext::lookup_const_iterator I, E;
@ -681,6 +722,17 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
DeclarationName Name = R.getLookupName();
// If this is the name of an implicitly-declared special member function,
// go through the scope stack to implicitly declare
if (isImplicitlyDeclaredMemberFunctionName(Name)) {
for (Scope *PreS = S; PreS; PreS = PreS->getParent())
if (DeclContext *DC = static_cast<DeclContext *>(PreS->getEntity()))
DeclareImplicitMemberFunctionsWithName(*this, Name, DC);
}
// Implicitly declare member functions with the name we're looking for, if in
// fact we are in a scope where it matters.
Scope *Initial = S;
IdentifierResolver::iterator
I = IdResolver.begin(Name),