diff options
-rw-r--r-- | include/clang/AST/DeclCXX.h | 12 | ||||
-rw-r--r-- | include/clang/AST/Type.h | 8 | ||||
-rw-r--r-- | lib/AST/DeclCXX.cpp | 73 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 33 | ||||
-rw-r--r-- | test/CXX/except/except.spec/p14.cpp | 17 |
5 files changed, 141 insertions, 2 deletions
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 1fbed63b71..78f8cb9f0c 100644 --- a/include/clang/AST/DeclCXX.h +++ b/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/include/clang/AST/Type.h b/include/clang/AST/Type.h index b1ca6a2e73..e540f945b1 100644 --- a/include/clang/AST/Type.h +++ b/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/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 71b5835c12..ab8a20994e 100644 --- a/lib/AST/DeclCXX.cpp +++ b/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/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 130aa576b6..9d18e177fd 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/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/test/CXX/except/except.spec/p14.cpp b/test/CXX/except/except.spec/p14.cpp index 74284e51e5..9450b1cf80 100644 --- a/test/CXX/except/except.spec/p14.cpp +++ b/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}} +} |