[cfe-commits] r107406 - in /cfe/trunk: include/clang/AST/DeclCXX.h include/clang/AST/Type.h lib/AST/DeclCXX.cpp lib/Sema/SemaDeclCXX.cpp test/CXX/except/except.spec/p14.cpp

Douglas Gregor dgregor at apple.com
Thu Jul 1 10:48:08 PDT 2010


Author: dgregor
Date: Thu Jul  1 12:48:08 2010
New Revision: 107406

URL: http://llvm.org/viewvc/llvm-project?rev=107406&view=rev
Log:
Provide an exception-specification for an implicitly-declared
copy-assignment operator.

Modified:
    cfe/trunk/include/clang/AST/DeclCXX.h
    cfe/trunk/include/clang/AST/Type.h
    cfe/trunk/lib/AST/DeclCXX.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/test/CXX/except/except.spec/p14.cpp

Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=107406&r1=107405&r2=107406&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Thu Jul  1 12:48:08 2010
@@ -537,6 +537,18 @@
   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.

Modified: cfe/trunk/include/clang/AST/Type.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Type.h?rev=107406&r1=107405&r2=107406&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Type.h (original)
+++ cfe/trunk/include/clang/AST/Type.h Thu Jul  1 12:48:08 2010
@@ -271,6 +271,8 @@
     }
   }
 
+  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 @@
   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

Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=107406&r1=107405&r2=107406&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Thu Jul  1 12:48:08 2010
@@ -232,6 +232,79 @@
   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) {

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=107406&r1=107405&r2=107406&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Thu Jul  1 12:48:08 2010
@@ -4626,6 +4626,33 @@
     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::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,

Modified: cfe/trunk/test/CXX/except/except.spec/p14.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/except/except.spec/p14.cpp?rev=107406&r1=107405&r2=107406&view=diff
==============================================================================
--- cfe/trunk/test/CXX/except/except.spec/p14.cpp (original)
+++ cfe/trunk/test/CXX/except/except.spec/p14.cpp Thu Jul  1 12:48:08 2010
@@ -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 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}}
+}





More information about the cfe-commits mailing list