]> granicus.if.org Git - clang/commitdiff
Keep track of whether a class is abstract or not. This is currently only used for...
authorAnders Carlsson <andersca@mac.com>
Sun, 22 Mar 2009 01:52:17 +0000 (01:52 +0000)
committerAnders Carlsson <andersca@mac.com>
Sun, 22 Mar 2009 01:52:17 +0000 (01:52 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@67461 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Decl.h
include/clang/AST/DeclCXX.h
lib/AST/DeclCXX.cpp
lib/AST/ExprCXX.cpp
lib/Parse/ParseExpr.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
test/SemaCXX/abstract.cpp [new file with mode: 0644]

index a5ead71305c256be0421461dde7df9380d29c9c6..b1c4feedcbb1847dd3d1487098ff667f9129c2cc 100644 (file)
@@ -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
index 740d37f30bccda609080fafe4941e66bd5656cbb..c689b77632b9bf490392cc79da5257f1a360d501 100644 (file)
@@ -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.
index 5e9b0cd9f458d1aaadce6608f829acf4ad8c0dbb..33e870bc0d65d4ff07d554de8ae2b7cac50a84d5 100644 (file)
@@ -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,
index 53be84b20d2fe3206e9add877fe5c603cd9cd05f..5a9de19254d3be63974c5bb83e29d66911284259 100644 (file)
@@ -214,6 +214,10 @@ bool UnaryTypeTraitExpr::EvaluateTrait() const {
       return cast<CXXRecordDecl>(Record->getDecl())->isPolymorphic();
     }
     return false;
+  case UTT_IsAbstract:
+    if (const RecordType *RT = QueriedType->getAsRecordType())
+      return cast<CXXRecordDecl>(RT->getDecl())->isAbstract();
+    return false;
   }
 }
 
index f2892c136729b4066b22d220030bda43b542e868..b140046fe4363e67b383559923aa9f637799a39a 100644 (file)
@@ -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: {
index 7c80fa553e114febad93e197cad7524dc75aa112..7b45d5232fb295b00261627abeb293fa8efdb8b8 100644 (file)
@@ -2224,9 +2224,12 @@ void Sema::AddInitializerToDecl(DeclTy *dcl, ExprArg init, bool DirectInit) {
     Expr *Init = static_cast<Expr *>(init.get());
     if ((IL = dyn_cast<IntegerLiteral>(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<CXXRecordDecl>(CurContext)->setAbstract(true);
+      } else {
         Diag(Method->getLocation(), diag::err_non_virtual_pure)
           << Method->getDeclName() << Init->getSourceRange();
         Method->setInvalidDecl();
index 10e7bd4c97323e9f4e865ce6952e3b06d2e8f486..b18b6c8f84c5c71f5a651ddcee4a6b8937d3bf26 100644 (file)
@@ -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<const CXXMethodDecl*, 8> 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<CXXRecordDecl>(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<CXXMethodDecl>(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<CXXMethodDecl>(*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<CXXMethodDecl>(*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<CXXRecordDecl>((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<CXXRecordDecl>((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 (file)
index 0000000..a7a5211
--- /dev/null
@@ -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");