diff options
author | Douglas Gregor <dgregor@apple.com> | 2008-10-31 09:07:45 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2008-10-31 09:07:45 +0000 |
commit | b48fe3812047e84164925c8938ce82be0624c40c (patch) | |
tree | 8af13a860c7c055c80382ca3f0b1f9f2db7b7b23 /lib | |
parent | e10b0f236bc8487445bc99b8d14bd40666b1998d (diff) |
Add support for parsing and representing C++ constructor declarations.
Notes:
- Constructors are never found by name lookup, so they'll never get
pushed into any scope. Instead, they are stored as an
OverloadedFunctionDecl in CXXRecordDecl for easy overloading.
- There's a new action isCurrentClassName that determines whether an
identifier is the name of the innermost class currently being defined;
we use this to identify the declarator-id grammar rule that refers to
a type-name.
- MinimalAction does *not* support parsing constructors.
- We now handle virtual and explicit function specifiers.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58499 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r-- | lib/AST/DeclBase.cpp | 1 | ||||
-rw-r--r-- | lib/AST/DeclCXX.cpp | 19 | ||||
-rw-r--r-- | lib/Parse/DeclSpec.cpp | 16 | ||||
-rw-r--r-- | lib/Parse/MinimalAction.cpp | 6 | ||||
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 35 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 5 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 89 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 43 |
8 files changed, 207 insertions, 7 deletions
diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 686aba2460..3c6c91ea0a 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -237,6 +237,7 @@ void Decl::addDeclKind(Kind k) { case CXXRecord: nCXXSUC++; break; // FIXME: Statistics for C++ decls. case CXXMethod: + case CXXConstructor: case CXXClassVar: break; } diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 0d0b746ad6..3ec6824566 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -39,6 +39,14 @@ CXXRecordDecl::~CXXRecordDecl() { delete [] Bases; } +void CXXRecordDecl::Destroy(ASTContext &C) { + for (OverloadedFunctionDecl::function_iterator func + = Constructors.function_begin(); + func != Constructors.function_end(); ++func) + (*func)->Destroy(C); + RecordDecl::Destroy(C); +} + void CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, unsigned NumBases) { @@ -74,6 +82,17 @@ QualType CXXMethodDecl::getThisType(ASTContext &C) const { return C.getPointerType(ClassTy).withConst(); } +CXXConstructorDecl * +CXXConstructorDecl::Create(ASTContext &C, CXXRecordDecl *RD, + SourceLocation L, IdentifierInfo *Id, + QualType T, bool isExplicit, + bool isInline, bool isImplicitlyDeclared) { + void *Mem = C.getAllocator().Allocate<CXXConstructorDecl>(); + return new (Mem) CXXConstructorDecl(RD, L, Id, T, isExplicit, isInline, + isImplicitlyDeclared); +} + + CXXClassVarDecl *CXXClassVarDecl::Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation L, IdentifierInfo *Id, QualType T, ScopedDecl *PrevDecl) { diff --git a/lib/Parse/DeclSpec.cpp b/lib/Parse/DeclSpec.cpp index 1d42feda1e..d61d6c5d6c 100644 --- a/lib/Parse/DeclSpec.cpp +++ b/lib/Parse/DeclSpec.cpp @@ -30,7 +30,7 @@ unsigned DeclSpec::getParsedSpecifiers() const { if (hasTypeSpecifier()) Res |= PQ_TypeSpecifier; - if (FS_inline_specified) + if (FS_inline_specified || FS_virtual_specified || FS_explicit_specified) Res |= PQ_FunctionSpecifier; return Res; } @@ -206,6 +206,20 @@ bool DeclSpec::SetFunctionSpecInline(SourceLocation Loc, const char *&PrevSpec){ return false; } +bool DeclSpec::SetFunctionSpecVirtual(SourceLocation Loc, const char *&PrevSpec){ + // 'virtual virtual' is ok. + FS_virtual_specified = true; + FS_virtualLoc = Loc; + return false; +} + +bool DeclSpec::SetFunctionSpecExplicit(SourceLocation Loc, const char *&PrevSpec){ + // 'explicit explicit' is ok. + FS_explicit_specified = true; + FS_explicitLoc = Loc; + return false; +} + /// Finish - This does final analysis of the declspec, rejecting things like /// "_Imaginary" (lacking an FP type). This returns a diagnostic to issue or diff --git a/lib/Parse/MinimalAction.cpp b/lib/Parse/MinimalAction.cpp index c98830ca79..2fec359ecd 100644 --- a/lib/Parse/MinimalAction.cpp +++ b/lib/Parse/MinimalAction.cpp @@ -63,6 +63,12 @@ MinimalAction::isTypeName(const IdentifierInfo &II, Scope *S) { return 0; } +/// isCurrentClassName - Always returns false, because MinimalAction +/// does not support C++ classes with constructors. +bool MinimalAction::isCurrentClassName(const IdentifierInfo &, Scope *) { + return false; +} + /// ActOnDeclarator - If this is a typedef declarator, we modify the /// IdentifierInfo::FETokenInfo field to keep track of this fact, until S is /// popped. diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 243a15f3db..7a84171a05 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -383,7 +383,12 @@ void Parser::ParseSpecifierQualifierList(DeclSpec &DS) { // Issue diagnostic and remove function specfier if present. if (Specs & DeclSpec::PQ_FunctionSpecifier) { - Diag(DS.getInlineSpecLoc(), diag::err_typename_invalid_functionspec); + if (DS.isInlineSpecified()) + Diag(DS.getInlineSpecLoc(), diag::err_typename_invalid_functionspec); + if (DS.isVirtualSpecified()) + Diag(DS.getVirtualSpecLoc(), diag::err_typename_invalid_functionspec); + if (DS.isExplicitSpecified()) + Diag(DS.getExplicitSpecLoc(), diag::err_typename_invalid_functionspec); DS.ClearFunctionSpecs(); } } @@ -433,6 +438,8 @@ void Parser::ParseSpecifierQualifierList(DeclSpec &DS) { /// [C99] 'restrict' /// function-specifier: [C99 6.7.4] /// [C99] 'inline' +/// [C++] 'virtual' +/// [C++] 'explicit' /// void Parser::ParseDeclarationSpecifiers(DeclSpec &DS) { DS.SetRangeStart(Tok.getLocation()); @@ -462,6 +469,16 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS) { if (TypeRep == 0) goto DoneWithDeclSpec; + // C++: If the identifier is actually the name of the class type + // being defined and the next token is a '(', then this is a + // constructor declaration. We're done with the decl-specifiers + // and will treat this token as an identifier. + if (getLang().CPlusPlus && + CurScope->isCXXClassScope() && + Actions.isCurrentClassName(*Tok.getIdentifierInfo(), CurScope) && + NextToken().getKind() == tok::l_paren) + goto DoneWithDeclSpec; + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typedef, Loc, PrevSpec, TypeRep); if (isInvalid) @@ -607,6 +624,14 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS) { case tok::kw_inline: isInvalid = DS.SetFunctionSpecInline(Loc, PrevSpec); break; + + case tok::kw_virtual: + isInvalid = DS.SetFunctionSpecVirtual(Loc, PrevSpec); + break; + + case tok::kw_explicit: + isInvalid = DS.SetFunctionSpecExplicit(Loc, PrevSpec); + break; case tok::less: // GCC ObjC supports types like "<SomeProtocol>" as a synonym for @@ -1056,6 +1081,8 @@ bool Parser::isDeclarationSpecifier() const { // function-specifier case tok::kw_inline: + case tok::kw_virtual: + case tok::kw_explicit: // GNU typeof support. case tok::kw_typeof: @@ -1217,7 +1244,11 @@ void Parser::ParseDeclaratorInternal(Declarator &D) { /// parameter-type-list[opt] ')' /// [C++] direct-declarator '(' parameter-declaration-clause ')' /// cv-qualifier-seq[opt] exception-specification[opt] -/// +/// [C++] declarator-id +// +// declarator-id: [C++ 8] +// id-expression +// '::'[opt] nested-name-specifier[opt] type-name void Parser::ParseDirectDeclarator(Declarator &D) { // Parse the first direct-declarator seen. if (Tok.is(tok::identifier) && D.mayHaveIdentifier()) { diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 1cff55ea24..2a49e715c0 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -773,7 +773,8 @@ public: //===--------------------------------------------------------------------===// // C++ Classes // - + virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S); + virtual void ActOnStartCXXClassDef(Scope *S, DeclTy *TagDecl, SourceLocation LBrace); @@ -788,6 +789,8 @@ public: virtual void ActOnFinishCXXClassDef(DeclTy *TagDecl); + virtual DeclTy *ActOnConstructorDeclarator(CXXConstructorDecl *ConDecl); + //===--------------------------------------------------------------------===// // C++ Derived Classes // diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 2306151279..16053cecdb 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -737,8 +737,88 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { } bool isInline = D.getDeclSpec().isInlineSpecified(); + bool isVirtual = D.getDeclSpec().isVirtualSpecified(); + bool isExplicit = D.getDeclSpec().isExplicitSpecified(); + FunctionDecl *NewFD; - if (D.getContext() == Declarator::MemberContext) { + if (isCurrentClassName(*II, S)) { + // This is a C++ constructor declaration. + assert(D.getContext() == Declarator::MemberContext && + "Constructors can only be declared in a member context"); + + // C++ [class.ctor]p3: + // A constructor shall not be virtual (10.3) or static (9.4). A + // constructor can be invoked for a const, volatile or const + // volatile object. A constructor shall not be declared const, + // volatile, or const volatile (9.3.2). + if (isVirtual) { + Diag(D.getIdentifierLoc(), + diag::err_constructor_cannot_be, + "virtual", + SourceRange(D.getDeclSpec().getVirtualSpecLoc()), + SourceRange(D.getIdentifierLoc())); + isVirtual = false; + } + if (SC == FunctionDecl::Static) { + Diag(D.getIdentifierLoc(), + diag::err_constructor_cannot_be, + "static", + SourceRange(D.getDeclSpec().getStorageClassSpecLoc()), + SourceRange(D.getIdentifierLoc())); + isVirtual = false; + } + if (D.getDeclSpec().hasTypeSpecifier()) { + // Constructors don't have return types, but the parser will + // happily parse something like: + // + // class X { + // float X(float); + // }; + // + // The return type will be eliminated later. + Diag(D.getIdentifierLoc(), + diag::err_constructor_return_type, + SourceRange(D.getDeclSpec().getTypeSpecTypeLoc()), + SourceRange(D.getIdentifierLoc())); + } + if (R->getAsFunctionTypeProto()->getTypeQuals() != 0) { + DeclaratorChunk::FunctionTypeInfo &FTI = D.getTypeObject(0).Fun; + if (FTI.TypeQuals & QualType::Const) + Diag(D.getIdentifierLoc(), + diag::err_invalid_qualified_constructor, + "const", + SourceRange(D.getIdentifierLoc())); + if (FTI.TypeQuals & QualType::Volatile) + Diag(D.getIdentifierLoc(), + diag::err_invalid_qualified_constructor, + "volatile", + SourceRange(D.getIdentifierLoc())); + if (FTI.TypeQuals & QualType::Restrict) + Diag(D.getIdentifierLoc(), + diag::err_invalid_qualified_constructor, + "restrict", + SourceRange(D.getIdentifierLoc())); + } + + // Rebuild the function type "R" without any type qualifiers (in + // case any of the errors above fired) and with "void" as the + // return type, since constructors don't have return types. We + // *always* have to do this, because GetTypeForDeclarator will + // put in a result type of "int" when none was specified. + const FunctionTypeProto *Proto = R->getAsFunctionTypeProto(); + R = Context.getFunctionType(Context.VoidTy, Proto->arg_type_begin(), + Proto->getNumArgs(), + Proto->isVariadic(), + 0); + + // Create the new declaration + NewFD = CXXConstructorDecl::Create(Context, + cast<CXXRecordDecl>(CurContext), + D.getIdentifierLoc(), II, R, + isExplicit, isInline, + /*isImplicitlyDeclared=*/false); + + } else if (D.getContext() == Declarator::MemberContext) { // This is a C++ method declaration. NewFD = CXXMethodDecl::Create(Context, cast<CXXRecordDecl>(CurContext), D.getIdentifierLoc(), II, R, @@ -826,6 +906,13 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) { } } + // C++ constructors are handled by a separate routine, since they + // don't require any declaration merging (C++ [class.mfct]p2) and + // they aren't ever pushed into scope, because they can't be found + // by name lookup anyway (C++ [class.ctor]p2). + if (CXXConstructorDecl *ConDecl = dyn_cast<CXXConstructorDecl>(NewFD)) + return ActOnConstructorDeclarator(ConDecl); + // Merge the decl with the existing one if appropriate. Since C functions // are in a flat namespace, make sure we consider decls in outer scopes. if (PrevDecl && diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 817204c439..d5b631252d 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -257,6 +257,17 @@ void Sema::CheckCXXDefaultArguments(FunctionDecl *FD) { } } +/// isCurrentClassName - Determine whether the identifier II is the +/// name of the class type currently being defined. In the case of +/// nested classes, this will only return true if II is the name of +/// the innermost class. +bool Sema::isCurrentClassName(const IdentifierInfo &II, Scope *) { + if (CXXRecordDecl *CurDecl = dyn_cast_or_null<CXXRecordDecl>(CurContext)) + return &II == CurDecl->getIdentifier(); + else + return false; +} + /// ActOnBaseSpecifier - Parsed a base specifier. A base specifier is /// one entry in the base class list of a class specifier, for /// example: @@ -361,9 +372,21 @@ void Sema::ActOnBaseSpecifiers(DeclTy *ClassDecl, BaseTy **Bases, /// ActOnStartCXXClassDef - This is called at the start of a class/struct/union /// definition, when on C++. void Sema::ActOnStartCXXClassDef(Scope *S, DeclTy *D, SourceLocation LBrace) { - Decl *Dcl = static_cast<Decl *>(D); - PushDeclContext(cast<CXXRecordDecl>(Dcl)); + CXXRecordDecl *Dcl = cast<CXXRecordDecl>(static_cast<Decl *>(D)); + PushDeclContext(Dcl); FieldCollector->StartClass(); + + if (Dcl->getIdentifier()) { + // C++ [class]p2: + // [...] The class-name is also inserted into the scope of the + // class itself; this is known as the injected-class-name. For + // purposes of access checking, the injected-class-name is treated + // as if it were a public member name. + TypedefDecl *InjectedClassName + = TypedefDecl::Create(Context, Dcl, LBrace, Dcl->getIdentifier(), + Context.getTypeDeclType(Dcl), /*PrevDecl=*/0); + PushOnScopeChains(InjectedClassName, S); + } } /// ActOnCXXMemberDeclarator - This is invoked when a C++ class member @@ -539,6 +562,22 @@ void Sema::ActOnFinishCXXClassDef(DeclTy *D) { Consumer.HandleTagDeclDefinition(Rec); } +/// ActOnConstructorDeclarator - Called by ActOnDeclarator to complete +/// the declaration of the given C++ constructor ConDecl that was +/// built from declarator D. This routine is responsible for checking +/// that the newly-created constructor declaration is well-formed and +/// for recording it in the C++ class. Example: +/// +/// @code +/// class X { +/// X(); // X::X() will be the ConDecl. +/// }; +/// @endcode +Sema::DeclTy *Sema::ActOnConstructorDeclarator(CXXConstructorDecl *ConDecl) { + assert(ConDecl && "Expected to receive a constructor declaration"); + return (DeclTy *)ConDecl; +} + //===----------------------------------------------------------------------===// // Namespace Handling //===----------------------------------------------------------------------===// |