From 97f34576d4a50a24f4154b85bcff9e29b240e8ac Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 10 Feb 2009 00:53:15 +0000 Subject: [PATCH] Teach the type-id/expression disambiguator about different disambiguation contexts, so that we properly parse template arguments such as A 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 --- .../clang/Basic/DiagnosticSemaKinds.def | 2 - clang/include/clang/Parse/Parser.h | 17 +++++---- clang/lib/Parse/ParseTemplate.cpp | 2 +- clang/lib/Parse/ParseTentative.cpp | 37 +++++++++++++------ clang/lib/Sema/SemaTemplate.cpp | 4 +- clang/test/SemaTemplate/temp_arg_nontype.cpp | 14 +++++++ clang/test/SemaTemplate/temp_arg_type.cpp | 7 ++-- 7 files changed, 56 insertions(+), 27 deletions(-) create mode 100644 clang/test/SemaTemplate/temp_arg_nontype.cpp diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.def b/clang/include/clang/Basic/DiagnosticSemaKinds.def index d4d68a4d3499..fd6055103b17 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.def +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.def @@ -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, diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 2d777ff7ba24..ab43c516cdbc 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -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. diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index f860058117b2..25aea352242a 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -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(); } diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 445dd8b6eff7..06c136df9187 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -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(); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 1a2da846429d..ecbc860d1df0 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -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(*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. diff --git a/clang/test/SemaTemplate/temp_arg_nontype.cpp b/clang/test/SemaTemplate/temp_arg_nontype.cpp new file mode 100644 index 000000000000..8cbede4cd0d3 --- /dev/null +++ b/clang/test/SemaTemplate/temp_arg_nontype.cpp @@ -0,0 +1,14 @@ +// RUN: clang -fsyntax-only -std=c++98 -verify %s + +template struct A; // expected-note 2{{template parameter is declared here}} + +A<0> *a0; + +A *a1; // expected-error{{template argument for non-type template parameter is treated as type 'int (void)'}} + +A *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 *a4; diff --git a/clang/test/SemaTemplate/temp_arg_type.cpp b/clang/test/SemaTemplate/temp_arg_type.cpp index 08b103230970..c75536288447 100644 --- a/clang/test/SemaTemplate/temp_arg_type.cpp +++ b/clang/test/SemaTemplate/temp_arg_type.cpp @@ -1,5 +1,5 @@ // RUN: clang -fsyntax-only -verify %s -template class A; // expected-error 2 {{template parameter is declared here}} +template 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 *a2; // expected-error{{template argument for template type parameter must be a type}} A *a3; -// FIXME: The two below are well-formed, but we're not parsing them as type-ids. -// A *a4; -// A *a5; +A *a4; +A *a5; A > *a6; // [temp.arg.type]p2