diff options
-rw-r--r-- | include/clang/AST/DeclCXX.h | 100 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticKinds.def | 8 | ||||
-rw-r--r-- | include/clang/Parse/Action.h | 24 | ||||
-rw-r--r-- | include/clang/Parse/Parser.h | 4 | ||||
-rw-r--r-- | lib/AST/DeclCXX.cpp | 14 | ||||
-rw-r--r-- | lib/Parse/ParseDeclCXX.cpp | 24 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 10 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 75 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.cpp | 4 | ||||
-rw-r--r-- | test/SemaCXX/inherit.cpp | 7 |
10 files changed, 234 insertions, 36 deletions
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index ae16d99c8b..ccafaae317 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -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)); } diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 352cc91de4..f265f3ba26 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -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=)") diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index a3272925b7..799fbec973 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -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, diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 55b48ae99e..46a43e7608 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -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; }; diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 9fd7ff974e..86db8532c8 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -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, diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index abd432ce9f..21c7d6f0fe 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -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 diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index b176bc9ceb..48b32e8ce1 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index fef205cb11..7c4444fab6 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -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); } //===----------------------------------------------------------------------===// diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index cafdf8794a..19bc13a146 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -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 diff --git a/test/SemaCXX/inherit.cpp b/test/SemaCXX/inherit.cpp index 82d8db38cf..8ccecddaf6 100644 --- a/test/SemaCXX/inherit.cpp +++ b/test/SemaCXX/inherit.cpp @@ -23,3 +23,10 @@ union U1 : public A { }; // expected-error{{unions cannot have base classes}} union U2 {};
class G : public U2 { }; // expected-error{{unions cannot be base classes}}
+
+typedef G G_copy;
+typedef G G_copy_2;
+typedef G_copy G_copy_3;
+
+class H : G_copy, A, G_copy_2, // expected-error{{base class 'G_copy' specified more than once as a direct base class}}
+ public G_copy_3 { }; // expected-error{{base class 'G_copy' specified more than once as a direct base class}}
|