From: Anders Carlsson Date: Sun, 22 Mar 2009 01:52:17 +0000 (+0000) Subject: Keep track of whether a class is abstract or not. This is currently only used for... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=67e4dd2e9936d828d68b20e01922b6442c6ce31b;p=clang Keep track of whether a class is abstract or not. This is currently only used for the __is_abstract type trait. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@67461 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index a5ead71305..b1c4feedcb 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -629,7 +629,7 @@ public: /// Whether this virtual function is pure, i.e. makes the containing class /// abstract. - bool isPure() { return IsPure; } + bool isPure() const { return IsPure; } void setPure() { IsPure = true; } /// \brief Whether this function has a prototype, either because one diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 740d37f30b..c689b77632 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -219,6 +219,10 @@ class CXXRecordDecl : public RecordDecl { /// virtual member or derives from a polymorphic class. bool Polymorphic : 1; + /// Abstract - True when this class is abstract, i.e. has at least one + /// pure virtual function, (that can come from a base class). + bool Abstract : 1; + /// Bases - Base classes of this class. /// FIXME: This is wasted space for a union. CXXBaseSpecifier *Bases; @@ -352,6 +356,13 @@ public: /// [class.virtual]). void setPolymorphic(bool Poly) { Polymorphic = Poly; } + /// isAbstract - Whether this class is abstract (C++ [class.abstract]), + /// which means that the class contains or inherits a pure virtual function. + bool isAbstract() const { return Abstract; } + + /// setAbstract - Set whether this class is abstract (C++ [class.abstract]) + void setAbstract(bool Abs) { Abstract = Abs; } + /// viewInheritance - Renders and displays an inheritance diagram /// for this C++ class and all of its base classes (transitively) using /// GraphViz. diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 5e9b0cd9f4..33e870bc0d 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -27,8 +27,8 @@ CXXRecordDecl::CXXRecordDecl(Kind K, TagKind TK, DeclContext *DC, : RecordDecl(K, TK, DC, L, Id), UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false), UserDeclaredCopyAssignment(false), UserDeclaredDestructor(false), - Aggregate(true), PlainOldData(true), Polymorphic(false), Bases(0), - NumBases(0), Conversions(DC, DeclarationName()) { } + Aggregate(true), PlainOldData(true), Polymorphic(false), Abstract(false), + Bases(0), NumBases(0), Conversions(DC, DeclarationName()) { } CXXRecordDecl *CXXRecordDecl::Create(ASTContext &C, TagKind TK, DeclContext *DC, SourceLocation L, IdentifierInfo *Id, diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index 53be84b20d..5a9de19254 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -214,6 +214,10 @@ bool UnaryTypeTraitExpr::EvaluateTrait() const { return cast(Record->getDecl())->isPolymorphic(); } return false; + case UTT_IsAbstract: + if (const RecordType *RT = QueriedType->getAsRecordType()) + return cast(RT->getDecl())->isAbstract(); + return false; } } diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index f2892c1367..b140046fe4 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -768,6 +768,7 @@ Parser::OwningExprResult Parser::ParseCastExpression(bool isUnaryExpression, case tok::kw___is_enum: case tok::kw___is_union: case tok::kw___is_polymorphic: + case tok::kw___is_abstract: return ParseUnaryTypeTrait(); case tok::at: { diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 7c80fa553e..7b45d5232f 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2224,9 +2224,12 @@ void Sema::AddInitializerToDecl(DeclTy *dcl, ExprArg init, bool DirectInit) { Expr *Init = static_cast(init.get()); if ((IL = dyn_cast(Init)) && IL->getValue() == 0 && Context.getCanonicalType(IL->getType()) == Context.IntTy) { - if (Method->isVirtual()) + if (Method->isVirtual()) { Method->setPure(); - else { + + // A class is abstract if at least one function is pure virtual. + cast(CurContext)->setAbstract(true); + } else { Diag(Method->getLocation(), diag::err_non_virtual_pure) << Method->getDeclName() << Init->getSourceRange(); Method->setInvalidDecl(); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 10e7bd4c97..b18b6c8f84 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -705,6 +705,77 @@ Sema::ActOnMemInitializer(DeclTy *ConstructorD, return new CXXBaseOrMemberInitializer(BaseType, (Expr **)Args, NumArgs); } +namespace { + /// PureVirtualMethodCollector - traverses a class and its superclasses + /// and determines if it has any pure virtual methods. + class VISIBILITY_HIDDEN PureVirtualMethodCollector { + ASTContext &Context; + + typedef llvm::SmallVector MethodList; + 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(); } + }; + + 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()->getAsRecordType()) { + const CXXRecordDecl *BaseDecl + = cast(RT->getDecl()); + if (BaseDecl && BaseDecl->isAbstract()) + Collect(BaseDecl, Methods); + } + } + + // Next, zero out any pure virtual methods that this class overrides. + for (size_t i = 0, e = Methods.size(); i != e; ++i) { + const CXXMethodDecl *VMD = dyn_cast_or_null(Methods[i]); + if (!VMD) + continue; + + DeclContext::lookup_const_iterator I, E; + for (llvm::tie(I, E) = RD->lookup(VMD->getDeclName()); I != E; ++I) { + if (const CXXMethodDecl *MD = dyn_cast(*I)) { + if (Context.getCanonicalType(MD->getType()) == + Context.getCanonicalType(VMD->getType())) { + // We did find a matching method, which means that this is not a + // pure virtual method in the current class. Zero it out. + Methods[i] = 0; + } + } + } + } + + // Finally, add pure virtual methods from this class. + for (RecordDecl::decl_iterator i = RD->decls_begin(), e = RD->decls_end(); + i != e; ++i) { + if (CXXMethodDecl *MD = dyn_cast(*i)) { + if (MD->isPure()) + Methods.push_back(MD); + } + } + } +} void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc, DeclTy *TagDecl, @@ -715,8 +786,17 @@ void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc, (DeclTy**)FieldCollector->getCurFields(), FieldCollector->getCurNumFields(), LBrac, RBrac, 0); + CXXRecordDecl *RD = cast((Decl*)TagDecl); + if (!RD->isAbstract()) { + // Collect all the pure virtual methods and see if this is an abstract + // class after all. + PureVirtualMethodCollector Collector(Context, RD); + if (!Collector.empty()) + RD->setAbstract(true); + } + if (!Template) - AddImplicitlyDeclaredMembersToClass(cast((Decl*)TagDecl)); + AddImplicitlyDeclaredMembersToClass(RD); } /// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared diff --git a/test/SemaCXX/abstract.cpp b/test/SemaCXX/abstract.cpp new file mode 100644 index 0000000000..a7a52116da --- /dev/null +++ b/test/SemaCXX/abstract.cpp @@ -0,0 +1,26 @@ +// RUN: clang -fsyntax-only -verify %s -std=c++0x + +#ifndef __GXX_EXPERIMENTAL_CXX0X__ +#define __CONCAT(__X, __Y) __CONCAT1(__X, __Y) +#define __CONCAT1(__X, __Y) __X ## __Y + +#define static_assert(__b, __m) \ + typedef int __CONCAT(__sa, __LINE__)[__b ? 1 : -1] +#endif + +class C { + virtual void f() = 0; +}; + +static_assert(__is_abstract(C), "C has a pure virtual function"); + +class D : C { +}; + +static_assert(__is_abstract(D), "D inherits from an abstract class"); + +class E : D { + virtual void f(); +}; + +static_assert(!__is_abstract(E), "E inherits from an abstract class but implements f");