diff mbox series

[v4] c++: DR2237, cdtor and template-id tweaks [PR107126]

Message ID Zca5QmzwV7UP1Wh1@redhat.com
State New
Headers show
Series [v4] c++: DR2237, cdtor and template-id tweaks [PR107126] | expand

Commit Message

Marek Polacek Feb. 9, 2024, 11:46 p.m. UTC
On Fri, Feb 09, 2024 at 10:20:04AM -0500, Jason Merrill wrote:
> On 2/8/24 16:26, Marek Polacek wrote:
> > This patch does *not* fix
> > <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97202#c8>
> > where the C++20 diagnostic is missing altogether.
> 
> What would it take to fix that as well?

It's the "further DR2237 fix" below which is basically a one liner.
I'd thought it would be more involved than that.
 
> > 	* doc/invoke.texi: Document -Wdeprecated-template-id-cdtor.
> 
> Was it ever deprecated?  I'm not seeing that in
> 
> https://timsong-cpp.github.io/cppwp/n4659/#depr (C++17)

Aha, [diff] != [depr]...
 
> Let's drop the word "deprecated" from the option name and documentation.

Done throughout.
 
> > @@ -32331,11 +32338,11 @@ cp_parser_constructor_declarator_p (cp_parser *parser, cp_parser_flags flags,
> >     if (next_token->type != CPP_NAME
> >         && next_token->type != CPP_SCOPE
> >         && next_token->type != CPP_NESTED_NAME_SPECIFIER
> > -      /* DR 2237 (C++20 only): A simple-template-id is no longer valid as the
> > -	 declarator-id of a constructor or destructor.  */
> > -      && (next_token->type != CPP_TEMPLATE_ID || cxx_dialect >= cxx20))
> > +      && next_token->type != CPP_TEMPLATE_ID)
> >       return false;
> > +  const bool saw_template_id = (next_token->type == CPP_TEMPLATE_ID);
> 
> Please incorporate your "further DR2237 fix" patch into this one.

Patches squashed.
 
> > @@ -32552,6 +32559,19 @@ cp_parser_constructor_declarator_p (cp_parser *parser, cp_parser_flags flags,
> >     /* We did not really want to consume any tokens.  */
> >     cp_parser_abort_tentative_parse (parser);
> > +  /* DR 2237 (C++20 only): A simple-template-id is no longer valid as the
> > +     declarator-id of a constructor or destructor.  */
> > +  if (constructor_p && saw_template_id)
> > +    {
> > +      gcc_checking_assert
> > +	(!cp_parser_uncommitted_to_tentative_parse_p (parser));
> 
> Now I see the abort_ just above, so this seems unnecessary after all.

Done, thanks.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
Since my r11-532 changes to implement DR2237, for this test:

  template<typename T>
  struct S {
    S<T>();
  };

in C++20 we emit the ugly:

q.C:3:8: error: expected unqualified-id before ')' token
    3 |   S<T>();

which doesn't explain what the problem is.  This patch improves that
diagnostic, reduces the error to a pedwarn, and adds a -Wc++20-compat
diagnostic.  We now say:

q.C:3:7: warning: template-id not allowed for constructor in C++20 [-Wtemplate-id-cdtor]
    3 |   S<T>();
q.C:3:7: note: remove the '< >'

This patch also fixes
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97202#c8>
where the C++20 diagnostic was missing altogether: The problem was that I checked
for CPP_TEMPLATE_ID too early, at a point at which cp_parser_template_id may not
have been called yet.  So let's check for it at the end of the function, after
the tentative parse and rollback.

-Wc++20-compat triggered in libitm/; I sent a patch for that.

	DR 2237
	PR c++/107126
	PR c++/97202

gcc/c-family/ChangeLog:

	* c-opts.cc (c_common_post_options): In C++20 or with -Wc++20-compat,
	turn on -Wtemplate-id-cdtor.
	* c.opt (Wtemplate-id-cdtor): New.

gcc/cp/ChangeLog:

	* parser.cc (cp_parser_unqualified_id): Downgrade the DR2237 error to
	a pedwarn.
	(cp_parser_constructor_declarator_p): Likewise.

gcc/ChangeLog:

	* doc/invoke.texi: Document -Wtemplate-id-cdtor.

gcc/testsuite/ChangeLog:

	* g++.dg/DRs/dr2237.C: Adjust dg-error.
	* g++.dg/parse/constructor2.C: Likewise.
	* g++.dg/template/error34.C: Likewise.
	* g++.old-deja/g++.pt/ctor2.C: Likewise.
	* g++.dg/DRs/dr2237-2.C: New test.
	* g++.dg/DRs/dr2237-3.C: New test.
	* g++.dg/DRs/dr2237-4.C: New test.
	* g++.dg/DRs/dr2237-5.C: New test.
	* g++.dg/warn/Wtemplate-id-cdtor-1.C: New test.
	* g++.dg/warn/Wtemplate-id-cdtor-2.C: New test.
	* g++.dg/warn/Wtemplate-id-cdtor-3.C: New test.
	* g++.dg/warn/Wtemplate-id-cdtor-4.C: New test.
---
 gcc/c-family/c-opts.cc                        |  5 +++
 gcc/c-family/c.opt                            |  4 +++
 gcc/cp/parser.cc                              | 33 ++++++++++++++-----
 gcc/doc/invoke.texi                           | 19 ++++++++++-
 gcc/testsuite/g++.dg/DRs/dr2237-2.C           |  9 +++++
 gcc/testsuite/g++.dg/DRs/dr2237-3.C           | 16 +++++++++
 gcc/testsuite/g++.dg/DRs/dr2237-4.C           | 11 +++++++
 gcc/testsuite/g++.dg/DRs/dr2237-5.C           |  7 ++++
 gcc/testsuite/g++.dg/DRs/dr2237.C             |  2 +-
 gcc/testsuite/g++.dg/parse/constructor2.C     | 16 ++++-----
 gcc/testsuite/g++.dg/template/error34.C       | 10 +++---
 .../g++.dg/warn/Wtemplate-id-cdtor-1.C        |  9 +++++
 .../g++.dg/warn/Wtemplate-id-cdtor-2.C        |  9 +++++
 .../g++.dg/warn/Wtemplate-id-cdtor-3.C        |  9 +++++
 .../g++.dg/warn/Wtemplate-id-cdtor-4.C        |  9 +++++
 gcc/testsuite/g++.old-deja/g++.pt/ctor2.C     |  2 +-
 16 files changed, 146 insertions(+), 24 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/DRs/dr2237-2.C
 create mode 100644 gcc/testsuite/g++.dg/DRs/dr2237-3.C
 create mode 100644 gcc/testsuite/g++.dg/DRs/dr2237-4.C
 create mode 100644 gcc/testsuite/g++.dg/DRs/dr2237-5.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-1.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-2.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-3.C
 create mode 100644 gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-4.C


base-commit: f29f7f86935e29786bf9f976ec99d7639b381b14

Comments

Jason Merrill Feb. 9, 2024, 11:59 p.m. UTC | #1
On 2/9/24 18:46, Marek Polacek wrote:
> On Fri, Feb 09, 2024 at 10:20:04AM -0500, Jason Merrill wrote:
>> On 2/8/24 16:26, Marek Polacek wrote:
>>> This patch does *not* fix
>>> <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97202#c8>
>>> where the C++20 diagnostic is missing altogether.
>>
>> What would it take to fix that as well?
> 
> It's the "further DR2237 fix" below which is basically a one liner.
> I'd thought it would be more involved than that.
>   
>>> 	* doc/invoke.texi: Document -Wdeprecated-template-id-cdtor.
>>
>> Was it ever deprecated?  I'm not seeing that in
>>
>> https://timsong-cpp.github.io/cppwp/n4659/#depr (C++17)
> 
> Aha, [diff] != [depr]...
>   
>> Let's drop the word "deprecated" from the option name and documentation.
> 
> Done throughout.
>   
>>> @@ -32331,11 +32338,11 @@ cp_parser_constructor_declarator_p (cp_parser *parser, cp_parser_flags flags,
>>>      if (next_token->type != CPP_NAME
>>>          && next_token->type != CPP_SCOPE
>>>          && next_token->type != CPP_NESTED_NAME_SPECIFIER
>>> -      /* DR 2237 (C++20 only): A simple-template-id is no longer valid as the
>>> -	 declarator-id of a constructor or destructor.  */
>>> -      && (next_token->type != CPP_TEMPLATE_ID || cxx_dialect >= cxx20))
>>> +      && next_token->type != CPP_TEMPLATE_ID)
>>>        return false;
>>> +  const bool saw_template_id = (next_token->type == CPP_TEMPLATE_ID);
>>
>> Please incorporate your "further DR2237 fix" patch into this one.
> 
> Patches squashed.
>   
>>> @@ -32552,6 +32559,19 @@ cp_parser_constructor_declarator_p (cp_parser *parser, cp_parser_flags flags,
>>>      /* We did not really want to consume any tokens.  */
>>>      cp_parser_abort_tentative_parse (parser);
>>> +  /* DR 2237 (C++20 only): A simple-template-id is no longer valid as the
>>> +     declarator-id of a constructor or destructor.  */
>>> +  if (constructor_p && saw_template_id)
>>> +    {
>>> +      gcc_checking_assert
>>> +	(!cp_parser_uncommitted_to_tentative_parse_p (parser));
>>
>> Now I see the abort_ just above, so this seems unnecessary after all.
> 
> Done, thanks.
> 
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

OK, thanks.

> -- >8 --
> Since my r11-532 changes to implement DR2237, for this test:
> 
>    template<typename T>
>    struct S {
>      S<T>();
>    };
> 
> in C++20 we emit the ugly:
> 
> q.C:3:8: error: expected unqualified-id before ')' token
>      3 |   S<T>();
> 
> which doesn't explain what the problem is.  This patch improves that
> diagnostic, reduces the error to a pedwarn, and adds a -Wc++20-compat
> diagnostic.  We now say:
> 
> q.C:3:7: warning: template-id not allowed for constructor in C++20 [-Wtemplate-id-cdtor]
>      3 |   S<T>();
> q.C:3:7: note: remove the '< >'
> 
> This patch also fixes
> <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97202#c8>
> where the C++20 diagnostic was missing altogether: The problem was that I checked
> for CPP_TEMPLATE_ID too early, at a point at which cp_parser_template_id may not
> have been called yet.  So let's check for it at the end of the function, after
> the tentative parse and rollback.
> 
> -Wc++20-compat triggered in libitm/; I sent a patch for that.
> 
> 	DR 2237
> 	PR c++/107126
> 	PR c++/97202
> 
> gcc/c-family/ChangeLog:
> 
> 	* c-opts.cc (c_common_post_options): In C++20 or with -Wc++20-compat,
> 	turn on -Wtemplate-id-cdtor.
> 	* c.opt (Wtemplate-id-cdtor): New.
> 
> gcc/cp/ChangeLog:
> 
> 	* parser.cc (cp_parser_unqualified_id): Downgrade the DR2237 error to
> 	a pedwarn.
> 	(cp_parser_constructor_declarator_p): Likewise.
> 
> gcc/ChangeLog:
> 
> 	* doc/invoke.texi: Document -Wtemplate-id-cdtor.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/DRs/dr2237.C: Adjust dg-error.
> 	* g++.dg/parse/constructor2.C: Likewise.
> 	* g++.dg/template/error34.C: Likewise.
> 	* g++.old-deja/g++.pt/ctor2.C: Likewise.
> 	* g++.dg/DRs/dr2237-2.C: New test.
> 	* g++.dg/DRs/dr2237-3.C: New test.
> 	* g++.dg/DRs/dr2237-4.C: New test.
> 	* g++.dg/DRs/dr2237-5.C: New test.
> 	* g++.dg/warn/Wtemplate-id-cdtor-1.C: New test.
> 	* g++.dg/warn/Wtemplate-id-cdtor-2.C: New test.
> 	* g++.dg/warn/Wtemplate-id-cdtor-3.C: New test.
> 	* g++.dg/warn/Wtemplate-id-cdtor-4.C: New test.
> ---
>   gcc/c-family/c-opts.cc                        |  5 +++
>   gcc/c-family/c.opt                            |  4 +++
>   gcc/cp/parser.cc                              | 33 ++++++++++++++-----
>   gcc/doc/invoke.texi                           | 19 ++++++++++-
>   gcc/testsuite/g++.dg/DRs/dr2237-2.C           |  9 +++++
>   gcc/testsuite/g++.dg/DRs/dr2237-3.C           | 16 +++++++++
>   gcc/testsuite/g++.dg/DRs/dr2237-4.C           | 11 +++++++
>   gcc/testsuite/g++.dg/DRs/dr2237-5.C           |  7 ++++
>   gcc/testsuite/g++.dg/DRs/dr2237.C             |  2 +-
>   gcc/testsuite/g++.dg/parse/constructor2.C     | 16 ++++-----
>   gcc/testsuite/g++.dg/template/error34.C       | 10 +++---
>   .../g++.dg/warn/Wtemplate-id-cdtor-1.C        |  9 +++++
>   .../g++.dg/warn/Wtemplate-id-cdtor-2.C        |  9 +++++
>   .../g++.dg/warn/Wtemplate-id-cdtor-3.C        |  9 +++++
>   .../g++.dg/warn/Wtemplate-id-cdtor-4.C        |  9 +++++
>   gcc/testsuite/g++.old-deja/g++.pt/ctor2.C     |  2 +-
>   16 files changed, 146 insertions(+), 24 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/DRs/dr2237-2.C
>   create mode 100644 gcc/testsuite/g++.dg/DRs/dr2237-3.C
>   create mode 100644 gcc/testsuite/g++.dg/DRs/dr2237-4.C
>   create mode 100644 gcc/testsuite/g++.dg/DRs/dr2237-5.C
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-1.C
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-2.C
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-3.C
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-4.C
> 
> diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc
> index b845aff2226..be3058dca63 100644
> --- a/gcc/c-family/c-opts.cc
> +++ b/gcc/c-family/c-opts.cc
> @@ -998,6 +998,11 @@ c_common_post_options (const char **pfilename)
>   		       warn_deprecated_enum_float_conv,
>   		       cxx_dialect >= cxx20 && warn_deprecated);
>   
> +  /* -Wtemplate-id-cdtor is enabled by default in C++20.  */
> +  SET_OPTION_IF_UNSET (&global_options, &global_options_set,
> +		       warn_template_id_cdtor,
> +		       cxx_dialect >= cxx20 || warn_cxx20_compat);
> +
>     /* Declone C++ 'structors if -Os.  */
>     if (flag_declone_ctor_dtor == -1)
>       flag_declone_ctor_dtor = optimize_size;
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index 9c0a28092fc..b7a4a1a68e3 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -1408,6 +1408,10 @@ Wtautological-compare
>   C ObjC C++ ObjC++ Var(warn_tautological_compare) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
>   Warn if a comparison always evaluates to true or false.
>   
> +Wtemplate-id-cdtor
> +C++ ObjC++ Var(warn_template_id_cdtor) Warning
> +Warn about simple-template-id in a constructor or destructor.
> +
>   Wterminate
>   C++ ObjC++ Warning Var(warn_terminate) Init(1)
>   Warn if a throw expression will always result in a call to terminate().
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 09ecfa23b5d..8bba2fdef96 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -6719,12 +6719,19 @@ cp_parser_unqualified_id (cp_parser* parser,
>   
>   	/* DR 2237 (C++20 only): A simple-template-id is no longer valid as the
>   	   declarator-id of a constructor or destructor.  */
> -	if (token->type == CPP_TEMPLATE_ID && declarator_p
> -	    && cxx_dialect >= cxx20)
> +	if (token->type == CPP_TEMPLATE_ID && declarator_p)
>   	  {
> -	    if (!cp_parser_simulate_error (parser))
> -	      error_at (tilde_loc, "template-id not allowed for destructor");
> -	    return error_mark_node;
> +	    auto_diagnostic_group d;
> +	    bool w = false;
> +	    if (cxx_dialect >= cxx20 && !cp_parser_simulate_error (parser))
> +	      w = pedwarn (tilde_loc, OPT_Wtemplate_id_cdtor,
> +			   "template-id not allowed for destructor in C++20");
> +	    else if (cxx_dialect < cxx20
> +		     && !cp_parser_uncommitted_to_tentative_parse_p (parser))
> +	      w = warning_at (tilde_loc, OPT_Wtemplate_id_cdtor,
> +			      "template-id not allowed for destructor in C++20");
> +	    if (w)
> +	      inform (tilde_loc, "remove the %qs", "< >");
>   	  }
>   
>   	/* If there was an explicit qualification (S::~T), first look
> @@ -32331,9 +32338,7 @@ cp_parser_constructor_declarator_p (cp_parser *parser, cp_parser_flags flags,
>     if (next_token->type != CPP_NAME
>         && next_token->type != CPP_SCOPE
>         && next_token->type != CPP_NESTED_NAME_SPECIFIER
> -      /* DR 2237 (C++20 only): A simple-template-id is no longer valid as the
> -	 declarator-id of a constructor or destructor.  */
> -      && (next_token->type != CPP_TEMPLATE_ID || cxx_dialect >= cxx20))
> +      && next_token->type != CPP_TEMPLATE_ID)
>       return false;
>   
>     /* Parse tentatively; we are going to roll back all of the tokens
> @@ -32552,6 +32557,18 @@ cp_parser_constructor_declarator_p (cp_parser *parser, cp_parser_flags flags,
>     /* We did not really want to consume any tokens.  */
>     cp_parser_abort_tentative_parse (parser);
>   
> +  /* DR 2237 (C++20 only): A simple-template-id is no longer valid as the
> +     declarator-id of a constructor or destructor.  */
> +  if (constructor_p
> +      && cp_lexer_peek_token (parser->lexer)->type == CPP_TEMPLATE_ID)
> +    {
> +      auto_diagnostic_group d;
> +      if (emit_diagnostic (cxx_dialect >= cxx20 ? DK_PEDWARN : DK_WARNING,
> +			   input_location, OPT_Wtemplate_id_cdtor,
> +			   "template-id not allowed for constructor in C++20"))
> +	inform (input_location, "remove the %qs", "< >");
> +    }
> +
>     return constructor_p;
>   }
>   
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 71339b8b30f..0de184f6241 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -270,7 +270,7 @@ in the following sections.
>   -Wno-non-template-friend  -Wold-style-cast
>   -Woverloaded-virtual  -Wno-pmf-conversions -Wself-move -Wsign-promo
>   -Wsized-deallocation  -Wsuggest-final-methods
> --Wsuggest-final-types  -Wsuggest-override
> +-Wsuggest-final-types  -Wsuggest-override  -Wno-template-id-cdtor
>   -Wno-terminate  -Wno-vexing-parse  -Wvirtual-inheritance
>   -Wno-virtual-move-assign  -Wvolatile  -Wzero-as-null-pointer-constant}
>   
> @@ -4604,6 +4604,23 @@ namespaces, and this may be used to enforce that rule.  The warning is
>   inactive inside a system header file, such as the STL, so one can still
>   use the STL.  One may also use using directives and qualified names.
>   
> +@opindex Wtemplate-id-cdtor
> +@opindex Wno-template-id-cdtor
> +@item -Wno-template-id-cdtor @r{(C++ and Objective-C++ only)}
> +Disable the warning about the use of simple-template-id as the declarator-id
> +of a constructor or destructor, which became invalid in C++20 via DR 2237.
> +For example:
> +
> +@smallexample
> +template<typename T> struct S @{
> +  S<T>(); // should be S();
> +  ~S<T>();  // should be ~S();
> +@};
> +@end smallexample
> +
> +@option{-Wtemplate-id-cdtor} is enabled by default with
> +@option{-std=c++20}; it is also enabled by @option{-Wc++20-compat}.
> +
>   @opindex Wterminate
>   @opindex Wno-terminate
>   @item -Wno-terminate @r{(C++ and Objective-C++ only)}
> diff --git a/gcc/testsuite/g++.dg/DRs/dr2237-2.C b/gcc/testsuite/g++.dg/DRs/dr2237-2.C
> new file mode 100644
> index 00000000000..1d99347229c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/DRs/dr2237-2.C
> @@ -0,0 +1,9 @@
> +// DR 2237 - Can a template-id name a constructor?
> +// { dg-options "" }
> +
> +template<class T>
> +struct X {
> +  X<T>(); // { dg-warning "template-id not allowed for constructor" "" { target c++20 } }
> +  X(int); // OK, injected-class-name used
> +  ~X<T>(); // { dg-warning "template-id not allowed for destructor" "" { target c++20 } }
> +};
> diff --git a/gcc/testsuite/g++.dg/DRs/dr2237-3.C b/gcc/testsuite/g++.dg/DRs/dr2237-3.C
> new file mode 100644
> index 00000000000..c8ad6852389
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/DRs/dr2237-3.C
> @@ -0,0 +1,16 @@
> +// PR c++/107126
> +// { dg-options "" }
> +
> +template<typename T>
> +struct C
> +{
> +    ~C();
> +};
> +template<typename T>
> +C<T>::~C<T>()	      // { dg-warning "template-id not allowed for destructor" "" { target c++20 } }
> +{
> +}
> +int main()
> +{
> +    C<int> c;;
> +}
> diff --git a/gcc/testsuite/g++.dg/DRs/dr2237-4.C b/gcc/testsuite/g++.dg/DRs/dr2237-4.C
> new file mode 100644
> index 00000000000..a358dd521b5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/DRs/dr2237-4.C
> @@ -0,0 +1,11 @@
> +// PR c++/97202
> +// { dg-options "" }
> +
> +template<typename T>
> +struct F
> +{
> +  F<T>();  // { dg-warning "template-id not allowed for constructor" "" { target c++20 } }
> +};
> +
> +template<typename T>
> +inline F<T>::F() { }
> diff --git a/gcc/testsuite/g++.dg/DRs/dr2237-5.C b/gcc/testsuite/g++.dg/DRs/dr2237-5.C
> new file mode 100644
> index 00000000000..fd51968f7e1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/DRs/dr2237-5.C
> @@ -0,0 +1,7 @@
> +// PR c++/97202
> +// { dg-options "" }
> +
> +template<typename Base> struct S : Base {
> +  inline S<Base>() {} // { dg-warning "template-id not allowed for constructor" "" { target c++20 } }
> +  inline ~S<Base>() {} // { dg-warning "template-id not allowed for destructor" "" { target c++20 } }
> +};
> diff --git a/gcc/testsuite/g++.dg/DRs/dr2237.C b/gcc/testsuite/g++.dg/DRs/dr2237.C
> index f3d6d11e61e..830c8f5a2a6 100644
> --- a/gcc/testsuite/g++.dg/DRs/dr2237.C
> +++ b/gcc/testsuite/g++.dg/DRs/dr2237.C
> @@ -2,7 +2,7 @@
>   
>   template<class T>
>   struct X {
> -  X<T>(); // { dg-error "expected" "" { target c++20 } }
> +  X<T>(); // { dg-error "template-id not allowed for constructor" "" { target c++20 } }
>     X(int); // OK, injected-class-name used
>     ~X<T>(); // { dg-error "template-id not allowed for destructor" "" { target c++20 } }
>   };
> diff --git a/gcc/testsuite/g++.dg/parse/constructor2.C b/gcc/testsuite/g++.dg/parse/constructor2.C
> index d620f41e027..e4b2ad3f2fe 100644
> --- a/gcc/testsuite/g++.dg/parse/constructor2.C
> +++ b/gcc/testsuite/g++.dg/parse/constructor2.C
> @@ -1,11 +1,11 @@
>   // PR c++/14260
>   
> -template <class TClass>
> -class T
> -{
> -public:
> -  T(short,short f=0) {}
> -  T<TClass>(int f) {} // { dg-error "expected" "" { target c++20 } }
> -  T<TClass>(int f=0,const char* b=0) {} // { dg-error "expected" "" { target c++20 } }
> -};
> +template <class TClass>
> +class T
> +{
> +public:
> +  T(short,short f=0) {}
> +  T<TClass>(int f) {} // { dg-error "template-id not allowed for constructor" "" { target c++20 } }
> +  T<TClass>(int f=0,const char* b=0) {} // { dg-error "template-id not allowed for constructor" "" { target c++20 } }
> +};
>   
> diff --git a/gcc/testsuite/g++.dg/template/error34.C b/gcc/testsuite/g++.dg/template/error34.C
> index ab688d9ba8c..921cb8fb729 100644
> --- a/gcc/testsuite/g++.dg/template/error34.C
> +++ b/gcc/testsuite/g++.dg/template/error34.C
> @@ -3,27 +3,27 @@
>   
>   template<typename T> struct A
>   {
> -  A<__builtin_offsetof(T, x)>();	// { dg-error "type/value mismatch|offsetof\\(T, x\\)|expected" }
> +  A<__builtin_offsetof(T, x)>();	// { dg-error "type/value mismatch|offsetof\\(T, x\\)|template-id" }
>   };
>   
>   template<typename T> struct B
>   {
> -  B<__builtin_offsetof(T, x.y)>();	// { dg-error "type/value mismatch|offsetof\\(T, x.y\\)|expected" }
> +  B<__builtin_offsetof(T, x.y)>();	// { dg-error "type/value mismatch|offsetof\\(T, x.y\\)|template-id" }
>   };
>   
>   template<typename T> struct C
>   {
> -  C<__builtin_offsetof(T, x[6])>();	// { dg-error "type/value mismatch|offsetof\\(T, x\\\[6\\\]\\)|expected" }
> +  C<__builtin_offsetof(T, x[6])>();	// { dg-error "type/value mismatch|offsetof\\(T, x\\\[6\\\]\\)|template-id" }
>   };
>   
>   template<typename T> struct D
>   {
> -  D<__builtin_offsetof(T, x.y[6].z)>();	// { dg-error "type/value mismatch|offsetof\\(T, x.y\\\[6\\\].z\\)|expected" }
> +  D<__builtin_offsetof(T, x.y[6].z)>();	// { dg-error "type/value mismatch|offsetof\\(T, x.y\\\[6\\\].z\\)|template-id" }
>   };
>   
>   struct E { int x; };
>   
>   template<typename T> struct F
>   {
> -  F<__builtin_offsetof(E, x)>();	// { dg-error "type/value mismatch|offsetof\\(E, x\\)|expected" }
> +  F<__builtin_offsetof(E, x)>();	// { dg-error "type/value mismatch|offsetof\\(E, x\\)|template-id" }
>   };
> diff --git a/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-1.C b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-1.C
> new file mode 100644
> index 00000000000..429490708bc
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-1.C
> @@ -0,0 +1,9 @@
> +// PR c++/107126
> +// { dg-do compile }
> +// { dg-options "-Wc++20-compat" }
> +
> +template<class T>
> +struct X {
> +  X<T>(); // { dg-warning "template-id not allowed for constructor" }
> +  ~X<T>(); // { dg-warning "template-id not allowed for destructor" }
> +};
> diff --git a/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-2.C b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-2.C
> new file mode 100644
> index 00000000000..2b1c4ea8222
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-2.C
> @@ -0,0 +1,9 @@
> +// PR c++/107126
> +// { dg-do compile }
> +// { dg-options "-Wtemplate-id-cdtor" }
> +
> +template<class T>
> +struct X {
> +  X<T>(); // { dg-warning "template-id not allowed for constructor" }
> +  ~X<T>(); // { dg-warning "template-id not allowed for destructor" }
> +};
> diff --git a/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-3.C b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-3.C
> new file mode 100644
> index 00000000000..bed96e84bd2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-3.C
> @@ -0,0 +1,9 @@
> +// PR c++/107126
> +// { dg-do compile }
> +// { dg-options "-Wc++20-compat -Wno-template-id-cdtor" }
> +
> +template<class T>
> +struct X {
> +  X<T>(); // { dg-bogus "template-id not allowed for constructor" }
> +  ~X<T>(); // { dg-bogus "template-id not allowed for destructor" }
> +};
> diff --git a/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-4.C b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-4.C
> new file mode 100644
> index 00000000000..706e5746e60
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-4.C
> @@ -0,0 +1,9 @@
> +// PR c++/107126
> +// { dg-do compile }
> +// { dg-options "-Wtemplate-id-cdtor -Wno-c++20-compat" }
> +
> +template<class T>
> +struct X {
> +  X<T>(); // { dg-warning "template-id not allowed for constructor" }
> +  ~X<T>(); // { dg-warning "template-id not allowed for destructor" }
> +};
> diff --git a/gcc/testsuite/g++.old-deja/g++.pt/ctor2.C b/gcc/testsuite/g++.old-deja/g++.pt/ctor2.C
> index bf418ba48c7..56b4232d04b 100644
> --- a/gcc/testsuite/g++.old-deja/g++.pt/ctor2.C
> +++ b/gcc/testsuite/g++.old-deja/g++.pt/ctor2.C
> @@ -4,7 +4,7 @@
>   
>   template <class T>
>   struct A {
> -  A<T>(); // { dg-error "expected" "" { target c++20 } }
> +  A<T>(); // { dg-error "template-id" "" { target c++20 } }
>   };
>   
>   template <class T>
> 
> base-commit: f29f7f86935e29786bf9f976ec99d7639b381b14
diff mbox series

Patch

diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc
index b845aff2226..be3058dca63 100644
--- a/gcc/c-family/c-opts.cc
+++ b/gcc/c-family/c-opts.cc
@@ -998,6 +998,11 @@  c_common_post_options (const char **pfilename)
 		       warn_deprecated_enum_float_conv,
 		       cxx_dialect >= cxx20 && warn_deprecated);
 
+  /* -Wtemplate-id-cdtor is enabled by default in C++20.  */
+  SET_OPTION_IF_UNSET (&global_options, &global_options_set,
+		       warn_template_id_cdtor,
+		       cxx_dialect >= cxx20 || warn_cxx20_compat);
+
   /* Declone C++ 'structors if -Os.  */
   if (flag_declone_ctor_dtor == -1)
     flag_declone_ctor_dtor = optimize_size;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 9c0a28092fc..b7a4a1a68e3 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1408,6 +1408,10 @@  Wtautological-compare
 C ObjC C++ ObjC++ Var(warn_tautological_compare) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
 Warn if a comparison always evaluates to true or false.
 
+Wtemplate-id-cdtor
+C++ ObjC++ Var(warn_template_id_cdtor) Warning
+Warn about simple-template-id in a constructor or destructor.
+
 Wterminate
 C++ ObjC++ Warning Var(warn_terminate) Init(1)
 Warn if a throw expression will always result in a call to terminate().
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 09ecfa23b5d..8bba2fdef96 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -6719,12 +6719,19 @@  cp_parser_unqualified_id (cp_parser* parser,
 
 	/* DR 2237 (C++20 only): A simple-template-id is no longer valid as the
 	   declarator-id of a constructor or destructor.  */
-	if (token->type == CPP_TEMPLATE_ID && declarator_p
-	    && cxx_dialect >= cxx20)
+	if (token->type == CPP_TEMPLATE_ID && declarator_p)
 	  {
-	    if (!cp_parser_simulate_error (parser))
-	      error_at (tilde_loc, "template-id not allowed for destructor");
-	    return error_mark_node;
+	    auto_diagnostic_group d;
+	    bool w = false;
+	    if (cxx_dialect >= cxx20 && !cp_parser_simulate_error (parser))
+	      w = pedwarn (tilde_loc, OPT_Wtemplate_id_cdtor,
+			   "template-id not allowed for destructor in C++20");
+	    else if (cxx_dialect < cxx20
+		     && !cp_parser_uncommitted_to_tentative_parse_p (parser))
+	      w = warning_at (tilde_loc, OPT_Wtemplate_id_cdtor,
+			      "template-id not allowed for destructor in C++20");
+	    if (w)
+	      inform (tilde_loc, "remove the %qs", "< >");
 	  }
 
 	/* If there was an explicit qualification (S::~T), first look
@@ -32331,9 +32338,7 @@  cp_parser_constructor_declarator_p (cp_parser *parser, cp_parser_flags flags,
   if (next_token->type != CPP_NAME
       && next_token->type != CPP_SCOPE
       && next_token->type != CPP_NESTED_NAME_SPECIFIER
-      /* DR 2237 (C++20 only): A simple-template-id is no longer valid as the
-	 declarator-id of a constructor or destructor.  */
-      && (next_token->type != CPP_TEMPLATE_ID || cxx_dialect >= cxx20))
+      && next_token->type != CPP_TEMPLATE_ID)
     return false;
 
   /* Parse tentatively; we are going to roll back all of the tokens
@@ -32552,6 +32557,18 @@  cp_parser_constructor_declarator_p (cp_parser *parser, cp_parser_flags flags,
   /* We did not really want to consume any tokens.  */
   cp_parser_abort_tentative_parse (parser);
 
+  /* DR 2237 (C++20 only): A simple-template-id is no longer valid as the
+     declarator-id of a constructor or destructor.  */
+  if (constructor_p
+      && cp_lexer_peek_token (parser->lexer)->type == CPP_TEMPLATE_ID)
+    {
+      auto_diagnostic_group d;
+      if (emit_diagnostic (cxx_dialect >= cxx20 ? DK_PEDWARN : DK_WARNING,
+			   input_location, OPT_Wtemplate_id_cdtor,
+			   "template-id not allowed for constructor in C++20"))
+	inform (input_location, "remove the %qs", "< >");
+    }
+
   return constructor_p;
 }
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 71339b8b30f..0de184f6241 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -270,7 +270,7 @@  in the following sections.
 -Wno-non-template-friend  -Wold-style-cast
 -Woverloaded-virtual  -Wno-pmf-conversions -Wself-move -Wsign-promo
 -Wsized-deallocation  -Wsuggest-final-methods
--Wsuggest-final-types  -Wsuggest-override
+-Wsuggest-final-types  -Wsuggest-override  -Wno-template-id-cdtor
 -Wno-terminate  -Wno-vexing-parse  -Wvirtual-inheritance
 -Wno-virtual-move-assign  -Wvolatile  -Wzero-as-null-pointer-constant}
 
@@ -4604,6 +4604,23 @@  namespaces, and this may be used to enforce that rule.  The warning is
 inactive inside a system header file, such as the STL, so one can still
 use the STL.  One may also use using directives and qualified names.
 
+@opindex Wtemplate-id-cdtor
+@opindex Wno-template-id-cdtor
+@item -Wno-template-id-cdtor @r{(C++ and Objective-C++ only)}
+Disable the warning about the use of simple-template-id as the declarator-id
+of a constructor or destructor, which became invalid in C++20 via DR 2237.
+For example:
+
+@smallexample
+template<typename T> struct S @{
+  S<T>(); // should be S();
+  ~S<T>();  // should be ~S();
+@};
+@end smallexample
+
+@option{-Wtemplate-id-cdtor} is enabled by default with
+@option{-std=c++20}; it is also enabled by @option{-Wc++20-compat}.
+
 @opindex Wterminate
 @opindex Wno-terminate
 @item -Wno-terminate @r{(C++ and Objective-C++ only)}
diff --git a/gcc/testsuite/g++.dg/DRs/dr2237-2.C b/gcc/testsuite/g++.dg/DRs/dr2237-2.C
new file mode 100644
index 00000000000..1d99347229c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/DRs/dr2237-2.C
@@ -0,0 +1,9 @@ 
+// DR 2237 - Can a template-id name a constructor?
+// { dg-options "" }
+
+template<class T>
+struct X {
+  X<T>(); // { dg-warning "template-id not allowed for constructor" "" { target c++20 } }
+  X(int); // OK, injected-class-name used
+  ~X<T>(); // { dg-warning "template-id not allowed for destructor" "" { target c++20 } }
+};
diff --git a/gcc/testsuite/g++.dg/DRs/dr2237-3.C b/gcc/testsuite/g++.dg/DRs/dr2237-3.C
new file mode 100644
index 00000000000..c8ad6852389
--- /dev/null
+++ b/gcc/testsuite/g++.dg/DRs/dr2237-3.C
@@ -0,0 +1,16 @@ 
+// PR c++/107126
+// { dg-options "" }
+
+template<typename T>
+struct C
+{
+    ~C();
+};
+template<typename T>
+C<T>::~C<T>()	      // { dg-warning "template-id not allowed for destructor" "" { target c++20 } }
+{
+}
+int main()
+{
+    C<int> c;;
+}
diff --git a/gcc/testsuite/g++.dg/DRs/dr2237-4.C b/gcc/testsuite/g++.dg/DRs/dr2237-4.C
new file mode 100644
index 00000000000..a358dd521b5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/DRs/dr2237-4.C
@@ -0,0 +1,11 @@ 
+// PR c++/97202
+// { dg-options "" }
+
+template<typename T>
+struct F
+{
+  F<T>();  // { dg-warning "template-id not allowed for constructor" "" { target c++20 } }
+};
+
+template<typename T>
+inline F<T>::F() { }
diff --git a/gcc/testsuite/g++.dg/DRs/dr2237-5.C b/gcc/testsuite/g++.dg/DRs/dr2237-5.C
new file mode 100644
index 00000000000..fd51968f7e1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/DRs/dr2237-5.C
@@ -0,0 +1,7 @@ 
+// PR c++/97202
+// { dg-options "" }
+
+template<typename Base> struct S : Base {
+  inline S<Base>() {} // { dg-warning "template-id not allowed for constructor" "" { target c++20 } }
+  inline ~S<Base>() {} // { dg-warning "template-id not allowed for destructor" "" { target c++20 } }
+};
diff --git a/gcc/testsuite/g++.dg/DRs/dr2237.C b/gcc/testsuite/g++.dg/DRs/dr2237.C
index f3d6d11e61e..830c8f5a2a6 100644
--- a/gcc/testsuite/g++.dg/DRs/dr2237.C
+++ b/gcc/testsuite/g++.dg/DRs/dr2237.C
@@ -2,7 +2,7 @@ 
 
 template<class T>
 struct X {
-  X<T>(); // { dg-error "expected" "" { target c++20 } }
+  X<T>(); // { dg-error "template-id not allowed for constructor" "" { target c++20 } }
   X(int); // OK, injected-class-name used
   ~X<T>(); // { dg-error "template-id not allowed for destructor" "" { target c++20 } }
 };
diff --git a/gcc/testsuite/g++.dg/parse/constructor2.C b/gcc/testsuite/g++.dg/parse/constructor2.C
index d620f41e027..e4b2ad3f2fe 100644
--- a/gcc/testsuite/g++.dg/parse/constructor2.C
+++ b/gcc/testsuite/g++.dg/parse/constructor2.C
@@ -1,11 +1,11 @@ 
 // PR c++/14260
 
-template <class TClass> 
-class T 
-{ 
-public: 
-  T(short,short f=0) {} 
-  T<TClass>(int f) {} // { dg-error "expected" "" { target c++20 } }
-  T<TClass>(int f=0,const char* b=0) {} // { dg-error "expected" "" { target c++20 } }
-}; 
+template <class TClass>
+class T
+{
+public:
+  T(short,short f=0) {}
+  T<TClass>(int f) {} // { dg-error "template-id not allowed for constructor" "" { target c++20 } }
+  T<TClass>(int f=0,const char* b=0) {} // { dg-error "template-id not allowed for constructor" "" { target c++20 } }
+};
 
diff --git a/gcc/testsuite/g++.dg/template/error34.C b/gcc/testsuite/g++.dg/template/error34.C
index ab688d9ba8c..921cb8fb729 100644
--- a/gcc/testsuite/g++.dg/template/error34.C
+++ b/gcc/testsuite/g++.dg/template/error34.C
@@ -3,27 +3,27 @@ 
 
 template<typename T> struct A
 {
-  A<__builtin_offsetof(T, x)>();	// { dg-error "type/value mismatch|offsetof\\(T, x\\)|expected" }
+  A<__builtin_offsetof(T, x)>();	// { dg-error "type/value mismatch|offsetof\\(T, x\\)|template-id" }
 };
 
 template<typename T> struct B
 {
-  B<__builtin_offsetof(T, x.y)>();	// { dg-error "type/value mismatch|offsetof\\(T, x.y\\)|expected" }
+  B<__builtin_offsetof(T, x.y)>();	// { dg-error "type/value mismatch|offsetof\\(T, x.y\\)|template-id" }
 };
 
 template<typename T> struct C
 {
-  C<__builtin_offsetof(T, x[6])>();	// { dg-error "type/value mismatch|offsetof\\(T, x\\\[6\\\]\\)|expected" }
+  C<__builtin_offsetof(T, x[6])>();	// { dg-error "type/value mismatch|offsetof\\(T, x\\\[6\\\]\\)|template-id" }
 };
 
 template<typename T> struct D
 {
-  D<__builtin_offsetof(T, x.y[6].z)>();	// { dg-error "type/value mismatch|offsetof\\(T, x.y\\\[6\\\].z\\)|expected" }
+  D<__builtin_offsetof(T, x.y[6].z)>();	// { dg-error "type/value mismatch|offsetof\\(T, x.y\\\[6\\\].z\\)|template-id" }
 };
 
 struct E { int x; };
 
 template<typename T> struct F
 {
-  F<__builtin_offsetof(E, x)>();	// { dg-error "type/value mismatch|offsetof\\(E, x\\)|expected" }
+  F<__builtin_offsetof(E, x)>();	// { dg-error "type/value mismatch|offsetof\\(E, x\\)|template-id" }
 };
diff --git a/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-1.C b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-1.C
new file mode 100644
index 00000000000..429490708bc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-1.C
@@ -0,0 +1,9 @@ 
+// PR c++/107126
+// { dg-do compile }
+// { dg-options "-Wc++20-compat" }
+
+template<class T>
+struct X {
+  X<T>(); // { dg-warning "template-id not allowed for constructor" }
+  ~X<T>(); // { dg-warning "template-id not allowed for destructor" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-2.C b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-2.C
new file mode 100644
index 00000000000..2b1c4ea8222
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-2.C
@@ -0,0 +1,9 @@ 
+// PR c++/107126
+// { dg-do compile }
+// { dg-options "-Wtemplate-id-cdtor" }
+
+template<class T>
+struct X {
+  X<T>(); // { dg-warning "template-id not allowed for constructor" }
+  ~X<T>(); // { dg-warning "template-id not allowed for destructor" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-3.C b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-3.C
new file mode 100644
index 00000000000..bed96e84bd2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-3.C
@@ -0,0 +1,9 @@ 
+// PR c++/107126
+// { dg-do compile }
+// { dg-options "-Wc++20-compat -Wno-template-id-cdtor" }
+
+template<class T>
+struct X {
+  X<T>(); // { dg-bogus "template-id not allowed for constructor" }
+  ~X<T>(); // { dg-bogus "template-id not allowed for destructor" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-4.C b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-4.C
new file mode 100644
index 00000000000..706e5746e60
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wtemplate-id-cdtor-4.C
@@ -0,0 +1,9 @@ 
+// PR c++/107126
+// { dg-do compile }
+// { dg-options "-Wtemplate-id-cdtor -Wno-c++20-compat" }
+
+template<class T>
+struct X {
+  X<T>(); // { dg-warning "template-id not allowed for constructor" }
+  ~X<T>(); // { dg-warning "template-id not allowed for destructor" }
+};
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/ctor2.C b/gcc/testsuite/g++.old-deja/g++.pt/ctor2.C
index bf418ba48c7..56b4232d04b 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/ctor2.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/ctor2.C
@@ -4,7 +4,7 @@ 
 
 template <class T>
 struct A {
-  A<T>(); // { dg-error "expected" "" { target c++20 } }
+  A<T>(); // { dg-error "template-id" "" { target c++20 } }
 };
 
 template <class T>