Parse A::template B as an identifier rather than as a template-id with no

template arguments.

This fixes some cases where we'd incorrectly accept "A::template B" when B is a
kind of template that requires template arguments (in particular, a variable
template or a concept).

llvm-svn: 331013
This commit is contained in:
Richard Smith 2018-04-27 02:00:13 +00:00
parent 93979f67f8
commit c08b693e30
19 changed files with 143 additions and 65 deletions

View File

@ -633,6 +633,8 @@ def err_template_spec_syntax_non_template : Error<
"<unused>|refers to a variable template|<unused>}1">; "<unused>|refers to a variable template|<unused>}1">;
def err_id_after_template_in_nested_name_spec : Error< def err_id_after_template_in_nested_name_spec : Error<
"expected template name after 'template' keyword in nested name specifier">; "expected template name after 'template' keyword in nested name specifier">;
def err_unexpected_template_in_unqualified_id : Error<
"'template' keyword not permitted here">;
def err_two_right_angle_brackets_need_space : Error< def err_two_right_angle_brackets_need_space : Error<
"a space is required between consecutive right angle brackets (use '> >')">; "a space is required between consecutive right angle brackets (use '> >')">;
def err_right_angle_bracket_equal_needs_space : Error< def err_right_angle_bracket_equal_needs_space : Error<

View File

@ -2544,12 +2544,11 @@ private:
struct UsingDeclarator { struct UsingDeclarator {
SourceLocation TypenameLoc; SourceLocation TypenameLoc;
CXXScopeSpec SS; CXXScopeSpec SS;
SourceLocation TemplateKWLoc;
UnqualifiedId Name; UnqualifiedId Name;
SourceLocation EllipsisLoc; SourceLocation EllipsisLoc;
void clear() { void clear() {
TypenameLoc = TemplateKWLoc = EllipsisLoc = SourceLocation(); TypenameLoc = EllipsisLoc = SourceLocation();
SS.clear(); SS.clear();
Name.clear(); Name.clear();
} }
@ -2744,7 +2743,7 @@ public:
bool AllowConstructorName, bool AllowConstructorName,
bool AllowDeductionGuide, bool AllowDeductionGuide,
ParsedType ObjectType, ParsedType ObjectType,
SourceLocation& TemplateKWLoc, SourceLocation *TemplateKWLoc,
UnqualifiedId &Result); UnqualifiedId &Result);
private: private:

View File

@ -6074,7 +6074,7 @@ public:
TemplateNameKind isTemplateName(Scope *S, TemplateNameKind isTemplateName(Scope *S,
CXXScopeSpec &SS, CXXScopeSpec &SS,
bool hasTemplateKeyword, bool hasTemplateKeyword,
UnqualifiedId &Name, const UnqualifiedId &Name,
ParsedType ObjectType, ParsedType ObjectType,
bool EnteringContext, bool EnteringContext,
TemplateTy &Template, TemplateTy &Template,
@ -6244,7 +6244,7 @@ public:
TemplateNameKind ActOnDependentTemplateName( TemplateNameKind ActOnDependentTemplateName(
Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
UnqualifiedId &Name, ParsedType ObjectType, bool EnteringContext, const UnqualifiedId &Name, ParsedType ObjectType, bool EnteringContext,
TemplateTy &Template, bool AllowInjectedClassName = false); TemplateTy &Template, bool AllowInjectedClassName = false);
DeclResult DeclResult

View File

@ -5600,12 +5600,11 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
D.getContext() == DeclaratorContext::MemberContext); D.getContext() == DeclaratorContext::MemberContext);
} }
SourceLocation TemplateKWLoc;
bool HadScope = D.getCXXScopeSpec().isValid(); bool HadScope = D.getCXXScopeSpec().isValid();
if (ParseUnqualifiedId(D.getCXXScopeSpec(), if (ParseUnqualifiedId(D.getCXXScopeSpec(),
/*EnteringContext=*/true, /*EnteringContext=*/true,
/*AllowDestructorName=*/true, AllowConstructorName, /*AllowDestructorName=*/true, AllowConstructorName,
AllowDeductionGuide, nullptr, TemplateKWLoc, AllowDeductionGuide, nullptr, nullptr,
D.getName()) || D.getName()) ||
// Once we're past the identifier, if the scope was bad, mark the // Once we're past the identifier, if the scope was bad, mark the
// whole declarator bad. // whole declarator bad.

View File

@ -602,7 +602,7 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context,
/*AllowConstructorName=*/!(Tok.is(tok::identifier) && /*AllowConstructorName=*/!(Tok.is(tok::identifier) &&
NextToken().is(tok::equal)), NextToken().is(tok::equal)),
/*AllowDeductionGuide=*/false, /*AllowDeductionGuide=*/false,
nullptr, D.TemplateKWLoc, D.Name)) nullptr, nullptr, D.Name))
return true; return true;
} }
@ -2476,7 +2476,7 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
SourceLocation TemplateKWLoc; SourceLocation TemplateKWLoc;
UnqualifiedId Name; UnqualifiedId Name;
if (ParseUnqualifiedId(SS, false, true, true, false, nullptr, if (ParseUnqualifiedId(SS, false, true, true, false, nullptr,
TemplateKWLoc, Name)) { &TemplateKWLoc, Name)) {
SkipUntil(tok::semi); SkipUntil(tok::semi);
return nullptr; return nullptr;
} }
@ -2488,6 +2488,7 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
return nullptr; return nullptr;
} }
// FIXME: We should do something with the 'template' keyword here.
return DeclGroupPtrTy::make(DeclGroupRef(Actions.ActOnUsingDeclaration( return DeclGroupPtrTy::make(DeclGroupRef(Actions.ActOnUsingDeclaration(
getCurScope(), AS, /*UsingLoc*/ SourceLocation(), getCurScope(), AS, /*UsingLoc*/ SourceLocation(),
/*TypenameLoc*/ SourceLocation(), SS, Name, /*TypenameLoc*/ SourceLocation(), SS, Name,

View File

@ -1755,7 +1755,7 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
/*AllowConstructorName=*/ /*AllowConstructorName=*/
getLangOpts().MicrosoftExt, getLangOpts().MicrosoftExt,
/*AllowDeductionGuide=*/false, /*AllowDeductionGuide=*/false,
ObjectType, TemplateKWLoc, Name)) { ObjectType, &TemplateKWLoc, Name)) {
(void)Actions.CorrectDelayedTyposInExpr(LHS); (void)Actions.CorrectDelayedTyposInExpr(LHS);
LHS = ExprError(); LHS = ExprError();
} }

View File

@ -553,7 +553,7 @@ ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS, bool isAddressOfOpe
/*AllowDestructorName=*/false, /*AllowDestructorName=*/false,
/*AllowConstructorName=*/false, /*AllowConstructorName=*/false,
/*AllowDeductionGuide=*/false, /*AllowDeductionGuide=*/false,
/*ObjectType=*/nullptr, TemplateKWLoc, Name)) /*ObjectType=*/nullptr, &TemplateKWLoc, Name))
return ExprError(); return ExprError();
// This is only the direct operand of an & operator if it is not // This is only the direct operand of an & operator if it is not
@ -2044,9 +2044,8 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
ParsedType ObjectType, ParsedType ObjectType,
UnqualifiedId &Id, UnqualifiedId &Id,
bool AssumeTemplateId) { bool AssumeTemplateId) {
assert((AssumeTemplateId || Tok.is(tok::less)) && assert(Tok.is(tok::less) && "Expected '<' to finish parsing a template-id");
"Expected '<' to finish parsing a template-id");
TemplateTy Template; TemplateTy Template;
TemplateNameKind TNK = TNK_Non_template; TemplateNameKind TNK = TNK_Non_template;
switch (Id.getKind()) { switch (Id.getKind()) {
@ -2142,10 +2141,10 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
// Parse the enclosed template argument list. // Parse the enclosed template argument list.
SourceLocation LAngleLoc, RAngleLoc; SourceLocation LAngleLoc, RAngleLoc;
TemplateArgList TemplateArgs; TemplateArgList TemplateArgs;
if (Tok.is(tok::less) && ParseTemplateIdAfterTemplateName( if (ParseTemplateIdAfterTemplateName(true, LAngleLoc, TemplateArgs,
true, LAngleLoc, TemplateArgs, RAngleLoc)) RAngleLoc))
return true; return true;
if (Id.getKind() == UnqualifiedIdKind::IK_Identifier || if (Id.getKind() == UnqualifiedIdKind::IK_Identifier ||
Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId || Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId ||
Id.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId) { Id.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId) {
@ -2465,16 +2464,23 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
bool AllowConstructorName, bool AllowConstructorName,
bool AllowDeductionGuide, bool AllowDeductionGuide,
ParsedType ObjectType, ParsedType ObjectType,
SourceLocation& TemplateKWLoc, SourceLocation *TemplateKWLoc,
UnqualifiedId &Result) { UnqualifiedId &Result) {
if (TemplateKWLoc)
*TemplateKWLoc = SourceLocation();
// Handle 'A::template B'. This is for template-ids which have not // Handle 'A::template B'. This is for template-ids which have not
// already been annotated by ParseOptionalCXXScopeSpecifier(). // already been annotated by ParseOptionalCXXScopeSpecifier().
bool TemplateSpecified = false; bool TemplateSpecified = false;
if (getLangOpts().CPlusPlus && Tok.is(tok::kw_template) && if (Tok.is(tok::kw_template)) {
(ObjectType || SS.isSet())) { if (TemplateKWLoc && (ObjectType || SS.isSet())) {
TemplateSpecified = true; TemplateSpecified = true;
TemplateKWLoc = ConsumeToken(); *TemplateKWLoc = ConsumeToken();
} else {
SourceLocation TemplateLoc = ConsumeToken();
Diag(TemplateLoc, diag::err_unexpected_template_in_unqualified_id)
<< FixItHint::CreateRemoval(TemplateLoc);
}
} }
// unqualified-id: // unqualified-id:
@ -2513,11 +2519,18 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
} }
// If the next token is a '<', we may have a template. // If the next token is a '<', we may have a template.
if (TemplateSpecified || Tok.is(tok::less)) TemplateTy Template;
return ParseUnqualifiedIdTemplateId(SS, TemplateKWLoc, Id, IdLoc, if (Tok.is(tok::less))
EnteringContext, ObjectType, return ParseUnqualifiedIdTemplateId(
Result, TemplateSpecified); SS, TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), Id, IdLoc,
EnteringContext, ObjectType, Result, TemplateSpecified);
else if (TemplateSpecified &&
Actions.ActOnDependentTemplateName(
getCurScope(), SS, *TemplateKWLoc, Result, ObjectType,
EnteringContext, Template,
/*AllowInjectedClassName*/ true) == TNK_Non_template)
return true;
return false; return false;
} }
@ -2558,7 +2571,14 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
// We have already parsed a template-id; consume the annotation token as // We have already parsed a template-id; consume the annotation token as
// our unqualified-id. // our unqualified-id.
Result.setTemplateId(TemplateId); Result.setTemplateId(TemplateId);
TemplateKWLoc = TemplateId->TemplateKWLoc; SourceLocation TemplateLoc = TemplateId->TemplateKWLoc;
if (TemplateLoc.isValid()) {
if (TemplateKWLoc && (ObjectType || SS.isSet()))
*TemplateKWLoc = TemplateLoc;
else
Diag(TemplateLoc, diag::err_unexpected_template_in_unqualified_id)
<< FixItHint::CreateRemoval(TemplateLoc);
}
ConsumeAnnotationToken(); ConsumeAnnotationToken();
return false; return false;
} }
@ -2575,13 +2595,20 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
// //
// template-id: // template-id:
// operator-function-id < template-argument-list[opt] > // operator-function-id < template-argument-list[opt] >
TemplateTy Template;
if ((Result.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId || if ((Result.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId ||
Result.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId) && Result.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId) &&
(TemplateSpecified || Tok.is(tok::less))) Tok.is(tok::less))
return ParseUnqualifiedIdTemplateId(SS, TemplateKWLoc, return ParseUnqualifiedIdTemplateId(
nullptr, SourceLocation(), SS, TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), nullptr,
EnteringContext, ObjectType, SourceLocation(), EnteringContext, ObjectType, Result,
Result, TemplateSpecified); TemplateSpecified);
else if (TemplateSpecified &&
Actions.ActOnDependentTemplateName(
getCurScope(), SS, *TemplateKWLoc, Result, ObjectType,
EnteringContext, Template,
/*AllowInjectedClassName*/ true) == TNK_Non_template)
return true;
return false; return false;
} }
@ -2649,12 +2676,11 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
IdentifierInfo *ClassName = Tok.getIdentifierInfo(); IdentifierInfo *ClassName = Tok.getIdentifierInfo();
SourceLocation ClassNameLoc = ConsumeToken(); SourceLocation ClassNameLoc = ConsumeToken();
if (TemplateSpecified || Tok.is(tok::less)) { if (Tok.is(tok::less)) {
Result.setDestructorName(TildeLoc, nullptr, ClassNameLoc); Result.setDestructorName(TildeLoc, nullptr, ClassNameLoc);
return ParseUnqualifiedIdTemplateId(SS, TemplateKWLoc, return ParseUnqualifiedIdTemplateId(
ClassName, ClassNameLoc, SS, TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), ClassName,
EnteringContext, ObjectType, ClassNameLoc, EnteringContext, ObjectType, Result, TemplateSpecified);
Result, TemplateSpecified);
} }
// Note that this is a destructor name. // Note that this is a destructor name.

View File

@ -1154,7 +1154,6 @@ bool Parser::ParseOpenMPSimpleVarList(
// Read tokens while ')' or annot_pragma_openmp_end is not found. // Read tokens while ')' or annot_pragma_openmp_end is not found.
while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end)) { while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end)) {
CXXScopeSpec SS; CXXScopeSpec SS;
SourceLocation TemplateKWLoc;
UnqualifiedId Name; UnqualifiedId Name;
// Read var name. // Read var name.
Token PrevTok = Tok; Token PrevTok = Tok;
@ -1166,7 +1165,7 @@ bool Parser::ParseOpenMPSimpleVarList(
SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
StopBeforeMatch); StopBeforeMatch);
} else if (ParseUnqualifiedId(SS, false, false, false, false, nullptr, } else if (ParseUnqualifiedId(SS, false, false, false, false, nullptr,
TemplateKWLoc, Name)) { nullptr, Name)) {
IsCorrect = false; IsCorrect = false;
SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
StopBeforeMatch); StopBeforeMatch);
@ -1648,7 +1647,6 @@ OMPClause *Parser::ParseOpenMPSingleExprWithArgClause(OpenMPClauseKind Kind,
static bool ParseReductionId(Parser &P, CXXScopeSpec &ReductionIdScopeSpec, static bool ParseReductionId(Parser &P, CXXScopeSpec &ReductionIdScopeSpec,
UnqualifiedId &ReductionId) { UnqualifiedId &ReductionId) {
SourceLocation TemplateKWLoc;
if (ReductionIdScopeSpec.isEmpty()) { if (ReductionIdScopeSpec.isEmpty()) {
auto OOK = OO_None; auto OOK = OO_None;
switch (P.getCurToken().getKind()) { switch (P.getCurToken().getKind()) {
@ -1690,7 +1688,7 @@ static bool ParseReductionId(Parser &P, CXXScopeSpec &ReductionIdScopeSpec,
/*AllowDestructorName*/ false, /*AllowDestructorName*/ false,
/*AllowConstructorName*/ false, /*AllowConstructorName*/ false,
/*AllowDeductionGuide*/ false, /*AllowDeductionGuide*/ false,
nullptr, TemplateKWLoc, ReductionId); nullptr, nullptr, ReductionId);
} }
/// Parses clauses with list. /// Parses clauses with list.

View File

@ -239,7 +239,7 @@ ExprResult Parser::ParseMSAsmIdentifier(llvm::SmallVectorImpl<Token> &LineToks,
/*AllowDestructorName=*/false, /*AllowDestructorName=*/false,
/*AllowConstructorName=*/false, /*AllowConstructorName=*/false,
/*AllowDeductionGuide=*/false, /*AllowDeductionGuide=*/false,
/*ObjectType=*/nullptr, TemplateKWLoc, Id); /*ObjectType=*/nullptr, &TemplateKWLoc, Id);
// Perform the lookup. // Perform the lookup.
Result = Actions.LookupInlineAsmIdentifier(SS, TemplateKWLoc, Id, Result = Actions.LookupInlineAsmIdentifier(SS, TemplateKWLoc, Id,
IsUnevaluatedContext); IsUnevaluatedContext);

View File

@ -2002,7 +2002,7 @@ bool Parser::ParseMicrosoftIfExistsCondition(IfExistsCondition& Result) {
if (ParseUnqualifiedId( if (ParseUnqualifiedId(
Result.SS, /*EnteringContext*/false, /*AllowDestructorName*/true, Result.SS, /*EnteringContext*/false, /*AllowDestructorName*/true,
/*AllowConstructorName*/true, /*AllowDeductionGuide*/false, nullptr, /*AllowConstructorName*/true, /*AllowDeductionGuide*/false, nullptr,
TemplateKWLoc, Result.Name)) { &TemplateKWLoc, Result.Name)) {
T.skipToEnd(); T.skipToEnd();
return true; return true;
} }

View File

@ -2080,7 +2080,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
(Id.getKind() == UnqualifiedIdKind::IK_ImplicitSelfParam) (Id.getKind() == UnqualifiedIdKind::IK_ImplicitSelfParam)
? LookupObjCImplicitSelfParam ? LookupObjCImplicitSelfParam
: LookupOrdinaryName); : LookupOrdinaryName);
if (TemplateArgs) { if (TemplateKWLoc.isValid() || TemplateArgs) {
// Lookup the template name again to correctly establish the context in // Lookup the template name again to correctly establish the context in
// which it was found. This is really unfortunate as we already did the // which it was found. This is really unfortunate as we already did the
// lookup to determine that it was a template name in the first place. If // lookup to determine that it was a template name in the first place. If
@ -2089,7 +2089,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
bool MemberOfUnknownSpecialization; bool MemberOfUnknownSpecialization;
LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false, LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false,
MemberOfUnknownSpecialization); MemberOfUnknownSpecialization);
if (MemberOfUnknownSpecialization || if (MemberOfUnknownSpecialization ||
(R.getResultKind() == LookupResult::NotFoundInCurrentInstantiation)) (R.getResultKind() == LookupResult::NotFoundInCurrentInstantiation))
return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo, return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo,
@ -2155,6 +2155,9 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
if (SS.isValid()) if (SS.isValid())
CCC->setTypoNNS(SS.getScopeRep()); CCC->setTypoNNS(SS.getScopeRep());
} }
// FIXME: DiagnoseEmptyLookup produces bad diagnostics if we're looking for
// a template name, but we happen to have always already looked up the name
// before we get here if it must be a template name.
if (DiagnoseEmptyLookup(S, SS, R, if (DiagnoseEmptyLookup(S, SS, R,
CCC ? std::move(CCC) : std::move(DefaultValidator), CCC ? std::move(CCC) : std::move(DefaultValidator),
nullptr, None, &TE)) { nullptr, None, &TE)) {

View File

@ -759,9 +759,9 @@ Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType,
TypoExpr *TE = nullptr; TypoExpr *TE = nullptr;
QualType RecordTy = BaseType; QualType RecordTy = BaseType;
if (IsArrow) RecordTy = RecordTy->getAs<PointerType>()->getPointeeType(); if (IsArrow) RecordTy = RecordTy->getAs<PointerType>()->getPointeeType();
if (LookupMemberExprInRecord(*this, R, nullptr, if (LookupMemberExprInRecord(
RecordTy->getAs<RecordType>(), OpLoc, IsArrow, *this, R, nullptr, RecordTy->getAs<RecordType>(), OpLoc, IsArrow,
SS, TemplateArgs != nullptr, TE)) SS, TemplateKWLoc.isValid() || TemplateArgs != nullptr, TE))
return ExprError(); return ExprError();
if (TE) if (TE)
return TE; return TE;
@ -769,10 +769,10 @@ Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType,
// Explicit member accesses. // Explicit member accesses.
} else { } else {
ExprResult BaseResult = Base; ExprResult BaseResult = Base;
ExprResult Result = LookupMemberExpr( ExprResult Result =
*this, R, BaseResult, IsArrow, OpLoc, SS, LookupMemberExpr(*this, R, BaseResult, IsArrow, OpLoc, SS,
ExtraArgs ? ExtraArgs->ObjCImpDecl : nullptr, ExtraArgs ? ExtraArgs->ObjCImpDecl : nullptr,
TemplateArgs != nullptr); TemplateKWLoc.isValid() || TemplateArgs != nullptr);
if (BaseResult.isInvalid()) if (BaseResult.isInvalid())
return ExprError(); return ExprError();

View File

@ -158,7 +158,7 @@ bool Sema::hasAnyAcceptableTemplateNames(LookupResult &R,
TemplateNameKind Sema::isTemplateName(Scope *S, TemplateNameKind Sema::isTemplateName(Scope *S,
CXXScopeSpec &SS, CXXScopeSpec &SS,
bool hasTemplateKeyword, bool hasTemplateKeyword,
UnqualifiedId &Name, const UnqualifiedId &Name,
ParsedType ObjectTypePtr, ParsedType ObjectTypePtr,
bool EnteringContext, bool EnteringContext,
TemplateTy &TemplateResult, TemplateTy &TemplateResult,
@ -4102,7 +4102,7 @@ Sema::BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS,
TemplateNameKind Sema::ActOnDependentTemplateName(Scope *S, TemplateNameKind Sema::ActOnDependentTemplateName(Scope *S,
CXXScopeSpec &SS, CXXScopeSpec &SS,
SourceLocation TemplateKWLoc, SourceLocation TemplateKWLoc,
UnqualifiedId &Name, const UnqualifiedId &Name,
ParsedType ObjectType, ParsedType ObjectType,
bool EnteringContext, bool EnteringContext,
TemplateTy &Result, TemplateTy &Result,

View File

@ -67,7 +67,10 @@ namespace dr108 { // dr108: yes
namespace dr109 { // dr109: yes namespace dr109 { // dr109: yes
struct A { template<typename T> void f(T); }; struct A { template<typename T> void f(T); };
template<typename T> struct B : T { template<typename T> struct B : T {
using T::template f; // expected-error {{using declaration cannot refer to a template}} using T::template f; // expected-error {{'template' keyword not permitted here}}
using T::template f<int>; // expected-error {{'template' keyword not permitted here}} expected-error {{using declaration cannot refer to a template specialization}}
// FIXME: We shouldn't suggest using the 'template' keyword in a location where it's not valid.
using T::f<int>; // expected-error {{use 'template' keyword}} expected-error {{using declaration cannot refer to a template specialization}}
void g() { this->f<int>(123); } // expected-error {{use 'template'}} void g() { this->f<int>(123); } // expected-error {{use 'template'}}
}; };
} }

View File

@ -318,8 +318,8 @@ namespace dr420 { // dr420: yes
q->~id<int>(); q->~id<int>();
p->id<int>::~id<int>(); p->id<int>::~id<int>();
q->id<int>::~id<int>(); q->id<int>::~id<int>();
p->template id<int>::~id<int>(); // expected-error {{expected unqualified-id}} p->template id<int>::~id<int>(); // expected-error {{'template' keyword not permitted here}} expected-error {{base type 'int' is not a struct}}
q->template id<int>::~id<int>(); // expected-error {{expected unqualified-id}} q->template id<int>::~id<int>(); // expected-error {{'template' keyword not permitted here}} expected-error {{base type 'int' is not a struct}}
p->A::template id<int>::~id<int>(); p->A::template id<int>::~id<int>();
q->A::template id<int>::~id<int>(); q->A::template id<int>::~id<int>();
} }

View File

@ -34,5 +34,5 @@ template<typename T> template<typename U> struct A<T>::B {
// a type, and then complain about the rest of the tokens, and then complain // a type, and then complain about the rest of the tokens, and then complain
// that we didn't get a function declaration. // that we didn't get a function declaration.
friend A<U>::C<T> f7(); // expected-error {{use 'template' keyword to treat 'C' as a dependent template name}} expected-error 3{{}} friend A<U>::C<T> f7(); // expected-error {{use 'template' keyword to treat 'C' as a dependent template name}} expected-error 3{{}}
friend A<U>::template C<T> f8(); // expected-error 3{{}} friend A<U>::template C<T> f8(); // expected-error 4{{}}
}; };

View File

@ -237,10 +237,11 @@ namespace PR5066 {
namespace PR17255 { namespace PR17255 {
void foo() { void foo() {
typename A::template B<>; // expected-error {{use of undeclared identifier 'A'}} typename A::template B<>; // expected-error {{use of undeclared identifier 'A'}}
// expected-error@-1 {{'template' keyword not permitted here}}
#if __cplusplus <= 199711L #if __cplusplus <= 199711L
// expected-error@-2 {{'template' keyword outside of a template}} // expected-error@-3 {{'template' keyword outside of a template}}
#endif #endif
// expected-error@-4 {{expected a qualified name after 'typename'}} // expected-error@-5 {{expected a qualified name after 'typename'}}
} }
} }

View File

@ -382,16 +382,16 @@ int main() {
namespace dependent_static_var_template { namespace dependent_static_var_template {
struct A { struct A {
template<int = 0> static int n; // expected-note {{here}} template<int = 0> static int n; // expected-note 2{{here}}
}; };
int &r = A::template n; // FIXME: ill-formed int &r = A::template n; // expected-error {{use of variable template 'n' requires template arguments}}
template<typename T> template<typename T>
int &f() { return T::template n; } // expected-error {{use of variable template 'n' requires template arguments}} int &f() { return T::template n; } // expected-error {{use of variable template 'n' requires template arguments}}
int &s = f<A>(); // expected-note {{instantiation of}} int &s = f<A>(); // expected-note {{instantiation of}}
namespace B { namespace B {
template<int = 0> static int n; template<int = 0> static int n; // expected-note {{here}}
} }
int &t = B::template n; // FIXME: ill-formed int &t = B::template n; // expected-error {{use of variable template 'n' requires template arguments}}
} }

View File

@ -85,6 +85,52 @@ struct Y0 {
} }
}; };
template<typename U> void Y0
::template // expected-error {{expected unqualified-id}}
f1(U) {}
// FIXME: error recovery is awful without this.
;
template<typename T>
struct Y1 {
template<typename U>
void f1(U);
template<typename U>
static void f2(U);
void f3(int);
static int f4(int);
template<typename U>
static void f4(U);
template<typename U>
void f() {
Y1::template f1<U>(0);
Y1::template f1(0);
this->template f1(0);
Y1::template f2<U>(0);
Y1::template f2(0);
Y1::template f3(0); // expected-error {{'f3' following the 'template' keyword does not refer to a template}}
Y1::template f3(); // expected-error {{'f3' following the 'template' keyword does not refer to a template}}
int x;
x = Y1::f4(0);
x = Y1::f4<int>(0); // expected-error {{assigning to 'int' from incompatible type 'void'}}
x = Y1::template f4(0); // expected-error {{assigning to 'int' from incompatible type 'void'}}
x = this->f4(0);
x = this->f4<int>(0); // expected-error {{assigning to 'int' from incompatible type 'void'}}
x = this->template f4(0); // expected-error {{assigning to 'int' from incompatible type 'void'}}
}
};
void use_Y1(Y1<int> y1) { y1.f<int>(); } // expected-note {{in instantiation of}}
struct A { struct A {
template<int I> template<int I>
struct B { struct B {