From 7b2fc9d3c97f2526528a8b686af1589054025ca0 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 23 Mar 2010 23:47:56 +0000 Subject: [PATCH] Implement computation of the final overriders for each virtual function within a class hierarchy (C++ [class.virtual]p2). We use the final-overrider computation to determine when a particular class is ill-formed because it has multiple final overriders for a given virtual function (e.g., because two virtual functions override the same virtual function in the same virtual base class). Fixes PR5973. We also use the final-overrider computation to determine which virtual member functions are pure when determining whether a class is abstract or diagnosing the improper use of an abstract class. The prior approach to determining whether there were any pure virtual functions in a class didn't cope with virtual base class subobjects properly, and could not easily be fixed to deal with the oddities of subobject hiding. Fixes PR6631. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@99351 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/CXXInheritance.h | 132 +++++++++++ include/clang/AST/DeclCXX.h | 8 +- include/clang/Basic/DiagnosticSemaKinds.td | 6 +- lib/AST/CXXInheritance.cpp | 237 +++++++++++++++++++ lib/Sema/SemaDeclCXX.cpp | 181 +++++++------- test/CXX/class.derived/class.abstract/p4.cpp | 80 +++++++ test/CXX/class.derived/class.abstract/p5.cpp | 23 ++ test/CXX/class.derived/class.virtual/p2.cpp | 37 +++ 8 files changed, 608 insertions(+), 96 deletions(-) create mode 100644 test/CXX/class.derived/class.abstract/p4.cpp create mode 100644 test/CXX/class.derived/class.abstract/p5.cpp create mode 100644 test/CXX/class.derived/class.virtual/p2.cpp diff --git a/include/clang/AST/CXXInheritance.h b/include/clang/AST/CXXInheritance.h index c89e5e4b16..edd633e8e1 100644 --- a/include/clang/AST/CXXInheritance.h +++ b/include/clang/AST/CXXInheritance.h @@ -19,6 +19,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/Type.h" #include "clang/AST/TypeOrdering.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include #include @@ -227,6 +228,137 @@ public: /// object. void swap(CXXBasePaths &Other); }; + +/// \brief Uniquely identifies a virtual method within a class +/// hierarchy by the method itself and a class subobject number. +struct UniqueVirtualMethod { + UniqueVirtualMethod() : Method(0), Subobject(0), InVirtualSubobject(0) { } + + UniqueVirtualMethod(CXXMethodDecl *Method, unsigned Subobject, + const CXXRecordDecl *InVirtualSubobject) + : Method(Method), Subobject(Subobject), + InVirtualSubobject(InVirtualSubobject) { } + + /// \brief The overriding virtual method. + CXXMethodDecl *Method; + + /// \brief The subobject in which the overriding virtual method + /// resides. + unsigned Subobject; + + /// \brief The virtual base class subobject of which this overridden + /// virtual method is a part. Note that this records the closest + /// derived virtual base class subobject. + const CXXRecordDecl *InVirtualSubobject; + + friend bool operator==(const UniqueVirtualMethod &X, + const UniqueVirtualMethod &Y) { + return X.Method == Y.Method && X.Subobject == Y.Subobject && + X.InVirtualSubobject == Y.InVirtualSubobject; + } + + friend bool operator!=(const UniqueVirtualMethod &X, + const UniqueVirtualMethod &Y) { + return !(X == Y); + } +}; + +/// \brief The set of methods that override a given virtual method in +/// each subobject where it occurs. +/// +/// The first part of the pair is the subobject in which the +/// overridden virtual function occurs, while the second part of the +/// pair is the virtual method that overrides it (including the +/// subobject in which that virtual function occurs). +class OverridingMethods { + llvm::DenseMap > + Overrides; + +public: + // Iterate over the set of subobjects that have overriding methods. + typedef llvm::DenseMap > + ::iterator iterator; + typedef llvm::DenseMap > + ::const_iterator const_iterator; + iterator begin() { return Overrides.begin(); } + const_iterator begin() const { return Overrides.begin(); } + iterator end() { return Overrides.end(); } + const_iterator end() const { return Overrides.end(); } + unsigned size() const { return Overrides.size(); } + + // Iterate over the set of overriding virtual methods in a given + // subobject. + typedef llvm::SmallVector::iterator + overriding_iterator; + typedef llvm::SmallVector::const_iterator + overriding_const_iterator; + + // Add a new overriding method for a particular subobject. + void add(unsigned OverriddenSubobject, UniqueVirtualMethod Overriding); + + // Add all of the overriding methods from "other" into overrides for + // this method. Used when merging the overrides from multiple base + // class subobjects. + void add(const OverridingMethods &Other); + + // Replace all overriding virtual methods in all subobjects with the + // given virtual method. + void replaceAll(UniqueVirtualMethod Overriding); +}; + +/// \brief A mapping from each virtual member function to its set of +/// final overriders. +/// +/// Within a class hierarchy for a given derived class, each virtual +/// member function in that hierarchy has one or more "final +/// overriders" (C++ [class.virtual]p2). A final overrider for a +/// virtual function "f" is the virtual function that will actually be +/// invoked when dispatching a call to "f" through the +/// vtable. Well-formed classes have a single final overrider for each +/// virtual function; in abstract classes, the final overrider for at +/// least one virtual function is a pure virtual function. Due to +/// multiple, virtual inheritance, it is possible for a class to have +/// more than one final overrider. Athough this is an error (per C++ +/// [class.virtual]p2), it is not considered an error here: the final +/// overrider map can represent multiple final overriders for a +/// method, and it is up to the client to determine whether they are +/// problem. For example, the following class \c D has two final +/// overriders for the virtual function \c A::f(), one in \c C and one +/// in \c D: +/// +/// \code +/// struct A { virtual void f(); }; +/// struct B : virtual A { virtual void f(); }; +/// struct C : virtual A { virtual void f(); }; +/// struct D : B, C { }; +/// \endcode +/// +/// This data structure contaings a mapping from every virtual +/// function *that does not override an existing virtual function* and +/// in every subobject where that virtual function occurs to the set +/// of virtual functions that override it. Thus, the same virtual +/// function \c A::f can actually occur in multiple subobjects of type +/// \c A due to multiple inheritance, and may be overriden by +/// different virtual functions in each, as in the following example: +/// +/// \code +/// struct A { virtual void f(); }; +/// struct B : A { virtual void f(); }; +/// struct C : A { virtual void f(); }; +/// struct D : B, C { }; +/// \endcode +/// +/// Unlike in the previous example, where the virtual functions \c +/// B::f and \c C::f both overrode \c A::f in the same subobject of +/// type \c A, in this example the two virtual functions both override +/// \c A::f but in *different* subobjects of type A. This is +/// represented by numbering the subobjects in which the overridden +/// and the overriding virtual member functions are located. Subobject +/// 0 represents the virtua base class subobject of that type, while +/// subobject numbers greater than 0 refer to non-virtual base class +/// subobjects of that type. +class CXXFinalOverriderMap + : public llvm::DenseMap { }; } // end namespace clang diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 6b04ed5410..4e273d1dae 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -33,6 +33,7 @@ class CXXDestructorDecl; class CXXMethodDecl; class CXXRecordDecl; class CXXMemberLookupCriteria; +class CXXFinalOverriderMap; class FriendDecl; /// \brief Represents any kind of function declaration, whether it is a @@ -879,7 +880,12 @@ public: static bool FindNestedNameSpecifierMember(const CXXBaseSpecifier *Specifier, CXXBasePath &Path, void *UserData); - + + /// \brief Retrieve the final overriders for each virtual member + /// function in the class hierarchy where this class is the + /// most-derived class in the class hierarchy. + void getFinalOverriders(CXXFinalOverriderMap &FinaOverriders) const; + /// viewInheritance - Renders and displays an inheritance diagram /// for this C++ class and all of its base classes (transitively) using /// GraphViz. diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index f774e6860b..c0e497a6b1 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -391,7 +391,11 @@ def err_abstract_type_in_decl : Error< "%select{return|parameter|variable|field}0 type %1 is an abstract class">; def err_allocation_of_abstract_type : Error< "allocation of an object of abstract type %0">; - + +def err_multiple_final_overriders : Error< + "virtual function %q0 has more than one final overrider in %1">; +def note_final_overrider : Note<"final overrider of %q0 in %1">; + def err_type_defined_in_type_specifier : Error< "%0 can not be defined in a type specifier">; def err_type_defined_in_result_type : Error< diff --git a/lib/AST/CXXInheritance.cpp b/lib/AST/CXXInheritance.cpp index 70f8ee4bca..a9f2230458 100644 --- a/lib/AST/CXXInheritance.cpp +++ b/lib/AST/CXXInheritance.cpp @@ -416,3 +416,240 @@ FindNestedNameSpecifierMember(const CXXBaseSpecifier *Specifier, return false; } + +void OverridingMethods::add(unsigned OverriddenSubobject, + UniqueVirtualMethod Overriding) { + llvm::SmallVector &SubobjectOverrides + = Overrides[OverriddenSubobject]; + if (std::find(SubobjectOverrides.begin(), SubobjectOverrides.end(), + Overriding) == SubobjectOverrides.end()) + SubobjectOverrides.push_back(Overriding); +} + +void OverridingMethods::add(const OverridingMethods &Other) { + for (const_iterator I = Other.begin(), IE = Other.end(); I != IE; ++I) { + for (overriding_const_iterator M = I->second.begin(), + MEnd = I->second.end(); + M != MEnd; + ++M) + add(I->first, *M); + } +} + +void OverridingMethods::replaceAll(UniqueVirtualMethod Overriding) { + for (iterator I = begin(), IEnd = end(); I != IEnd; ++I) { + I->second.clear(); + I->second.push_back(Overriding); + } +} + + +namespace { + class FinalOverriderCollector { + /// \brief The number of subobjects of a given class type that + /// occur within the class hierarchy. + llvm::DenseMap SubobjectCount; + + /// \brief Overriders for each virtual base subobject. + llvm::DenseMap VirtualOverriders; + + CXXFinalOverriderMap FinalOverriders; + + public: + ~FinalOverriderCollector(); + + void Collect(const CXXRecordDecl *RD, bool VirtualBase, + const CXXRecordDecl *InVirtualSubobject, + CXXFinalOverriderMap &Overriders); + }; +} + +void FinalOverriderCollector::Collect(const CXXRecordDecl *RD, + bool VirtualBase, + const CXXRecordDecl *InVirtualSubobject, + CXXFinalOverriderMap &Overriders) { + unsigned SubobjectNumber = 0; + if (!VirtualBase) + SubobjectNumber + = ++SubobjectCount[cast(RD->getCanonicalDecl())]; + + for (CXXRecordDecl::base_class_const_iterator Base = RD->bases_begin(), + BaseEnd = RD->bases_end(); Base != BaseEnd; ++Base) { + if (const RecordType *RT = Base->getType()->getAs()) { + const CXXRecordDecl *BaseDecl = cast(RT->getDecl()); + if (!BaseDecl->isPolymorphic()) + continue; + + if (Overriders.empty() && !Base->isVirtual()) { + // There are no other overriders of virtual member functions, + // so let the base class fill in our overriders for us. + Collect(BaseDecl, false, InVirtualSubobject, Overriders); + continue; + } + + // Collect all of the overridders from the base class subobject + // and merge them into the set of overridders for this class. + // For virtual base classes, populate or use the cached virtual + // overrides so that we do not walk the virtual base class (and + // its base classes) more than once. + CXXFinalOverriderMap ComputedBaseOverriders; + CXXFinalOverriderMap *BaseOverriders = &ComputedBaseOverriders; + if (Base->isVirtual()) { + CXXFinalOverriderMap *&MyVirtualOverriders = VirtualOverriders[BaseDecl]; + if (!MyVirtualOverriders) { + MyVirtualOverriders = new CXXFinalOverriderMap; + Collect(BaseDecl, true, BaseDecl, *MyVirtualOverriders); + } + + BaseOverriders = MyVirtualOverriders; + } else + Collect(BaseDecl, false, InVirtualSubobject, ComputedBaseOverriders); + + // Merge the overriders from this base class into our own set of + // overriders. + for (CXXFinalOverriderMap::iterator OM = BaseOverriders->begin(), + OMEnd = BaseOverriders->end(); + OM != OMEnd; + ++OM) { + const CXXMethodDecl *CanonOM + = cast(OM->first->getCanonicalDecl()); + Overriders[CanonOM].add(OM->second); + } + } + } + + for (CXXRecordDecl::method_iterator M = RD->method_begin(), + MEnd = RD->method_end(); + M != MEnd; + ++M) { + // We only care about virtual methods. + if (!M->isVirtual()) + continue; + + CXXMethodDecl *CanonM = cast(M->getCanonicalDecl()); + + if (CanonM->begin_overridden_methods() + == CanonM->end_overridden_methods()) { + // This is a new virtual function that does not override any + // other virtual function. Add it to the map of virtual + // functions for which we are tracking overridders. + + // C++ [class.virtual]p2: + // For convenience we say that any virtual function overrides itself. + Overriders[CanonM].add(SubobjectNumber, + UniqueVirtualMethod(CanonM, SubobjectNumber, + InVirtualSubobject)); + continue; + } + + // This virtual method overrides other virtual methods, so it does + // not add any new slots into the set of overriders. Instead, we + // replace entries in the set of overriders with the new + // overrider. To do so, we dig down to the original virtual + // functions using data recursion and update all of the methods it + // overrides. + typedef std::pair OverriddenMethods; + llvm::SmallVector Stack; + Stack.push_back(std::make_pair(CanonM->begin_overridden_methods(), + CanonM->end_overridden_methods())); + while (!Stack.empty()) { + OverriddenMethods OverMethods = Stack.back(); + Stack.pop_back(); + + for (; OverMethods.first != OverMethods.second; ++OverMethods.first) { + const CXXMethodDecl *CanonOM + = cast((*OverMethods.first)->getCanonicalDecl()); + if (CanonOM->begin_overridden_methods() + == CanonOM->end_overridden_methods()) { + // C++ [class.virtual]p2: + // A virtual member function C::vf of a class object S is + // a final overrider unless the most derived class (1.8) + // of which S is a base class subobject (if any) declares + // or inherits another member function that overrides vf. + // + // Treating this object like the most derived class, we + // replace any overrides from base classes with this + // overriding virtual function. + Overriders[CanonOM].replaceAll( + UniqueVirtualMethod(CanonM, SubobjectNumber, + InVirtualSubobject)); + continue; + } + + // Continue recursion to the methods that this virtual method + // overrides. + Stack.push_back(std::make_pair(CanonOM->begin_overridden_methods(), + CanonOM->end_overridden_methods())); + } + } + } +} + +FinalOverriderCollector::~FinalOverriderCollector() { + for (llvm::DenseMap::iterator + VO = VirtualOverriders.begin(), VOEnd = VirtualOverriders.end(); + VO != VOEnd; + ++VO) + delete VO->second; +} + +void +CXXRecordDecl::getFinalOverriders(CXXFinalOverriderMap &FinalOverriders) const { + FinalOverriderCollector Collector; + Collector.Collect(this, false, 0, FinalOverriders); + + // Weed out any final overriders that come from virtual base class + // subobjects that were hidden by other subobjects along any path. + // This is the final-overrider variant of C++ [class.member.lookup]p10. + for (CXXFinalOverriderMap::iterator OM = FinalOverriders.begin(), + OMEnd = FinalOverriders.end(); + OM != OMEnd; + ++OM) { + for (OverridingMethods::iterator SO = OM->second.begin(), + SOEnd = OM->second.end(); + SO != SOEnd; + ++SO) { + llvm::SmallVector &Overriding = SO->second; + if (Overriding.size() < 2) + continue; + + for (llvm::SmallVector::iterator + Pos = Overriding.begin(), PosEnd = Overriding.end(); + Pos != PosEnd; + /* increment in loop */) { + if (!Pos->InVirtualSubobject) { + ++Pos; + continue; + } + + // We have an overriding method in a virtual base class + // subobject (or non-virtual base class subobject thereof); + // determine whether there exists an other overriding method + // in a base class subobject that hides the virtual base class + // subobject. + bool Hidden = false; + for (llvm::SmallVector::iterator + OP = Overriding.begin(), OPEnd = Overriding.end(); + OP != OPEnd && !Hidden; + ++OP) { + if (Pos == OP) + continue; + + if (OP->Method->getParent()->isVirtuallyDerivedFrom( + const_cast(Pos->InVirtualSubobject))) + Hidden = true; + } + + if (Hidden) { + // The current overriding function is hidden by another + // overriding function; remove this one. + Pos = Overriding.erase(Pos); + PosEnd = Overriding.end(); + } else { + ++Pos; + } + } + } + } +} diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 1522d6399a..3e97b6bf71 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1932,87 +1932,6 @@ void Sema::ActOnDefaultCtorInitializers(DeclPtrTy CDtorDecl) { SetBaseOrMemberInitializers(Constructor, 0, 0, false, false); } -namespace { - /// PureVirtualMethodCollector - traverses a class and its superclasses - /// and determines if it has any pure virtual methods. - class PureVirtualMethodCollector { - ASTContext &Context; - - public: - typedef llvm::SmallVector MethodList; - - private: - MethodList Methods; - - void Collect(const CXXRecordDecl* RD, MethodList& Methods); - - public: - PureVirtualMethodCollector(ASTContext &Ctx, const CXXRecordDecl* RD) - : Context(Ctx) { - - MethodList List; - Collect(RD, List); - - // Copy the temporary list to methods, and make sure to ignore any - // null entries. - for (size_t i = 0, e = List.size(); i != e; ++i) { - if (List[i]) - Methods.push_back(List[i]); - } - } - - bool empty() const { return Methods.empty(); } - - MethodList::const_iterator methods_begin() { return Methods.begin(); } - MethodList::const_iterator methods_end() { return Methods.end(); } - }; - - void PureVirtualMethodCollector::Collect(const CXXRecordDecl* RD, - MethodList& Methods) { - // First, collect the pure virtual methods for the base classes. - for (CXXRecordDecl::base_class_const_iterator Base = RD->bases_begin(), - BaseEnd = RD->bases_end(); Base != BaseEnd; ++Base) { - if (const RecordType *RT = Base->getType()->getAs()) { - const CXXRecordDecl *BaseDecl = cast(RT->getDecl()); - if (BaseDecl && BaseDecl->isAbstract()) - Collect(BaseDecl, Methods); - } - } - - // Next, zero out any pure virtual methods that this class overrides. - typedef llvm::SmallPtrSet MethodSetTy; - - MethodSetTy OverriddenMethods; - size_t MethodsSize = Methods.size(); - - for (RecordDecl::decl_iterator i = RD->decls_begin(), e = RD->decls_end(); - i != e; ++i) { - // Traverse the record, looking for methods. - if (CXXMethodDecl *MD = dyn_cast(*i)) { - // If the method is pure virtual, add it to the methods vector. - if (MD->isPure()) - Methods.push_back(MD); - - // Record all the overridden methods in our set. - for (CXXMethodDecl::method_iterator I = MD->begin_overridden_methods(), - E = MD->end_overridden_methods(); I != E; ++I) { - // Keep track of the overridden methods. - OverriddenMethods.insert(*I); - } - } - } - - // Now go through the methods and zero out all the ones we know are - // overridden. - for (size_t i = 0, e = MethodsSize; i != e; ++i) { - if (OverriddenMethods.count(Methods[i])) - Methods[i] = 0; - } - - } -} - - bool Sema::RequireNonAbstractType(SourceLocation Loc, QualType T, unsigned DiagID, AbstractDiagSelID SelID, const CXXRecordDecl *CurrentRD) { @@ -2066,14 +1985,32 @@ bool Sema::RequireNonAbstractType(SourceLocation Loc, QualType T, if (PureVirtualClassDiagSet && PureVirtualClassDiagSet->count(RD)) return true; - PureVirtualMethodCollector Collector(Context, RD); + CXXFinalOverriderMap FinalOverriders; + RD->getFinalOverriders(FinalOverriders); - for (PureVirtualMethodCollector::MethodList::const_iterator I = - Collector.methods_begin(), E = Collector.methods_end(); I != E; ++I) { - const CXXMethodDecl *MD = *I; + for (CXXFinalOverriderMap::iterator M = FinalOverriders.begin(), + MEnd = FinalOverriders.end(); + M != MEnd; + ++M) { + for (OverridingMethods::iterator SO = M->second.begin(), + SOEnd = M->second.end(); + SO != SOEnd; ++SO) { + // C++ [class.abstract]p4: + // A class is abstract if it contains or inherits at least one + // pure virtual function for which the final overrider is pure + // virtual. - Diag(MD->getLocation(), diag::note_pure_virtual_function) << - MD->getDeclName(); + // + if (SO->second.size() != 1) + continue; + + if (!SO->second.front().Method->isPure()) + continue; + + Diag(SO->second.front().Method->getLocation(), + diag::note_pure_virtual_function) + << SO->second.front().Method->getDeclName(); + } } if (!PureVirtualClassDiagSet) @@ -2162,15 +2099,71 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { for (UnresolvedSetIterator I = Convs->begin(), E = Convs->end(); I != E; ++I) Convs->setAccess(I, (*I)->getAccess()); - if (!Record->isAbstract()) { - // Collect all the pure virtual methods and see if this is an abstract - // class after all. - PureVirtualMethodCollector Collector(Context, Record); - if (!Collector.empty()) - Record->setAbstract(true); + // Determine whether we need to check for final overriders. We do + // this either when there are virtual base classes (in which case we + // may end up finding multiple final overriders for a given virtual + // function) or any of the base classes is abstract (in which case + // we might detect that this class is abstract). + bool CheckFinalOverriders = false; + if (Record->isPolymorphic() && !Record->isInvalidDecl() && + !Record->isDependentType()) { + if (Record->getNumVBases()) + CheckFinalOverriders = true; + else if (!Record->isAbstract()) { + for (CXXRecordDecl::base_class_const_iterator B = Record->bases_begin(), + BEnd = Record->bases_end(); + B != BEnd; ++B) { + CXXRecordDecl *BaseDecl + = cast(B->getType()->getAs()->getDecl()); + if (BaseDecl->isAbstract()) { + CheckFinalOverriders = true; + break; + } + } + } } - if (Record->isAbstract()) + if (CheckFinalOverriders) { + CXXFinalOverriderMap FinalOverriders; + Record->getFinalOverriders(FinalOverriders); + + for (CXXFinalOverriderMap::iterator M = FinalOverriders.begin(), + MEnd = FinalOverriders.end(); + M != MEnd; ++M) { + for (OverridingMethods::iterator SO = M->second.begin(), + SOEnd = M->second.end(); + SO != SOEnd; ++SO) { + assert(SO->second.size() > 0 && + "All virtual functions have overridding virtual functions"); + if (SO->second.size() == 1) { + // C++ [class.abstract]p4: + // A class is abstract if it contains or inherits at least one + // pure virtual function for which the final overrider is pure + // virtual. + if (SO->second.front().Method->isPure()) + Record->setAbstract(true); + continue; + } + + // C++ [class.virtual]p2: + // In a derived class, if a virtual member function of a base + // class subobject has more than one final overrider the + // program is ill-formed. + Diag(Record->getLocation(), diag::err_multiple_final_overriders) + << (NamedDecl *)M->first << Record; + Diag(M->first->getLocation(), diag::note_overridden_virtual_function); + for (OverridingMethods::overriding_iterator OM = SO->second.begin(), + OMEnd = SO->second.end(); + OM != OMEnd; ++OM) + Diag(OM->Method->getLocation(), diag::note_final_overrider) + << (NamedDecl *)M->first << OM->Method->getParent(); + + Record->setInvalidDecl(); + } + } + } + + if (Record->isAbstract() && !Record->isInvalidDecl()) (void)AbstractClassUsageDiagnoser(*this, Record); } diff --git a/test/CXX/class.derived/class.abstract/p4.cpp b/test/CXX/class.derived/class.abstract/p4.cpp new file mode 100644 index 0000000000..ca99bf7f16 --- /dev/null +++ b/test/CXX/class.derived/class.abstract/p4.cpp @@ -0,0 +1,80 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +namespace PR6631 { + struct A { + virtual void f() = 0; + }; + + struct B : virtual A { }; + + struct C : virtual A { + virtual void f(); + }; + + struct D : public B, public C { + virtual void f(); + }; + + void f() { + (void)new D; // okay + } +} + +// Check cases where we have a virtual function that is pure in one +// subobject but not pure in another subobject. +namespace PartlyPure { + struct A { + virtual void f() = 0; // expected-note{{pure virtual function}} + }; + + struct B : A { + virtual void f(); + }; + + struct C : virtual A { }; + + struct D : B, C { }; + + void f() { + (void) new D; // expected-error{{abstract type}} + } +} + +namespace NonPureAlongOnePath { + struct A { + virtual void f() = 0; + }; + + struct B : virtual A { + virtual void f(); + }; + + struct C : virtual A { }; + + struct D : B, C { }; + + void f() { + (void) new D; // okay + } +} + +namespace NonPureAlongOnePath2 { + struct Aprime { + virtual void f() = 0; + }; + + struct A : Aprime { + }; + + struct B : virtual A { + virtual void f(); + }; + + struct C : virtual A { }; + + struct D : B, C { }; + + void f() { + (void) new D; // okay + } +} diff --git a/test/CXX/class.derived/class.abstract/p5.cpp b/test/CXX/class.derived/class.abstract/p5.cpp new file mode 100644 index 0000000000..207519d17e --- /dev/null +++ b/test/CXX/class.derived/class.abstract/p5.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +struct A { + virtual void f() = 0; // expected-note{{pure virtual function}} +}; + +struct B : A { + virtual void f(); +}; + +struct C : B { + virtual void f() = 0; // expected-note 2{{pure virtual function}} +}; + +struct D : C { +}; + +void test() { + (void)new A; // expected-error{{object of abstract type}} + (void)new B; + (void)new C; // expected-error{{object of abstract type}} + (void)new D; // expected-error{{object of abstract type}} +} diff --git a/test/CXX/class.derived/class.virtual/p2.cpp b/test/CXX/class.derived/class.virtual/p2.cpp new file mode 100644 index 0000000000..64d93c8836 --- /dev/null +++ b/test/CXX/class.derived/class.virtual/p2.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +struct A { + virtual void f() = 0; // expected-note 2{{overridden virtual function}} +}; + +struct Aprime : virtual A { + virtual void f(); +}; + +struct B : Aprime { + virtual void f(); // expected-note 3{{final overrider of 'A::f'}} +}; + +struct C : virtual A { + virtual void f(); // expected-note{{final overrider of 'A::f'}} +}; + +struct D : B, C { }; // expected-error{{virtual function 'A::f' has more than one final overrider in 'D'}} + +struct B2 : B { }; + +struct E : B, B2 { }; //expected-error{{virtual function 'A::f' has more than one final overrider in 'E'}} + +struct F : B, B2 { + virtual void f(); // okay +}; + +struct G : F { }; // okay + +struct H : G, A { }; // okay + +namespace MultipleSubobjects { + struct A { virtual void f(); }; + struct B : A { virtual void f(); }; + struct C : A { virtual void f(); }; + struct D : B, C { }; // okay +}