diff options
| author | Douglas Gregor <dgregor@apple.com> | 2010-07-02 21:50:04 +0000 | 
|---|---|---|
| committer | Douglas Gregor <dgregor@apple.com> | 2010-07-02 21:50:04 +0000 | 
| commit | 330b9cff74d26ce9bc83e5f97fcb6e953bf33008 (patch) | |
| tree | f6eb821b8a07b9c67dac21cfa667139b6a1b9942 /clang | |
| parent | 3b1657b7776c75796a66d05d083e23ad0c9dec36 (diff) | |
| download | bcm5719-llvm-330b9cff74d26ce9bc83e5f97fcb6e953bf33008.tar.gz bcm5719-llvm-330b9cff74d26ce9bc83e5f97fcb6e953bf33008.zip  | |
Lazily declare copy-assignment operators.
llvm-svn: 107521
Diffstat (limited to 'clang')
| -rw-r--r-- | clang/include/clang/AST/ASTContext.h | 7 | ||||
| -rw-r--r-- | clang/include/clang/AST/DeclCXX.h | 25 | ||||
| -rw-r--r-- | clang/lib/AST/ASTContext.cpp | 5 | ||||
| -rw-r--r-- | clang/lib/AST/DeclCXX.cpp | 52 | ||||
| -rw-r--r-- | clang/lib/Frontend/PCHReaderDecl.cpp | 1 | ||||
| -rw-r--r-- | clang/lib/Frontend/PCHWriterDecl.cpp | 1 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 95 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaLookup.cpp | 80 | 
8 files changed, 181 insertions, 85 deletions
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index f109d481592..5e3f891e486 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -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; diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index f67faa86ec8..624bf631197 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -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. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 40342b36ba6..f44eb657215 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/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/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 05e3680ea94..b54f0e2ca60 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/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/clang/lib/Frontend/PCHReaderDecl.cpp b/clang/lib/Frontend/PCHReaderDecl.cpp index 08ab9a26f69..fa7382c2a02 100644 --- a/clang/lib/Frontend/PCHReaderDecl.cpp +++ b/clang/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/clang/lib/Frontend/PCHWriterDecl.cpp b/clang/lib/Frontend/PCHWriterDecl.cpp index d865e7c1b26..41e8a012a7a 100644 --- a/clang/lib/Frontend/PCHWriterDecl.cpp +++ b/clang/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/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 32999995363..93caad669d9 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/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/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index fc5c8e868db..3a62c1151a0 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/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),  | 

