aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2010-07-02 21:50:04 +0000
committerDouglas Gregor <dgregor@apple.com>2010-07-02 21:50:04 +0000
commita376d10acfacf19d6dfa41069f7929739a18dd7a (patch)
tree42d4b579f5fd14884a277b9578e01bb502069dba /lib
parent234faa72224a038e3259ad82d4214174850b8b75 (diff)
Lazily declare copy-assignment operators.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@107521 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r--lib/AST/ASTContext.cpp5
-rw-r--r--lib/AST/DeclCXX.cpp52
-rw-r--r--lib/Frontend/PCHReaderDecl.cpp1
-rw-r--r--lib/Frontend/PCHWriterDecl.cpp1
-rw-r--r--lib/Sema/SemaDeclCXX.cpp95
-rw-r--r--lib/Sema/SemaLookup.cpp80
6 files changed, 156 insertions, 78 deletions
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 40342b36ba..f44eb65721 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -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);
diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp
index 05e3680ea9..b54f0e2ca6 100644
--- a/lib/AST/DeclCXX.cpp
+++ b/lib/AST/DeclCXX.cpp
@@ -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.
diff --git a/lib/Frontend/PCHReaderDecl.cpp b/lib/Frontend/PCHReaderDecl.cpp
index 08ab9a26f6..fa7382c2a0 100644
--- a/lib/Frontend/PCHReaderDecl.cpp
+++ b/lib/Frontend/PCHReaderDecl.cpp
@@ -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
diff --git a/lib/Frontend/PCHWriterDecl.cpp b/lib/Frontend/PCHWriterDecl.cpp
index d865e7c1b2..41e8a012a7 100644
--- a/lib/Frontend/PCHWriterDecl.cpp
+++ b/lib/Frontend/PCHWriterDecl.cpp
@@ -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());
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 3299999536..93caad669d 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -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;
diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp
index fc5c8e868d..3a62c1151a 100644
--- a/lib/Sema/SemaLookup.cpp
+++ b/lib/Sema/SemaLookup.cpp
@@ -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),