forked from OSchip/llvm-project
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:
parent
85e0f66250
commit
97f34576d4
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue