Teach the type-id/expression disambiguator about different

disambiguation contexts, so that we properly parse template arguments
such as

  A<int()>

as type-ids rather than as expressions. Since this can be confusing
(especially when the template parameter is a non-type template
parameter), we try to give a friendly error message.

Almost, eliminate a redundant error message (that should have been a
note) and add some ultra-basic checks for non-type template
arguments.

llvm-svn: 64189
This commit is contained in:
Douglas Gregor 2009-02-10 00:53:15 +00:00
parent 85e0f66250
commit 97f34576d4
7 changed files with 56 additions and 27 deletions

View File

@ -497,8 +497,6 @@ DIAG(note_template_nontype_parm_prev_declaration, NOTE,
// C++ Template Argument Lists
DIAG(err_template_arg_list_different_arity, ERROR,
"%select{too few|too many}0 template arguments for %select{class template|function template|template template parameter|template}1 %2")
DIAG(note_template_parameter_here, ERROR,
"template parameter is declared here")
DIAG(err_template_arg_must_be_type, ERROR,
"template argument for template type parameter must be a type")
DIAG(err_template_arg_must_be_expr, ERROR,

View File

@ -819,12 +819,20 @@ private:
return isDeclarationSpecifier();
}
/// \brief Specifies the context in which type-id/expression
/// disambiguation will occur.
enum TentativeCXXTypeIdContext {
TypeIdInParens,
TypeIdAsTemplateArgument
};
/// isTypeIdInParens - Assumes that a '(' was parsed and now we want to know
/// whether the parens contain an expression or a type-id.
/// Returns true for a type-id and false for an expression.
bool isTypeIdInParens() {
if (getLang().CPlusPlus)
return isCXXTypeIdInParens();
return isCXXTypeId(TypeIdInParens);
return isTypeSpecifierQualifier();
}
@ -855,12 +863,7 @@ private:
/// the function returns true to let the declaration parsing code handle it.
bool isCXXConditionDeclaration();
/// isCXXTypeIdInParens - Assumes that a '(' was parsed and now we want to
/// know whether the parens contain an expression or a type-id.
/// Returns true for a type-id and false for an expression.
/// If during the disambiguation process a parsing error is encountered,
/// the function returns true to let the declaration parsing code handle it.
bool isCXXTypeIdInParens();
bool isCXXTypeId(TentativeCXXTypeIdContext Context);
/// TPResult - Used as the result value for functions whose purpose is to
/// disambiguate C++ constructs by "tentatively parsing" them.

View File

@ -456,7 +456,7 @@ void *Parser::ParseTemplateArgument(bool &ArgIsType) {
// the corresponding template-parameter.
//
// Therefore, we initially try to parse a type-id.
if (isTypeIdInParens()) {
if (isCXXTypeId(TypeIdAsTemplateArgument)) {
ArgIsType = true;
return ParseTypeName();
}

View File

@ -270,16 +270,24 @@ bool Parser::isCXXConditionDeclaration() {
return TPR == TPResult::True();
}
/// isCXXTypeIdInParens - Assumes that a '(' was parsed and now we want to
/// know whether the parens contain an expression or a type-id.
/// Returns true for a type-id and false for an expression.
/// If during the disambiguation process a parsing error is encountered,
/// the function returns true to let the declaration parsing code handle it.
///
/// type-id:
/// type-specifier-seq abstract-declarator[opt]
///
bool Parser::isCXXTypeIdInParens() {
/// \brief Determine whether the next set of tokens contains a type-id.
///
/// The context parameter states what context we're parsing right
/// now, which affects how this routine copes with the token
/// following the type-id. If the context is TypeIdInParens, we have
/// already parsed the '(' and we will cease lookahead when we hit
/// the corresponding ')'. If the context is
/// TypeIdAsTemplateArgument, we've already parsed the '<' or ','
/// before this template argument, and will cease lookahead when we
/// hit a '>', '>>' (in C++0x), or ','. Returns true for a type-id
/// and false for an expression. If during the disambiguation
/// process a parsing error is encountered, the function returns
/// true to let the declaration parsing code handle it.
///
/// type-id:
/// type-specifier-seq abstract-declarator[opt]
///
bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context) {
// C++ 8.2p2:
// The ambiguity arising from the similarity between a function-style cast and
@ -318,7 +326,14 @@ bool Parser::isCXXTypeIdInParens() {
if (TPR == TPResult::Ambiguous()) {
// We are supposed to be inside parens, so if after the abstract declarator
// we encounter a ')' this is a type-id, otherwise it's an expression.
if (Tok.is(tok::r_paren))
if (Context == TypeIdInParens && Tok.is(tok::r_paren))
TPR = TPResult::True();
// We are supposed to be inside a template argument, so if after
// the abstract declarator we encounter a '>', '>>' (in C++0x), or
// ',', this is a type-id. Otherwise, it's an expression.
else if (Context == TypeIdAsTemplateArgument &&
(Tok.is(tok::greater) || Tok.is(tok::comma) ||
(getLang().CPlusPlus0x && Tok.is(tok::greatergreater))))
TPR = TPResult::True();
else
TPR = TPResult::False();

View File

@ -458,7 +458,7 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
// is an expression.
Diag(ArgExpr->getSourceRange().getBegin(),
diag::err_template_arg_must_be_type);
Diag((*Param)->getLocation(), diag::note_template_parameter_here);
Diag((*Param)->getLocation(), diag::note_template_param_here);
Invalid = true;
} else if (NonTypeTemplateParmDecl *NTTP
= dyn_cast<NonTypeTemplateParmDecl>(*Param)) {
@ -484,7 +484,7 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
<< ArgType;
else
Diag(ArgLoc, diag::err_template_arg_must_be_expr);
Diag((*Param)->getLocation(), diag::note_template_parameter_here);
Diag((*Param)->getLocation(), diag::note_template_param_here);
Invalid = true;
} else {
// Check template template parameters.

View File

@ -0,0 +1,14 @@
// RUN: clang -fsyntax-only -std=c++98 -verify %s
template<int N> struct A; // expected-note 2{{template parameter is declared here}}
A<0> *a0;
A<int()> *a1; // expected-error{{template argument for non-type template parameter is treated as type 'int (void)'}}
A<int> *a2; // expected-error{{template argument for non-type template parameter must be an expression}}
A<1 >> 2> *a3;
// FIXME: We haven't tried actually checking the expressions yet.
// A<A> *a4;

View File

@ -1,5 +1,5 @@
// RUN: clang -fsyntax-only -verify %s
template<typename T> class A; // expected-error 2 {{template parameter is declared here}}
template<typename T> class A; // expected-note 2 {{template parameter is declared here}}
// [temp.arg.type]p1
A<0> *a1; // expected-error{{template argument for template type parameter must be a type}}
@ -7,9 +7,8 @@ A<0> *a1; // expected-error{{template argument for template type parameter must
A<A> *a2; // expected-error{{template argument for template type parameter must be a type}}
A<int> *a3;
// FIXME: The two below are well-formed, but we're not parsing them as type-ids.
// A<int()> *a4;
// A<int(float)> *a5;
A<int()> *a4;
A<int(float)> *a5;
A<A<int> > *a6;
// [temp.arg.type]p2