forked from OSchip/llvm-project
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:
parent
ba61fed5d3
commit
120635bc9c
|
@ -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:
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
Loading…
Reference in New Issue