diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp index 18a574c89c20..ff02f9a31cb2 100644 --- a/clang/lib/AST/TemplateBase.cpp +++ b/clang/lib/AST/TemplateBase.cpp @@ -59,7 +59,8 @@ void TemplateArgument::Profile(llvm::FoldingSetNodeID &ID, break; case Template: - ID.AddPointer(getAsTemplate().getAsVoidPointer()); + ID.AddPointer(Context.getCanonicalTemplateName(getAsTemplate()) + .getAsVoidPointer()); break; case Integral: diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index a7e7648c1f56..b2ecc9e827f1 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -109,52 +109,43 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, if (!HasScopeSpecifier && !ObjectType) break; + TentativeParsingAction TPA(*this); SourceLocation TemplateKWLoc = ConsumeToken(); UnqualifiedId TemplateName; if (Tok.is(tok::identifier)) { - TemplateName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); - - // If the next token is not '<', we may have a stray 'template' keyword. - // Complain and suggest removing the template keyword, but otherwise - // allow parsing to continue. - if (NextToken().isNot(tok::less)) { - Diag(NextToken().getLocation(), - diag::err_less_after_template_name_in_nested_name_spec) - << Tok.getIdentifierInfo()->getName() - << CodeModificationHint::CreateRemoval(SourceRange(TemplateKWLoc)); - break; - } - // Consume the identifier. + TemplateName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); ConsumeToken(); } else if (Tok.is(tok::kw_operator)) { if (ParseUnqualifiedIdOperator(SS, EnteringContext, ObjectType, - TemplateName)) + TemplateName)) { + TPA.Commit(); break; + } if (TemplateName.getKind() != UnqualifiedId::IK_OperatorFunctionId) { Diag(TemplateName.getSourceRange().getBegin(), diag::err_id_after_template_in_nested_name_spec) << TemplateName.getSourceRange(); - break; - } else if (Tok.isNot(tok::less)) { - std::string OperatorName = "operator "; - OperatorName += getOperatorSpelling( - TemplateName.OperatorFunctionId.Operator); - Diag(Tok.getLocation(), - diag::err_less_after_template_name_in_nested_name_spec) - << OperatorName - << TemplateName.getSourceRange(); + TPA.Commit(); break; } } else { - Diag(Tok.getLocation(), - diag::err_id_after_template_in_nested_name_spec) - << SourceRange(TemplateKWLoc); + TPA.Revert(); break; } + // If the next token is not '<', we have a qualified-id that refers + // to a template name, such as T::template apply, but is not a + // template-id. + if (Tok.isNot(tok::less)) { + TPA.Revert(); + break; + } + + // Commit to parsing the template-id. + TPA.Commit(); TemplateTy Template = Actions.ActOnDependentTemplateName(TemplateKWLoc, SS, TemplateName, ObjectType); diff --git a/clang/test/SemaTemplate/default-arguments.cpp b/clang/test/SemaTemplate/default-arguments.cpp index fc3f9fe9b9b6..8352afdb0dc4 100644 --- a/clang/test/SemaTemplate/default-arguments.cpp +++ b/clang/test/SemaTemplate/default-arguments.cpp @@ -1,5 +1,4 @@ // RUN: clang-cc -fsyntax-only -verify %s - template struct X; // expected-note{{template is declared here}} X *x1; @@ -80,16 +79,31 @@ X2::NonType1<> x2_nontype1_bad; // expected-note{{instantiation of default X2::Inner3::VeryInner<> vi; X2::Inner3::NonType2<> x2_deep_nontype; - template struct is_same { static const bool value = false; }; template struct is_same { static const bool value = true; }; -static int array1[is_same<__typeof__(vi), +int array1[is_same<__typeof__(vi), X2::Inner3::VeryInner >::value? 1 : -1]; -static int array2[is_same<__typeof(x2_deep_nontype), - X2::Inner3::NonType2::Inner3::NonType2 >::value? 1 : -1]; + +// Template template parameter defaults +template class X = X2> struct X3 { }; +int array3[is_same, X3 >::value? 1 : -1]; + +struct add_pointer { + template + struct apply { + typedef T* type; + }; +}; + +template class X = T::template apply> + struct X4; +int array4[is_same, + X4 >::value? 1 : -1]; diff --git a/clang/test/SemaTemplate/nested-name-spec-template.cpp b/clang/test/SemaTemplate/nested-name-spec-template.cpp index 1bdc7a89d4e8..e936640f5e3e 100644 --- a/clang/test/SemaTemplate/nested-name-spec-template.cpp +++ b/clang/test/SemaTemplate/nested-name-spec-template.cpp @@ -28,11 +28,8 @@ N::M::Promote::type *ret_intptr5(int* ip) { return ip; } ::N::M::Promote::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}} +N::M::template; // expected-error{{expected unqualified-id}} +N::M::template Promote; // expected-error{{expected unqualified-id}} namespace N { template struct A; @@ -49,13 +46,9 @@ struct ::N::A::X { int foo; }; -#if 0 -// FIXME: the following crashes the parser, because Sema has no way to -// communicate that the "dependent" template-name N::template B doesn't -// actually refer to a template. template struct TestA { - typedef typename N::template B::type type; // xpected-error{{'B' following the 'template' keyword does not refer to a template}} - // FIXME: should show what B *does* refer to. + typedef typename N::template B::type type; // expected-error{{'B' following the 'template' keyword does not refer to a template}} \ + // expected-error{{identifier or template-id}} \ + // expected-error{{expected member name}} }; -#endif