aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2008-10-22 17:49:05 +0000
committerDouglas Gregor <dgregor@apple.com>2008-10-22 17:49:05 +0000
commitf8268ae3196002bbab6adb830302e79b0f368f13 (patch)
tree755160cb56f57dd7d7dafccc13858533037e619b
parented2cb285522513d33b001900acf211cc5ee2175b (diff)
Add representation of base classes in the AST, and verify that we
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
-rw-r--r--include/clang/AST/DeclCXX.h100
-rw-r--r--include/clang/Basic/DiagnosticKinds.def8
-rw-r--r--include/clang/Parse/Action.h24
-rw-r--r--include/clang/Parse/Parser.h4
-rw-r--r--lib/AST/DeclCXX.cpp14
-rw-r--r--lib/Parse/ParseDeclCXX.cpp24
-rw-r--r--lib/Sema/Sema.h10
-rw-r--r--lib/Sema/SemaDeclCXX.cpp75
-rw-r--r--lib/Sema/SemaOverload.cpp4
-rw-r--r--test/SemaCXX/inherit.cpp7
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}}