Fix speculative parsing of dependent template names in

nested-name-specifiers so that they don't gobble the template name (or
operator-function-id) unless there is also a
template-argument-list. For example, given

  T::template apply

we would previously consume both "template" and "apply" as part of
parsing the nested-name-specifier, then error when we see that there
is no "<" starting a template argument list. Now, we parse such
constructs tentatively, and back off if the "<" is not present. This
allows us to parse dependent template names as one would use them for,
e.g., template template parameters:

  template<typename T, template<class> class X = T::template apply>
    struct MetaSomething;

Also, test default arguments for template template parameters.

llvm-svn: 86841
This commit is contained in:
Douglas Gregor 2009-11-11 16:39:34 +00:00
parent ba61fed5d3
commit 120635bc9c
4 changed files with 43 additions and 44 deletions

View File

@ -59,7 +59,8 @@ void TemplateArgument::Profile(llvm::FoldingSetNodeID &ID,
break; break;
case Template: case Template:
ID.AddPointer(getAsTemplate().getAsVoidPointer()); ID.AddPointer(Context.getCanonicalTemplateName(getAsTemplate())
.getAsVoidPointer());
break; break;
case Integral: case Integral:

View File

@ -109,52 +109,43 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
if (!HasScopeSpecifier && !ObjectType) if (!HasScopeSpecifier && !ObjectType)
break; break;
TentativeParsingAction TPA(*this);
SourceLocation TemplateKWLoc = ConsumeToken(); SourceLocation TemplateKWLoc = ConsumeToken();
UnqualifiedId TemplateName; UnqualifiedId TemplateName;
if (Tok.is(tok::identifier)) { 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. // Consume the identifier.
TemplateName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
ConsumeToken(); ConsumeToken();
} else if (Tok.is(tok::kw_operator)) { } else if (Tok.is(tok::kw_operator)) {
if (ParseUnqualifiedIdOperator(SS, EnteringContext, ObjectType, if (ParseUnqualifiedIdOperator(SS, EnteringContext, ObjectType,
TemplateName)) TemplateName)) {
TPA.Commit();
break; break;
}
if (TemplateName.getKind() != UnqualifiedId::IK_OperatorFunctionId) { if (TemplateName.getKind() != UnqualifiedId::IK_OperatorFunctionId) {
Diag(TemplateName.getSourceRange().getBegin(), Diag(TemplateName.getSourceRange().getBegin(),
diag::err_id_after_template_in_nested_name_spec) diag::err_id_after_template_in_nested_name_spec)
<< TemplateName.getSourceRange(); << TemplateName.getSourceRange();
break; TPA.Commit();
} 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();
break; break;
} }
} else { } else {
Diag(Tok.getLocation(), TPA.Revert();
diag::err_id_after_template_in_nested_name_spec)
<< SourceRange(TemplateKWLoc);
break; 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 TemplateTy Template
= Actions.ActOnDependentTemplateName(TemplateKWLoc, SS, TemplateName, = Actions.ActOnDependentTemplateName(TemplateKWLoc, SS, TemplateName,
ObjectType); ObjectType);

View File

@ -1,5 +1,4 @@
// RUN: clang-cc -fsyntax-only -verify %s // RUN: clang-cc -fsyntax-only -verify %s
template<typename T, int N = 2> struct X; // expected-note{{template is declared here}} template<typename T, int N = 2> struct X; // expected-note{{template is declared here}}
X<int, 1> *x1; X<int, 1> *x1;
@ -80,16 +79,31 @@ X2<int>::NonType1<> x2_nontype1_bad; // expected-note{{instantiation of default
X2<int>::Inner3<float>::VeryInner<> vi; X2<int>::Inner3<float>::VeryInner<> vi;
X2<char>::Inner3<int>::NonType2<> x2_deep_nontype; X2<char>::Inner3<int>::NonType2<> x2_deep_nontype;
template<typename T, typename U> template<typename T, typename U>
struct is_same { static const bool value = false; }; struct is_same { static const bool value = false; };
template<typename T> template<typename T>
struct is_same<T, T> { static const bool value = true; }; struct is_same<T, T> { static const bool value = true; };
static int array1[is_same<__typeof__(vi), int array1[is_same<__typeof__(vi),
X2<int>::Inner3<float>::VeryInner<int, float> >::value? 1 : -1]; X2<int>::Inner3<float>::VeryInner<int, float> >::value? 1 : -1];
static int array2[is_same<__typeof(x2_deep_nontype), int array2[is_same<__typeof(x2_deep_nontype),
X2<char>::Inner3<int>::NonType2<sizeof(char), sizeof(int), X2<char>::Inner3<int>::NonType2<sizeof(char), sizeof(int),
sizeof(char)+sizeof(int)> >::value? 1 : -1]; sizeof(char)+sizeof(int)> >::value? 1 : -1];
// Template template parameter defaults
template<template<typename T> class X = X2> struct X3 { };
int array3[is_same<X3<>, X3<X2> >::value? 1 : -1];
struct add_pointer {
template<typename T>
struct apply {
typedef T* type;
};
};
template<typename T, template<typename> class X = T::template apply>
struct X4;
int array4[is_same<X4<add_pointer>,
X4<add_pointer, add_pointer::apply> >::value? 1 : -1];

View File

@ -28,11 +28,8 @@ N::M::Promote<int>::type *ret_intptr5(int* ip) { return ip; }
::N::M::Promote<int>::type *ret_intptr6(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}} \ N::M::template; // expected-error{{expected unqualified-id}}
// expected-error{{expected unqualified-id}} N::M::template Promote; // 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}}
namespace N { namespace N {
template<typename T> struct A; template<typename T> struct A;
@ -49,13 +46,9 @@ struct ::N::A<int>::X {
int foo; 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<typename T> template<typename T>
struct TestA { struct TestA {
typedef typename N::template B<T>::type type; // xpected-error{{'B' following the 'template' keyword does not refer to a template}} typedef typename N::template B<T>::type type; // expected-error{{'B' following the 'template' keyword does not refer to a template}} \
// FIXME: should show what B *does* refer to. // expected-error{{identifier or template-id}} \
// expected-error{{expected member name}}
}; };
#endif