From: Douglas Gregor Date: Thu, 18 Dec 2008 19:37:40 +0000 (+0000) Subject: Ultrasimplistic sketch for the parsing of C++ template-ids. This won't X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d6fb7ef028d9aa0b3e8943b7bc049c524437b407;p=clang Ultrasimplistic sketch for the parsing of C++ template-ids. This won't become useful or correct until we (1) parse template arguments correctly, (2) have some way to turn template-ids into types, declarators, etc., and (3) have a real representation of templates. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@61208 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index 706c5fb85f..150a2eb13c 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -369,9 +369,12 @@ OBJC2_AT_KEYWORD(dynamic) // bycopy/byref/in/inout/oneway/out? ANNOTATION(cxxscope) // annotation for a C++ scope spec, e.g. "::foo::bar::" -ANNOTATION(qualtypename) // annotation for a C typedef name, or a C++ (possibly - // qualified) typename, e.g. "foo::MyClass" - +ANNOTATION(qualtypename) // annotation for a C typedef name, a C++ (possibly + // qualified) typename, e.g. "foo::MyClass", or + // template-id that names a type ("std::vector") +ANNOTATION(template_id) // annotation for a C++ template-id that names a + // function template specialization (not a type), + // e.g., "std::swap" #undef ANNOTATION #undef OBJC2_AT_KEYWORD #undef OBJC1_AT_KEYWORD diff --git a/include/clang/Lex/Token.h b/include/clang/Lex/Token.h index ce687856f0..7e397171be 100644 --- a/include/clang/Lex/Token.h +++ b/include/clang/Lex/Token.h @@ -78,7 +78,9 @@ public: bool isNot(tok::TokenKind K) const { return Kind != (unsigned) K; } bool isAnnotationToken() const { - return is(tok::annot_qualtypename) || is(tok::annot_cxxscope); + return is(tok::annot_qualtypename) || + is(tok::annot_cxxscope) || + is(tok::annot_template_id); } /// getLocation - Return a source location identifier for the specified @@ -211,6 +213,28 @@ struct PPConditionalInfo { bool FoundElse; }; +/// TemplateIdAnnotation - Information about a template-id annotation +/// token, which contains the template declaration, template +/// arguments, and the source locations for important tokens. +struct TemplateIdAnnotation { + /// TemplateNameLoc - The location of the template name within the + /// source. + SourceLocation TemplateNameLoc; + + /// Template - The declaration of the template corresponding to the + /// template-name. This is an Action::DeclTy*. + void *Template; + + /// LAngleLoc - The location of the '<' before the template argument + /// list. + SourceLocation LAngleLoc; + + /// NumArgs - The number of template arguments. The arguments + /// themselves are Action::TemplateArgTy pointers allocated directly + /// following the TemplateIdAnnotation structure. + unsigned NumArgs; +}; + } // end namespace clang #endif diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 2822538564..47ee53bcc4 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -63,6 +63,7 @@ public: typedef void BaseTy; typedef void MemInitTy; typedef void CXXScopeTy; + typedef void TemplateArgTy; /// Expr/Stmt/Type/BaseResult - Provide a unique type to wrap /// ExprTy/StmtTy/TypeTy/BaseTy, providing strong typing and @@ -76,27 +77,40 @@ public: /// Same, but with ownership. typedef ASTOwningResult<&ActionBase::DeleteExpr> OwningExprResult; typedef ASTOwningResult<&ActionBase::DeleteStmt> OwningStmtResult; + typedef ASTOwningResult<&ActionBase::DeleteTemplateArg> + OwningTemplateArgResult; // Note that these will replace ExprResult and StmtResult when the transition // is complete. /// Single expressions or statements as arguments. typedef ASTOwningPtr<&ActionBase::DeleteExpr> ExprArg; typedef ASTOwningPtr<&ActionBase::DeleteStmt> StmtArg; + typedef ASTOwningPtr<&ActionBase::DeleteTemplateArg> TemplateArgArg; /// Multiple expressions or statements as arguments. typedef ASTMultiPtr<&ActionBase::DeleteExpr> MultiExprArg; typedef ASTMultiPtr<&ActionBase::DeleteStmt> MultiStmtArg; - + typedef ASTMultiPtr<&ActionBase::DeleteTemplateArg> MultiTemplateArgArg; // Utilities for Action implementations to return smart results. OwningExprResult ExprError() { return OwningExprResult(*this, true); } OwningStmtResult StmtError() { return OwningStmtResult(*this, true); } + OwningTemplateArgResult TemplateArgError() { + return OwningTemplateArgResult(*this, true); + } + OwningExprResult ExprError(const DiagnosticBuilder&) { return ExprError(); } OwningStmtResult StmtError(const DiagnosticBuilder&) { return StmtError(); } + OwningTemplateArgResult TemplateArgError(const DiagnosticBuilder&) { + return TemplateArgError(); + } OwningExprResult ExprEmpty() { return OwningExprResult(*this, false); } OwningStmtResult StmtEmpty() { return OwningStmtResult(*this, false); } + OwningTemplateArgResult TemplateArgEmpty() { + return OwningTemplateArgResult(*this, false); + } /// Statistics. virtual void PrintStats() const {} @@ -117,6 +131,14 @@ public: virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S, const CXXScopeSpec *SS = 0) = 0; + /// isTemplateName - Determines whether the identifier II is a + /// template name in the current scope, and returns the template + /// declaration if II names a template. An optional CXXScope can be + /// passed to indicate the C++ scope in which the identifier will be + /// found. + virtual DeclTy *isTemplateName(IdentifierInfo &II, Scope *S, + const CXXScopeSpec *SS = 0) = 0; + /// ActOnCXXGlobalScopeSpecifier - Return the object that represents the /// global scope ('::'). virtual CXXScopeTy *ActOnCXXGlobalScopeSpecifier(Scope *S, @@ -918,6 +940,8 @@ public: return 0; } + + //===----------------------- Obj-C Declarations -------------------------===// // ActOnStartClassInterface - this action is called immediately after parsing @@ -1164,6 +1188,14 @@ public: virtual bool isCurrentClassName(const IdentifierInfo& II, Scope *S, const CXXScopeSpec *SS); + /// isTemplateName - Determines whether the identifier II is a + /// template name in the current scope, and returns the template + /// declaration if II names a template. An optional CXXScope can be + /// passed to indicate the C++ scope in which the identifier will be + /// found. + virtual DeclTy *isTemplateName(IdentifierInfo &II, Scope *S, + const CXXScopeSpec *SS = 0); + /// 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/include/clang/Parse/Ownership.h b/include/clang/Parse/Ownership.h index e6cf4a2ebf..36e9dc9329 100644 --- a/include/clang/Parse/Ownership.h +++ b/include/clang/Parse/Ownership.h @@ -30,6 +30,7 @@ namespace clang // what types are required to be identical for the actions. typedef void ExprTy; typedef void StmtTy; + typedef void TemplateArgTy; /// ActionResult - This structure is used while parsing/acting on /// expressions, stmts, etc. It encapsulates both the object returned by @@ -57,6 +58,7 @@ namespace clang /// pointers need access to them. virtual void DeleteExpr(ExprTy *E) {} virtual void DeleteStmt(StmtTy *E) {} + virtual void DeleteTemplateArg(TemplateArgTy *E) {} }; /// ASTDestroyer - The type of an AST node destruction function pointer. @@ -71,6 +73,9 @@ namespace clang template <> struct DestroyerToUID<&ActionBase::DeleteStmt> { static const unsigned UID = 1; }; + template <> struct DestroyerToUID<&ActionBase::DeleteTemplateArg> { + static const unsigned UID = 1; + }; /// ASTOwningResult - A moveable smart pointer for AST nodes that also /// has an extra flag to indicate an additional success status. diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 7094180989..cfc9e3b7cc 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -78,14 +78,16 @@ public: typedef Action::BaseTy BaseTy; typedef Action::MemInitTy MemInitTy; typedef Action::CXXScopeTy CXXScopeTy; + typedef Action::TemplateArgTy TemplateArgTy; - typedef Action::ExprResult ExprResult; - typedef Action::StmtResult StmtResult; - typedef Action::BaseResult BaseResult; - typedef Action::MemInitResult MemInitResult; + typedef Action::ExprResult ExprResult; + typedef Action::StmtResult StmtResult; + typedef Action::BaseResult BaseResult; + typedef Action::MemInitResult MemInitResult; typedef Action::OwningExprResult OwningExprResult; typedef Action::OwningStmtResult OwningStmtResult; + typedef Action::OwningTemplateArgResult OwningTemplateArgResult; typedef Action::ExprArg ExprArg; @@ -100,8 +102,15 @@ public: OwningExprResult ExprError() { return OwningExprResult(Actions, true); } OwningStmtResult StmtError() { return OwningStmtResult(Actions, true); } + OwningTemplateArgResult TemplateArgError() { + return OwningTemplateArgResult(Actions, true); + } + OwningExprResult ExprError(const DiagnosticBuilder &) { return ExprError(); } OwningStmtResult StmtError(const DiagnosticBuilder &) { return StmtError(); } + OwningTemplateArgResult TemplateArgError(const DiagnosticBuilder &) { + return TemplateArgError(); + } // Parsing methods. @@ -936,6 +945,12 @@ private: DeclTy *ParseTypeParameter(); DeclTy *ParseTemplateTemplateParameter(); DeclTy *ParseNonTypeTemplateParameter(); + // C++ 14.3: Template arguments [temp.arg] + typedef llvm::SmallVector TemplateArgList; + void AnnotateTemplateIdToken(DeclTy *Template, const CXXScopeSpec *SS = 0); + bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs); + OwningTemplateArgResult ParseTemplateArgument(); + }; } // end namespace clang diff --git a/lib/Parse/MinimalAction.cpp b/lib/Parse/MinimalAction.cpp index 583781056e..c4e7ecc0c3 100644 --- a/lib/Parse/MinimalAction.cpp +++ b/lib/Parse/MinimalAction.cpp @@ -73,6 +73,16 @@ bool MinimalAction::isCurrentClassName(const IdentifierInfo &, Scope *, return false; } + /// isTemplateName - Determines whether the identifier II is a + /// template name in the current scope, and returns the template + /// declaration if II names a template. An optional CXXScope can be + /// passed to indicate the C++ scope in which the identifier will be + /// found. +Action::DeclTy *MinimalAction::isTemplateName(IdentifierInfo &II, Scope *S, + const CXXScopeSpec *SS ) { + return 0; +} + /// 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 407aa9c7d9..16e7e774aa 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1452,13 +1452,27 @@ void Parser::ParseDirectDeclarator(Declarator &D) { if (Tok.is(tok::identifier)) { assert(Tok.getIdentifierInfo() && "Not an identifier?"); - // Determine whether this identifier is a C++ constructor name or - // a normal identifier. - if (Actions.isCurrentClassName(*Tok.getIdentifierInfo(), CurScope)) { + + // If this identifier is followed by a '<', we may have a template-id. + DeclTy *Template; + if (getLang().CPlusPlus && NextToken().is(tok::less) && + (Template = Actions.isTemplateName(*Tok.getIdentifierInfo(), + CurScope))) { + IdentifierInfo *II = Tok.getIdentifierInfo(); + AnnotateTemplateIdToken(Template, 0); + // FIXME: Set the declarator to a template-id. How? I don't + // know... for now, just use the identifier. + D.SetIdentifier(II, Tok.getLocation()); + } + // If this identifier is the name of the current class, it's a + // constructor name. + else if (getLang().CPlusPlus && + Actions.isCurrentClassName(*Tok.getIdentifierInfo(), CurScope)) D.setConstructor(Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope), Tok.getLocation()); - } else + // This is a normal identifier. + else D.SetIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); ConsumeToken(); goto PastIdentifier; diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index d21dc4aa6d..5d0c4b5350 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -46,8 +46,19 @@ Parser::DeclTy *Parser::ParseTemplateDeclaration(unsigned Context) { // Try to parse the template parameters, and the declaration if // successful. DeclTy *TemplateDecl = 0; - if(ParseTemplateParameters(0)) + if (Tok.is(tok::less) && NextToken().is(tok::greater)) { + // This is a template specialization. Just consume the angle + // brackets and parse the declaration or function definition that + // follows. + // FIXME: Record somehow that we're in an explicit specialization. + ConsumeToken(); + ConsumeToken(); + TemplateParmScope.Exit(); TemplateDecl = ParseDeclarationOrFunctionDefinition(); + } else { + if(ParseTemplateParameters(0)) + TemplateDecl = ParseDeclarationOrFunctionDefinition(); + } return TemplateDecl; } @@ -243,7 +254,7 @@ Parser::DeclTy* Parser::ParseTemplateTemplateParameter() { /// ParseNonTypeTemplateParameter - Handle the parsing of non-type /// template parameters (e.g., in "template class array;"). - +/// /// template-parameter: /// ... /// parameter-declaration @@ -289,3 +300,96 @@ Parser::DeclTy* Parser::ParseNonTypeTemplateParameter() { return Param; } + +/// AnnotateTemplateIdToken - The current token is an identifier that +/// refers to the template declaration Template, and is followed by a +/// '<'. Turn this template-id into a template-id annotation token. +void Parser::AnnotateTemplateIdToken(DeclTy *Template, const CXXScopeSpec *SS) { + assert(getLang().CPlusPlus && "Can only annotate template-ids in C++"); + assert(Template && Tok.is(tok::identifier) && NextToken().is(tok::less) && + "Parser isn't at the beginning of a template-id"); + + // Consume the template-name. + SourceLocation TemplateNameLoc = ConsumeToken(); + + // Consume the '<'. + SourceLocation LAngleLoc = ConsumeToken(); + + // Parse the optional template-argument-list. + TemplateArgList TemplateArgs; + if (Tok.isNot(tok::greater) && ParseTemplateArgumentList(TemplateArgs)) { + // Try to find the closing '>'. + SkipUntil(tok::greater, true, true); + + // FIXME: What's our recovery strategy for failed template-argument-lists? + return; + } + + if (Tok.isNot(tok::greater)) + return; + + // Determine the location of the '>'. We won't actually consume this + // token, because we'll be replacing it with the template-id. + SourceLocation RAngleLoc = Tok.getLocation(); + + Tok.setKind(tok::annot_template_id); + Tok.setAnnotationEndLoc(RAngleLoc); + Tok.setLocation(TemplateNameLoc); + if (SS && SS->isNotEmpty()) + Tok.setLocation(SS->getBeginLoc()); + + TemplateIdAnnotation *TemplateId + = (TemplateIdAnnotation *)malloc(sizeof(TemplateIdAnnotation) + + sizeof(TemplateArgTy*) * TemplateArgs.size()); + TemplateId->TemplateNameLoc = TemplateNameLoc; + TemplateId->Template = Template; + TemplateId->LAngleLoc = LAngleLoc; + TemplateId->NumArgs = TemplateArgs.size(); + TemplateArgTy **Args = (TemplateArgTy**)(TemplateId + 1); + for (unsigned Arg = 0, ArgEnd = TemplateArgs.size(); Arg != ArgEnd; ++Arg) + Args[Arg] = TemplateArgs[Arg]; + Tok.setAnnotationValue(TemplateId); + + // In case the tokens were cached, have Preprocessor replace them with the + // annotation token. + PP.AnnotateCachedTokens(Tok); +} + +/// ParseTemplateArgument - Parse a C++ template argument (C++ [temp.names]). +/// +/// template-argument: [C++ 14.2] +/// assignment-expression +/// type-id +/// id-expression +Parser::OwningTemplateArgResult Parser::ParseTemplateArgument() { + // FIXME: Implement this! + return TemplateArgError(); +} + +/// ParseTemplateArgumentList - Parse a C++ template-argument-list +/// (C++ [temp.names]). Returns true if there was an error. +/// +/// template-argument-list: [C++ 14.2] +/// template-argument +/// template-argument-list ',' template-argument +bool Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs) { + while (true) { + OwningTemplateArgResult Arg = ParseTemplateArgument(); + if (Arg.isInvalid()) { + SkipUntil(tok::comma, tok::greater, true, true); + return true; + } + else + TemplateArgs.push_back(Arg.release()); + + // If the next token is a comma, consume it and keep reading + // arguments. + if (Tok.isNot(tok::comma)) break; + + // Consume the comma. + ConsumeToken(); + } + + return Tok.isNot(tok::greater); +} + diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 058182eee6..48b0d5f422 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -722,6 +722,7 @@ Parser::OwningExprResult Parser::ParseSimpleAsm() { /// specifier, and another one to get the actual type inside /// ParseDeclarationSpecifiers). void Parser::TryAnnotateTypeOrScopeToken() { + // FIXME: what about template-ids? if (Tok.is(tok::annot_qualtypename) || Tok.is(tok::annot_cxxscope)) return; @@ -730,23 +731,39 @@ void Parser::TryAnnotateTypeOrScopeToken() { MaybeParseCXXScopeSpecifier(SS); if (Tok.is(tok::identifier)) { - TypeTy *Ty = Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope, &SS); - if (Ty) { - // This is a typename. Replace the current token in-place with an - // annotation type token. - Tok.setKind(tok::annot_qualtypename); - Tok.setAnnotationValue(Ty); - Tok.setAnnotationEndLoc(Tok.getLocation()); - if (SS.isNotEmpty()) // it was a C++ qualified type name. - Tok.setLocation(SS.getBeginLoc()); - - // In case the tokens were cached, have Preprocessor replace them with the - // annotation token. - PP.AnnotateCachedTokens(Tok); - return; + DeclTy *Template = 0; + // If this is a template-id, annotate the template-id token. + if (getLang().CPlusPlus && NextToken().is(tok::less) && + (Template = Actions.isTemplateName(*Tok.getIdentifierInfo(), CurScope, + &SS))) + AnnotateTemplateIdToken(Template, &SS); + else { + // Determine whether the identifier is a type name. + TypeTy *Ty = Actions.isTypeName(*Tok.getIdentifierInfo(), CurScope, &SS); + if (Ty) { + // This is a typename. Replace the current token in-place with an + // annotation type token. + Tok.setKind(tok::annot_qualtypename); + Tok.setAnnotationValue(Ty); + Tok.setAnnotationEndLoc(Tok.getLocation()); + if (SS.isNotEmpty()) // it was a C++ qualified type name. + Tok.setLocation(SS.getBeginLoc()); + + // In case the tokens were cached, have Preprocessor replace + // them with the annotation token. + PP.AnnotateCachedTokens(Tok); + return; + } } + + // We either have an identifier that is not a type name or we have + // just created a template-id that might be a type name. Both + // cases will be handled below. } + // FIXME: check for a template-id token here, and look it up if it + // names a type. + if (SS.isNotEmpty()) { // A C++ scope specifier that isn't followed by a typename. // Push the current token back into the token stream (or revert it if it is diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 66230314c0..0534f3dce2 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1005,6 +1005,8 @@ public: //===--------------------------------------------------------------------===// // C++ Templates [C++ 14] // + virtual DeclTy *isTemplateName(IdentifierInfo &II, Scope *S, + const CXXScopeSpec *SS = 0); bool DiagnoseTemplateParameterShadow(SourceLocation Loc, Decl *PrevDecl); virtual DeclTy *ActOnTypeParameter(Scope *S, bool Typename, SourceLocation KeyLoc, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 2dcb9fe336..d300f283b6 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -18,6 +18,42 @@ using namespace clang; +/// isTemplateName - Determines whether the identifier II is a +/// template name in the current scope, and returns the template +/// declaration if II names a template. An optional CXXScope can be +/// passed to indicate the C++ scope in which the identifier will be +/// found. +Sema::DeclTy *Sema::isTemplateName(IdentifierInfo &II, Scope *S, + const CXXScopeSpec *SS) { + DeclContext *DC = 0; + if (SS) { + if (SS->isInvalid()) + return 0; + DC = static_cast(SS->getScopeRep()); + } + Decl *IIDecl = LookupDecl(&II, Decl::IDNS_Ordinary, S, DC, false); + + if (IIDecl) { + // FIXME: We need to represent templates via some kind of + // TemplateDecl, because what follows is a hack that only works in + // one specific case. + if (FunctionDecl *FD = dyn_cast(IIDecl)) { + if (FD->getType()->isDependentType()) + return FD; + } else if (OverloadedFunctionDecl *Ovl + = dyn_cast(IIDecl)) { + for (OverloadedFunctionDecl::function_iterator F = Ovl->function_begin(), + FEnd = Ovl->function_end(); + F != FEnd; ++F) { + if ((*F)->getType()->isDependentType()) + return Ovl; + } + } + return 0; + } + return 0; +} + /// DiagnoseTemplateParameterShadow - Produce a diagnostic complaining /// that the template parameter 'PrevDecl' is being shadowed by a new /// declaration at location Loc. Returns true to indicate that this is diff --git a/test/SemaCXX/template-specialization.cpp b/test/SemaCXX/template-specialization.cpp new file mode 100644 index 0000000000..5442514abc --- /dev/null +++ b/test/SemaCXX/template-specialization.cpp @@ -0,0 +1,4 @@ +// RUN: clang -fsyntax-only -verify %s +template void f(int (&array)[N]); + +template<> void f<1>(int (&array)[1]) { } diff --git a/test/SemaObjCXX/overload.mm b/test/SemaObjCXX/overload.mm index 8a42746424..01ca1c1ce5 100644 --- a/test/SemaObjCXX/overload.mm +++ b/test/SemaObjCXX/overload.mm @@ -1,4 +1,4 @@ -// RUN clang -fsyntax-only -verify %s +// RUN: clang -fsyntax-only -verify %s @protocol P0 @end