]> granicus.if.org Git - clang/commitdiff
Lazily declare copy-assignment operators.
authorDouglas Gregor <dgregor@apple.com>
Fri, 2 Jul 2010 21:50:04 +0000 (21:50 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 2 Jul 2010 21:50:04 +0000 (21:50 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@107521 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/ASTContext.h
include/clang/AST/DeclCXX.h
lib/AST/ASTContext.cpp
lib/AST/DeclCXX.cpp
lib/Frontend/PCHReaderDecl.cpp
lib/Frontend/PCHWriterDecl.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaLookup.cpp

index f109d4815927dc989543740b83fd1eb1a22fb749..5e3f891e486b4753f525a1c830a2f26630969d85 100644 (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;
   
index f67faa86ec829adcd06b12fa072dba9800808af6..624bf631197e0da3881d431fc2a101a64d7fd8e3 100644 (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.
index 40342b36ba613dc296b84fbdec7152d28edcde82..f44eb65721501d60ca18d70902bac6179d190cd2 100644 (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);
   
index 05e3680ea9401a727cb1f8815bf5cf78b11c3a64..b54f0e2ca60f7fef1baf55d822a0adca2ec7688a 100644 (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.
index 08ab9a26f690d6b3ce69720de493a080be571115..fa7382c2a025de30855a51bd1b23931f888be87e 100644 (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
index d865e7c1b267c464a9b28e8b842bbb23a8982d3f..41e8a012a7a0bfe304cf376f6bfec2dd9132fef6 100644 (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());
index 32999995363053562b0519e108f6dd8c082a886c..93caad669d9676d1858152ade2895ad3f748f524 100644 (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;
index fc5c8e868db71877984c8695be87f9056c09d682..3a62c1151a067597cdfedfddd32e274be7f71766 100644 (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),