Implement parsing of nested-name-specifiers that involve template-ids, e.g.,

std::vector<int>::allocator_type

When we parse a template-id that names a type, it will become either a
template-id annotation (which is a parsed representation of a
template-id that has not yet been through semantic analysis) or a
typename annotation (where semantic analysis has resolved the
template-id to an actual type), depending on the context. We only
produce a type in contexts where we know that we only need type
information, e.g., in a type specifier. Otherwise, we create a
template-id annotation that can later be "upgraded" by transforming it
into a typename annotation when the parser needs a type. This occurs,
for example, when we've parsed "std::vector<int>" above and then see
the '::' after it. However, it means that when writing something like
this:

  template<> class Outer::Inner<int> { ... };

We have two tokens to represent Outer::Inner<int>: one token for the
nested name specifier Outer::, and one template-id annotation token
for Inner<int>, which will be passed to semantic analysis to define
the class template specialization.

Most of the churn in the template tests in this patch come from an
improvement in our error recovery from ill-formed template-ids.

llvm-svn: 65467
This commit is contained in:
Douglas Gregor 2009-02-25 19:37:18 +00:00
parent 22a87f94a9
commit 7f74112756
25 changed files with 570 additions and 232 deletions

View File

@ -628,12 +628,10 @@ by the Action::ActOnCXXGlobalScopeSpecifier and
Action::ActOnCXXNestedNameSpecifier callbacks. In the case of Sema, this is a Action::ActOnCXXNestedNameSpecifier callbacks. In the case of Sema, this is a
<tt>DeclContext*</tt>.</li> <tt>DeclContext*</tt>.</li>
<li><b>tok::annot_template_id</b>: This annotation token represents a C++ <li><b>tok::annot_template_id</b>: This annotation token represents a
template-id such as "foo&lt;int, 4&gt;", which may refer to a function or type C++ template-id such as "foo&lt;int, 4&gt;", where "foo" is the name
depending on whether foo is a function template or class template. The of a template. The AnnotationValue pointer is a pointer to a malloc'd
AnnotationValue pointer is a pointer to a malloc'd TemplateIdAnnotation object. TemplateIdAnnotation object. Depending on the context, a parsed template-id that names a type might become a typename annotation token (if all we care about is the named type, e.g., because it occurs in a type specifier) or might remain a template-id token (if we want to retain more source location information or produce a new type, e.g., in a declaration of a class template specialization). template-id annotation tokens that refer to a type can be "upgraded" to typename annotation tokens by the parser.</li>
FIXME: I don't think the parsing logic is right for this. Shouldn't type
templates be turned into annot_typename??</li>
</ol> </ol>

View File

@ -274,6 +274,10 @@ DIAG(err_expected_class_before, ERROR,
"expected 'class' before '%0'") "expected 'class' before '%0'")
DIAG(err_template_spec_syntax_non_template, ERROR, DIAG(err_template_spec_syntax_non_template, ERROR,
"identifier followed by '<' indicates a class template specialization but %0 %select{does not refer to a template|refers to a function template|<unused>|refers to a template template parameter}1") "identifier followed by '<' indicates a class template specialization but %0 %select{does not refer to a template|refers to a function template|<unused>|refers to a template template parameter}1")
DIAG(err_id_after_template_in_nested_name_spec, ERROR,
"expected template name after 'template' keyword in nested name specifier")
DIAG(err_less_after_template_name_in_nested_name_spec, ERROR,
"expected '<' after 'template %0' in nested name specifier")
// Language specific pragmas // Language specific pragmas

View File

@ -0,0 +1,37 @@
//===--- TemplateKinds.h - Enum values for C++ Template Kinds ---*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the TemplateNameKind enum.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TEMPLATEKINDS_H
#define LLVM_CLANG_TEMPLATEKINDS_H
namespace clang {
/// \brief Specifies the kind of template name that an identifier refers to.
enum TemplateNameKind {
/// The name does not refer to a template.
TNK_Non_template = 0,
/// The name refers to a function template or a set of overloaded
/// functions that includes at least one function template.
TNK_Function_template,
/// The name refers to a class template.
TNK_Class_template,
/// The name referes to a template template parameter.
TNK_Template_template_parm,
/// The name is dependent and is known to be a template name based
/// on syntax, e.g., "Alloc::template rebind<Other>".
TNK_Dependent_template_name
};
}
#endif

View File

@ -431,7 +431,21 @@ public:
if (CachedLexPos != 0 && isBacktrackEnabled()) if (CachedLexPos != 0 && isBacktrackEnabled())
AnnotatePreviousCachedTokens(Tok); AnnotatePreviousCachedTokens(Tok);
} }
/// \brief Replace the last token with an annotation token.
///
/// Like AnnotateCachedTokens(), this routine replaces an
/// already-parsed (and resolved) token with an annotation
/// token. However, this routine only replaces the last token with
/// the annotation token; it does not affect any other cached
/// tokens. This function has no effect if backtracking is not
/// enabled.
void ReplaceLastTokenWithAnnotation(const Token &Tok) {
assert(Tok.isAnnotation() && "Expected annotation token");
if (CachedLexPos != 0 && isBacktrackEnabled())
CachedTokens[CachedLexPos-1] = Tok;
}
/// Diag - Forwarding function for diagnostics. This emits a diagnostic at /// Diag - Forwarding function for diagnostics. This emits a diagnostic at
/// the specified Token's location, translating the token's start /// the specified Token's location, translating the token's start
/// position in the current buffer into a SourcePosition object for rendering. /// position in the current buffer into a SourcePosition object for rendering.

View File

@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_TOKEN_H #ifndef LLVM_CLANG_TOKEN_H
#define LLVM_CLANG_TOKEN_H #define LLVM_CLANG_TOKEN_H
#include "clang/Basic/TemplateKinds.h"
#include "clang/Basic/TokenKinds.h" #include "clang/Basic/TokenKinds.h"
#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceLocation.h"
@ -247,24 +248,62 @@ struct PPConditionalInfo {
/// TemplateIdAnnotation - Information about a template-id annotation /// TemplateIdAnnotation - Information about a template-id annotation
/// token, which contains the template declaration, template /// token, which contains the template declaration, template
/// arguments, and the source locations for important tokens. /// arguments, whether those template arguments were types or
/// expressions, and the source locations for important tokens. All of
/// the information about template arguments is allocated directly
/// after this structure.
struct TemplateIdAnnotation { struct TemplateIdAnnotation {
/// TemplateNameLoc - The location of the template name within the /// TemplateNameLoc - The location of the template name within the
/// source. /// source.
SourceLocation TemplateNameLoc; SourceLocation TemplateNameLoc;
/// Template - The declaration of the template corresponding to the /// FIXME: Temporarily stores the name of a specialization
IdentifierInfo *Name;
/// The declaration of the template corresponding to the
/// template-name. This is an Action::DeclTy*. /// template-name. This is an Action::DeclTy*.
void *Template; void *Template;
/// LAngleLoc - The location of the '<' before the template argument /// The kind of template that Template refers to.
TemplateNameKind Kind;
/// The location of the '<' before the template argument
/// list. /// list.
SourceLocation LAngleLoc; SourceLocation LAngleLoc;
/// NumArgs - The number of template arguments. The arguments /// The location of the '>' after the template argument
/// themselves are Action::TemplateArgTy pointers allocated directly /// list.
/// following the TemplateIdAnnotation structure. SourceLocation RAngleLoc;
/// NumArgs - The number of template arguments.
unsigned NumArgs; unsigned NumArgs;
/// \brief Retrieves a pointer to the template arguments
void **getTemplateArgs() { return (void **)(this + 1); }
/// \brief Retrieves a pointer to the array of template argument
/// locations.
SourceLocation *getTemplateArgLocations() {
return (SourceLocation *)(getTemplateArgs() + NumArgs);
}
/// \brief Retrieves a pointer to the array of flags that states
/// whether the template arguments are types.
bool *getTemplateArgIsType() {
return (bool *)(getTemplateArgLocations() + NumArgs);
}
static TemplateIdAnnotation* Allocate(unsigned NumArgs) {
TemplateIdAnnotation *TemplateId
= (TemplateIdAnnotation *)malloc(sizeof(TemplateIdAnnotation) +
sizeof(void*) * NumArgs +
sizeof(SourceLocation) * NumArgs +
sizeof(bool) * NumArgs);
TemplateId->NumArgs = NumArgs;
return TemplateId;
}
void Destroy() { free(this); }
}; };
} // end namespace clang } // end namespace clang

View File

@ -16,6 +16,7 @@
#include "clang/Basic/IdentifierTable.h" #include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceLocation.h"
#include "clang/Basic/TemplateKinds.h"
#include "clang/Basic/TypeTraits.h" #include "clang/Basic/TypeTraits.h"
#include "clang/Parse/AccessSpecifier.h" #include "clang/Parse/AccessSpecifier.h"
#include "clang/Parse/Ownership.h" #include "clang/Parse/Ownership.h"
@ -134,20 +135,6 @@ public:
virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S, virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S,
const CXXScopeSpec *SS = 0) = 0; const CXXScopeSpec *SS = 0) = 0;
/// \brief Specifies the kind of template name. Returned from
/// isTemplateName.
enum TemplateNameKind {
/// The name does not refer to a template.
TNK_Non_template = 0,
/// The name refers to a function template or a set of overloaded
/// functions that includes at least one function template.
TNK_Function_template,
/// The name refers to a class template.
TNK_Class_template,
/// The name referes to a template template parameter.
TNK_Template_template_parm
};
/// \brief Determines whether the identifier II is a template name /// \brief Determines whether the identifier II is a template name
/// in the current scope. If so, the kind of template name is /// in the current scope. If so, the kind of template name is
/// returned, and \p TemplateDecl receives the declaration. An /// returned, and \p TemplateDecl receives the declaration. An
@ -178,6 +165,22 @@ public:
return 0; return 0;
} }
/// ActOnCXXNestedNameSpecifier - Called during parsing of a
/// nested-name-specifier that involves a template-id, e.g.,
/// "foo::bar<int, float>::", and now we need to build a scope
/// specifier. \p SS is empty or the previously parsed nested-name
/// part ("foo::"), \p Type is the already-parsed class template
/// specialization (or other template-id that names a type), \p
/// TypeRange is the source range where the type is located, and \p
/// CCLoc is the location of the trailing '::'.
virtual CXXScopeTy *ActOnCXXNestedNameSpecifier(Scope *S,
const CXXScopeSpec &SS,
TypeTy *Type,
SourceRange TypeRange,
SourceLocation CCLoc) {
return 0;
}
/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global /// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
/// scope or nested-name-specifier) is parsed, part of a declarator-id. /// scope or nested-name-specifier) is parsed, part of a declarator-id.
/// After this method is called, according to [C++ 3.4.3p3], names should be /// After this method is called, according to [C++ 3.4.3p3], names should be

View File

@ -538,7 +538,7 @@ namespace clang
void **Args; void **Args;
bool *ArgIsType; bool *ArgIsType;
mutable unsigned Count; mutable unsigned Count;
#if !defined(DISABLE_SMART_POINTERS) #if !defined(DISABLE_SMART_POINTERS)
void destroy() { void destroy() {
if (!Count) if (!Count)

View File

@ -993,7 +993,6 @@ private:
//===--------------------------------------------------------------------===// //===--------------------------------------------------------------------===//
// C++ 14: Templates [temp] // C++ 14: Templates [temp]
typedef llvm::SmallVector<DeclTy *, 4> TemplateParameterList; typedef llvm::SmallVector<DeclTy *, 4> TemplateParameterList;
typedef Action::TemplateNameKind TemplateNameKind;
// C++ 14.1: Template Parameters [temp.param] // C++ 14.1: Template Parameters [temp.param]
DeclTy *ParseTemplateDeclarationOrSpecialization(unsigned Context); DeclTy *ParseTemplateDeclarationOrSpecialization(unsigned Context);
@ -1023,7 +1022,10 @@ private:
SourceLocation &RAngleLoc); SourceLocation &RAngleLoc);
void AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK, void AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK,
const CXXScopeSpec *SS = 0); const CXXScopeSpec *SS,
SourceLocation TemplateKWLoc = SourceLocation(),
bool AllowTypeAnnotation = true);
bool AnnotateTemplateIdTokenAsType(const CXXScopeSpec *SS = 0);
bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs, bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs,
TemplateArgIsTypeList &TemplateArgIsType, TemplateArgIsTypeList &TemplateArgIsType,
TemplateArgLocationList &TemplateArgLocations); TemplateArgLocationList &TemplateArgLocations);

View File

@ -162,7 +162,7 @@ ClassTemplateSpecializationDecl(DeclContext *DC, SourceLocation L,
NumTemplateArgs(NumTemplateArgs), SpecializationKind(TSK_Undeclared) { NumTemplateArgs(NumTemplateArgs), SpecializationKind(TSK_Undeclared) {
TemplateArgument *Arg = reinterpret_cast<TemplateArgument *>(this + 1); TemplateArgument *Arg = reinterpret_cast<TemplateArgument *>(this + 1);
for (unsigned ArgIdx = 0; ArgIdx < NumTemplateArgs; ++ArgIdx, ++Arg) for (unsigned ArgIdx = 0; ArgIdx < NumTemplateArgs; ++ArgIdx, ++Arg)
*Arg = TemplateArgs[ArgIdx]; new (Arg) TemplateArgument(TemplateArgs[ArgIdx]);
} }
ClassTemplateSpecializationDecl * ClassTemplateSpecializationDecl *

View File

@ -95,7 +95,7 @@ bool MinimalAction::isCurrentClassName(const IdentifierInfo &, Scope *,
return false; return false;
} }
Action::TemplateNameKind TemplateNameKind
MinimalAction::isTemplateName(IdentifierInfo &II, Scope *S, MinimalAction::isTemplateName(IdentifierInfo &II, Scope *S,
DeclTy *&TemplateDecl, DeclTy *&TemplateDecl,
const CXXScopeSpec *SS) { const CXXScopeSpec *SS) {

View File

@ -558,21 +558,8 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
TypeTy *TypeRep = Actions.getTypeName(*Tok.getIdentifierInfo(), TypeTy *TypeRep = Actions.getTypeName(*Tok.getIdentifierInfo(),
Tok.getLocation(), CurScope); Tok.getLocation(), CurScope);
if (TypeRep == 0 && getLang().CPlusPlus && NextToken().is(tok::less)) {
// If we have a template name, annotate the token and try again.
DeclTy *Template = 0;
if (TemplateNameKind TNK =
Actions.isTemplateName(*Tok.getIdentifierInfo(), CurScope,
Template)) {
AnnotateTemplateIdToken(Template, TNK, 0);
continue;
}
}
if (TypeRep == 0) if (TypeRep == 0)
goto DoneWithDeclSpec; goto DoneWithDeclSpec;
// C++: If the identifier is actually the name of the class type // C++: If the identifier is actually the name of the class type
// being defined and the next token is a '(', then this is a // being defined and the next token is a '(', then this is a
@ -610,6 +597,25 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
// If a type specifier follows, it will be diagnosed elsewhere. // If a type specifier follows, it will be diagnosed elsewhere.
continue; continue;
} }
// type-name
case tok::annot_template_id: {
TemplateIdAnnotation *TemplateId
= static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
if (TemplateId->Kind != TNK_Class_template) {
// This template-id does not refer to a type name, so we're
// done with the type-specifiers.
goto DoneWithDeclSpec;
}
// Turn the template-id annotation token into a type annotation
// token, then try again to parse it as a type-specifier.
if (AnnotateTemplateIdTokenAsType())
DS.SetTypeSpecError();
continue;
}
// GNU attributes support. // GNU attributes support.
case tok::kw___attribute: case tok::kw___attribute:
DS.AddAttributes(ParseAttributes()); DS.AddAttributes(ParseAttributes());
@ -1749,7 +1755,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
/// operator-function-id /// operator-function-id
/// conversion-function-id [TODO] /// conversion-function-id [TODO]
/// '~' class-name /// '~' class-name
/// template-id [TODO] /// template-id
/// ///
void Parser::ParseDirectDeclarator(Declarator &D) { void Parser::ParseDirectDeclarator(Declarator &D) {
DeclaratorScopeObj DeclScopeObj(*this, D.getCXXScopeSpec()); DeclaratorScopeObj DeclScopeObj(*this, D.getCXXScopeSpec());
@ -1768,21 +1774,9 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
if (Tok.is(tok::identifier)) { if (Tok.is(tok::identifier)) {
assert(Tok.getIdentifierInfo() && "Not an identifier?"); assert(Tok.getIdentifierInfo() && "Not an identifier?");
// If this identifier is followed by a '<', we may have a template-id.
DeclTy *Template;
Action::TemplateNameKind TNK;
if (getLang().CPlusPlus && NextToken().is(tok::less) &&
(TNK = Actions.isTemplateName(*Tok.getIdentifierInfo(),
CurScope, Template))) {
IdentifierInfo *II = Tok.getIdentifierInfo();
AnnotateTemplateIdToken(Template, TNK, 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 // If this identifier is the name of the current class, it's a
// constructor name. // constructor name.
else if (Actions.isCurrentClassName(*Tok.getIdentifierInfo(),CurScope)){ if (Actions.isCurrentClassName(*Tok.getIdentifierInfo(),CurScope)){
D.setConstructor(Actions.getTypeName(*Tok.getIdentifierInfo(), D.setConstructor(Actions.getTypeName(*Tok.getIdentifierInfo(),
Tok.getLocation(), CurScope), Tok.getLocation(), CurScope),
Tok.getLocation()); Tok.getLocation());
@ -1791,6 +1785,21 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
D.SetIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); D.SetIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
ConsumeToken(); ConsumeToken();
goto PastIdentifier; goto PastIdentifier;
} else if (Tok.is(tok::annot_template_id)) {
TemplateIdAnnotation *TemplateId
= static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
// FIXME: Could this template-id name a constructor?
// FIXME: This is an egregious hack, where we silently ignore
// the specialization (which should be a function template
// specialization name) and use the name instead. This hack
// will go away when we have support for function
// specializations.
D.SetIdentifier(TemplateId->Name, Tok.getLocation());
TemplateId->Destroy();
ConsumeToken();
goto PastIdentifier;
} else if (Tok.is(tok::kw_operator)) { } else if (Tok.is(tok::kw_operator)) {
SourceLocation OperatorLoc = Tok.getLocation(); SourceLocation OperatorLoc = Tok.getLocation();
SourceLocation EndLoc; SourceLocation EndLoc;

View File

@ -309,75 +309,42 @@ void Parser::ParseClassSpecifier(DeclSpec &DS,
// Parse the (optional) nested-name-specifier. // Parse the (optional) nested-name-specifier.
CXXScopeSpec SS; CXXScopeSpec SS;
if (getLang().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS)) { if (getLang().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS))
// FIXME: can we get a class template specialization or if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id))
// template-id token here?
if (Tok.isNot(tok::identifier))
Diag(Tok, diag::err_expected_ident); Diag(Tok, diag::err_expected_ident);
}
// These variables encode the simple-template-id that we might end
// up parsing below. We don't translate this into a type
// automatically because (1) we want to create a separate
// declaration for each specialization, and (2) we want to retain
// more information about source locations that types provide.
DeclTy *Template = 0;
SourceLocation LAngleLoc, RAngleLoc;
TemplateArgList TemplateArgs;
TemplateArgIsTypeList TemplateArgIsType;
TemplateArgLocationList TemplateArgLocations;
ASTTemplateArgsPtr TemplateArgsPtr(Actions, 0, 0, 0);
// Parse the (optional) class name or simple-template-id. // Parse the (optional) class name or simple-template-id.
IdentifierInfo *Name = 0; IdentifierInfo *Name = 0;
SourceLocation NameLoc; SourceLocation NameLoc;
TemplateIdAnnotation *TemplateId = 0;
if (Tok.is(tok::identifier)) { if (Tok.is(tok::identifier)) {
Name = Tok.getIdentifierInfo(); Name = Tok.getIdentifierInfo();
NameLoc = ConsumeToken(); NameLoc = ConsumeToken();
} else if (Tok.is(tok::annot_template_id)) {
TemplateId = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
NameLoc = ConsumeToken();
if (Tok.is(tok::less)) { if (TemplateId->Kind != TNK_Class_template) {
// This is a simple-template-id. // The template-name in the simple-template-id refers to
Action::TemplateNameKind TNK // something other than a class template. Give an appropriate
= Actions.isTemplateName(*Name, CurScope, Template, &SS); // error message and skip to the ';'.
SourceRange Range(NameLoc);
if (SS.isNotEmpty())
Range.setBegin(SS.getBeginLoc());
bool Invalid = false; Diag(TemplateId->LAngleLoc, diag::err_template_spec_syntax_non_template)
<< Name << static_cast<int>(TemplateId->Kind) << Range;
// Parse the enclosed template argument list.
if (TNK != Action::TNK_Non_template)
Invalid = ParseTemplateIdAfterTemplateName(Template, NameLoc,
&SS, true, LAngleLoc,
TemplateArgs,
TemplateArgIsType,
TemplateArgLocations,
RAngleLoc);
TemplateArgsPtr.reset(&TemplateArgs[0], &TemplateArgIsType[0], DS.SetTypeSpecError();
TemplateArgs.size()); SkipUntil(tok::semi, false, true);
TemplateId->Destroy();
if (TNK != Action::TNK_Class_template) { return;
// The template-name in the simple-template-id refers to
// something other than a class template. Give an appropriate
// error message and skip to the ';'.
SourceRange Range(NameLoc);
if (SS.isNotEmpty())
Range.setBegin(SS.getBeginLoc());
else if (!Invalid)
Diag(LAngleLoc, diag::err_template_spec_syntax_non_template)
<< Name << static_cast<int>(TNK) << Range;
DS.SetTypeSpecError();
SkipUntil(tok::semi, false, true);
return;
}
} }
} }
// There are three options here. If we have 'struct foo;', then // There are three options here. If we have 'struct foo;', then
// this is a forward declaration. If we have 'struct foo {...' or // this is a forward declaration. If we have 'struct foo {...' or
// 'struct fo :...' then this is a definition. Otherwise we have // 'struct foo :...' then this is a definition. Otherwise we have
// something like 'struct foo xyz', a reference. // something like 'struct foo xyz', a reference.
Action::TagKind TK; Action::TagKind TK;
if (Tok.is(tok::l_brace) || (getLang().CPlusPlus && Tok.is(tok::colon))) if (Tok.is(tok::l_brace) || (getLang().CPlusPlus && Tok.is(tok::colon)))
@ -387,35 +354,43 @@ void Parser::ParseClassSpecifier(DeclSpec &DS,
else else
TK = Action::TK_Reference; TK = Action::TK_Reference;
if (!Name && TK != Action::TK_Definition) { if (!Name && !TemplateId && TK != Action::TK_Definition) {
// We have a declaration or reference to an anonymous class. // We have a declaration or reference to an anonymous class.
Diag(StartLoc, diag::err_anon_type_definition) Diag(StartLoc, diag::err_anon_type_definition)
<< DeclSpec::getSpecifierName(TagType); << DeclSpec::getSpecifierName(TagType);
// Skip the rest of this declarator, up until the comma or semicolon. // Skip the rest of this declarator, up until the comma or semicolon.
SkipUntil(tok::comma, true); SkipUntil(tok::comma, true);
if (TemplateId)
TemplateId->Destroy();
return; return;
} }
// Create the tag portion of the class or class template. // Create the tag portion of the class or class template.
DeclTy *TagOrTempDecl; DeclTy *TagOrTempDecl;
if (Template && TK != Action::TK_Reference) if (TemplateId && TK != Action::TK_Reference) {
// Explicit specialization or class template partial // Explicit specialization or class template partial
// specialization. Let semantic analysis decide. // specialization. Let semantic analysis decide.
ASTTemplateArgsPtr TemplateArgsPtr(Actions,
// FIXME: we want a source range covering the simple-template-id. TemplateId->getTemplateArgs(),
TemplateId->getTemplateArgIsType(),
TemplateId->NumArgs);
TagOrTempDecl TagOrTempDecl
= Actions.ActOnClassTemplateSpecialization(CurScope, TagType, TK, = Actions.ActOnClassTemplateSpecialization(CurScope, TagType, TK,
StartLoc, SS, /*Range*/ StartLoc, SS,
Template, NameLoc, TemplateId->Template,
LAngleLoc, TemplateArgsPtr, TemplateId->TemplateNameLoc,
&TemplateArgLocations[0], TemplateId->LAngleLoc,
RAngleLoc, Attr, TemplateArgsPtr,
TemplateId->getTemplateArgLocations(),
TemplateId->RAngleLoc,
Attr,
Action::MultiTemplateParamsArg(Actions, Action::MultiTemplateParamsArg(Actions,
TemplateParams? &(*TemplateParams)[0] : 0, TemplateParams? &(*TemplateParams)[0] : 0,
TemplateParams? TemplateParams->size() : 0)); TemplateParams? TemplateParams->size() : 0));
TemplateId->Destroy();
else if (TemplateParams && TK != Action::TK_Reference) } else if (TemplateParams && TK != Action::TK_Reference)
TagOrTempDecl = Actions.ActOnClassTemplate(CurScope, TagType, TK, StartLoc, TagOrTempDecl = Actions.ActOnClassTemplate(CurScope, TagType, TK, StartLoc,
SS, Name, NameLoc, Attr, SS, Name, NameLoc, Attr,
Action::MultiTemplateParamsArg(Actions, Action::MultiTemplateParamsArg(Actions,

View File

@ -42,6 +42,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS) {
return true; return true;
} }
bool HasScopeSpecifier = false;
if (Tok.is(tok::coloncolon)) { if (Tok.is(tok::coloncolon)) {
// ::new and ::delete aren't nested-name-specifiers. // ::new and ::delete aren't nested-name-specifiers.
tok::TokenKind NextKind = NextToken().getKind(); tok::TokenKind NextKind = NextToken().getKind();
@ -53,32 +55,132 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS) {
SS.setBeginLoc(CCLoc); SS.setBeginLoc(CCLoc);
SS.setScopeRep(Actions.ActOnCXXGlobalScopeSpecifier(CurScope, CCLoc)); SS.setScopeRep(Actions.ActOnCXXGlobalScopeSpecifier(CurScope, CCLoc));
SS.setEndLoc(CCLoc); SS.setEndLoc(CCLoc);
} else if (Tok.is(tok::identifier) && NextToken().is(tok::coloncolon)) { HasScopeSpecifier = true;
SS.setBeginLoc(Tok.getLocation());
} else {
// Not a CXXScopeSpecifier.
return false;
} }
// nested-name-specifier: while (true) {
// type-name '::' // nested-name-specifier:
// namespace-name '::' // type-name '::'
// nested-name-specifier identifier '::' // namespace-name '::'
// nested-name-specifier 'template'[opt] simple-template-id '::' [TODO] // nested-name-specifier identifier '::'
while (Tok.is(tok::identifier) && NextToken().is(tok::coloncolon)) { if (Tok.is(tok::identifier) && NextToken().is(tok::coloncolon)) {
IdentifierInfo *II = Tok.getIdentifierInfo(); // We have an identifier followed by a '::'. Lookup this name
SourceLocation IdLoc = ConsumeToken(); // as the name in a nested-name-specifier.
assert(Tok.is(tok::coloncolon) && "NextToken() not working properly!"); IdentifierInfo *II = Tok.getIdentifierInfo();
SourceLocation CCLoc = ConsumeToken(); SourceLocation IdLoc = ConsumeToken();
if (SS.isInvalid()) assert(Tok.is(tok::coloncolon) && "NextToken() not working properly!");
SourceLocation CCLoc = ConsumeToken();
if (!HasScopeSpecifier) {
SS.setBeginLoc(IdLoc);
HasScopeSpecifier = true;
}
if (SS.isInvalid())
continue;
SS.setScopeRep(
Actions.ActOnCXXNestedNameSpecifier(CurScope, SS, IdLoc, CCLoc, *II));
SS.setEndLoc(CCLoc);
continue; continue;
}
SS.setScopeRep( // nested-name-specifier:
Actions.ActOnCXXNestedNameSpecifier(CurScope, SS, IdLoc, CCLoc, *II)); // type-name '::'
SS.setEndLoc(CCLoc); // nested-name-specifier 'template'[opt] simple-template-id '::'
if ((Tok.is(tok::identifier) && NextToken().is(tok::less)) ||
Tok.is(tok::kw_template)) {
// Parse the optional 'template' keyword, then make sure we have
// 'identifier <' after it.
SourceLocation TemplateKWLoc;
if (Tok.is(tok::kw_template)) {
TemplateKWLoc = ConsumeToken();
if (Tok.isNot(tok::identifier)) {
Diag(Tok.getLocation(),
diag::err_id_after_template_in_nested_name_spec)
<< SourceRange(TemplateKWLoc);
break;
}
if (NextToken().isNot(tok::less)) {
Diag(NextToken().getLocation(),
diag::err_less_after_template_name_in_nested_name_spec)
<< Tok.getIdentifierInfo()->getName()
<< SourceRange(TemplateKWLoc, Tok.getLocation());
break;
}
}
else {
// FIXME: If the nested-name-specifier thus far is dependent,
// we need to break out of here, because this '<' is taken as
// an operator and not as part of a simple-template-id.
}
DeclTy *Template = 0;
TemplateNameKind TNK = TNK_Non_template;
// FIXME: If the nested-name-specifier thus far is dependent,
// set TNK = TNK_Dependent_template_name and skip the
// "isTemplateName" check.
TNK = Actions.isTemplateName(*Tok.getIdentifierInfo(),
CurScope, Template, &SS);
if (TNK) {
// We have found a template name, so annotate this this token
// with a template-id annotation. We do not permit the
// template-id to be translated into a type annotation,
// because some clients (e.g., the parsing of class template
// specializations) still want to see the original template-id
// token.
AnnotateTemplateIdToken(Template, TNK, &SS, TemplateKWLoc, false);
continue;
}
}
if (Tok.is(tok::annot_template_id) && NextToken().is(tok::coloncolon)) {
// We have
//
// simple-template-id '::'
//
// So we need to check whether the simple-template-id is of the
// right kind (it should name a type), and then convert it into
// a type within the nested-name-specifier.
TemplateIdAnnotation *TemplateId
= static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
if (TemplateId->Kind == TNK_Class_template) {
if (AnnotateTemplateIdTokenAsType(&SS))
SS.setScopeRep(0);
assert(Tok.is(tok::annot_typename) &&
"AnnotateTemplateIdTokenAsType isn't working");
Token TypeToken = Tok;
ConsumeToken();
assert(Tok.is(tok::coloncolon) && "NextToken() not working properly!");
SourceLocation CCLoc = ConsumeToken();
if (!HasScopeSpecifier) {
SS.setBeginLoc(TypeToken.getLocation());
HasScopeSpecifier = true;
}
SS.setScopeRep(
Actions.ActOnCXXNestedNameSpecifier(CurScope, SS,
TypeToken.getAnnotationValue(),
TypeToken.getAnnotationRange(),
CCLoc));
SS.setEndLoc(CCLoc);
continue;
} else
assert(false && "FIXME: Only class template names supported here");
}
// We don't have any tokens that form the beginning of a
// nested-name-specifier, so we're done.
break;
} }
return true; return HasScopeSpecifier;
} }
/// ParseCXXIdExpression - Handle id-expression. /// ParseCXXIdExpression - Handle id-expression.

View File

@ -444,16 +444,49 @@ Parser::ParseTemplateIdAfterTemplateName(DeclTy *Template,
return false; return false;
} }
/// AnnotateTemplateIdToken - The current token is an identifier that /// \brief Replace the tokens that form a simple-template-id with an
/// refers to the template declaration Template, and is followed by a /// annotation token containing the complete template-id.
/// '<'. Turn this template-id into a template-id annotation token. ///
/// The first token in the stream must be the name of a template that
/// is followed by a '<'. This routine will parse the complete
/// simple-template-id and replace the tokens with a single annotation
/// token with one of two different kinds: if the template-id names a
/// type (and \p AllowTypeAnnotation is true), the annotation token is
/// a type annotation that includes the optional nested-name-specifier
/// (\p SS). Otherwise, the annotation token is a template-id
/// annotation that does not include the optional
/// nested-name-specifier.
///
/// \param Template the declaration of the template named by the first
/// token (an identifier), as returned from \c Action::isTemplateName().
///
/// \param TemplateNameKind the kind of template that \p Template
/// refers to, as returned from \c Action::isTemplateName().
///
/// \param SS if non-NULL, the nested-name-specifier that precedes
/// this template name.
///
/// \param TemplateKWLoc if valid, specifies that this template-id
/// annotation was preceded by the 'template' keyword and gives the
/// location of that keyword. If invalid (the default), then this
/// template-id was not preceded by a 'template' keyword.
///
/// \param AllowTypeAnnotation if true (the default), then a
/// simple-template-id that refers to a class template, template
/// template parameter, or other template that produces a type will be
/// replaced with a type annotation token. Otherwise, the
/// simple-template-id is always replaced with a template-id
/// annotation token.
void Parser::AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK, void Parser::AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK,
const CXXScopeSpec *SS) { const CXXScopeSpec *SS,
SourceLocation TemplateKWLoc,
bool AllowTypeAnnotation) {
assert(getLang().CPlusPlus && "Can only annotate template-ids in C++"); assert(getLang().CPlusPlus && "Can only annotate template-ids in C++");
assert(Template && Tok.is(tok::identifier) && NextToken().is(tok::less) && assert(Template && Tok.is(tok::identifier) && NextToken().is(tok::less) &&
"Parser isn't at the beginning of a template-id"); "Parser isn't at the beginning of a template-id");
// Consume the template-name. // Consume the template-name.
IdentifierInfo *Name = Tok.getIdentifierInfo();
SourceLocation TemplateNameLoc = ConsumeToken(); SourceLocation TemplateNameLoc = ConsumeToken();
// Parse the enclosed template argument list. // Parse the enclosed template argument list.
@ -476,7 +509,7 @@ void Parser::AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK,
return; return;
// Build the annotation token. // Build the annotation token.
if (TNK == Action::TNK_Class_template) { if (TNK == TNK_Class_template && AllowTypeAnnotation) {
Action::TypeResult Type Action::TypeResult Type
= Actions.ActOnClassTemplateId(Template, TemplateNameLoc, = Actions.ActOnClassTemplateId(Template, TemplateNameLoc,
LAngleLoc, TemplateArgsPtr, LAngleLoc, TemplateArgsPtr,
@ -487,34 +520,96 @@ void Parser::AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK,
Tok.setKind(tok::annot_typename); Tok.setKind(tok::annot_typename);
Tok.setAnnotationValue(Type.get()); Tok.setAnnotationValue(Type.get());
if (SS && SS->isNotEmpty())
Tok.setLocation(SS->getBeginLoc());
else if (TemplateKWLoc.isValid())
Tok.setLocation(TemplateKWLoc);
else
Tok.setLocation(TemplateNameLoc);
} else { } else {
// This is a function template. We'll be building a template-id // This is a function template. We'll be building a template-id
// annotation token. // annotation token.
Tok.setKind(tok::annot_template_id); Tok.setKind(tok::annot_template_id);
TemplateIdAnnotation *TemplateId TemplateIdAnnotation *TemplateId
= (TemplateIdAnnotation *)malloc(sizeof(TemplateIdAnnotation) + = TemplateIdAnnotation::Allocate(TemplateArgs.size());
sizeof(void*) * TemplateArgs.size());
TemplateId->TemplateNameLoc = TemplateNameLoc; TemplateId->TemplateNameLoc = TemplateNameLoc;
TemplateId->Name = Name;
TemplateId->Template = Template; TemplateId->Template = Template;
TemplateId->Kind = TNK;
TemplateId->LAngleLoc = LAngleLoc; TemplateId->LAngleLoc = LAngleLoc;
TemplateId->NumArgs = TemplateArgs.size(); TemplateId->RAngleLoc = RAngleLoc;
void **Args = (void**)(TemplateId + 1); void **Args = TemplateId->getTemplateArgs();
for (unsigned Arg = 0, ArgEnd = TemplateArgs.size(); Arg != ArgEnd; ++Arg) bool *ArgIsType = TemplateId->getTemplateArgIsType();
SourceLocation *ArgLocs = TemplateId->getTemplateArgLocations();
for (unsigned Arg = 0, ArgEnd = TemplateArgs.size(); Arg != ArgEnd; ++Arg) {
Args[Arg] = TemplateArgs[Arg]; Args[Arg] = TemplateArgs[Arg];
ArgIsType[Arg] = TemplateArgIsType[Arg];
ArgLocs[Arg] = TemplateArgLocations[Arg];
}
Tok.setAnnotationValue(TemplateId); Tok.setAnnotationValue(TemplateId);
if (TemplateKWLoc.isValid())
Tok.setLocation(TemplateKWLoc);
else
Tok.setLocation(TemplateNameLoc);
TemplateArgsPtr.release();
} }
// Common fields for the annotation token // Common fields for the annotation token
Tok.setAnnotationEndLoc(RAngleLoc); Tok.setAnnotationEndLoc(RAngleLoc);
Tok.setLocation(TemplateNameLoc);
if (SS && SS->isNotEmpty())
Tok.setLocation(SS->getBeginLoc());
// In case the tokens were cached, have Preprocessor replace them with the // In case the tokens were cached, have Preprocessor replace them with the
// annotation token. // annotation token.
PP.AnnotateCachedTokens(Tok); PP.AnnotateCachedTokens(Tok);
} }
/// \brief Replaces a template-id annotation token with a type
/// annotation token.
///
/// \returns true if there was an error, false otherwise.
bool Parser::AnnotateTemplateIdTokenAsType(const CXXScopeSpec *SS) {
assert(Tok.is(tok::annot_template_id) && "Requires template-id tokens");
TemplateIdAnnotation *TemplateId
= static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
assert(TemplateId->Kind == TNK_Class_template &&
"Only works for class templates");
ASTTemplateArgsPtr TemplateArgsPtr(Actions,
TemplateId->getTemplateArgs(),
TemplateId->getTemplateArgIsType(),
TemplateId->NumArgs);
Action::TypeResult Type
= Actions.ActOnClassTemplateId(TemplateId->Template,
TemplateId->TemplateNameLoc,
TemplateId->LAngleLoc,
TemplateArgsPtr,
TemplateId->getTemplateArgLocations(),
TemplateId->RAngleLoc, SS);
if (Type.isInvalid()) {
// FIXME: better recovery?
ConsumeToken();
TemplateId->Destroy();
return true;
}
// Create the new "type" annotation token.
Tok.setKind(tok::annot_typename);
Tok.setAnnotationValue(Type.get());
if (SS && SS->isNotEmpty()) // it was a C++ qualified type name.
Tok.setLocation(SS->getBeginLoc());
// We might be backtracking, in which case we need to replace the
// template-id annotation token with the type annotation within the
// set of cached tokens. That way, we won't try to form the same
// class template specialization again.
PP.ReplaceLastTokenWithAnnotation(Tok);
TemplateId->Destroy();
return false;
}
/// ParseTemplateArgument - Parse a C++ template argument (C++ [temp.names]). /// ParseTemplateArgument - Parse a C++ template argument (C++ [temp.names]).
/// ///
/// template-argument: [C++ 14.2] /// template-argument: [C++ 14.2]

View File

@ -778,31 +778,41 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
// them with the annotation token. // them with the annotation token.
PP.AnnotateCachedTokens(Tok); PP.AnnotateCachedTokens(Tok);
return true; return true;
} else if (!getLang().CPlusPlus) { }
if (!getLang().CPlusPlus) {
// If we're in C, we can't have :: tokens at all (the lexer won't return // If we're in C, we can't have :: tokens at all (the lexer won't return
// them). If the identifier is not a type, then it can't be scope either, // them). If the identifier is not a type, then it can't be scope either,
// just early exit. // just early exit.
return false; return false;
} }
// If this is a template-id, annotate the template-id token. // If this is a template-id, annotate with a template-id or type token.
if (NextToken().is(tok::less)) { if (NextToken().is(tok::less)) {
DeclTy *Template; DeclTy *Template;
if (TemplateNameKind TNK if (TemplateNameKind TNK
= Actions.isTemplateName(*Tok.getIdentifierInfo(), = Actions.isTemplateName(*Tok.getIdentifierInfo(),
CurScope, Template, &SS)) { CurScope, Template, &SS))
AnnotateTemplateIdToken(Template, TNK, &SS); AnnotateTemplateIdToken(Template, TNK, &SS);
return true;
}
} }
// We either have an identifier that is not a type name or we have // The current token, which is either an identifier or a
// just created a template-id that might be a type name. Both // template-id, is not part of the annotation. Fall through to
// cases will be handled below. // push that token back into the stream and complete the C++ scope
// specifier annotation.
} }
// FIXME: check for a template-id token here, and look it up if it if (Tok.is(tok::annot_template_id)) {
// names a type. TemplateIdAnnotation *TemplateId
= static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
if (TemplateId->Kind == TNK_Class_template) {
// A template-id that refers to a type was parsed into a
// template-id annotation in a context where we weren't allowed
// to produce a type annotation token. Update the template-id
// annotation token to a type annotation token now.
return !AnnotateTemplateIdTokenAsType(&SS);
}
}
if (SS.isEmpty()) if (SS.isEmpty())
return false; return false;
@ -825,8 +835,8 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
} }
/// TryAnnotateScopeToken - Like TryAnnotateTypeOrScopeToken but only /// TryAnnotateScopeToken - Like TryAnnotateTypeOrScopeToken but only
/// annotates C++ scope specifiers. This returns true if the token was /// annotates C++ scope specifiers and template-ids. This returns
/// annotated. /// true if the token was annotated.
/// ///
/// Note that this routine emits an error if you call it with ::new or ::delete /// Note that this routine emits an error if you call it with ::new or ::delete
/// as the current tokens, so only call it in contexts where these are invalid. /// as the current tokens, so only call it in contexts where these are invalid.
@ -838,7 +848,7 @@ bool Parser::TryAnnotateCXXScopeToken() {
CXXScopeSpec SS; CXXScopeSpec SS;
if (!ParseOptionalCXXScopeSpecifier(SS)) if (!ParseOptionalCXXScopeSpecifier(SS))
return false; return Tok.is(tok::annot_template_id);
// Push the current token back into the token stream (or revert it if it is // Push the current token back into the token stream (or revert it if it is
// cached) and use an annotation scope token for current token. // cached) and use an annotation scope token for current token.

View File

@ -1338,6 +1338,20 @@ public:
SourceLocation CCLoc, SourceLocation CCLoc,
IdentifierInfo &II); IdentifierInfo &II);
/// ActOnCXXNestedNameSpecifier - Called during parsing of a
/// nested-name-specifier that involves a template-id, e.g.,
/// "foo::bar<int, float>::", and now we need to build a scope
/// specifier. \p SS is empty or the previously parsed nested-name
/// part ("foo::"), \p Type is the already-parsed class template
/// specialization (or other template-id that names a type), \p
/// TypeRange is the source range where the type is located, and \p
/// CCLoc is the location of the trailing '::'.
virtual CXXScopeTy *ActOnCXXNestedNameSpecifier(Scope *S,
const CXXScopeSpec &SS,
TypeTy *Type,
SourceRange TypeRange,
SourceLocation CCLoc);
/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global /// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
/// scope or nested-name-specifier) is parsed, part of a declarator-id. /// scope or nested-name-specifier) is parsed, part of a declarator-id.
/// After this method is called, according to [C++ 3.4.3p3], names should be /// After this method is called, according to [C++ 3.4.3p3], names should be

View File

@ -74,6 +74,17 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
return 0; return 0;
} }
Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
const CXXScopeSpec &SS,
TypeTy *Ty,
SourceRange TypeRange,
SourceLocation CCLoc) {
QualType Type = QualType::getFromOpaquePtr(Ty);
assert(Type->isRecordType() &&
"Types in a nested-name-specifier always refer to a record type");
return cast<DeclContext>(Type->getAsRecordType()->getDecl());
}
/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global /// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
/// scope or nested-name-specifier) is parsed, part of a declarator-id. /// scope or nested-name-specifier) is parsed, part of a declarator-id.
/// After this method is called, according to [C++ 3.4.3p3], names should be /// After this method is called, according to [C++ 3.4.3p3], names should be

View File

@ -26,9 +26,9 @@ using namespace clang;
/// declaration if II names a template. An optional CXXScope can be /// declaration if II names a template. An optional CXXScope can be
/// passed to indicate the C++ scope in which the identifier will be /// passed to indicate the C++ scope in which the identifier will be
/// found. /// found.
Sema::TemplateNameKind Sema::isTemplateName(IdentifierInfo &II, Scope *S, TemplateNameKind Sema::isTemplateName(IdentifierInfo &II, Scope *S,
DeclTy *&Template, DeclTy *&Template,
const CXXScopeSpec *SS) { const CXXScopeSpec *SS) {
NamedDecl *IIDecl = LookupParsedName(S, SS, &II, LookupOrdinaryName); NamedDecl *IIDecl = LookupParsedName(S, SS, &II, LookupOrdinaryName);
if (IIDecl) { if (IIDecl) {

View File

@ -40,3 +40,5 @@ void testme(X<int_type> *x1, X<float, int> *x2) {
x1->foo(); // okay: refers to #1 x1->foo(); // okay: refers to #1
x2->bar(); // okay: refers to #2 x2->bar(); // okay: refers to #2
} }
// FIXME: diagnose specializations in a different namespace

View File

@ -5,8 +5,7 @@ template<typename T, int N = 2> struct X; // expected-note{{template is declared
X<int, 1> *x1; X<int, 1> *x1;
X<int> *x2; X<int> *x2;
X<> *x3; // expected-error{{too few template arguments for class template 'X'}} \ X<> *x3; // expected-error{{too few template arguments for class template 'X'}}
// FIXME: expected-error{{expected unqualified-id}}
template<typename U = float, int M> struct X; template<typename U = float, int M> struct X;

View File

@ -0,0 +1,50 @@
// RUN: clang -fsyntax-only -verify %s
namespace N {
namespace M {
template<typename T> struct Promote; // expected-note{{previous definition is here}}
template<> struct Promote<short> {
typedef int type;
};
template<> struct Promote<int> {
typedef int type;
};
template<> struct Promote<float> {
typedef double type;
};
Promote<short>::type *ret_intptr(int* ip) { return ip; }
Promote<int>::type *ret_intptr2(int* ip) { return ip; }
}
M::Promote<int>::type *ret_intptr3(int* ip) { return ip; }
M::template Promote<int>::type *ret_intptr4(int* ip) { return ip; }
}
N::M::Promote<int>::type *ret_intptr5(int* ip) { return ip; }
::N::M::Promote<int>::type *ret_intptr6(int* ip) { return ip; }
N::M::template; // expected-error{{expected template name after 'template' keyword in nested name specifier}} \
// expected-error{{expected unqualified-id}}
N::M::template Promote; // expected-error{{expected '<' after 'template Promote' in nested name specifier}} \
// expected-error{{C++ requires a type specifier for all declarations}} \
// expected-error{{redefinition of 'Promote' as different kind of symbol}} \
// expected-error{{no member named 'Promote'}}
namespace N {
template<typename T> struct A;
template<>
struct A<int> {
struct X;
};
}
struct ::N::A<int>::X {
int foo;
};

View File

@ -8,7 +8,5 @@ template<typename> class X;
A<int, 0, X> * a1; A<int, 0, X> * a1;
A<float, 1, X, double> *a2; // expected-error{{too many template arguments for class template 'A'}} \ A<float, 1, X, double> *a2; // expected-error{{too many template arguments for class template 'A'}}
// expected-error{{unqualified-id}} A<float, 1> *a3; // expected-error{{too few template arguments for class template 'A'}}
A<float, 1> *a3; // expected-error{{too few template arguments for class template 'A'}} \
// expected-error{{unqualified-id}}

View File

@ -3,26 +3,21 @@ template<int N> struct A; // expected-note 5{{template parameter is declared her
A<0> *a0; A<0> *a0;
A<int()> *a1; // expected-error{{template argument for non-type template parameter is treated as type 'int (void)'}} \ A<int()> *a1; // expected-error{{template argument for non-type template parameter is treated as type 'int (void)'}}
// FIXME: expected-error{{unqualified-id}}
A<int> *a2; // expected-error{{template argument for non-type template parameter must be an expression}} \ A<int> *a2; // expected-error{{template argument for non-type template parameter must be an expression}}
// FIXME: expected-error{{unqualified-id}}
A<1 >> 2> *a3; A<1 >> 2> *a3;
// C++ [temp.arg.nontype]p5: // C++ [temp.arg.nontype]p5:
A<A> *a4; // expected-error{{must have an integral or enumeration type}} \ A<A> *a4; // expected-error{{must have an integral or enumeration type}} \
// FIXME: the error message above is a bit lame \ // FIXME: the error message above is a bit lame
// FIXME: expected-error{{expected unqualified-id}}
enum E { Enumerator = 17 }; enum E { Enumerator = 17 };
A<E> *a5; // expected-error{{template argument for non-type template parameter must be an expression}} \ A<E> *a5; // expected-error{{template argument for non-type template parameter must be an expression}}
// FIXME: expected-error{{unqualified-id}}
template<E Value> struct A1; // expected-note{{template parameter is declared here}} template<E Value> struct A1; // expected-note{{template parameter is declared here}}
A1<Enumerator> *a6; // okay A1<Enumerator> *a6; // okay
A1<17> *a7; // expected-error{{non-type template argument of type 'int' cannot be converted to a value of type 'enum E'}} \ A1<17> *a7; // expected-error{{non-type template argument of type 'int' cannot be converted to a value of type 'enum E'}}
// FIXME: expected-error{{expected unqualified-id}}
const long LongValue = 12345678; const long LongValue = 12345678;
A<LongValue> *a8; A<LongValue> *a8;
@ -30,8 +25,7 @@ const short ShortValue = 17;
A<ShortValue> *a9; A<ShortValue> *a9;
int f(int); int f(int);
A<f(17)> *a10; // expected-error{{non-type template argument of type 'int' is not an integral constant expression}} \ A<f(17)> *a10; // expected-error{{non-type template argument of type 'int' is not an integral constant expression}}
// FIXME: expected-error{{expected unqualified-id}}
class X { class X {
public: public:
@ -39,8 +33,7 @@ public:
X(int, int); X(int, int);
operator int() const; operator int() const;
}; };
A<X(17, 42)> *a11; // expected-error{{non-type template argument of type 'class X' must have an integral or enumeration type}} \ A<X(17, 42)> *a11; // expected-error{{non-type template argument of type 'class X' must have an integral or enumeration type}}
// FIXME:expected-error{{expected unqualified-id}}
template<X const *Ptr> struct A2; template<X const *Ptr> struct A2;
@ -50,8 +43,7 @@ X array_of_Xs[10];
A2<X_ptr> *a12; A2<X_ptr> *a12;
A2<array_of_Xs> *a13; A2<array_of_Xs> *a13;
A2<&an_X> *a13_2; A2<&an_X> *a13_2;
A2<(&an_X)> *a13_2; // expected-error{{non-type template argument cannot be surrounded by parentheses}} \ A2<(&an_X)> *a13_3; // expected-error{{non-type template argument cannot be surrounded by parentheses}}
// FIXME: expected-error{{unqualified-id}}
float f(float); float f(float);
@ -66,10 +58,8 @@ A3<h> *a14_1;
A3<&h> *a14_2; A3<&h> *a14_2;
A3<f> *a14_3; A3<f> *a14_3;
A3<&f> *a14_4; A3<&f> *a14_4;
A3<h2> *a14_6; // expected-error{{non-type template argument of type 'float (*)(float)' cannot be converted to a value of type 'int (*)(int)'}} \ A3<h2> *a14_6; // expected-error{{non-type template argument of type 'float (*)(float)' cannot be converted to a value of type 'int (*)(int)'}}
// FIXME: expected-error{{expected unqualified-id}} A3<g> *a14_7; // expected-error{{non-type template argument of type '<overloaded function type>' cannot be converted to a value of type 'int (*)(int)'}}
A3<g> *a14_7; // expected-error{{non-type template argument of type '<overloaded function type>' cannot be converted to a value of type 'int (*)(int)'}}\
// FIXME: expected-error{{expected unqualified-id}}
// FIXME: the first error includes the string <overloaded function // FIXME: the first error includes the string <overloaded function
// type>, which makes Doug slightly unhappy. // type>, which makes Doug slightly unhappy.
@ -79,18 +69,15 @@ struct Y { } y;
volatile X * X_volatile_ptr; volatile X * X_volatile_ptr;
template<X const &AnX> struct A4; // expected-note 2{{template parameter is declared here}} template<X const &AnX> struct A4; // expected-note 2{{template parameter is declared here}}
A4<an_X> *a15_1; // okay A4<an_X> *a15_1; // okay
A4<*X_volatile_ptr> *a15_2; // expected-error{{reference binding of non-type template parameter of type 'class X const &' to template argument of type 'class X volatile' ignores qualifiers}} \ A4<*X_volatile_ptr> *a15_2; // expected-error{{reference binding of non-type template parameter of type 'class X const &' to template argument of type 'class X volatile' ignores qualifiers}}
// FIXME: expected-error{{expected unqualified-id}} A4<y> *15_3; // expected-error{{non-type template parameter of reference type 'class X const &' cannot bind to template argument of type 'struct Y'}} \
A4<y> *15_3; // expected-error{{non-type template parameter of reference type 'class X const &' cannot bind to template argument of type 'struct Y'}}\ // FIXME: expected-error{{expected unqualified-id}}
// FIXME: expected-error{{expected unqualified-id}}
template<int (&fr)(int)> struct A5; // expected-note 2{{template parameter is declared here}} template<int (&fr)(int)> struct A5; // expected-note 2{{template parameter is declared here}}
A5<h> *a16_1; A5<h> *a16_1;
A5<f> *a16_3; A5<f> *a16_3;
A5<h2> *a16_6; // expected-error{{non-type template argument of type 'float (float)' cannot be converted to a value of type 'int (&)(int)'}} \ A5<h2> *a16_6; // expected-error{{non-type template argument of type 'float (float)' cannot be converted to a value of type 'int (&)(int)'}}
// FIXME: expected-error{{expected unqualified-id}} A5<g> *a14_7; // expected-error{{non-type template argument of type '<overloaded function type>' cannot be converted to a value of type 'int (&)(int)'}}
A5<g> *a14_7; // expected-error{{non-type template argument of type '<overloaded function type>' cannot be converted to a value of type 'int (&)(int)'}}\
// FIXME: expected-error{{expected unqualified-id}}
// FIXME: the first error includes the string <overloaded function // FIXME: the first error includes the string <overloaded function
// type>, which makes Doug slightly unhappy. // type>, which makes Doug slightly unhappy.
@ -106,15 +93,12 @@ struct Z {
template<int (Z::*pmf)(int)> struct A6; // expected-note{{template parameter is declared here}} template<int (Z::*pmf)(int)> struct A6; // expected-note{{template parameter is declared here}}
A6<&Z::foo> *a17_1; A6<&Z::foo> *a17_1;
A6<&Z::bar> *a17_2; A6<&Z::bar> *a17_2;
A6<&Z::baz> *a17_3; // expected-error{{non-type template argument of type 'double (struct Z::*)(double)' cannot be converted to a value of type 'int (struct Z::*)(int)'}} \ A6<&Z::baz> *a17_3; // expected-error{{non-type template argument of type 'double (struct Z::*)(double)' cannot be converted to a value of type 'int (struct Z::*)(int)'}}
// FIXME: expected-error{{expected unqualified-id}}
template<int Z::*pm> struct A7; // expected-note{{template parameter is declared here}} template<int Z::*pm> struct A7; // expected-note{{template parameter is declared here}}
template<int Z::*pm> struct A7c; template<int Z::*pm> struct A7c;
A7<&Z::int_member> *a18_1; A7<&Z::int_member> *a18_1;
A7c<&Z::int_member> *a18_2; A7c<&Z::int_member> *a18_2;
A7<&Z::float_member> *a18_3; // expected-error{{non-type template argument of type 'float struct Z::*' cannot be converted to a value of type 'int struct Z::*'}} \ A7<&Z::float_member> *a18_3; // expected-error{{non-type template argument of type 'float struct Z::*' cannot be converted to a value of type 'int struct Z::*'}}
// FIXME: expected-error{{unqualified-id}} A7c<(&Z::int_member)> *a18_3; // expected-error{{non-type template argument cannot be surrounded by parentheses}}
A7c<(&Z::int_member)> *a18_3; // expected-error{{non-type template argument cannot be surrounded by parentheses}} \
// FIXME: expected-error{{expected unqualified-id}}

View File

@ -20,23 +20,18 @@ A<X> *a1;
A<N::Z> *a2; A<N::Z> *a2;
A< ::N::Z> *a3; A< ::N::Z> *a3;
A<Y> *a4; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}} \ A<Y> *a4; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
// FIXME::expected-error{{expected unqualified-id}} A<TooMany> *a5; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
A<TooMany> *a5; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}} \ B<X> *a6; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
// FIXME::expected-error{{expected unqualified-id}}
B<X> *a6; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}} \
// FIXME::expected-error{{expected unqualified-id}}
C<Y> *a7; C<Y> *a7;
C<Ylong> *a8; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}} \ C<Ylong> *a8; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
// FIXME::expected-error{{expected unqualified-id}}
template<typename T> void f(int); template<typename T> void f(int);
// FIXME: we're right to provide an error message, but it should say // FIXME: we're right to provide an error message, but it should say
// that we need a class template. We won't get this right until name // that we need a class template. We won't get this right until name
// lookup of 'f' returns a TemplateDecl. // lookup of 'f' returns a TemplateDecl.
A<f> *a9; // expected-error{{template argument for template template parameter must be a template}} \ A<f> *a9; // expected-error{{template argument for template template parameter must be a template}}
// expected-error{{unqualified-id}}
// FIXME: The code below is ill-formed, because of the evil digraph '<:'. // FIXME: The code below is ill-formed, because of the evil digraph '<:'.
// We should provide a much better error message than we currently do. // We should provide a much better error message than we currently do.

View File

@ -2,11 +2,9 @@
template<typename T> class A; // expected-note 2 {{template parameter is declared here}} template<typename T> class A; // expected-note 2 {{template parameter is declared here}}
// [temp.arg.type]p1 // [temp.arg.type]p1
A<0> *a1; // expected-error{{template argument for template type parameter must be a type}} \ A<0> *a1; // expected-error{{template argument for template type parameter must be a type}}
// expected-error{{unqualified-id}}
A<A> *a2; // expected-error{{template argument for template type parameter must be a type}} \ A<A> *a2; // expected-error{{template argument for template type parameter must be a type}}
// expected-error{{unqualified-id}}
A<int> *a3; A<int> *a3;
A<int()> *a4; A<int()> *a4;
@ -16,13 +14,12 @@ A<A<int> > *a6;
// [temp.arg.type]p2 // [temp.arg.type]p2
void f() { void f() {
class X { }; class X { };
A<X> * a = 0; // expected-error{{template argument uses local type 'class X'}}\ A<X> * a = 0; // expected-error{{template argument uses local type 'class X'}} \
// FIXME: expected-error{{expected expression}} // FIXME: expected-error{{use of undeclared identifier 'a'}}
} }
struct { int x; } Unnamed; // expected-note{{unnamed type used in template argument was declared here}} struct { int x; } Unnamed; // expected-note{{unnamed type used in template argument was declared here}}
A<__typeof__(Unnamed)> *a7; // expected-error{{template argument uses unnamed type}} \ A<__typeof__(Unnamed)> *a7; // expected-error{{template argument uses unnamed type}}
// FIXME: expected-error{{expected unqualified-id}}
// FIXME: [temp.arg.type]p3. The check doesn't really belong here (it // FIXME: [temp.arg.type]p3. The check doesn't really belong here (it
// belongs somewhere in the template instantiation section). // belongs somewhere in the template instantiation section).