diff options
author | Jakub Jelinek <jakub@redhat.com> | 2022-04-29 13:50:10 +0200 |
---|---|---|
committer | Jakub Jelinek <jakub@redhat.com> | 2022-04-29 13:51:36 +0200 |
commit | a282da2243103d79262ca04f5e3a3cc7b9b06935 (patch) | |
tree | cfe1e5dde10785f65afd31b6f59d8c1894a5dedf | |
parent | 8c465ddd619bbb2949bc2bc31629c2a826a7c437 (diff) | |
download | gcc-a282da2243103d79262ca04f5e3a3cc7b9b06935.zip gcc-a282da2243103d79262ca04f5e3a3cc7b9b06935.tar.gz gcc-a282da2243103d79262ca04f5e3a3cc7b9b06935.tar.bz2 |
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 <jakub@redhat.com>
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.
-rw-r--r-- | gcc/cp/parser.cc | 51 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/parse/template28.C | 4 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/parse/template30.C | 49 |
3 files changed, 98 insertions, 6 deletions
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<class> struct A {}; -template<class T> void foo(A<T>=A<T>()) {} // { dg-error "24:variable or field .foo. declared void" } -// { dg-error "template" "" { target *-*-* } .-1 } +template<class T> void foo(A<T>=A<T>()) {} // { 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<typename T> struct A {}; +template<typename T> int z; // { dg-warning "variable templates only available with" "" { target c++11_down } } +template<int N> int w; // { dg-warning "variable templates only available with" "" { target c++11_down } } + +void +foo () +{ + z<int>=0; // { dg-error "'>=' should be '> =' to terminate a template argument list" } +} // { dg-error "expected ';' before numeric constant" "" { target *-*-* } .-1 } + +int +bar () +{ + return z<int>>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<int>>=0; // { dg-error "'>>=' should be '> >=' to terminate a template argument list" } +} // { dg-error "expected ';' before numeric constant" "" { target *-*-* } .-1 } + +int +qux () +{ + return z<A<int>>=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<B()>=5> = 5; +} +#endif |