]> granicus.if.org Git - clang/commitdiff
Add representation of base classes in the AST, and verify that we
authorDouglas Gregor <dgregor@apple.com>
Wed, 22 Oct 2008 17:49:05 +0000 (17:49 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 22 Oct 2008 17:49:05 +0000 (17:49 +0000)
don't have duplicated direct base classes.

Seriliazation of base class specifiers is not yet implemented.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@57991 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/DeclCXX.h
include/clang/Basic/DiagnosticKinds.def
include/clang/Parse/Action.h
include/clang/Parse/Parser.h
lib/AST/DeclCXX.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Sema/Sema.h
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaOverload.cpp
test/SemaCXX/inherit.cpp

index ae16d99c8b72828a1f936e4a8a4d77e4eb95aa65..ccafaae317300493c00e16e390199bf0ae9626d2 100644 (file)
@@ -40,17 +40,113 @@ public:
   static bool classof(const CXXFieldDecl *D) { return true; }
 };
 
+/// BaseSpecifier - A base class of a C++ class.
+class CXXBaseSpecifier {
+  SourceRange Range;
+
+  bool Virtual : 1;
+
+  /// BaseOfClass - Whether this is the base of a class (true) or of a
+  /// struct (false). This determines the mapping from the access
+  /// specifier as written in the source code to the access specifier
+  /// used for semantic analysis.
+  bool BaseOfClass : 1; 
+
+  /// Access specifier as written in the source code (which may be
+  /// AS_none). The actual type of data stored here is an
+  /// AccessSpecifier, but we use "unsigned" here to work around a
+  /// VC++ bug.
+  unsigned Access : 2;
+
+  QualType BaseType;
+  
+  CXXBaseSpecifier(SourceRange R, bool V, bool BC, AccessSpecifier A, QualType T)
+    : Range(R), Virtual(V), BaseOfClass(BC), Access(A), BaseType(T) { }
+
+public:
+  static CXXBaseSpecifier *Create(ASTContext &C, SourceRange R, bool V, bool BC, 
+                                  AccessSpecifier A, QualType T);
+
+  /// getSourceRange - Retrieves the source range that contains the
+  /// entire base specifier.
+  SourceRange getSourceRange() const { return Range; }
+  
+  /// isVirtual - Determines whether the base class is a virtual base
+  /// class (or not).
+  bool isVirtual() const { return Virtual; }
+
+  /// getAccessSpecifier - Returns the access specifier for this base
+  /// specifier. This is the actual base specifier as used for
+  /// semantic analysis, so the result can never be AS_none. To
+  /// retrieve the access specifier as written in the source code, use
+  /// getAccessSpecifierAsWritten().
+  AccessSpecifier getAccessSpecifier() const { 
+    if ((AccessSpecifier)Access == AS_none)
+      return BaseOfClass? AS_private : AS_public;
+    else
+      return (AccessSpecifier)Access; 
+  }
+
+  /// getAccessSpecifierAsWritten - Retrieves the access specifier as
+  /// written in the source code (which may mean that no access
+  /// specifier was explicitly written). Use getAccessSpecifier() to
+  /// retrieve the access specifier for use in semantic analysis.
+  AccessSpecifier getAccessSpecifierAsWritten() const {
+    return (AccessSpecifier)Access;
+  }
+
+  /// getType - Retrieves the type of the base class. This type will
+  /// always be an unqualified class type.
+  QualType getType() const { return BaseType; }
+};
+
 /// CXXRecordDecl - Represents a C++ struct/union/class.
-/// The only difference with RecordDecl is that CXXRecordDecl is a DeclContext.
+/// CXXRecordDecl differs from RecordDecl in several ways. First, it
+/// is a DeclContext, because it can contain other
+/// declarations. Second, it provides additional C++ fields, including
+/// storage for base classes.
 class CXXRecordDecl : public RecordDecl, public DeclContext {
+  /// Bases - Base classes of this class.
+  /// FIXME: This is wasted space for a union.
+  CXXBaseSpecifier **Bases;
+
+  /// NumBases - The number of base class specifiers in Bases.
+  unsigned NumBases;
+
   CXXRecordDecl(TagKind TK, DeclContext *DC,
                 SourceLocation L, IdentifierInfo *Id) 
-    : RecordDecl(CXXRecord, TK, DC, L, Id), DeclContext(CXXRecord) {}
+    : RecordDecl(CXXRecord, TK, DC, L, Id), DeclContext(CXXRecord),
+      Bases(0), NumBases(0) {}
+
+  ~CXXRecordDecl();
+
 public:
   static CXXRecordDecl *Create(ASTContext &C, TagKind TK, DeclContext *DC,
                                SourceLocation L, IdentifierInfo *Id,
                                CXXRecordDecl* PrevDecl=0);
   
+  /// setBases - Sets the base classes of this struct or class.
+  void setBases(CXXBaseSpecifier **Bases, unsigned NumBases) {
+    this->Bases = Bases;
+    this->NumBases = NumBases;
+  }
+
+  /// getNumBases - Retrieves the number of base classes of this
+  /// class.
+  unsigned getNumBases() const { return NumBases; }
+
+  /// getBase - Retrieve the ith base class.
+  CXXBaseSpecifier *getBase(unsigned i) { 
+    assert(i < NumBases && "Base index out of range");
+    return Bases[i];
+  }
+
+  /// getBase - Retrieve the ith base class.
+  const CXXBaseSpecifier *getBase(unsigned i) const { 
+    assert(i < NumBases && "Base index out of range");
+    return Bases[i];
+  }
+
   const CXXFieldDecl *getMember(unsigned i) const {
     return cast<const CXXFieldDecl>(RecordDecl::getMember(i));
   }
index 352cc91de45c276f594675d43d56ca3ef43b9d66..f265f3ba26741fc8c2f77ea866fd59e97b309f6b 100644 (file)
@@ -1163,12 +1163,14 @@ DIAG(err_overload_multiple_match, ERROR,
      "more than one matching function found in __builtin_overload")
 
 // Classes.
-DIAG(err_dup_virtual, ERROR,
-     "duplicate 'virtual' in base specifier")
 DIAG(err_expected_class_name, ERROR,
      "expected class name")
 DIAG(err_anon_type_definition, ERROR,
      "declaration of anonymous %0 must be a definition")
+
+// Derived classes.
+DIAG(err_dup_virtual, ERROR,
+     "duplicate 'virtual' in base specifier")
 DIAG(err_base_clause_on_union, ERROR,
      "unions cannot have base classes")
 DIAG(err_base_must_be_class, ERROR,
@@ -1177,6 +1179,8 @@ DIAG(err_union_as_base_class, ERROR,
      "unions cannot be base classes")
 DIAG(err_incomplete_base_class, ERROR,
      "base class has incomplete type")
+DIAG(err_duplicate_base_class, ERROR,
+     "base class '%0' specified more than once as a direct base class")
 
 DIAG(warn_not_compound_assign, WARNING,
      "use of unary operator that may be intended as compound assignment (%0=)")
index a3272925b796bc04b1f651ebbb8f6ef9c1ea8b69..799fbec97365776e460301df2bf8abf99261f5d2 100644 (file)
@@ -55,7 +55,8 @@ public:
   typedef void DeclTy;
   typedef void TypeTy;
   typedef void AttrTy;
-  
+  typedef void BaseTy;
+
   /// ActionResult - This structure is used while parsing/acting on expressions,
   /// stmts, etc.  It encapsulates both the object returned by the action, plus
   /// a sense of whether or not it is valid.
@@ -75,12 +76,14 @@ public:
     }
   };
 
-  /// Expr/Stmt/TypeResult - Provide a unique type to wrap ExprTy/StmtTy/TypeTy,
-  /// providing strong typing and allowing for failure.
+  /// Expr/Stmt/Type/BaseResult - Provide a unique type to wrap
+  /// ExprTy/StmtTy/TypeTy/BaseTy, providing strong typing and
+  /// allowing for failure.
   typedef ActionResult<0> ExprResult;
   typedef ActionResult<1> StmtResult;
   typedef ActionResult<2> TypeResult;
-  
+  typedef ActionResult<3> BaseResult;
+
   /// Deletion callbacks - Since the parser doesn't know the concrete types of
   /// the AST nodes being generated, it must do callbacks to delete objects when
   /// recovering from errors.
@@ -641,11 +644,18 @@ public:
 
   //===---------------------------- C++ Classes ---------------------------===//
   /// ActOnBaseSpecifier - Parsed a base specifier
-  virtual void ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange,
-                                  bool Virtual, AccessSpecifier Access,
-                                  TypeTy *basetype, SourceLocation BaseLoc) {
+  virtual BaseResult ActOnBaseSpecifier(DeclTy *classdecl, 
+                                        SourceRange SpecifierRange,
+                                        bool Virtual, AccessSpecifier Access,
+                                        TypeTy *basetype, 
+                                        SourceLocation BaseLoc) {
+    return 0;
   }
 
+  virtual void ActOnBaseSpecifiers(DeclTy *ClassDecl, BaseTy **Bases, 
+                                   unsigned NumBases) {
+  }
+                                   
   /// ActOnStartCXXClassDef - This is called at the start of a class/struct/union
   /// definition, when on C++.
   virtual void ActOnStartCXXClassDef(Scope *S, DeclTy *TagDecl,
index 55b48ae99e82270e401cb83a167315cb060b49f7..46a43e7608dd1fd3e1eae05530e3b6a88fa82d55 100644 (file)
@@ -73,6 +73,7 @@ public:
   typedef Action::StmtTy StmtTy;
   typedef Action::DeclTy DeclTy;
   typedef Action::TypeTy TypeTy;
+  typedef Action::BaseTy BaseTy;
   
   // Parsing methods.
   
@@ -315,6 +316,7 @@ private:
  
   typedef Action::ExprResult ExprResult;
   typedef Action::StmtResult StmtResult;
+  typedef Action::BaseResult BaseResult;
     
   //===--------------------------------------------------------------------===//
   // Lexing and parsing of C++ inline methods.
@@ -708,7 +710,7 @@ private:
   //===--------------------------------------------------------------------===//
   // C++ 10: Derived classes [class.derived]
   void ParseBaseClause(DeclTy *ClassDecl);
-  bool ParseBaseSpecifier(DeclTy *ClassDecl);
+  BaseResult ParseBaseSpecifier(DeclTy *ClassDecl);
   AccessSpecifier getAccessSpecifierIfPresent() const;
 };
 
index 9fd7ff974e91f41c773b886ebf9817888e0d9b48..86db8532c8f38fa1746a680fdca697156048a158 100644 (file)
@@ -26,6 +26,14 @@ CXXFieldDecl *CXXFieldDecl::Create(ASTContext &C, CXXRecordDecl *RD,
   return new (Mem) CXXFieldDecl(RD, L, Id, T, BW);
 }
 
+CXXBaseSpecifier *CXXBaseSpecifier::Create(ASTContext &C, SourceRange R, bool V, 
+                                           bool BC, AccessSpecifier A, QualType T)
+{
+  void *Mem = C.getAllocator().Allocate<CXXBaseSpecifier>();
+  CXXBaseSpecifier* BS = new (Mem) CXXBaseSpecifier(R, V, BC, A, T);
+  return BS;
+}
+
 CXXRecordDecl *CXXRecordDecl::Create(ASTContext &C, TagKind TK, DeclContext *DC,
                                      SourceLocation L, IdentifierInfo *Id,
                                      CXXRecordDecl* PrevDecl) {
@@ -35,6 +43,12 @@ CXXRecordDecl *CXXRecordDecl::Create(ASTContext &C, TagKind TK, DeclContext *DC,
   return R;
 }
 
+CXXRecordDecl::~CXXRecordDecl() {
+  for (unsigned i = 0; i < NumBases; ++i)
+    delete Bases[i];
+  delete [] Bases;
+}
+
 CXXMethodDecl *
 CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD,
                       SourceLocation L, IdentifierInfo *Id,
index abd432ce9f49b512771773257b2a79179aa042f5..21c7d6f0fe15c544fcc350ddabecf499bfe9d256 100644 (file)
@@ -256,12 +256,19 @@ void Parser::ParseBaseClause(DeclTy *ClassDecl)
   assert(Tok.is(tok::colon) && "Not a base clause");
   ConsumeToken();
 
+  // Build up an array of parsed base specifiers.
+  llvm::SmallVector<BaseTy *, 8> BaseInfo;
+
   while (true) {
     // Parse a base-specifier.
-    if (ParseBaseSpecifier(ClassDecl)) {
+    BaseResult Result = ParseBaseSpecifier(ClassDecl);
+    if (Result.isInvalid) {
       // Skip the rest of this base specifier, up until the comma or
       // opening brace.
-      SkipUntil(tok::comma, tok::l_brace);
+      SkipUntil(tok::comma, tok::l_brace, true, true);
+    } else {
+      // Add this to our array of base specifiers.
+      BaseInfo.push_back(Result.Val);
     }
 
     // If the next token is a comma, consume it and keep reading
@@ -271,6 +278,9 @@ void Parser::ParseBaseClause(DeclTy *ClassDecl)
     // Consume the comma.
     ConsumeToken();
   }
+
+  // Attach the base specifiers
+  Actions.ActOnBaseSpecifiers(ClassDecl, &BaseInfo[0], BaseInfo.size());
 }
 
 /// ParseBaseSpecifier - Parse a C++ base-specifier. A base-specifier is
@@ -284,7 +294,7 @@ void Parser::ParseBaseClause(DeclTy *ClassDecl)
 ///                        class-name
 ///         access-specifier 'virtual'[opt] ::[opt] nested-name-specifier[opt]
 ///                        class-name
-bool Parser::ParseBaseSpecifier(DeclTy *ClassDecl)
+Parser::BaseResult Parser::ParseBaseSpecifier(DeclTy *ClassDecl)
 {
   bool IsVirtual = false;
   SourceLocation StartLoc = Tok.getLocation();
@@ -306,7 +316,8 @@ bool Parser::ParseBaseSpecifier(DeclTy *ClassDecl)
     SourceLocation VirtualLoc = ConsumeToken();
     if (IsVirtual) {
       // Complain about duplicate 'virtual'
-      Diag(VirtualLoc, diag::err_dup_virtual);
+      Diag(VirtualLoc, diag::err_dup_virtual, 
+           SourceRange(VirtualLoc, VirtualLoc));
     }
 
     IsVirtual = true;
@@ -339,9 +350,8 @@ bool Parser::ParseBaseSpecifier(DeclTy *ClassDecl)
   
   // Notify semantic analysis that we have parsed a complete
   // base-specifier.
-  Actions.ActOnBaseSpecifier(ClassDecl, Range, IsVirtual, Access, BaseType,
-                             BaseLoc);
-  return false;
+  return Actions.ActOnBaseSpecifier(ClassDecl, Range, IsVirtual, Access, BaseType,
+                                    BaseLoc);
 }
 
 /// getAccessSpecifierIfPresent - Determine whether the next token is
index b176bc9cebaaa43809427c693044d76763549fc1..48b32e8ce13bc47ebfc357ba1fda4a6a5d8d412a 100644 (file)
@@ -753,10 +753,14 @@ public:
   // C++ Classes
   //
   /// ActOnBaseSpecifier - Parsed a base specifier
-  virtual void ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange,
-                                  bool Virtual, AccessSpecifier Access,
-                                  TypeTy *basetype, SourceLocation BaseLoc);
+  virtual BaseResult ActOnBaseSpecifier(DeclTy *classdecl, 
+                                        SourceRange SpecifierRange,
+                                        bool Virtual, AccessSpecifier Access,
+                                        TypeTy *basetype, SourceLocation BaseLoc);
   
+  virtual void ActOnBaseSpecifiers(DeclTy *ClassDecl, BaseTy **Bases, 
+                                   unsigned NumBases);
+
   virtual void ActOnStartCXXClassDef(Scope *S, DeclTy *TagDecl,
                                      SourceLocation LBrace);
 
index fef205cb11a393d484fd49973d025751a92c95c3..7c4444fab61113b525217fbfadbcd45b28d24e0b 100644 (file)
@@ -20,6 +20,8 @@
 #include "clang/Parse/DeclSpec.h"
 #include "llvm/Support/Compiler.h"
 #include <algorithm> // for std::equal
+#include <functional>
+#include <map>
 
 using namespace clang;
 
@@ -260,23 +262,24 @@ void Sema::CheckCXXDefaultArguments(FunctionDecl *FD) {
 /// example: 
 ///    class foo : public bar, virtual private baz { 
 /// 'public bar' and 'virtual private baz' are each base-specifiers.
-void Sema::ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange,
-                              bool Virtual, AccessSpecifier Access,
-                              TypeTy *basetype, SourceLocation BaseLoc) {
+Sema::BaseResult 
+Sema::ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange,
+                         bool Virtual, AccessSpecifier Access,
+                         TypeTy *basetype, SourceLocation BaseLoc) {
   RecordDecl *Decl = (RecordDecl*)classdecl;
   QualType BaseType = Context.getTypeDeclType((TypeDecl*)basetype);
 
   // Base specifiers must be record types.
   if (!BaseType->isRecordType()) {
     Diag(BaseLoc, diag::err_base_must_be_class, SpecifierRange);
-    return;
+    return true;
   }
 
   // C++ [class.union]p1:
   //   A union shall not be used as a base class.
   if (BaseType->isUnionType()) {
     Diag(BaseLoc, diag::err_union_as_base_class, SpecifierRange);
-    return;
+    return true;
   }
 
   // C++ [class.union]p1:
@@ -284,8 +287,7 @@ void Sema::ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange,
   if (Decl->isUnion()) {
     Diag(Decl->getLocation(), diag::err_base_clause_on_union,
          SpecifierRange);
-    Decl->setInvalidDecl();
-    return;
+    return true;
   }
 
   // C++ [class.derived]p2:
@@ -293,14 +295,63 @@ void Sema::ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange,
   //   defined class.
   if (BaseType->isIncompleteType()) {
     Diag(BaseLoc, diag::err_incomplete_base_class, SpecifierRange);
-    return;
+    return true;
+  }
+
+  // Create the base specifier.
+  CXXBaseSpecifier *BS = CXXBaseSpecifier::Create(Context, SpecifierRange, 
+                                                  Virtual, 
+                                                  BaseType->isClassType(),
+                                                  Access, BaseType);
+  return BS;
+}
+
+/// QualTypeOrder - Function object that provides a total ordering on
+/// QualType values.
+struct QualTypeOrdering : std::binary_function<QualType, QualType, bool> {
+  bool operator()(QualType T1, QualType T2) {
+    return std::less<void*>()(T1.getAsOpaquePtr(), T2.getAsOpaquePtr());
   }
+};
+
+/// ActOnBaseSpecifiers - Attach the given base specifiers to the
+/// class, after checking whether there are any duplicate base
+/// classes.
+void Sema::ActOnBaseSpecifiers(DeclTy *ClassDecl, BaseTy **Bases, 
+                               unsigned NumBases) {
+  if (NumBases == 0)
+    return;
 
-  // FIXME: C++ [class.mi]p3:
-  //   A class shall not be specified as a direct base class of a
-  //   derived class more than once.
+  // Used to keep track of which base types we have already seen, so
+  // that we can properly diagnose redundant direct base types. Note
+  // that the key is always the canonical type.
+  std::map<QualType, CXXBaseSpecifier*, QualTypeOrdering> KnownBaseTypes;
+
+  // Copy non-redundant base specifiers into permanent storage.
+  CXXBaseSpecifier **InBaseSpecs = (CXXBaseSpecifier **)Bases;
+  CXXBaseSpecifier **StoredBaseSpecs = new CXXBaseSpecifier* [NumBases];
+  unsigned outIdx = 0;
+  for (unsigned inIdx = 0; inIdx < NumBases; ++inIdx) {
+    QualType NewBaseType 
+      = Context.getCanonicalType(InBaseSpecs[inIdx]->getType());
+    if (KnownBaseTypes[NewBaseType]) {
+      // C++ [class.mi]p3:
+      //   A class shall not be specified as a direct base class of a
+      //   derived class more than once.
+      Diag(InBaseSpecs[inIdx]->getSourceRange().getBegin(),
+           diag::err_duplicate_base_class, 
+           KnownBaseTypes[NewBaseType]->getType().getAsString(),
+           InBaseSpecs[inIdx]->getSourceRange());
+    } else {
+      // Okay, add this new base class.
+      KnownBaseTypes[NewBaseType] = InBaseSpecs[inIdx];
+      StoredBaseSpecs[outIdx++] = InBaseSpecs[inIdx];
+    }
+  }
 
-  // FIXME: Attach base class to the record.
+  // Attach the remaining base class specifiers to the derived class.
+  CXXRecordDecl *Decl = (CXXRecordDecl*)ClassDecl;
+  Decl->setBases(StoredBaseSpecs, outIdx);
 }
 
 //===----------------------------------------------------------------------===//
index cafdf8794a2fd4c2c14012cf6a1d0cd12642ada5..19bc13a14666aa422127118fd2c9a8999de05936 100644 (file)
@@ -664,7 +664,7 @@ Sema::IsQualificationConversion(QualType FromType, QualType ToType)
     // Within each iteration of the loop, we check the qualifiers to
     // determine if this still looks like a qualification
     // conversion. Then, if all is well, we unwrap one more level of
-    // pointers (FIXME: or pointers-to-members) and do it all again
+    // pointers or pointers-to-members and do it all again
     // until there are no more pointers or pointers-to-members left to
     // unwrap.
     UnwrappedAnyPointer = true;
@@ -839,7 +839,7 @@ Sema::CompareQualificationConversions(const StandardConversionSequence& SCS1,
     // Within each iteration of the loop, we check the qualifiers to
     // determine if this still looks like a qualification
     // conversion. Then, if all is well, we unwrap one more level of
-    // pointers (FIXME: or pointers-to-members) and do it all again
+    // pointers or pointers-to-members and do it all again
     // until there are no more pointers or pointers-to-members left
     // to unwrap. This essentially mimics what
     // IsQualificationConversion does, but here we're checking for a
index 82d8db38cfd887eefcc6d3604ec8254985e694f1..8ccecddaf659e14cb70dd4c75ff41fdd99e39fe3 100644 (file)
@@ -23,3 +23,10 @@ union U1 : public A { }; // expected-error{{unions cannot have base classes}}
 union U2 {};\r
 \r
 class G : public U2 { }; // expected-error{{unions cannot be base classes}}\r
+\r
+typedef G G_copy;\r
+typedef G G_copy_2;\r
+typedef G_copy G_copy_3;\r
+\r
+class H : G_copy, A, G_copy_2, // expected-error{{base class 'G_copy' specified more than once as a direct base class}}\r
+          public G_copy_3 { }; // expected-error{{base class 'G_copy' specified more than once as a direct base class}}\r