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
This commit is contained in:
Douglas Gregor 2010-03-23 23:47:56 +00:00
Родитель a2a87c975a
Коммит 7b2fc9d3c9
8 изменённых файлов: 608 добавлений и 96 удалений

Просмотреть файл

@ -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 <list>
#include <map>
@ -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<unsigned, llvm::SmallVector<UniqueVirtualMethod, 4> >
Overrides;
public:
// Iterate over the set of subobjects that have overriding methods.
typedef llvm::DenseMap<unsigned, llvm::SmallVector<UniqueVirtualMethod, 4> >
::iterator iterator;
typedef llvm::DenseMap<unsigned, llvm::SmallVector<UniqueVirtualMethod, 4> >
::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<UniqueVirtualMethod, 4>::iterator
overriding_iterator;
typedef llvm::SmallVector<UniqueVirtualMethod, 4>::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<const CXXMethodDecl *, OverridingMethods> { };
} // end namespace clang

Просмотреть файл

@ -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.

Просмотреть файл

@ -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<

Просмотреть файл

@ -416,3 +416,240 @@ FindNestedNameSpecifierMember(const CXXBaseSpecifier *Specifier,
return false;
}
void OverridingMethods::add(unsigned OverriddenSubobject,
UniqueVirtualMethod Overriding) {
llvm::SmallVector<UniqueVirtualMethod, 4> &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<const CXXRecordDecl *, unsigned> SubobjectCount;
/// \brief Overriders for each virtual base subobject.
llvm::DenseMap<const CXXRecordDecl *, CXXFinalOverriderMap *> 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<CXXRecordDecl>(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<RecordType>()) {
const CXXRecordDecl *BaseDecl = cast<CXXRecordDecl>(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<CXXMethodDecl>(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<CXXMethodDecl>(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<CXXMethodDecl::method_iterator,
CXXMethodDecl::method_iterator> OverriddenMethods;
llvm::SmallVector<OverriddenMethods, 4> 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<CXXMethodDecl>((*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<const CXXRecordDecl *, CXXFinalOverriderMap *>::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<UniqueVirtualMethod, 4> &Overriding = SO->second;
if (Overriding.size() < 2)
continue;
for (llvm::SmallVector<UniqueVirtualMethod, 4>::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<UniqueVirtualMethod, 4>::iterator
OP = Overriding.begin(), OPEnd = Overriding.end();
OP != OPEnd && !Hidden;
++OP) {
if (Pos == OP)
continue;
if (OP->Method->getParent()->isVirtuallyDerivedFrom(
const_cast<CXXRecordDecl *>(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;
}
}
}
}
}

Просмотреть файл

@ -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<const CXXMethodDecl*, 8> 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<RecordType>()) {
const CXXRecordDecl *BaseDecl = cast<CXXRecordDecl>(RT->getDecl());
if (BaseDecl && BaseDecl->isAbstract())
Collect(BaseDecl, Methods);
}
}
// Next, zero out any pure virtual methods that this class overrides.
typedef llvm::SmallPtrSet<const CXXMethodDecl*, 4> 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<CXXMethodDecl>(*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<CXXRecordDecl>(B->getType()->getAs<RecordType>()->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);
}

Просмотреть файл

@ -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
}
}

Просмотреть файл

@ -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}}
}

Просмотреть файл

@ -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
}