]> granicus.if.org Git - clang/commitdiff
This patch adds very basic support for parsing and type-checking class
authorDouglas Gregor <dgregor@apple.com>
Sun, 13 Apr 2008 21:30:24 +0000 (21:30 +0000)
committerDouglas Gregor <dgregor@apple.com>
Sun, 13 Apr 2008 21:30:24 +0000 (21:30 +0000)
inheritance in C++. It'll parse the base-specifier list, e.g.,

 class D : public B1, virtual public B2 { };

and do some of the simpler semantic checks (B1 and B2 are classes;
they aren't unions or incomplete types, etc).

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

include/clang/Basic/DiagnosticKinds.def
include/clang/Parse/AccessSpecifier.h [new file with mode: 0644]
include/clang/Parse/Action.h
include/clang/Parse/Parser.h
lib/Parse/ParseDecl.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Sema/Sema.h
lib/Sema/SemaDeclCXX.cpp
test/Sema/decl-invalid.c
test/Sema/inherit.cpp [new file with mode: 0644]

index 0c953cad0f4bcfc85b72fffcdf303094b64c4f18..8e2f69b222bc93e66993c6e43e13e8def3458f5e 100644 (file)
@@ -910,6 +910,22 @@ DIAG(err_overload_no_match, ERROR,
 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")
+DIAG(err_base_clause_on_union, ERROR,
+     "unions cannot have base classes")
+DIAG(err_base_must_be_class, ERROR,
+     "base specifier must name a class")
+DIAG(err_union_as_base_class, ERROR,
+     "unions cannot be base classes")
+DIAG(err_incomplete_base_class, ERROR,
+     "base class has incomplete type")
+
 // CHECK: printf format string errors
 DIAG(warn_printf_not_string_constant, WARNING,
      "format string is not a string literal (potentially insecure)")
diff --git a/include/clang/Parse/AccessSpecifier.h b/include/clang/Parse/AccessSpecifier.h
new file mode 100644 (file)
index 0000000..a35725a
--- /dev/null
@@ -0,0 +1,30 @@
+//===--- AccessSpecifier.h - C++ Access Specifiers -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines interfaces used for Declaration Specifiers and Declarators.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_PARSE_ACCESS_SPECIFIER_H
+#define LLVM_CLANG_PARSE_ACCESS_SPECIFIER_H
+
+namespace clang {
+
+/// AccessSpecifier - A C++ access specifier (none, public, private,
+/// protected).
+enum AccessSpecifier {
+  AS_none,
+  AS_public,
+  AS_protected,
+  AS_private
+};
+
+} // end namespace clang
+
+#endif
index c6282e5788d74243e043266964869eb88ef15077..8d2c62bbb272066ff600884e6d03025723697822 100644 (file)
@@ -17,6 +17,7 @@
 #include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/TokenKinds.h"
+#include "clang/Parse/AccessSpecifier.h"
 
 namespace clang {
   // Semantic.
@@ -545,6 +546,14 @@ public:
                                    ExprTy *Op = 0) {
     return 0;
   }
+
+  //===---------------------------- C++ Classes ---------------------------===//
+  /// ActOnBaseSpecifier - Parsed a base specifier
+  virtual void ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange,
+                                  bool Virtual, AccessSpecifier Access,
+                                  DeclTy *basetype, SourceLocation BaseLoc) {
+  }
+
   //===----------------------- Obj-C Declarations -------------------------===//
   
   // ActOnStartClassInterface - this action is called immediately after parsing
index 13535805e2444c3422ce6ef11fd3eace3180f4a6..9ebe91610d57efbe4798224160784488b5b6f9cb 100644 (file)
@@ -451,7 +451,6 @@ private:
   bool ParseTag(DeclTy *&Decl, unsigned TagType, SourceLocation StartLoc);
   void ParseEnumSpecifier(DeclSpec &DS);
   void ParseEnumBody(SourceLocation StartLoc, DeclTy *TagDecl);
-  void ParseStructUnionSpecifier(DeclSpec &DS);
   void ParseStructUnionBody(SourceLocation StartLoc, unsigned TagType,
                             DeclTy *TagDecl);
   void ParseStructDeclaration(DeclSpec &DS,
@@ -482,6 +481,15 @@ private:
   DeclTy *ParseNamespace(unsigned Context);
   DeclTy *ParseLinkage(unsigned Context);
 
+  //===--------------------------------------------------------------------===//
+  // C++ 9: classes [class] and C structs/unions.
+  void ParseClassSpecifier(DeclSpec &DS);
+
+  //===--------------------------------------------------------------------===//
+  // C++ 10: Derived classes [class.derived]
+  void ParseBaseClause(DeclTy *ClassDecl);
+  bool ParseBaseSpecifier(DeclTy *ClassDecl);
+  AccessSpecifier getAccessSpecifierIfPresent();
 };
 
 }  // end namespace clang
index 8a0dcce31c8b257bb060f924375e5bc521236b87..65f1124e6328b9fcaa8ab8eb459f1e960fbaf274 100644 (file)
@@ -519,7 +519,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS) {
     case tok::kw_class:
     case tok::kw_struct:
     case tok::kw_union:
-      ParseStructUnionSpecifier(DS);
+      ParseClassSpecifier(DS);
       continue;
     case tok::kw_enum:
       ParseEnumSpecifier(DS);
@@ -608,42 +608,6 @@ bool Parser::ParseTag(DeclTy *&Decl, unsigned TagType, SourceLocation StartLoc){
   return false;
 }
 
-
-/// ParseStructUnionSpecifier
-///       struct-or-union-specifier: [C99 6.7.2.1]
-///         struct-or-union identifier[opt] '{' struct-contents '}'
-///         struct-or-union identifier
-/// [GNU]   struct-or-union attributes[opt] identifier[opt] '{' struct-contents
-///                                                         '}' attributes[opt]
-/// [GNU]   struct-or-union attributes[opt] identifier
-///       struct-or-union:
-///         'struct'
-///         'union'
-///
-void Parser::ParseStructUnionSpecifier(DeclSpec &DS) {
-  assert((Tok.is(tok::kw_class) || 
-          Tok.is(tok::kw_struct) || 
-          Tok.is(tok::kw_union)) &&
-         "Not a class/struct/union specifier");
-  DeclSpec::TST TagType =
-    Tok.is(tok::kw_class) ? DeclSpec::TST_class :
-    Tok.is(tok::kw_union) ? DeclSpec::TST_union : DeclSpec::TST_struct;
-  SourceLocation StartLoc = ConsumeToken();
-
-  // Parse the tag portion of this.
-  DeclTy *TagDecl;
-  if (ParseTag(TagDecl, TagType, StartLoc))
-    return;
-  
-  // If there is a body, parse it and inform the actions module.
-  if (Tok.is(tok::l_brace))
-    ParseStructUnionBody(StartLoc, TagType, TagDecl);
-
-  const char *PrevSpec = 0;
-  if (DS.SetTypeSpecType(TagType, StartLoc, PrevSpec, TagDecl))
-    Diag(StartLoc, diag::err_invalid_decl_spec_combination, PrevSpec);
-}
-
 /// ParseStructDeclaration - Parse a struct declaration without the terminating
 /// semicolon.
 ///
@@ -736,7 +700,7 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
   
   // Empty structs are an extension in C (C99 6.7.2.1p7), but are allowed in
   // C++.
-  if (Tok.is(tok::r_brace))
+  if (Tok.is(tok::r_brace) && !getLang().CPlusPlus)
     Diag(Tok, diag::ext_empty_struct_union_enum, 
          DeclSpec::getSpecifierName((DeclSpec::TST)TagType));
 
index 46dcb5748191552bab99cc824f3cc1952649cf04..606ff572871a1ba7d04c08fe0282d81184c39e6d 100644 (file)
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Parse/DeclSpec.h"
 #include "clang/Parse/Parser.h"
 #include "clang/Parse/Scope.h"
-#include "clang/Basic/Diagnostic.h"
 using namespace clang;
 
 /// ParseNamespace - We know that the current token is a namespace keyword. This
@@ -117,3 +118,233 @@ Parser::DeclTy *Parser::ParseLinkage(unsigned Context) {
 
   return Actions.ActOnLinkageSpec(Loc, LBrace, RBrace, LangBufPtr, StrSize, D);
 }
+
+/// ParseClassSpecifier - Parse a C++ class-specifier [C++ class] or
+/// elaborated-type-specifier [C++ dcl.type.elab]; we can't tell which
+/// until we reach the start of a definition or see a token that
+/// cannot start a definition.
+///
+///       class-specifier: [C++ class]
+///         class-head '{' member-specification[opt] '}'
+///         class-head '{' member-specification[opt] '}' attributes[opt]
+///       class-head:
+///         class-key identifier[opt] base-clause[opt]
+///         class-key nested-name-specifier identifier base-clause[opt]
+///         class-key nested-name-specifier[opt] simple-template-id
+///                          base-clause[opt]
+/// [GNU]   class-key attributes[opt] identifier[opt] base-clause[opt]
+/// [GNU]   class-key attributes[opt] nested-name-specifier 
+///                          identifier base-clause[opt]
+/// [GNU]   class-key attributes[opt] nested-name-specifier[opt] 
+///                          simple-template-id base-clause[opt]
+///       class-key:
+///         'class'
+///         'struct'
+///         'union'
+///
+///       elaborated-type-specifier: [C++ dcl.type.elab]
+///         class-key ::[opt] nested-name-specifier[opt] identifier 
+///         class-key ::[opt] nested-name-specifier[opt] 'template'[opt] 
+///                          simple-template-id 
+///
+///  Note that the C++ class-specifier and elaborated-type-specifier,
+///  together, subsume the C99 struct-or-union-specifier:
+///
+///       struct-or-union-specifier: [C99 6.7.2.1]
+///         struct-or-union identifier[opt] '{' struct-contents '}'
+///         struct-or-union identifier
+/// [GNU]   struct-or-union attributes[opt] identifier[opt] '{' struct-contents
+///                                                         '}' attributes[opt]
+/// [GNU]   struct-or-union attributes[opt] identifier
+///       struct-or-union:
+///         'struct'
+///         'union'
+void Parser::ParseClassSpecifier(DeclSpec &DS) {
+  assert((Tok.is(tok::kw_class) || 
+          Tok.is(tok::kw_struct) || 
+          Tok.is(tok::kw_union)) &&
+         "Not a class specifier");
+  DeclSpec::TST TagType =
+    Tok.is(tok::kw_class) ? DeclSpec::TST_class : 
+    Tok.is(tok::kw_struct) ? DeclSpec::TST_struct : 
+    DeclSpec::TST_union;
+
+  SourceLocation StartLoc = ConsumeToken();
+
+  AttributeList *Attr = 0;
+  // If attributes exist after tag, parse them.
+  if (Tok.is(tok::kw___attribute))
+    Attr = ParseAttributes();
+
+  // FIXME: Parse the (optional) nested-name-specifier.
+
+  // Parse the (optional) class name.
+  // FIXME: Alternatively, parse a simple-template-id.
+  IdentifierInfo *Name = 0;
+  SourceLocation NameLoc;
+  if (Tok.is(tok::identifier)) {
+    Name = Tok.getIdentifierInfo();
+    NameLoc = ConsumeToken();
+  }
+
+  // There are three options here.  If we have 'struct foo;', then
+  // this is a forward declaration.  If we have 'struct foo {...' or
+  // 'struct fo :...' then this is a definition. Otherwise we have
+  // something like 'struct foo xyz', a reference.
+  Action::TagKind TK;
+  if (Tok.is(tok::l_brace) || (getLang().CPlusPlus && Tok.is(tok::colon)))
+    TK = Action::TK_Definition;
+  else if (Tok.is(tok::semi))
+    TK = Action::TK_Declaration;
+  else
+    TK = Action::TK_Reference;
+
+  if (!Name && TK != Action::TK_Definition) {
+    // We have a declaration or reference to an anonymous class.
+    Diag(StartLoc, diag::err_anon_type_definition, 
+         DeclSpec::getSpecifierName(TagType));
+
+    // Skip the rest of this declarator, up until the comma or semicolon.
+    SkipUntil(tok::comma, true);
+    return;
+  }
+
+  // Parse the tag portion of this.
+  DeclTy *TagDecl = Actions.ActOnTag(CurScope, TagType, TK, StartLoc, Name, 
+                                     NameLoc, Attr);
+
+  // Parse the optional base clause (C++ only).
+  if (getLang().CPlusPlus && Tok.is(tok::colon)) {
+    ParseBaseClause(TagDecl);
+  }
+
+  // If there is a body, parse it and inform the actions module.
+  if (Tok.is(tok::l_brace))
+    ParseStructUnionBody(StartLoc, TagType, TagDecl);
+  else if (TK == Action::TK_Definition) {
+    // FIXME: Complain that we have a base-specifier list but no
+    // definition.
+    Diag(Tok.getLocation(), diag::err_expected_lbrace);
+  }
+
+  const char *PrevSpec = 0;
+  if (DS.SetTypeSpecType(TagType, StartLoc, PrevSpec, TagDecl))
+    Diag(StartLoc, diag::err_invalid_decl_spec_combination, PrevSpec);
+}
+
+/// ParseBaseClause - Parse the base-clause of a C++ class [C++ class.derived]. 
+///
+///       base-clause : [C++ class.derived]
+///         ':' base-specifier-list
+///       base-specifier-list:
+///         base-specifier '...'[opt]
+///         base-specifier-list ',' base-specifier '...'[opt]
+void Parser::ParseBaseClause(DeclTy *ClassDecl)
+{
+  assert(Tok.is(tok::colon) && "Not a base clause");
+  ConsumeToken();
+
+  while (true) {
+    // Parse a base-specifier.
+    if (ParseBaseSpecifier(ClassDecl)) {
+      // Skip the rest of this base specifier, up until the comma or
+      // opening brace.
+      SkipUntil(tok::comma, tok::l_brace);
+    }
+
+    // If the next token is a comma, consume it and keep reading
+    // base-specifiers.
+    if (Tok.isNot(tok::comma)) break;
+    
+    // Consume the comma.
+    ConsumeToken();
+  }
+}
+
+/// ParseBaseSpecifier - Parse a C++ base-specifier. A base-specifier is
+/// one entry in the base class list of a class specifier, for example:
+///    class foo : public bar, virtual private baz {
+/// 'public bar' and 'virtual private baz' are each base-specifiers.
+///
+///       base-specifier: [C++ class.derived]
+///         ::[opt] nested-name-specifier[opt] class-name
+///         'virtual' access-specifier[opt] ::[opt] nested-name-specifier[opt]
+///                        class-name
+///         access-specifier 'virtual'[opt] ::[opt] nested-name-specifier[opt]
+///                        class-name
+bool Parser::ParseBaseSpecifier(DeclTy *ClassDecl)
+{
+  bool IsVirtual = false;
+  SourceLocation StartLoc = Tok.getLocation();
+
+  // Parse the 'virtual' keyword.
+  if (Tok.is(tok::kw_virtual))  {
+    ConsumeToken();
+    IsVirtual = true;
+  }
+
+  // Parse an (optional) access specifier.
+  AccessSpecifier Access = getAccessSpecifierIfPresent();
+  if (Access)
+    ConsumeToken();
+  
+  // Parse the 'virtual' keyword (again!), in case it came after the
+  // access specifier.
+  if (Tok.is(tok::kw_virtual))  {
+    SourceLocation VirtualLoc = ConsumeToken();
+    if (IsVirtual) {
+      // Complain about duplicate 'virtual'
+      Diag(VirtualLoc, diag::err_dup_virtual);
+    }
+
+    IsVirtual = true;
+  }
+
+  // FIXME: Parse optional '::' and optional nested-name-specifier.
+
+  // Parse the class-name.
+  // FIXME: Alternatively, parse a simple-template-id.
+  if (Tok.isNot(tok::identifier)) {
+    Diag(Tok.getLocation(), diag::err_expected_class_name);
+    return true;
+  }
+
+  // We have an identifier; check whether it is actually a type.
+  DeclTy *BaseType = Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope);
+  if (!BaseType) {
+    Diag(Tok.getLocation(), diag::err_expected_class_name);
+    return true;
+  }
+
+  // The location of the base class itself.
+  SourceLocation BaseLoc = Tok.getLocation();
+  
+  // Find the complete source range for the base-specifier.  
+  SourceRange Range(StartLoc, BaseLoc);
+  
+  // Consume the identifier token (finally!).
+  ConsumeToken();
+  
+  // Notify semantic analysis that we have parsed a complete
+  // base-specifier.
+  Actions.ActOnBaseSpecifier(ClassDecl, Range, IsVirtual, Access, BaseType,
+                             BaseLoc);
+  return false;
+}
+
+/// getAccessSpecifierIfPresent - Determine whether the next token is
+/// a C++ access-specifier.
+///
+///       access-specifier: [C++ class.derived]
+///         'private'
+///         'protected'
+///         'public'
+AccessSpecifier Parser::getAccessSpecifierIfPresent()
+{
+  switch (Tok.getKind()) {
+  default: return AS_none;
+  case tok::kw_private: return AS_private;
+  case tok::kw_protected: return AS_protected;
+  case tok::kw_public: return AS_public;
+  }
+}
index 226ffa233d48052bbb438889e85cb5665f94702c..7b305737c2a91beb8f49623279a3cd8cec5a4758 100644 (file)
@@ -588,7 +588,16 @@ public:
                                                  SourceLocation ProtoLoc,
                                                  SourceLocation LParenLoc,
                                                  SourceLocation RParenLoc);
+
+  //===--------------------------------------------------------------------===//
+  // C++ Classes
+  //
+  /// ActOnBaseSpecifier - Parsed a base specifier
+  virtual void ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange,
+                                  bool Virtual, AccessSpecifier Access,
+                                  DeclTy *basetype, SourceLocation BaseLoc);
   
+
   // Objective-C declarations.
   virtual DeclTy *ActOnStartClassInterface(
                     SourceLocation AtInterafceLoc,
index 89440c0cf45d71e7630e692a0c48cf3721a1fbfc..7202d2f2f10ed94add0ab1d27c2df55937b04815 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "Sema.h"
 #include "clang/Basic/LangOptions.h"
+#include "clang/AST/ASTContext.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/AST/Type.h"
@@ -234,3 +235,51 @@ void Sema::CheckCXXDefaultArguments(FunctionDecl *FD) {
     }
   }
 }
+
+/// ActOnBaseSpecifier - Parsed a base specifier. A base specifier is
+/// one entry in the base class list of a class specifier, for
+/// 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,
+                              DeclTy *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;
+  }
+
+  // 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;
+  }
+
+  // C++ [class.union]p1:
+  //   A union shall not have base classes.
+  if (Decl->getKind() == Decl::Union) {
+    Diag(Decl->getLocation(), diag::err_base_clause_on_union,
+         SpecifierRange);
+    Decl->setInvalidDecl();
+    return;
+  }
+
+  // C++ [class.derived]p2:
+  //   The class-name in a base-specifier shall not be an incompletely
+  //   defined class.
+  if (BaseType->isIncompleteType()) {
+    Diag(BaseLoc, diag::err_incomplete_base_class, SpecifierRange);
+    return;
+  }
+
+  // FIXME: C++ [class.mi]p3:
+  //   A class shall not be specified as a direct base class of a
+  //   derived class more than once.
+
+  // FIXME: Attach base class to the record.
+}
index 281e8a8d5c052667bae033ba29c528717a8e11ca..b92146260ef11f66f8638f241d9b3de6b70aeeb0 100644 (file)
@@ -1,6 +1,6 @@
 // RUN: clang %s -fsyntax-only -verify
 
-typedef union <anonymous> __mbstate_t;  // expected-error: {{expected identifier or}}
+typedef union <anonymous> __mbstate_t;  // expected-error: {{declaration of anonymous union must be a definition}}
 
 
 // PR2017
diff --git a/test/Sema/inherit.cpp b/test/Sema/inherit.cpp
new file mode 100644 (file)
index 0000000..f1f96f1
--- /dev/null
@@ -0,0 +1,25 @@
+// RUN: clang -fsyntax-only -verify %s
+class A { };
+
+class B1 : A { };
+
+class B2 : virtual A { };
+
+class B3 : virtual virtual A { }; // expected-error{{duplicate 'virtual' in base specifier}}
+
+class C : public B1, private B2 { };
+
+
+class D;
+
+class E : public D { }; // expected-error{{base class has incomplete type}}
+
+typedef int I;
+
+class F : public I { }; // expected-error{{base specifier must name a class}}
+
+union U1 : public A { }; // expected-error{{unions cannot have base classes}}
+
+union U2 {};
+
+class G : public U2 { }; // expected-error{{unions cannot be base classes}}