// 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;
/// 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;
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
/// 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
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.
using namespace clang;
+unsigned ASTContext::NumImplicitCopyAssignmentOperators;
+unsigned ASTContext::NumImplicitCopyAssignmentOperatorsDeclared;
unsigned ASTContext::NumImplicitDestructors;
unsigned ASTContext::NumImplicitDestructorsDeclared;
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);
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) {
}
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));
// 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.
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
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());
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;
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
"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
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);
}
}
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);
++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);
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;
}
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.
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;
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),