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<int, 4>", 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<int, 4>", 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>
"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
--- /dev/null
+//===--- 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
+
+
if (CachedLexPos != 0 && isBacktrackEnabled())
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.
#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"
/// 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
#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"
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
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
void **Args;
bool *ArgIsType;
mutable unsigned Count;
-
+
#if !defined(DISABLE_SMART_POINTERS)
void destroy() {
if (!Count)
//===--------------------------------------------------------------------===//
// 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);
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);
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 *
return false;
}
-Action::TemplateNameKind
+TemplateNameKind
MinimalAction::isTemplateName(IdentifierInfo &II, Scope *S,
DeclTy *&TemplateDecl,
const CXXScopeSpec *SS) {
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
// 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());
/// operator-function-id
/// conversion-function-id [TODO]
/// '~' class-name
-/// template-id [TODO]
+/// template-id
///
void Parser::ParseDirectDeclarator(Declarator &D) {
DeclaratorScopeObj DeclScopeObj(*this, D.getCXXScopeSpec());
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());
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;
// 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);
-
- bool Invalid = false;
-
- // Parse the enclosed template argument list.
- if (TNK != Action::TNK_Non_template)
- Invalid = ParseTemplateIdAfterTemplateName(Template, NameLoc,
- &SS, true, LAngleLoc,
- TemplateArgs,
- TemplateArgIsType,
- TemplateArgLocations,
- RAngleLoc);
+ 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());
+
+ Diag(TemplateId->LAngleLoc, diag::err_template_spec_syntax_non_template)
+ << Name << static_cast<int>(TemplateId->Kind) << Range;
- 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)))
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,
return true;
}
+ bool HasScopeSpecifier = false;
+
if (Tok.is(tok::coloncolon)) {
// ::new and ::delete aren't nested-name-specifiers.
tok::TokenKind NextKind = NextToken().getKind();
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;
+ }
- return 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 HasScopeSpecifier;
}
/// ParseCXXIdExpression - Handle id-expression.
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.
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,
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);
+ 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]
// 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;
}
/// 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.
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.
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
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
/// 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) {
x1->foo(); // okay: refers to #1
x2->bar(); // okay: refers to #2
}
+
+// FIXME: diagnose specializations in a different namespace
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;
--- /dev/null
+// 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;
+};
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'}}
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;
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:
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;
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);
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.
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.
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}}
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.
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;
// [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).