diff options
| author | Douglas Gregor <dgregor@apple.com> | 2010-07-01 17:48:08 +0000 | 
|---|---|---|
| committer | Douglas Gregor <dgregor@apple.com> | 2010-07-01 17:48:08 +0000 | 
| commit | 68e1136585a4ffc2187c68f3a9a88115bd0ce0ae (patch) | |
| tree | 5c6ca43f81aff97e73082ac7405bd4d60228aece | |
| parent | 5e88700f284ab6ac3a05033a65b801a509662e51 (diff) | |
| download | bcm5719-llvm-68e1136585a4ffc2187c68f3a9a88115bd0ce0ae.tar.gz bcm5719-llvm-68e1136585a4ffc2187c68f3a9a88115bd0ce0ae.zip  | |
Provide an exception-specification for an implicitly-declared
copy-assignment operator.
llvm-svn: 107406
| -rw-r--r-- | clang/include/clang/AST/DeclCXX.h | 12 | ||||
| -rw-r--r-- | clang/include/clang/AST/Type.h | 8 | ||||
| -rw-r--r-- | clang/lib/AST/DeclCXX.cpp | 73 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 33 | ||||
| -rw-r--r-- | clang/test/CXX/except/except.spec/p14.cpp | 17 | 
5 files changed, 141 insertions, 2 deletions
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 1fbed63b719..78f8cb9f0cb 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -537,6 +537,18 @@ public:    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  +  /// class, using a simplistic form of overload resolution. +  /// +  /// \param ArgIsConst Whether the argument to the copy-assignment operator +  /// is const-qualified. +  /// +  /// \returns The copy-assignment operator that can be invoked, or NULL if +  /// a unique copy-assignment operator could not be found. +  CXXMethodDecl *getCopyAssignmentOperator(bool ArgIsConst) const; +      /// addedConstructor - Notify the class that another constructor has    /// been added. This routine helps maintain information about the    /// class based on which constructors have been added. diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index b1ca6a2e73a..e540f945b1f 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -271,6 +271,8 @@ public:      }    } +  bool isSupersetOf(Qualifiers Other) const; +    bool operator==(Qualifiers Other) const { return Mask == Other.Mask; }    bool operator!=(Qualifiers Other) const { return Mask != Other.Mask; } @@ -3369,6 +3371,12 @@ inline FunctionType::ExtInfo getFunctionExtInfo(QualType t) {    return getFunctionExtInfo(*t);  } +/// \brief Determine whether this set of qualifiers is a superset of the given  +/// set of qualifiers. +inline bool Qualifiers::isSupersetOf(Qualifiers Other) const { +  return Mask != Other.Mask && (Mask | Other.Mask) == Mask; +} +  /// isMoreQualifiedThan - Determine whether this type is more  /// qualified than the Other type. For example, "const volatile int"  /// is more qualified than "const int", "volatile int", and diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 71b5835c128..ab8a20994ee 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -232,6 +232,79 @@ bool CXXRecordDecl::hasConstCopyAssignment(ASTContext &Context,    return false;  } +/// \brief Perform a simplistic form of overload resolution that only considers +/// cv-qualifiers on a single parameter, and return the best overload candidate +/// (if there is one). +static CXXMethodDecl * +GetBestOverloadCandidateSimple( +  const llvm::SmallVectorImpl<std::pair<CXXMethodDecl *, Qualifiers> > &Cands) { +  if (Cands.empty()) +    return 0; +  if (Cands.size() == 1) +    return Cands[0].first; +   +  unsigned Best = 0, N = Cands.size(); +  for (unsigned I = 1; I != N; ++I) +    if (Cands[Best].second.isSupersetOf(Cands[I].second)) +      Best = I; +   +  for (unsigned I = 1; I != N; ++I) +    if (Cands[Best].second.isSupersetOf(Cands[I].second)) +      return 0; +   +  return Cands[Best].first; +} + +CXXMethodDecl *CXXRecordDecl::getCopyAssignmentOperator(bool ArgIsConst) const { +  ASTContext &Context = getASTContext(); +  QualType Class = Context.getTypeDeclType(const_cast<CXXRecordDecl *>(this)); +  DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal); +   +  llvm::SmallVector<std::pair<CXXMethodDecl *, Qualifiers>, 4> Found; +  DeclContext::lookup_const_iterator Op, OpEnd; +  for (llvm::tie(Op, OpEnd) = this->lookup(Name); 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 || Method->isStatic() || 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; +     +    QualType ArgType = FnType->getArgType(0); +    Qualifiers Quals; +    if (const LValueReferenceType *Ref = ArgType->getAs<LValueReferenceType>()) { +      ArgType = Ref->getPointeeType(); +      // If we have a const argument and we have a reference to a non-const, +      // this function does not match. +      if (ArgIsConst && !ArgType.isConstQualified()) +        continue; +       +      Quals = ArgType.getQualifiers(); +    } else { +      // By-value copy-assignment operators are treated like const X& +      // copy-assignment operators. +      Quals = Qualifiers::fromCVRMask(Qualifiers::Const); +    } +     +    if (!Context.hasSameUnqualifiedType(ArgType, Class)) +      continue; + +    // Save this copy-assignment operator. It might be "the one". +    Found.push_back(std::make_pair(const_cast<CXXMethodDecl *>(Method), Quals)); +  } +   +  // Use a simplistic form of overload resolution to find the candidate. +  return GetBestOverloadCandidateSimple(Found); +} +  void  CXXRecordDecl::addedConstructor(ASTContext &Context,                                  CXXConstructorDecl *ConDecl) { diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 130aa576b67..9d18e177fd5 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -4626,6 +4626,33 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(Scope *S,      ArgType = ArgType.withConst();    ArgType = Context.getLValueReferenceType(ArgType); +  // C++ [except.spec]p14: +  //   An implicitly declared special member function (Clause 12) shall have an  +  //   exception-specification. [...] +  ImplicitExceptionSpecification ExceptSpec(Context); +  for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), +                                       BaseEnd = ClassDecl->bases_end(); +       Base != BaseEnd; ++Base) { +    const CXXRecordDecl *BaseClassDecl +      = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl()); +    if (CXXMethodDecl *CopyAssign +           = BaseClassDecl->getCopyAssignmentOperator(HasConstCopyAssignment)) +      ExceptSpec.CalledDecl(CopyAssign); +  } +  for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(), +                                  FieldEnd = ClassDecl->field_end(); +       Field != FieldEnd; +       ++Field) { +    QualType FieldType = Context.getBaseElementType((*Field)->getType()); +    if (const RecordType *FieldClassType = FieldType->getAs<RecordType>()) { +      const CXXRecordDecl *FieldClassDecl +        = cast<CXXRecordDecl>(FieldClassType->getDecl()); +      if (CXXMethodDecl *CopyAssign +            = FieldClassDecl->getCopyAssignmentOperator(HasConstCopyAssignment)) +        ExceptSpec.CalledDecl(CopyAssign);       +    }       +  } +      //   An implicitly-declared copy assignment operator is an inline public    //   member of its class.    DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal); @@ -4633,8 +4660,10 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(Scope *S,      = CXXMethodDecl::Create(Context, ClassDecl, ClassDecl->getLocation(), Name,                              Context.getFunctionType(RetType, &ArgType, 1,                                                      false, 0, -                                              /*FIXME: hasExceptionSpec*/false, -                                                    false, 0, 0, +                                         ExceptSpec.hasExceptionSpecification(), +                                      ExceptSpec.hasAnyExceptionSpecification(), +                                                    ExceptSpec.size(), +                                                    ExceptSpec.data(),                                                      FunctionType::ExtInfo()),                              /*TInfo=*/0, /*isStatic=*/false,                              /*StorageClassAsWritten=*/FunctionDecl::None, diff --git a/clang/test/CXX/except/except.spec/p14.cpp b/clang/test/CXX/except/except.spec/p14.cpp index 74284e51e57..9450b1cf80d 100644 --- a/clang/test/CXX/except/except.spec/p14.cpp +++ b/clang/test/CXX/except/except.spec/p14.cpp @@ -1,7 +1,9 @@  // RUN: %clang_cc1 -fexceptions -verify %s  struct A { };  struct B { }; +struct C { }; +// Destructor  struct X0 {     virtual ~X0() throw(A); // expected-note{{overridden virtual function is here}}   }; @@ -10,3 +12,18 @@ struct X1 {  };  struct X2 : public X0, public X1 { }; // expected-error 2{{exception specification of overriding function is more lax than base version}} +// Copy-assignment operator. +struct CA0 { +  CA0 &operator=(const CA0&) throw(A); +}; +struct CA1 { +  CA1 &operator=(const CA1&) throw(B); +}; +struct CA2 : CA0, CA1 { }; + +void test_CA() { +  CA2 &(CA2::*captr1)(const CA2&) throw(A, B) = &CA2::operator=; +  CA2 &(CA2::*captr2)(const CA2&) throw(A, B, C) = &CA2::operator=; +  CA2 &(CA2::*captr3)(const CA2&) throw(A) = &CA2::operator=; // expected-error{{target exception specification is not superset of source}} +  CA2 &(CA2::*captr4)(const CA2&) throw(B) = &CA2::operator=; // expected-error{{target exception specification is not superset of source}} +}  | 

