[cfe-commits] r107521 - in /cfe/trunk: 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

Douglas Gregor dgregor at apple.com
Fri Jul 2 14:50:04 PDT 2010


Author: dgregor
Date: Fri Jul  2 16:50:04 2010
New Revision: 107521

URL: http://llvm.org/viewvc/llvm-project?rev=107521&view=rev
Log:
Lazily declare copy-assignment operators.

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

Modified: cfe/trunk/include/clang/AST/ASTContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=107521&r1=107520&r2=107521&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ASTContext.h (original)
+++ cfe/trunk/include/clang/AST/ASTContext.h Fri Jul  2 16:50:04 2010
@@ -1327,6 +1327,13 @@
   //                    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;
   

Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=107521&r1=107520&r2=107521&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Fri Jul  2 16:50:04 2010
@@ -319,6 +319,9 @@
     /// 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 @@
   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 @@
 
   /// 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 @@
     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.

Modified: cfe/trunk/lib/AST/ASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=107521&r1=107520&r2=107521&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Fri Jul  2 16:50:04 2010
@@ -31,6 +31,8 @@
 
 using namespace clang;
 
+unsigned ASTContext::NumImplicitCopyAssignmentOperators;
+unsigned ASTContext::NumImplicitCopyAssignmentOperatorsDeclared;
 unsigned ASTContext::NumImplicitDestructors;
 unsigned ASTContext::NumImplicitDestructorsDeclared;
 
@@ -257,6 +259,9 @@
   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);
   

Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=107521&r1=107520&r2=107521&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Fri Jul  2 16:50:04 2010
@@ -32,7 +32,7 @@
     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 @@
                                         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 @@
 
   // 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.

Modified: cfe/trunk/lib/Frontend/PCHReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PCHReaderDecl.cpp?rev=107521&r1=107520&r2=107521&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/PCHReaderDecl.cpp (original)
+++ cfe/trunk/lib/Frontend/PCHReaderDecl.cpp Fri Jul  2 16:50:04 2010
@@ -668,6 +668,7 @@
       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

Modified: cfe/trunk/lib/Frontend/PCHWriterDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PCHWriterDecl.cpp?rev=107521&r1=107520&r2=107521&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/PCHWriterDecl.cpp (original)
+++ cfe/trunk/lib/Frontend/PCHWriterDecl.cpp Fri Jul  2 16:50:04 2010
@@ -664,6 +664,7 @@
       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());

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=107521&r1=107520&r2=107521&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Fri Jul  2 16:50:04 2010
@@ -2659,8 +2659,16 @@
   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 @@
                         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 @@
            "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 @@
     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 @@
   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 @@
        ++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 @@
                                                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;

Modified: cfe/trunk/lib/Sema/SemaLookup.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLookup.cpp?rev=107521&r1=107520&r2=107521&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLookup.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLookup.cpp Fri Jul  2 16:50:04 2010
@@ -464,12 +464,65 @@
 }
 
 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 @@
   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 @@
 
   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),





More information about the cfe-commits mailing list