From 26236e8b7507017fded05da3474d842e802a1679 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 2 Dec 2008 00:41:28 +0000 Subject: [PATCH] A little more scaffolding for parsing templates: - Template parameter scope to hold the template parameters - Template parameter context for parsing declarators - Actions for template type parameters and non-type template parameters git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@60387 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Parse/Action.h | 31 ++++++++++++ include/clang/Parse/DeclSpec.h | 9 ++-- include/clang/Parse/Scope.h | 14 +++++- lib/Parse/ParseTemplate.cpp | 82 +++++++++++++++++-------------- test/Parser/cxx-template-decl.cpp | 5 +- 5 files changed, 96 insertions(+), 45 deletions(-) diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index cf18b6250d..3b8ab2b055 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -852,6 +852,37 @@ public: virtual void ActOnFinishCXXClassDef(DeclTy *TagDecl) { } + //===---------------------------C++ Templates----------------------------===// + + /// ActOnTypeParameter - Called when a C++ template type parameter + /// (e.g., "typename T") has been parsed. Typename specifies whether + /// the keyword "typename" was used to declare the type parameter + /// (otherwise, "class" was used), and KeyLoc is the location of the + /// "class" or "typename" keyword. ParamName is the name of the + /// parameter (NULL indicates an unnamed template parameter) and + /// ParamName is the location of the parameter name (if any). + /// If the type parameter has a default argument, it will be added + /// later via ActOnTypeParameterDefault. + virtual DeclTy *ActOnTypeParameter(Scope *S, bool Typename, + SourceLocation KeyLoc, + IdentifierInfo *ParamName, + SourceLocation ParamNameLoc) { + return 0; + } + + /// ActOnTypeParameterDefault - Adds a default argument (the type + /// Default) to the given template type parameter (TypeParam). + virtual void ActOnTypeParameterDefault(DeclTy *TypeParam, TypeTy *Default) { + } + + /// ActOnNonTypeTemplateParameter - Called when a C++ non-type + /// template parameter (e.g., "int Size" in "template + /// class Array") has been parsed. S is the current scope and D is + /// the parsed declarator. + virtual DeclTy *ActOnNonTypeTemplateParameter(Scope *S, Declarator &D) { + return 0; + } + //===----------------------- Obj-C Declarations -------------------------===// // ActOnStartClassInterface - this action is called immediately after parsing diff --git a/include/clang/Parse/DeclSpec.h b/include/clang/Parse/DeclSpec.h index 31ea8e709a..ab52a298fa 100644 --- a/include/clang/Parse/DeclSpec.h +++ b/include/clang/Parse/DeclSpec.h @@ -631,7 +631,8 @@ public: MemberContext, // Struct/Union field. BlockContext, // Declaration within a block in a function. ForContext, // Declaration within first part of a for loop. - ConditionContext // Condition declaration in a C++ if/switch/while/for. + ConditionContext, // Condition declaration in a C++ if/switch/while/for. + TemplateParamContext // Within a template parameter list. }; /// DeclaratorKind - The kind of declarator this represents. @@ -743,9 +744,11 @@ public: } /// mayOmitIdentifier - Return true if the identifier is either optional or - /// not allowed. This is true for typenames and prototypes. + /// not allowed. This is true for typenames, prototypes, and template + /// parameter lists. bool mayOmitIdentifier() const { - return Context == TypeNameContext || Context == PrototypeContext; + return Context == TypeNameContext || Context == PrototypeContext || + Context == TemplateParamContext; } /// mayHaveIdentifier - Return true if the identifier is either optional or diff --git a/include/clang/Parse/Scope.h b/include/clang/Parse/Scope.h index 17a61c78f1..8cb2f7434c 100644 --- a/include/clang/Parse/Scope.h +++ b/include/clang/Parse/Scope.h @@ -54,7 +54,13 @@ public: /// Blocks serve as top-level scopes for some objects like labels, they /// also prevent things like break and continue. BlockScopes have the /// other flags set as well. - BlockScope = 0x40 + BlockScope = 0x40, + + /// TemplateParamScope - This is a scope that corresponds to the + /// template parameters of a C++ template. Template parameter + /// scope starts at the 'template' keyword and ends when the + /// template declaration ends. + TemplateParamScope = 0x80 }; private: /// The parent scope for this scope. This is null for the translation-unit @@ -172,6 +178,12 @@ public: return false; } + /// isTemplateParamScope - Return true if this scope is a C++ + /// template parameter scope. + bool isTemplateParamScope() const { + return getFlags() & Scope::TemplateParamScope; + } + /// Init - This is used by the parser to implement scope caching. /// void Init(Scope *Parent, unsigned ScopeFlags) { diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index 9a7f6cbd73..6bcf81d906 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -40,15 +40,19 @@ Parser::DeclTy *Parser::ParseTemplateDeclaration(unsigned Context) { } SourceLocation TemplateLoc = ConsumeToken(); - // Try to parse the template parameters, and the declaration if successful. - if(ParseTemplateParameters(0)) { - // For some reason, this is generating a compiler error when parsing the - // declaration. Apparently, ParseDeclaration doesn't want to match a - // function-definition, but will match a function declaration. - // TODO: ParseDeclarationOrFunctionDefinition - return ParseDeclaration(Context); - } - return 0; + // Enter template-parameter scope. + EnterScope(Scope::TemplateParamScope|Scope::DeclScope); + + // Try to parse the template parameters, and the declaration if + // successful. + DeclTy *TemplateDecl = 0; + if(ParseTemplateParameters(0)) + TemplateDecl = ParseDeclarationOrFunctionDefinition(); + + // Leave template-parameter scope. + ExitScope(); + + return TemplateDecl; } /// ParseTemplateParameters - Parses a template-parameter-list enclosed in @@ -153,13 +157,19 @@ Parser::DeclTy *Parser::ParseTemplateParameter() { /// 'typename' identifier[opt] /// 'typename' identifier[opt] '=' type-id Parser::DeclTy *Parser::ParseTypeParameter() { - SourceLocation keyLoc = ConsumeToken(); + assert((Tok.is(tok::kw_class) || Tok.is(tok::kw_typename)) && + "A type-parameter starts with 'class' or 'typename'"); + + // Consume the 'class' or 'typename' keyword. + bool TypenameKeyword = Tok.is(tok::kw_typename); + SourceLocation KeyLoc = ConsumeToken(); // Grab the template parameter name (if given) - IdentifierInfo* paramName = 0; + SourceLocation NameLoc; + IdentifierInfo* ParamName = 0; if(Tok.is(tok::identifier)) { - paramName = Tok.getIdentifierInfo(); - ConsumeToken(); + ParamName = Tok.getIdentifierInfo(); + NameLoc = ConsumeToken(); } else if(Tok.is(tok::equal) || Tok.is(tok::comma) || Tok.is(tok::greater)) { // Unnamed template parameter. Don't have to do anything here, just @@ -169,17 +179,18 @@ Parser::DeclTy *Parser::ParseTypeParameter() { return 0; } + DeclTy *TypeParam = Actions.ActOnTypeParameter(CurScope, TypenameKeyword, + KeyLoc, ParamName, NameLoc); + // Grab a default type id (if given). - TypeTy* defaultType = 0; if(Tok.is(tok::equal)) { - ConsumeToken(); - defaultType = ParseTypeName(); - if(!defaultType) - return 0; + SourceLocation EqualLoc = ConsumeToken(); + TypeTy *DefaultType = ParseTypeName(); + if(DefaultType) + Actions.ActOnTypeParameterDefault(TypeParam, DefaultType); } - // FIXME: Add an action for type parameters. - return 0; + return TypeParam; } /// ParseTemplateTemplateParameter - Handle the parsing of template @@ -244,22 +255,21 @@ Parser::DeclTy* Parser::ParseTemplateTemplateParameter() { /// but that didn't work out to well. Instead, this tries to recrate the basic /// parsing of parameter declarations, but tries to constrain it for template /// parameters. -/// FIXME: We need to make ParseParameterDeclaration work for non-type -/// template parameters, too. -Parser::DeclTy* Parser::ParseNonTypeTemplateParameter() -{ - SourceLocation startLoc = Tok.getLocation(); +/// FIXME: We need to make a ParseParameterDeclaration that works for +/// non-type template parameters and normal function parameters. +Parser::DeclTy* Parser::ParseNonTypeTemplateParameter() { + SourceLocation StartLoc = Tok.getLocation(); // Parse the declaration-specifiers (i.e., the type). - // FIXME:: The type should probably be restricted in some way... Not all + // FIXME: The type should probably be restricted in some way... Not all // declarators (parts of declarators?) are accepted for parameters. - DeclSpec ds; - ParseDeclarationSpecifiers(ds); + DeclSpec DS; + ParseDeclarationSpecifiers(DS); // Parse this as a typename. - Declarator decl(ds, Declarator::TypeNameContext); - ParseDeclarator(decl); - if(ds.getTypeSpecType() == DeclSpec::TST_unspecified && !ds.getTypeRep()) { + Declarator ParamDecl(DS, Declarator::TemplateParamContext); + ParseDeclarator(ParamDecl); + if(DS.getTypeSpecType() == DeclSpec::TST_unspecified && !DS.getTypeRep()) { // This probably shouldn't happen - and it's more of a Sema thing, but // basically we didn't parse the type name because we couldn't associate // it with an AST node. we should just skip to the comma or greater. @@ -269,11 +279,8 @@ Parser::DeclTy* Parser::ParseNonTypeTemplateParameter() return 0; } - // If there's an identifier after the typename, parse that as part of the - // declarator - or something. - if(Tok.is(tok::identifier)) { - ConsumeToken(); - } + // Create the parameter. + DeclTy *Param = Actions.ActOnNonTypeTemplateParameter(CurScope, ParamDecl); // Is there a default value? Parsing this can be fairly annoying because // we have to stop on the first non-nested (paren'd) '>' as the closure @@ -283,6 +290,5 @@ Parser::DeclTy* Parser::ParseNonTypeTemplateParameter() SkipUntil(tok::comma, tok::greater, true, true); } - // FIXME: Add an action for non-type template parameters. - return 0; + return Param; } diff --git a/test/Parser/cxx-template-decl.cpp b/test/Parser/cxx-template-decl.cpp index abc7bfc649..e415ba15f2 100644 --- a/test/Parser/cxx-template-decl.cpp +++ b/test/Parser/cxx-template-decl.cpp @@ -13,9 +13,8 @@ template