From c08b693e309baa80f0a3dd7794af130c3393c777 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 27 Apr 2018 02:00:13 +0000 Subject: [PATCH] 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 --- clang/include/clang/Basic/DiagnosticParseKinds.td | 2 + clang/include/clang/Parse/Parser.h | 5 +- clang/include/clang/Sema/Sema.h | 4 +- clang/lib/Parse/ParseDecl.cpp | 3 +- clang/lib/Parse/ParseDeclCXX.cpp | 5 +- clang/lib/Parse/ParseExpr.cpp | 2 +- clang/lib/Parse/ParseExprCXX.cpp | 82 ++++++++++++++-------- clang/lib/Parse/ParseOpenMP.cpp | 6 +- clang/lib/Parse/ParseStmtAsm.cpp | 2 +- clang/lib/Parse/Parser.cpp | 2 +- clang/lib/Sema/SemaExpr.cpp | 7 +- clang/lib/Sema/SemaExprMember.cpp | 14 ++-- clang/lib/Sema/SemaTemplate.cpp | 4 +- clang/test/CXX/drs/dr1xx.cpp | 5 +- clang/test/CXX/drs/dr4xx.cpp | 4 +- clang/test/CXX/temp/temp.res/p3.cpp | 2 +- clang/test/Parser/cxx-decl.cpp | 5 +- .../SemaCXX/cxx1y-variable-templates_in_class.cpp | 8 +-- clang/test/SemaTemplate/template-id-expr.cpp | 46 ++++++++++++ 19 files changed, 143 insertions(+), 65 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 7c7c0fb..d12ac43 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -633,6 +633,8 @@ def err_template_spec_syntax_non_template : Error< "|refers to a variable template|}1">; def err_id_after_template_in_nested_name_spec : Error< "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< "a space is required between consecutive right angle brackets (use '> >')">; def err_right_angle_bracket_equal_needs_space : Error< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 619b563..2e643bd 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2544,12 +2544,11 @@ private: struct UsingDeclarator { SourceLocation TypenameLoc; CXXScopeSpec SS; - SourceLocation TemplateKWLoc; UnqualifiedId Name; SourceLocation EllipsisLoc; void clear() { - TypenameLoc = TemplateKWLoc = EllipsisLoc = SourceLocation(); + TypenameLoc = EllipsisLoc = SourceLocation(); SS.clear(); Name.clear(); } @@ -2744,7 +2743,7 @@ public: bool AllowConstructorName, bool AllowDeductionGuide, ParsedType ObjectType, - SourceLocation& TemplateKWLoc, + SourceLocation *TemplateKWLoc, UnqualifiedId &Result); private: diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 7f93c89..2b4c335 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6074,7 +6074,7 @@ public: TemplateNameKind isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword, - UnqualifiedId &Name, + const UnqualifiedId &Name, ParsedType ObjectType, bool EnteringContext, TemplateTy &Template, @@ -6244,7 +6244,7 @@ public: TemplateNameKind ActOnDependentTemplateName( Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, - UnqualifiedId &Name, ParsedType ObjectType, bool EnteringContext, + const UnqualifiedId &Name, ParsedType ObjectType, bool EnteringContext, TemplateTy &Template, bool AllowInjectedClassName = false); DeclResult diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 3d3abe3..fc0ca61 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -5600,12 +5600,11 @@ void Parser::ParseDirectDeclarator(Declarator &D) { D.getContext() == DeclaratorContext::MemberContext); } - SourceLocation TemplateKWLoc; bool HadScope = D.getCXXScopeSpec().isValid(); if (ParseUnqualifiedId(D.getCXXScopeSpec(), /*EnteringContext=*/true, /*AllowDestructorName=*/true, AllowConstructorName, - AllowDeductionGuide, nullptr, TemplateKWLoc, + AllowDeductionGuide, nullptr, nullptr, D.getName()) || // Once we're past the identifier, if the scope was bad, mark the // whole declarator bad. diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 0c789c9..ec1177a 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -602,7 +602,7 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context, /*AllowConstructorName=*/!(Tok.is(tok::identifier) && NextToken().is(tok::equal)), /*AllowDeductionGuide=*/false, - nullptr, D.TemplateKWLoc, D.Name)) + nullptr, nullptr, D.Name)) return true; } @@ -2476,7 +2476,7 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, SourceLocation TemplateKWLoc; UnqualifiedId Name; if (ParseUnqualifiedId(SS, false, true, true, false, nullptr, - TemplateKWLoc, Name)) { + &TemplateKWLoc, Name)) { SkipUntil(tok::semi); return nullptr; } @@ -2488,6 +2488,7 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, return nullptr; } + // FIXME: We should do something with the 'template' keyword here. return DeclGroupPtrTy::make(DeclGroupRef(Actions.ActOnUsingDeclaration( getCurScope(), AS, /*UsingLoc*/ SourceLocation(), /*TypenameLoc*/ SourceLocation(), SS, Name, diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 1b8865e..ca5d3bc 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1755,7 +1755,7 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { /*AllowConstructorName=*/ getLangOpts().MicrosoftExt, /*AllowDeductionGuide=*/false, - ObjectType, TemplateKWLoc, Name)) { + ObjectType, &TemplateKWLoc, Name)) { (void)Actions.CorrectDelayedTyposInExpr(LHS); LHS = ExprError(); } diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 7ebd90e..8e39adf 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -553,7 +553,7 @@ ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS, bool isAddressOfOpe /*AllowDestructorName=*/false, /*AllowConstructorName=*/false, /*AllowDeductionGuide=*/false, - /*ObjectType=*/nullptr, TemplateKWLoc, Name)) + /*ObjectType=*/nullptr, &TemplateKWLoc, Name)) return ExprError(); // This is only the direct operand of an & operator if it is not @@ -2044,9 +2044,8 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, ParsedType ObjectType, UnqualifiedId &Id, bool AssumeTemplateId) { - assert((AssumeTemplateId || Tok.is(tok::less)) && - "Expected '<' to finish parsing a template-id"); - + assert(Tok.is(tok::less) && "Expected '<' to finish parsing a template-id"); + TemplateTy Template; TemplateNameKind TNK = TNK_Non_template; switch (Id.getKind()) { @@ -2142,10 +2141,10 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, // Parse the enclosed template argument list. SourceLocation LAngleLoc, RAngleLoc; TemplateArgList TemplateArgs; - if (Tok.is(tok::less) && ParseTemplateIdAfterTemplateName( - true, LAngleLoc, TemplateArgs, RAngleLoc)) + if (ParseTemplateIdAfterTemplateName(true, LAngleLoc, TemplateArgs, + RAngleLoc)) return true; - + if (Id.getKind() == UnqualifiedIdKind::IK_Identifier || Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId || Id.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId) { @@ -2465,16 +2464,23 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, bool AllowConstructorName, bool AllowDeductionGuide, ParsedType ObjectType, - SourceLocation& TemplateKWLoc, + SourceLocation *TemplateKWLoc, UnqualifiedId &Result) { + if (TemplateKWLoc) + *TemplateKWLoc = SourceLocation(); // Handle 'A::template B'. This is for template-ids which have not // already been annotated by ParseOptionalCXXScopeSpecifier(). bool TemplateSpecified = false; - if (getLangOpts().CPlusPlus && Tok.is(tok::kw_template) && - (ObjectType || SS.isSet())) { - TemplateSpecified = true; - TemplateKWLoc = ConsumeToken(); + if (Tok.is(tok::kw_template)) { + if (TemplateKWLoc && (ObjectType || SS.isSet())) { + TemplateSpecified = true; + *TemplateKWLoc = ConsumeToken(); + } else { + SourceLocation TemplateLoc = ConsumeToken(); + Diag(TemplateLoc, diag::err_unexpected_template_in_unqualified_id) + << FixItHint::CreateRemoval(TemplateLoc); + } } // 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 (TemplateSpecified || Tok.is(tok::less)) - return ParseUnqualifiedIdTemplateId(SS, TemplateKWLoc, Id, IdLoc, - EnteringContext, ObjectType, - Result, TemplateSpecified); - + TemplateTy Template; + if (Tok.is(tok::less)) + return ParseUnqualifiedIdTemplateId( + 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; } @@ -2558,7 +2571,14 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, // We have already parsed a template-id; consume the annotation token as // our unqualified-id. 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(); return false; } @@ -2575,13 +2595,20 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, // // template-id: // operator-function-id < template-argument-list[opt] > + TemplateTy Template; if ((Result.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId || Result.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId) && - (TemplateSpecified || Tok.is(tok::less))) - return ParseUnqualifiedIdTemplateId(SS, TemplateKWLoc, - nullptr, SourceLocation(), - EnteringContext, ObjectType, - Result, TemplateSpecified); + Tok.is(tok::less)) + return ParseUnqualifiedIdTemplateId( + SS, TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), nullptr, + SourceLocation(), 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; } @@ -2649,12 +2676,11 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, IdentifierInfo *ClassName = Tok.getIdentifierInfo(); SourceLocation ClassNameLoc = ConsumeToken(); - if (TemplateSpecified || Tok.is(tok::less)) { + if (Tok.is(tok::less)) { Result.setDestructorName(TildeLoc, nullptr, ClassNameLoc); - return ParseUnqualifiedIdTemplateId(SS, TemplateKWLoc, - ClassName, ClassNameLoc, - EnteringContext, ObjectType, - Result, TemplateSpecified); + return ParseUnqualifiedIdTemplateId( + SS, TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), ClassName, + ClassNameLoc, EnteringContext, ObjectType, Result, TemplateSpecified); } // Note that this is a destructor name. diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index e00bfb8..bfd708fe 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -1154,7 +1154,6 @@ bool Parser::ParseOpenMPSimpleVarList( // Read tokens while ')' or annot_pragma_openmp_end is not found. while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end)) { CXXScopeSpec SS; - SourceLocation TemplateKWLoc; UnqualifiedId Name; // Read var name. Token PrevTok = Tok; @@ -1166,7 +1165,7 @@ bool Parser::ParseOpenMPSimpleVarList( SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); } else if (ParseUnqualifiedId(SS, false, false, false, false, nullptr, - TemplateKWLoc, Name)) { + nullptr, Name)) { IsCorrect = false; SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); @@ -1648,7 +1647,6 @@ OMPClause *Parser::ParseOpenMPSingleExprWithArgClause(OpenMPClauseKind Kind, static bool ParseReductionId(Parser &P, CXXScopeSpec &ReductionIdScopeSpec, UnqualifiedId &ReductionId) { - SourceLocation TemplateKWLoc; if (ReductionIdScopeSpec.isEmpty()) { auto OOK = OO_None; switch (P.getCurToken().getKind()) { @@ -1690,7 +1688,7 @@ static bool ParseReductionId(Parser &P, CXXScopeSpec &ReductionIdScopeSpec, /*AllowDestructorName*/ false, /*AllowConstructorName*/ false, /*AllowDeductionGuide*/ false, - nullptr, TemplateKWLoc, ReductionId); + nullptr, nullptr, ReductionId); } /// Parses clauses with list. diff --git a/clang/lib/Parse/ParseStmtAsm.cpp b/clang/lib/Parse/ParseStmtAsm.cpp index d81029e..290723c 100644 --- a/clang/lib/Parse/ParseStmtAsm.cpp +++ b/clang/lib/Parse/ParseStmtAsm.cpp @@ -239,7 +239,7 @@ ExprResult Parser::ParseMSAsmIdentifier(llvm::SmallVectorImpl &LineToks, /*AllowDestructorName=*/false, /*AllowConstructorName=*/false, /*AllowDeductionGuide=*/false, - /*ObjectType=*/nullptr, TemplateKWLoc, Id); + /*ObjectType=*/nullptr, &TemplateKWLoc, Id); // Perform the lookup. Result = Actions.LookupInlineAsmIdentifier(SS, TemplateKWLoc, Id, IsUnevaluatedContext); diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index d8f9f7a..036688c 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -2002,7 +2002,7 @@ bool Parser::ParseMicrosoftIfExistsCondition(IfExistsCondition& Result) { if (ParseUnqualifiedId( Result.SS, /*EnteringContext*/false, /*AllowDestructorName*/true, /*AllowConstructorName*/true, /*AllowDeductionGuide*/false, nullptr, - TemplateKWLoc, Result.Name)) { + &TemplateKWLoc, Result.Name)) { T.skipToEnd(); return true; } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 6f2800e..b1ecbfa 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2080,7 +2080,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, (Id.getKind() == UnqualifiedIdKind::IK_ImplicitSelfParam) ? LookupObjCImplicitSelfParam : LookupOrdinaryName); - if (TemplateArgs) { + if (TemplateKWLoc.isValid() || TemplateArgs) { // Lookup the template name again to correctly establish the context in // 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 @@ -2089,7 +2089,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, bool MemberOfUnknownSpecialization; LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false, MemberOfUnknownSpecialization); - + if (MemberOfUnknownSpecialization || (R.getResultKind() == LookupResult::NotFoundInCurrentInstantiation)) return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo, @@ -2155,6 +2155,9 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, if (SS.isValid()) 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, CCC ? std::move(CCC) : std::move(DefaultValidator), nullptr, None, &TE)) { diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 775008e..e03f143 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -759,9 +759,9 @@ Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType, TypoExpr *TE = nullptr; QualType RecordTy = BaseType; if (IsArrow) RecordTy = RecordTy->getAs()->getPointeeType(); - if (LookupMemberExprInRecord(*this, R, nullptr, - RecordTy->getAs(), OpLoc, IsArrow, - SS, TemplateArgs != nullptr, TE)) + if (LookupMemberExprInRecord( + *this, R, nullptr, RecordTy->getAs(), OpLoc, IsArrow, + SS, TemplateKWLoc.isValid() || TemplateArgs != nullptr, TE)) return ExprError(); if (TE) return TE; @@ -769,10 +769,10 @@ Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType, // Explicit member accesses. } else { ExprResult BaseResult = Base; - ExprResult Result = LookupMemberExpr( - *this, R, BaseResult, IsArrow, OpLoc, SS, - ExtraArgs ? ExtraArgs->ObjCImpDecl : nullptr, - TemplateArgs != nullptr); + ExprResult Result = + LookupMemberExpr(*this, R, BaseResult, IsArrow, OpLoc, SS, + ExtraArgs ? ExtraArgs->ObjCImpDecl : nullptr, + TemplateKWLoc.isValid() || TemplateArgs != nullptr); if (BaseResult.isInvalid()) return ExprError(); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 788eaca..d98176a 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -158,7 +158,7 @@ bool Sema::hasAnyAcceptableTemplateNames(LookupResult &R, TemplateNameKind Sema::isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword, - UnqualifiedId &Name, + const UnqualifiedId &Name, ParsedType ObjectTypePtr, bool EnteringContext, TemplateTy &TemplateResult, @@ -4102,7 +4102,7 @@ Sema::BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS, TemplateNameKind Sema::ActOnDependentTemplateName(Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, - UnqualifiedId &Name, + const UnqualifiedId &Name, ParsedType ObjectType, bool EnteringContext, TemplateTy &Result, diff --git a/clang/test/CXX/drs/dr1xx.cpp b/clang/test/CXX/drs/dr1xx.cpp index ee9f6a1..391b152 100644 --- a/clang/test/CXX/drs/dr1xx.cpp +++ b/clang/test/CXX/drs/dr1xx.cpp @@ -67,7 +67,10 @@ namespace dr108 { // dr108: yes namespace dr109 { // dr109: yes struct A { template void f(T); }; template 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; // 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; // expected-error {{use 'template' keyword}} expected-error {{using declaration cannot refer to a template specialization}} void g() { this->f(123); } // expected-error {{use 'template'}} }; } diff --git a/clang/test/CXX/drs/dr4xx.cpp b/clang/test/CXX/drs/dr4xx.cpp index d27adc428..b7d65e3 100644 --- a/clang/test/CXX/drs/dr4xx.cpp +++ b/clang/test/CXX/drs/dr4xx.cpp @@ -318,8 +318,8 @@ namespace dr420 { // dr420: yes q->~id(); p->id::~id(); q->id::~id(); - p->template id::~id(); // expected-error {{expected unqualified-id}} - q->template id::~id(); // expected-error {{expected unqualified-id}} + p->template id::~id(); // expected-error {{'template' keyword not permitted here}} expected-error {{base type 'int' is not a struct}} + q->template id::~id(); // expected-error {{'template' keyword not permitted here}} expected-error {{base type 'int' is not a struct}} p->A::template id::~id(); q->A::template id::~id(); } diff --git a/clang/test/CXX/temp/temp.res/p3.cpp b/clang/test/CXX/temp/temp.res/p3.cpp index ea87b8d..8984ce3 100644 --- a/clang/test/CXX/temp/temp.res/p3.cpp +++ b/clang/test/CXX/temp/temp.res/p3.cpp @@ -34,5 +34,5 @@ template template struct A::B { // a type, and then complain about the rest of the tokens, and then complain // that we didn't get a function declaration. friend A::C f7(); // expected-error {{use 'template' keyword to treat 'C' as a dependent template name}} expected-error 3{{}} - friend A::template C f8(); // expected-error 3{{}} + friend A::template C f8(); // expected-error 4{{}} }; diff --git a/clang/test/Parser/cxx-decl.cpp b/clang/test/Parser/cxx-decl.cpp index e1b36ec..c60e42f 100644 --- a/clang/test/Parser/cxx-decl.cpp +++ b/clang/test/Parser/cxx-decl.cpp @@ -237,10 +237,11 @@ namespace PR5066 { namespace PR17255 { void foo() { typename A::template B<>; // expected-error {{use of undeclared identifier 'A'}} + // expected-error@-1 {{'template' keyword not permitted here}} #if __cplusplus <= 199711L - // expected-error@-2 {{'template' keyword outside of a template}} + // expected-error@-3 {{'template' keyword outside of a template}} #endif - // expected-error@-4 {{expected a qualified name after 'typename'}} + // expected-error@-5 {{expected a qualified name after 'typename'}} } } diff --git a/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp b/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp index 2e1c9a4..4afc80f 100644 --- a/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp +++ b/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp @@ -382,16 +382,16 @@ int main() { namespace dependent_static_var_template { struct A { - template static int n; // expected-note {{here}} + template 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 int &f() { return T::template n; } // expected-error {{use of variable template 'n' requires template arguments}} int &s = f(); // expected-note {{instantiation of}} namespace B { - template static int n; + template 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}} } diff --git a/clang/test/SemaTemplate/template-id-expr.cpp b/clang/test/SemaTemplate/template-id-expr.cpp index 4e6e224..e9f63fa 100644 --- a/clang/test/SemaTemplate/template-id-expr.cpp +++ b/clang/test/SemaTemplate/template-id-expr.cpp @@ -85,6 +85,52 @@ struct Y0 { } }; +template void Y0 + ::template // expected-error {{expected unqualified-id}} + f1(U) {} + +// FIXME: error recovery is awful without this. + ; + +template +struct Y1 { + template + void f1(U); + + template + static void f2(U); + + void f3(int); + + static int f4(int); + template + static void f4(U); + + template + void f() { + Y1::template f1(0); + Y1::template f1(0); + this->template f1(0); + + Y1::template f2(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(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(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 y1) { y1.f(); } // expected-note {{in instantiation of}} + struct A { template struct B { -- 2.7.4