From: Jakub Jelinek Date: Fri, 29 Apr 2022 11:50:10 +0000 (+0200) Subject: c++: Improve diagnostics for template args terminated with >= or >>= [PR104319] X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a282da2243103d79262ca04f5e3a3cc7b9b06935;p=platform%2Fupstream%2Fgcc.git c++: Improve diagnostics for template args terminated with >= or >>= [PR104319] As mentioned in the PR, for C++98 we have diagnostics that expect >> terminating template arguments to be a mistake for > > (C++11 said it has to be treated that way), while if user trying to spare the spacebar doesn't separate > from following = or >> from following =, the diagnostics is confusing, while clang suggests adding space in between. The following patch does that for >= and >>= too. For some strange reason the error recovery emits further errors, not really sure what's going on because I overwrite the token->type like the code does for the C++11 >> case or for the C++98 >> cases, but at least the first error is nicer (well, for the C++98 nested template case and >>= I need to overwrite it to > and so the = is lost, so perhaps some follow-up errors are needed for that case). 2022-04-29 Jakub Jelinek PR c++/104319 * parser.cc (cp_parser_template_argument): Treat >= like C++98 >> after a type id by setting maybe_type_id and aborting tentative parse. (cp_parser_enclosed_template_argument_list): Handle CPP_GREATER_EQ like misspelled CPP_GREATER CPP_RQ and CPP_RSHIFT_EQ like misspelled CPP_GREATER CPP_GREATER_EQ or CPP_RSHIFT CPP_EQ or CPP_GREATER CPP_GREATER CPP_EQ. (cp_parser_next_token_ends_template_argument_p): Return true also for CPP_GREATER_EQ and CPP_RSHIFT_EQ. * g++.dg/parse/template28.C: Adjust expected diagnostics. * g++.dg/parse/template30.C: New test. --- diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 2235da1..ee69934 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -18818,8 +18818,13 @@ cp_parser_template_argument (cp_parser* parser) In C++0x, the '>>' will be considered two separate '>' tokens. */ if (!cp_parser_error_occurred (parser) - && cxx_dialect == cxx98 - && cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT)) + && ((cxx_dialect == cxx98 + && cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT)) + /* Similarly for >= which + cp_parser_next_token_ends_template_argument_p treats for + diagnostics purposes as mistyped > =, but can be valid + after a type-id. */ + || cp_lexer_next_token_is (parser->lexer, CPP_GREATER_EQ))) { maybe_type_id = true; cp_parser_abort_tentative_parse (parser); @@ -32045,7 +32050,9 @@ cp_parser_enclosed_template_argument_list (cp_parser* parser) cp_evaluated ev; /* Parse the template-argument-list itself. */ if (cp_lexer_next_token_is (parser->lexer, CPP_GREATER) - || cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT)) + || cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT) + || cp_lexer_next_token_is (parser->lexer, CPP_GREATER_EQ) + || cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT_EQ)) arguments = NULL_TREE; else arguments = cp_parser_template_argument_list (parser); @@ -32102,6 +32109,38 @@ cp_parser_enclosed_template_argument_list (cp_parser* parser) "a template argument list"); } } + /* Similarly for >>= and >=. */ + else if (cp_lexer_next_token_is (parser->lexer, CPP_GREATER_EQ) + || cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT_EQ)) + { + cp_token *token = cp_lexer_consume_token (parser->lexer); + gcc_rich_location richloc (token->location); + enum cpp_ttype new_type; + const char *replacement; + if (token->type == CPP_GREATER_EQ) + { + replacement = "> ="; + new_type = CPP_EQ; + } + else if (!saved_greater_than_is_operator_p) + { + if (cxx_dialect != cxx98) + replacement = ">> ="; + else + replacement = "> > ="; + new_type = CPP_GREATER; + } + else + { + replacement = "> >="; + new_type = CPP_GREATER_EQ; + } + richloc.add_fixit_replace (replacement); + error_at (&richloc, "%qs should be %qs to terminate a template " + "argument list", + cpp_type2name (token->type, token->flags), replacement); + token->type = new_type; + } else cp_parser_require_end_of_template_parameter_list (parser); /* The `>' token might be a greater-than operator again now. */ @@ -33180,7 +33219,11 @@ cp_parser_next_token_ends_template_argument_p (cp_parser *parser) return (token->type == CPP_COMMA || token->type == CPP_GREATER || token->type == CPP_ELLIPSIS - || ((cxx_dialect != cxx98) && token->type == CPP_RSHIFT)); + || ((cxx_dialect != cxx98) && token->type == CPP_RSHIFT) + /* For better diagnostics, treat >>= like that too, that + shouldn't appear non-nested in template arguments. */ + || token->type == CPP_GREATER_EQ + || token->type == CPP_RSHIFT_EQ); } /* Returns TRUE iff the n-th token is a "<", or the n-th is a "[" and the diff --git a/gcc/testsuite/g++.dg/parse/template28.C b/gcc/testsuite/g++.dg/parse/template28.C index bfd8af1..fc65a54 100644 --- a/gcc/testsuite/g++.dg/parse/template28.C +++ b/gcc/testsuite/g++.dg/parse/template28.C @@ -2,8 +2,8 @@ template struct A {}; -template void foo(A=A()) {} // { dg-error "24:variable or field .foo. declared void" } -// { dg-error "template" "" { target *-*-* } .-1 } +template void foo(A=A()) {} // { dg-error "'>=' should be '> =' to terminate a template argument list" } +// { dg-error "expected" "" { target *-*-* } .-1 } void bar() { diff --git a/gcc/testsuite/g++.dg/parse/template30.C b/gcc/testsuite/g++.dg/parse/template30.C new file mode 100644 index 0000000..fa89889 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/template30.C @@ -0,0 +1,49 @@ +// PR c++/104319 +// { dg-do compile } +// { dg-options "" } + +template struct A {}; +template int z; // { dg-warning "variable templates only available with" "" { target c++11_down } } +template int w; // { dg-warning "variable templates only available with" "" { target c++11_down } } + +void +foo () +{ + z=0; // { dg-error "'>=' should be '> =' to terminate a template argument list" } +} // { dg-error "expected ';' before numeric constant" "" { target *-*-* } .-1 } + +int +bar () +{ + return z>0; // { dg-error "spurious '>>', use '>' to terminate a template argument list" "" { target c++98_only } } +} // { dg-error "expected ';' before numeric constant" "" { target c++98_only } .-1 } + +int +baz () +{ + return z>=0; // { dg-error "'>>=' should be '> >=' to terminate a template argument list" } +} // { dg-error "expected ';' before numeric constant" "" { target *-*-* } .-1 } + +int +qux () +{ + return z>=0; // { dg-error "'>>=' should be '>> =' to terminate a template argument list" "" { target c++11 } } +} // { dg-error "'>>=' should be '> > =' to terminate a template argument list" "" { target c++98_only } .-1 } + // { dg-error "parse error in template argument list" "" { target *-*-* } .-2 } + // { dg-error "template argument 1 is invalid" "" { target *-*-* } .-3 } + +void +quux () +{ + w<5>=0>=6>=8> = 5; +} + +#if __cplusplus >= 201103L +struct B { constexpr bool operator >= (int) { return true; } }; + +void +corge () +{ + w=5> = 5; +} +#endif