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
<tt>DeclContext*</tt>.</li>
<li><b>tok::annot_template_id</b>: This annotation token represents a C++
template-id such as "foo&lt;int, 4&gt;", which may refer to a function or type
depending on whether foo is a function template or class template. The
AnnotationValue pointer is a pointer to a malloc'd TemplateIdAnnotation object.
FIXME: I don't think the parsing logic is right for this. Shouldn't type
templates be turned into annot_typename??</li>
<li><b>tok::annot_template_id</b>: This annotation token represents a
C++ template-id such as "foo&lt;int, 4&gt;", where "foo" is the name
of a template. The AnnotationValue pointer is a pointer to a malloc'd
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>
</ol>

View File

@ -274,6 +274,10 @@ DIAG(err_expected_class_before, ERROR,
"expected 'class' before '%0'")
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")
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

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

@ -432,6 +432,20 @@ public:
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
/// the specified Token's location, translating the token's start
/// position in the current buffer into a SourcePosition object for rendering.

View File

@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_TOKEN_H
#define LLVM_CLANG_TOKEN_H
#include "clang/Basic/TemplateKinds.h"
#include "clang/Basic/TokenKinds.h"
#include "clang/Basic/SourceLocation.h"
@ -247,24 +248,62 @@ struct PPConditionalInfo {
/// TemplateIdAnnotation - Information about a template-id annotation
/// 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 {
/// TemplateNameLoc - The location of the template name within the
/// source.
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*.
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.
SourceLocation LAngleLoc;
/// NumArgs - The number of template arguments. The arguments
/// themselves are Action::TemplateArgTy pointers allocated directly
/// following the TemplateIdAnnotation structure.
/// The location of the '>' after the template argument
/// list.
SourceLocation RAngleLoc;
/// NumArgs - The number of template arguments.
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

View File

@ -16,6 +16,7 @@
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/TemplateKinds.h"
#include "clang/Basic/TypeTraits.h"
#include "clang/Parse/AccessSpecifier.h"
#include "clang/Parse/Ownership.h"
@ -134,20 +135,6 @@ public:
virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S,
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
/// in the current scope. If so, the kind of template name is
/// returned, and \p TemplateDecl receives the declaration. An
@ -178,6 +165,22 @@ public:
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
/// 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

View File

@ -993,7 +993,6 @@ private:
//===--------------------------------------------------------------------===//
// C++ 14: Templates [temp]
typedef llvm::SmallVector<DeclTy *, 4> TemplateParameterList;
typedef Action::TemplateNameKind TemplateNameKind;
// C++ 14.1: Template Parameters [temp.param]
DeclTy *ParseTemplateDeclarationOrSpecialization(unsigned Context);
@ -1023,7 +1022,10 @@ private:
SourceLocation &RAngleLoc);
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,
TemplateArgIsTypeList &TemplateArgIsType,
TemplateArgLocationList &TemplateArgLocations);

View File

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

View File

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

View File

@ -558,22 +558,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
TypeTy *TypeRep = Actions.getTypeName(*Tok.getIdentifierInfo(),
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)
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
@ -610,6 +597,25 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
// If a type specifier follows, it will be diagnosed elsewhere.
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.
case tok::kw___attribute:
DS.AddAttributes(ParseAttributes());
@ -1749,7 +1755,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
/// operator-function-id
/// conversion-function-id [TODO]
/// '~' class-name
/// template-id [TODO]
/// template-id
///
void Parser::ParseDirectDeclarator(Declarator &D) {
DeclaratorScopeObj DeclScopeObj(*this, D.getCXXScopeSpec());
@ -1768,21 +1774,9 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
if (Tok.is(tok::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
// constructor name.
else if (Actions.isCurrentClassName(*Tok.getIdentifierInfo(),CurScope)){
if (Actions.isCurrentClassName(*Tok.getIdentifierInfo(),CurScope)){
D.setConstructor(Actions.getTypeName(*Tok.getIdentifierInfo(),
Tok.getLocation(), CurScope),
Tok.getLocation());
@ -1791,6 +1785,21 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
D.SetIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
ConsumeToken();
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)) {
SourceLocation OperatorLoc = Tok.getLocation();
SourceLocation EndLoc;

View File

@ -309,75 +309,42 @@ void Parser::ParseClassSpecifier(DeclSpec &DS,
// Parse the (optional) nested-name-specifier.
CXXScopeSpec SS;
if (getLang().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS)) {
// FIXME: can we get a class template specialization or
// template-id token here?
if (Tok.isNot(tok::identifier))
if (getLang().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS))
if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id))
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.
IdentifierInfo *Name = 0;
SourceLocation NameLoc;
TemplateIdAnnotation *TemplateId = 0;
if (Tok.is(tok::identifier)) {
Name = Tok.getIdentifierInfo();
NameLoc = ConsumeToken();
} else if (Tok.is(tok::annot_template_id)) {
TemplateId = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
NameLoc = ConsumeToken();
if (Tok.is(tok::less)) {
// This is a simple-template-id.
Action::TemplateNameKind TNK
= Actions.isTemplateName(*Name, CurScope, Template, &SS);
if (TemplateId->Kind != TNK_Class_template) {
// 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());
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],
TemplateArgs.size());
if (TNK != Action::TNK_Class_template) {
// 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;
}
DS.SetTypeSpecError();
SkipUntil(tok::semi, false, true);
TemplateId->Destroy();
return;
}
}
// There are three options here. If we have 'struct foo;', then
// 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.
Action::TagKind TK;
if (Tok.is(tok::l_brace) || (getLang().CPlusPlus && Tok.is(tok::colon)))
@ -387,35 +354,43 @@ void Parser::ParseClassSpecifier(DeclSpec &DS,
else
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.
Diag(StartLoc, diag::err_anon_type_definition)
<< DeclSpec::getSpecifierName(TagType);
// Skip the rest of this declarator, up until the comma or semicolon.
SkipUntil(tok::comma, true);
if (TemplateId)
TemplateId->Destroy();
return;
}
// Create the tag portion of the class or class template.
DeclTy *TagOrTempDecl;
if (Template && TK != Action::TK_Reference)
if (TemplateId && TK != Action::TK_Reference) {
// Explicit specialization or class template partial
// specialization. Let semantic analysis decide.
// FIXME: we want a source range covering the simple-template-id.
ASTTemplateArgsPtr TemplateArgsPtr(Actions,
TemplateId->getTemplateArgs(),
TemplateId->getTemplateArgIsType(),
TemplateId->NumArgs);
TagOrTempDecl
= Actions.ActOnClassTemplateSpecialization(CurScope, TagType, TK,
StartLoc, SS, /*Range*/
Template, NameLoc,
LAngleLoc, TemplateArgsPtr,
&TemplateArgLocations[0],
RAngleLoc, Attr,
StartLoc, SS,
TemplateId->Template,
TemplateId->TemplateNameLoc,
TemplateId->LAngleLoc,
TemplateArgsPtr,
TemplateId->getTemplateArgLocations(),
TemplateId->RAngleLoc,
Attr,
Action::MultiTemplateParamsArg(Actions,
TemplateParams? &(*TemplateParams)[0] : 0,
TemplateParams? TemplateParams->size() : 0));
else if (TemplateParams && TK != Action::TK_Reference)
TemplateId->Destroy();
} else if (TemplateParams && TK != Action::TK_Reference)
TagOrTempDecl = Actions.ActOnClassTemplate(CurScope, TagType, TK, StartLoc,
SS, Name, NameLoc, Attr,
Action::MultiTemplateParamsArg(Actions,

View File

@ -42,6 +42,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS) {
return true;
}
bool HasScopeSpecifier = false;
if (Tok.is(tok::coloncolon)) {
// ::new and ::delete aren't nested-name-specifiers.
tok::TokenKind NextKind = NextToken().getKind();
@ -53,32 +55,132 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS) {
SS.setBeginLoc(CCLoc);
SS.setScopeRep(Actions.ActOnCXXGlobalScopeSpecifier(CurScope, CCLoc));
SS.setEndLoc(CCLoc);
} else if (Tok.is(tok::identifier) && NextToken().is(tok::coloncolon)) {
SS.setBeginLoc(Tok.getLocation());
} else {
// Not a CXXScopeSpecifier.
return false;
HasScopeSpecifier = true;
}
// nested-name-specifier:
// type-name '::'
// namespace-name '::'
// nested-name-specifier identifier '::'
// nested-name-specifier 'template'[opt] simple-template-id '::' [TODO]
while (Tok.is(tok::identifier) && NextToken().is(tok::coloncolon)) {
IdentifierInfo *II = Tok.getIdentifierInfo();
SourceLocation IdLoc = ConsumeToken();
assert(Tok.is(tok::coloncolon) && "NextToken() not working properly!");
SourceLocation CCLoc = ConsumeToken();
if (SS.isInvalid())
while (true) {
// nested-name-specifier:
// type-name '::'
// namespace-name '::'
// nested-name-specifier identifier '::'
if (Tok.is(tok::identifier) && NextToken().is(tok::coloncolon)) {
// We have an identifier followed by a '::'. Lookup this name
// as the name in a nested-name-specifier.
IdentifierInfo *II = Tok.getIdentifierInfo();
SourceLocation IdLoc = ConsumeToken();
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;
}
SS.setScopeRep(
Actions.ActOnCXXNestedNameSpecifier(CurScope, SS, IdLoc, CCLoc, *II));
SS.setEndLoc(CCLoc);
// nested-name-specifier:
// type-name '::'
// 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.

View File

@ -444,16 +444,49 @@ Parser::ParseTemplateIdAfterTemplateName(DeclTy *Template,
return false;
}
/// 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.
/// \brief Replace the tokens that form a simple-template-id with an
/// annotation token containing the complete template-id.
///
/// 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,
const CXXScopeSpec *SS) {
const CXXScopeSpec *SS,
SourceLocation TemplateKWLoc,
bool AllowTypeAnnotation) {
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.
IdentifierInfo *Name = Tok.getIdentifierInfo();
SourceLocation TemplateNameLoc = ConsumeToken();
// Parse the enclosed template argument list.
@ -476,7 +509,7 @@ void Parser::AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK,
return;
// Build the annotation token.
if (TNK == Action::TNK_Class_template) {
if (TNK == TNK_Class_template && AllowTypeAnnotation) {
Action::TypeResult Type
= Actions.ActOnClassTemplateId(Template, TemplateNameLoc,
LAngleLoc, TemplateArgsPtr,
@ -487,34 +520,96 @@ void Parser::AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK,
Tok.setKind(tok::annot_typename);
Tok.setAnnotationValue(Type.get());
if (SS && SS->isNotEmpty())
Tok.setLocation(SS->getBeginLoc());
else if (TemplateKWLoc.isValid())
Tok.setLocation(TemplateKWLoc);
else
Tok.setLocation(TemplateNameLoc);
} else {
// This is a function template. We'll be building a template-id
// annotation token.
Tok.setKind(tok::annot_template_id);
TemplateIdAnnotation *TemplateId
= (TemplateIdAnnotation *)malloc(sizeof(TemplateIdAnnotation) +
sizeof(void*) * TemplateArgs.size());
= TemplateIdAnnotation::Allocate(TemplateArgs.size());
TemplateId->TemplateNameLoc = TemplateNameLoc;
TemplateId->Name = Name;
TemplateId->Template = Template;
TemplateId->Kind = TNK;
TemplateId->LAngleLoc = LAngleLoc;
TemplateId->NumArgs = TemplateArgs.size();
void **Args = (void**)(TemplateId + 1);
for (unsigned Arg = 0, ArgEnd = TemplateArgs.size(); Arg != ArgEnd; ++Arg)
TemplateId->RAngleLoc = RAngleLoc;
void **Args = TemplateId->getTemplateArgs();
bool *ArgIsType = TemplateId->getTemplateArgIsType();
SourceLocation *ArgLocs = TemplateId->getTemplateArgLocations();
for (unsigned Arg = 0, ArgEnd = TemplateArgs.size(); Arg != ArgEnd; ++Arg) {
Args[Arg] = TemplateArgs[Arg];
ArgIsType[Arg] = TemplateArgIsType[Arg];
ArgLocs[Arg] = TemplateArgLocations[Arg];
}
Tok.setAnnotationValue(TemplateId);
if (TemplateKWLoc.isValid())
Tok.setLocation(TemplateKWLoc);
else
Tok.setLocation(TemplateNameLoc);
TemplateArgsPtr.release();
}
// Common fields for the annotation token
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
// annotation token.
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]).
///
/// template-argument: [C++ 14.2]

View File

@ -778,31 +778,41 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
// them with the annotation token.
PP.AnnotateCachedTokens(Tok);
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
// them). If the identifier is not a type, then it can't be scope either,
// just early exit.
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)) {
DeclTy *Template;
if (TemplateNameKind TNK
= Actions.isTemplateName(*Tok.getIdentifierInfo(),
CurScope, Template, &SS)) {
CurScope, Template, &SS))
AnnotateTemplateIdToken(Template, TNK, &SS);
return true;
}
}
// 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.
// The current token, which is either an identifier or a
// template-id, is not part of the annotation. Fall through to
// 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
// names a type.
if (Tok.is(tok::annot_template_id)) {
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())
return false;
@ -825,8 +835,8 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
}
/// TryAnnotateScopeToken - Like TryAnnotateTypeOrScopeToken but only
/// annotates C++ scope specifiers. This returns true if the token was
/// annotated.
/// annotates C++ scope specifiers and template-ids. This returns
/// true if the token was annotated.
///
/// 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.
@ -838,7 +848,7 @@ bool Parser::TryAnnotateCXXScopeToken() {
CXXScopeSpec 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
// cached) and use an annotation scope token for current token.

View File

@ -1338,6 +1338,20 @@ public:
SourceLocation CCLoc,
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
/// 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

View File

@ -74,6 +74,17 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
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
/// 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

View File

@ -26,9 +26,9 @@ using namespace clang;
/// 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::TemplateNameKind Sema::isTemplateName(IdentifierInfo &II, Scope *S,
DeclTy *&Template,
const CXXScopeSpec *SS) {
TemplateNameKind Sema::isTemplateName(IdentifierInfo &II, Scope *S,
DeclTy *&Template,
const CXXScopeSpec *SS) {
NamedDecl *IIDecl = LookupParsedName(S, SS, &II, LookupOrdinaryName);
if (IIDecl) {

View File

@ -40,3 +40,5 @@ void testme(X<int_type> *x1, X<float, int> *x2) {
x1->foo(); // okay: refers to #1
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> *x2;
X<> *x3; // expected-error{{too few template arguments for class template 'X'}} \
// FIXME: expected-error{{expected unqualified-id}}
X<> *x3; // expected-error{{too few template arguments for class template '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<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'}} \
// expected-error{{unqualified-id}}
A<float, 1, X, double> *a2; // expected-error{{too many template arguments for class template 'A'}}
A<float, 1> *a3; // expected-error{{too few template arguments for class template 'A'}}

View File

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

View File

@ -20,23 +20,18 @@ A<X> *a1;
A<N::Z> *a2;
A< ::N::Z> *a3;
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}} \
// 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}}
A<Y> *a4; // 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}}
C<Y> *a7;
C<Ylong> *a8; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}} \
// FIXME::expected-error{{expected unqualified-id}}
C<Ylong> *a8; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
template<typename T> void f(int);
// 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
// lookup of 'f' returns a TemplateDecl.
A<f> *a9; // expected-error{{template argument for template template parameter must be a template}} \
// expected-error{{unqualified-id}}
A<f> *a9; // expected-error{{template argument for template template parameter must be a template}}
// FIXME: The code below is ill-formed, because of the evil digraph '<:'.
// 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}}
// [temp.arg.type]p1
A<0> *a1; // expected-error{{template argument for template type parameter must be a type}} \
// expected-error{{unqualified-id}}
A<0> *a1; // 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<A> *a2; // expected-error{{template argument for template type parameter must be a type}}
A<int> *a3;
A<int()> *a4;
@ -16,13 +14,12 @@ A<A<int> > *a6;
// [temp.arg.type]p2
void f() {
class X { };
A<X> * a = 0; // expected-error{{template argument uses local type 'class X'}}\
// FIXME: expected-error{{expected expression}}
A<X> * a = 0; // expected-error{{template argument uses local type 'class X'}} \
// FIXME: expected-error{{use of undeclared identifier 'a'}}
}
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}} \
// FIXME: expected-error{{expected unqualified-id}}
A<__typeof__(Unnamed)> *a7; // expected-error{{template argument uses unnamed type}}
// FIXME: [temp.arg.type]p3. The check doesn't really belong here (it
// belongs somewhere in the template instantiation section).