Lazily declare copy-assignment operators.
llvm-svn: 107521
This commit is contained in:
parent
3b1657b777
commit
330b9cff74
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in New Issue