diff mbox series

[v2] c++: Implement C++23 P2334R1 - #elifdef/#elifndef

Message ID 20211005202452.GZ304296@tucnak
State New
Headers show
Series [v2] c++: Implement C++23 P2334R1 - #elifdef/#elifndef | expand

Commit Message

Jakub Jelinek Oct. 5, 2021, 8:24 p.m. UTC
On Tue, Oct 05, 2021 at 05:23:26PM +0000, Joseph Myers wrote:
> > One is in the patch below, ignores that sentence and only implements it
> > for -std=c++23/-std=gnu++23 like it is only implemented for -std=c23.
> > Another option would be to implement it also in the older GNU modes but
> > not in the C/CXX modes (but it would be strange if we did that just for
> > C++ and not for C).
> > Yet another option is to enable it unconditionally.
> > And yet another option would be to enable it unconditionally but emit
> > a warning (or pedwarn) when it is seen.
> > Note, when it is enabled for the older language modes, as Joseph wrote
> > in the c11-elifdef-1.c testcase, it can result e.g. in rejecting previously
> > valid code:
> 
> It would probably be reasonable to enable it in older GNU modes for C as 
> well as C++ if desired (and, in that case, emit a pedwarn-if-pedantic when 
> it's acted on) - cases where it affects compatibility should be rare.  
> Enabling with a pedwarn in strict modes is problematic because it changes 
> semantics of valid code where it was inside #if 0, however.  It doesn't 
> make sense at all to me to think of a new feature like this (one with no 
> prior art in C mentioned in the WG14 proposal) as a defect fix.
> 
> Any normal directive - i.e. one that has no effect on the preprocessor #if 
> structure and so is ignored inside #if 0 for all language versions - can 
> more reasonably be enabled for all language versions with a pedwarn when 
> used for old versions.  (In particular, that will be appropriate for 
> #warning, where the "don't pedwarn in C2X modes" part needs implementing 
> after N2686 was accepted at the August / September WG14 meeting - I don't 
> know if C++ is doing anything with #warning.)

Ok, here is an updated version which accepts them in both
CPP_OPTION (pfile, elifdef) (aka -std={gnu,c}{2x,++2b,++23} modes) or
!CPP_OPTION (pfile, std) (aka -std=gnu* modes), but for the latter
pedwarns if pedantic (but only if the directive actually changes the
preprocessing behavior or if it would be rejected with corresponding
-std=c*).
The second hunk in directives.c is for the cases where it would otherwise
error about unknown directive, the third hunk is for the case where it
changes the skipping state.  If pfile->state.skipping is true before
encountering the directive and after it as well, then whether the directive
is there or not makes no difference.

2021-10-05  Jakub Jelinek  <jakub@redhat.com>

libcpp/
	* init.c (lang_defaults): Implement P2334R1, enable elifdef for
	-std=c++23 and -std=gnu++23.
	* directives.c (_cpp_handle_directive): Support elifdef/elifndef if
	either CPP_OPTION (pfile, elifdef) or !CPP_OPTION (pfile, std).
	(do_elif): For older non-std modes if pedantic pedwarn about
	#elifdef/#elifndef directives that change behavior.
gcc/testsuite/
	* gcc.dg/cpp/gnu11-elifdef-1.c: New test.
	* gcc.dg/cpp/gnu11-elifdef-2.c: New test.
	* gcc.dg/cpp/gnu11-elifdef-3.c: New test.
	* gcc.dg/cpp/gnu11-elifdef-4.c: New test.
	* g++.dg/cpp/elifdef-1.C: New test.
	* g++.dg/cpp/elifdef-2.C: New test.
	* g++.dg/cpp/elifdef-3.C: New test.
	* g++.dg/cpp/elifdef-4.C: New test.
	* g++.dg/cpp/elifdef-5.C: New test.
	* g++.dg/cpp/elifdef-6.C: New test.
	* g++.dg/cpp/elifdef-7.C: New test.



	Jakub

Comments

Jason Merrill Oct. 6, 2021, 3:02 a.m. UTC | #1
On 10/5/21 16:24, Jakub Jelinek wrote:
> On Tue, Oct 05, 2021 at 05:23:26PM +0000, Joseph Myers wrote:
>>> One is in the patch below, ignores that sentence and only implements it
>>> for -std=c++23/-std=gnu++23 like it is only implemented for -std=c23.
>>> Another option would be to implement it also in the older GNU modes but
>>> not in the C/CXX modes (but it would be strange if we did that just for
>>> C++ and not for C).
>>> Yet another option is to enable it unconditionally.
>>> And yet another option would be to enable it unconditionally but emit
>>> a warning (or pedwarn) when it is seen.
>>> Note, when it is enabled for the older language modes, as Joseph wrote
>>> in the c11-elifdef-1.c testcase, it can result e.g. in rejecting previously
>>> valid code:
>>
>> It would probably be reasonable to enable it in older GNU modes for C as
>> well as C++ if desired (and, in that case, emit a pedwarn-if-pedantic when
>> it's acted on) - cases where it affects compatibility should be rare.
>> Enabling with a pedwarn in strict modes is problematic because it changes
>> semantics of valid code where it was inside #if 0, however.  It doesn't
>> make sense at all to me to think of a new feature like this (one with no
>> prior art in C mentioned in the WG14 proposal) as a defect fix.
>>
>> Any normal directive - i.e. one that has no effect on the preprocessor #if
>> structure and so is ignored inside #if 0 for all language versions - can
>> more reasonably be enabled for all language versions with a pedwarn when
>> used for old versions.  (In particular, that will be appropriate for
>> #warning, where the "don't pedwarn in C2X modes" part needs implementing
>> after N2686 was accepted at the August / September WG14 meeting - I don't
>> know if C++ is doing anything with #warning.)
> 
> Ok, here is an updated version which accepts them in both
> CPP_OPTION (pfile, elifdef) (aka -std={gnu,c}{2x,++2b,++23} modes) or
> !CPP_OPTION (pfile, std) (aka -std=gnu* modes), but for the latter
> pedwarns if pedantic (but only if the directive actually changes the
> preprocessing behavior or if it would be rejected with corresponding
> -std=c*).
> The second hunk in directives.c is for the cases where it would otherwise
> error about unknown directive, the third hunk is for the case where it
> changes the skipping state.  If pfile->state.skipping is true before
> encountering the directive and after it as well, then whether the directive
> is there or not makes no difference.

LGTM.

> 2021-10-05  Jakub Jelinek  <jakub@redhat.com>
> 
> libcpp/
> 	* init.c (lang_defaults): Implement P2334R1, enable elifdef for
> 	-std=c++23 and -std=gnu++23.
> 	* directives.c (_cpp_handle_directive): Support elifdef/elifndef if
> 	either CPP_OPTION (pfile, elifdef) or !CPP_OPTION (pfile, std).
> 	(do_elif): For older non-std modes if pedantic pedwarn about
> 	#elifdef/#elifndef directives that change behavior.
> gcc/testsuite/
> 	* gcc.dg/cpp/gnu11-elifdef-1.c: New test.
> 	* gcc.dg/cpp/gnu11-elifdef-2.c: New test.
> 	* gcc.dg/cpp/gnu11-elifdef-3.c: New test.
> 	* gcc.dg/cpp/gnu11-elifdef-4.c: New test.
> 	* g++.dg/cpp/elifdef-1.C: New test.
> 	* g++.dg/cpp/elifdef-2.C: New test.
> 	* g++.dg/cpp/elifdef-3.C: New test.
> 	* g++.dg/cpp/elifdef-4.C: New test.
> 	* g++.dg/cpp/elifdef-5.C: New test.
> 	* g++.dg/cpp/elifdef-6.C: New test.
> 	* g++.dg/cpp/elifdef-7.C: New test.
> 
> --- libcpp/init.c.jj	2021-09-02 10:01:15.954715595 +0200
> +++ libcpp/init.c	2021-10-05 09:55:15.010620700 +0200
> @@ -122,8 +122,8 @@ static const struct lang_flags lang_defa
>     /* CXX17    */  { 1,  1,  1,  1,  1,  0,    1,  1,   1,   1,   1,    1,     1,     0,   1,      0,   1,     0,   0,   0 },
>     /* GNUCXX20 */  { 1,  1,  1,  1,  1,  0,    0,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   0,   0 },
>     /* CXX20    */  { 1,  1,  1,  1,  1,  0,    1,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   0,   0 },
> -  /* GNUCXX23 */  { 1,  1,  1,  1,  1,  1,    0,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   1,   0 },
> -  /* CXX23    */  { 1,  1,  1,  1,  1,  1,    1,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   1,   0 },
> +  /* GNUCXX23 */  { 1,  1,  1,  1,  1,  1,    0,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   1,   1 },
> +  /* CXX23    */  { 1,  1,  1,  1,  1,  1,    1,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   1,   1 },
>     /* ASM      */  { 0,  0,  1,  0,  0,  0,    0,  0,   0,   0,   0,    0,     0,     0,   0,      0,   0,     0,   0,   0 }
>   };
>   
> --- libcpp/directives.c.jj	2021-05-12 09:44:55.080621650 +0200
> +++ libcpp/directives.c	2021-10-05 22:05:52.303984796 +0200
> @@ -447,7 +447,11 @@ _cpp_handle_directive (cpp_reader *pfile
>         if (dname->val.node.node->is_directive)
>   	{
>   	  dir = &dtable[dname->val.node.node->directive_index];
> -	  if ((dir->flags & ELIFDEF) && !CPP_OPTION (pfile, elifdef))
> +	  if ((dir->flags & ELIFDEF)
> +	      && !CPP_OPTION (pfile, elifdef)
> +	      /* For -std=gnu* modes elifdef is supported with
> +		 a pedwarn if pedantic.  */
> +	      && CPP_OPTION (pfile, std))
>   	    dir = 0;
>   	}
>       }
> @@ -2117,7 +2121,26 @@ do_elif (cpp_reader *pfile)
>   	 are skipped and their controlling directives are processed as
>   	 if they were in a group that is skipped."  */
>         if (ifs->skip_elses)
> -	pfile->state.skipping = 1;
> +	{
> +	  /* In older GNU standards, #elifdef/#elifndef is supported
> +	     as an extension, but pedwarn if -pedantic if the presence
> +	     of the directive would be rejected.  */
> +	  if (pfile->directive != &dtable[T_ELIF]
> +	      && ! CPP_OPTION (pfile, elifdef)
> +	      && CPP_PEDANTIC (pfile)
> +	      && !pfile->state.skipping)
> +	    {
> +	      if (CPP_OPTION (pfile, cplusplus))
> +		cpp_error (pfile, CPP_DL_PEDWARN,
> +			   "#%s before C++23 is a GCC extension",
> +			   pfile->directive->name);
> +	      else
> +		cpp_error (pfile, CPP_DL_PEDWARN,
> +			   "#%s before C2X is a GCC extension",
> +			   pfile->directive->name);
> +	    }
> +	  pfile->state.skipping = 1;
> +	}
>         else
>   	{
>   	  if (pfile->directive == &dtable[T_ELIF])
> @@ -2139,6 +2162,22 @@ do_elif (cpp_reader *pfile)
>   		  if (pfile->cb.used)
>   		    pfile->cb.used (pfile, pfile->directive_line, node);
>   		  check_eol (pfile, false);
> +		  /* In older GNU standards, #elifdef/#elifndef is supported
> +		     as an extension, but pedwarn if -pedantic if the presence
> +		     of the directive would change behavior.  */
> +		  if (! CPP_OPTION (pfile, elifdef)
> +		      && CPP_PEDANTIC (pfile)
> +		      && pfile->state.skipping != skip)
> +		    {
> +		      if (CPP_OPTION (pfile, cplusplus))
> +			cpp_error (pfile, CPP_DL_PEDWARN,
> +				   "#%s before C++23 is a GCC extension",
> +				   pfile->directive->name);
> +		      else
> +			cpp_error (pfile, CPP_DL_PEDWARN,
> +				   "#%s before C2X is a GCC extension",
> +				   pfile->directive->name);
> +		    }
>   		  pfile->state.skipping = skip;
>   		}
>   	    }
> --- gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-1.c.jj	2021-10-05 21:18:52.451803413 +0200
> +++ gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-1.c	2021-10-05 21:20:26.883511595 +0200
> @@ -0,0 +1,5 @@
> +/* Test #elifdef and #elifndef in GNU11.  */
> +/* { dg-do preprocess } */
> +/* { dg-options "-std=gnu11" } */
> +
> +#include "c2x-elifdef-1.c"
> --- gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-2.c.jj	2021-10-05 21:19:49.714020075 +0200
> +++ gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-2.c	2021-10-05 21:21:14.554859458 +0200
> @@ -0,0 +1,63 @@
> +/* Test #elifdef and #elifndef in GNU11: erroneous usages.  */
> +/* { dg-do preprocess } */
> +/* { dg-options "-std=gnu11" } */
> +
> +#define A
> +#undef B
> +
> +#elifdef A /* { dg-error "#elifdef without #if" } */
> +#elifdef B /* { dg-error "#elifdef without #if" } */
> +#elifndef A /* { dg-error "#elifndef without #if" } */
> +#elifndef B /* { dg-error "#elifndef without #if" } */
> +
> +#if 1 /* { dg-error "-:began here" } */
> +#else
> +#elifdef A /* { dg-error "#elifdef after #else" } */
> +#endif
> +
> +#if 1 /* { dg-error "-:began here" } */
> +#else
> +#elifdef B /* { dg-error "#elifdef after #else" } */
> +#endif
> +
> +#if 1 /* { dg-error "-:began here" } */
> +#else
> +#elifndef A /* { dg-error "#elifndef after #else" } */
> +#endif
> +
> +#if 1 /* { dg-error "-:began here" } */
> +#else
> +#elifndef B /* { dg-error "#elifndef after #else" } */
> +#endif
> +
> +#if 0
> +#elifdef A = /* { dg-warning "extra tokens at end of #elifdef directive" } */
> +#endif
> +
> +#if 0
> +#elifdef B = /* { dg-warning "extra tokens at end of #elifdef directive" } */
> +#endif
> +
> +#if 0
> +#elifndef A = /* { dg-warning "extra tokens at end of #elifndef directive" } */
> +#endif
> +
> +#if 0
> +#elifndef B = /* { dg-warning "extra tokens at end of #elifndef directive" } */
> +#endif
> +
> +#if 0
> +#elifdef /* { dg-error "no macro name given in #elifdef directive" } */
> +#endif
> +
> +#if 0
> +#elifndef /* { dg-error "no macro name given in #elifndef directive" } */
> +#endif
> +
> +#if 0
> +#elifdef , /* { dg-error "macro names must be identifiers" } */
> +#endif
> +
> +#if 0
> +#elifndef , /* { dg-error "macro names must be identifiers" } */
> +#endif
> --- gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-3.c.jj	2021-10-05 21:22:42.682650532 +0200
> +++ gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-3.c	2021-10-05 22:15:51.698718704 +0200
> @@ -0,0 +1,65 @@
> +/* Test #elifdef and #elifndef in GNU11.  */
> +/* { dg-do preprocess } */
> +/* { dg-options "-std=gnu11 -pedantic" } */
> +
> +#define A
> +#undef B
> +
> +#if 0
> +#elifdef A	/* { dg-warning "#elifdef before C2X is a GCC extension" } */
> +#define M1 1
> +#endif
> +
> +#if M1 != 1
> +#error "#elifdef A did not apply"
> +#endif
> +
> +#if 0
> +#elifdef B
> +#error "#elifdef B applied"
> +#endif
> +
> +#if 0
> +#elifndef A
> +#error "#elifndef A applied"
> +#endif
> +
> +#if 0
> +#elifndef B	/* { dg-warning "#elifndef before C2X is a GCC extension" } */
> +#define M2 2
> +#endif
> +
> +#if M2 != 2
> +#error "#elifndef B did not apply"
> +#endif
> +
> +#if 0
> +#elifdef A	/* { dg-warning "#elifdef before C2X is a GCC extension" } */
> +#else
> +#error "#elifdef A did not apply"
> +#endif
> +
> +#if 0
> +#elifndef B	/* { dg-warning "#elifndef before C2X is a GCC extension" } */
> +#else
> +#error "#elifndef B did not apply"
> +#endif
> +
> +#if 1
> +#elifdef A	/* { dg-warning "#elifdef before C2X is a GCC extension" } */
> +#endif
> +
> +#if 1
> +#elifndef B	/* { dg-warning "#elifndef before C2X is a GCC extension" } */
> +#endif
> +
> +/* As with #elif, the syntax of the new directives is relaxed after a
> +   non-skipped group.  */
> +
> +#if 1
> +#elifdef x * y	/* { dg-warning "#elifdef before C2X is a GCC extension" } */
> +#endif
> +
> +#if 1
> +#elifndef !	/* { dg-warning "#elifndef before C2X is a GCC extension" } */
> +#endif
> --- gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-4.c.jj	2021-10-05 21:26:35.671449109 +0200
> +++ gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-4.c	2021-10-05 22:16:23.762276538 +0200
> @@ -0,0 +1,65 @@
> +/* Test #elifdef and #elifndef in GNU11.  */
> +/* { dg-do preprocess } */
> +/* { dg-options "-std=gnu11 -pedantic-errors" } */
> +
> +#define A
> +#undef B
> +
> +#if 0
> +#elifdef A	/* { dg-error "#elifdef before C2X is a GCC extension" } */
> +#define M1 1
> +#endif
> +
> +#if M1 != 1
> +#error "#elifdef A did not apply"
> +#endif
> +
> +#if 0
> +#elifdef B
> +#error "#elifdef B applied"
> +#endif
> +
> +#if 0
> +#elifndef A
> +#error "#elifndef A applied"
> +#endif
> +
> +#if 0
> +#elifndef B	/* { dg-error "#elifndef before C2X is a GCC extension" } */
> +#define M2 2
> +#endif
> +
> +#if M2 != 2
> +#error "#elifndef B did not apply"
> +#endif
> +
> +#if 0
> +#elifdef A	/* { dg-error "#elifdef before C2X is a GCC extension" } */
> +#else
> +#error "#elifdef A did not apply"
> +#endif
> +
> +#if 0
> +#elifndef B	/* { dg-error "#elifndef before C2X is a GCC extension" } */
> +#else
> +#error "#elifndef B did not apply"
> +#endif
> +
> +#if 1
> +#elifdef A	/* { dg-error "#elifdef before C2X is a GCC extension" } */
> +#endif
> +
> +#if 1
> +#elifndef B	/* { dg-error "#elifndef before C2X is a GCC extension" } */
> +#endif
> +
> +/* As with #elif, the syntax of the new directives is relaxed after a
> +   non-skipped group.  */
> +
> +#if 1
> +#elifdef x * y	/* { dg-error "#elifdef before C2X is a GCC extension" } */
> +#endif
> +
> +#if 1
> +#elifndef !	/* { dg-error "#elifndef before C2X is a GCC extension" } */
> +#endif
> --- gcc/testsuite/g++.dg/cpp/elifdef-1.C.jj	2021-10-05 10:00:41.410057024 +0200
> +++ gcc/testsuite/g++.dg/cpp/elifdef-1.C	2021-10-05 10:00:33.110173069 +0200
> @@ -0,0 +1,3 @@
> +// { dg-do preprocess { target { ! c++23 } } }
> +
> +#include "../../gcc.dg/cpp/c11-elifdef-1.c"
> --- gcc/testsuite/g++.dg/cpp/elifdef-2.C.jj	2021-10-05 10:01:30.345372808 +0200
> +++ gcc/testsuite/g++.dg/cpp/elifdef-2.C	2021-10-05 10:03:36.560608083 +0200
> @@ -0,0 +1,4 @@
> +// P2334R1
> +// { dg-do preprocess { target c++23 } }
> +
> +#include "../../gcc.dg/cpp/c2x-elifdef-1.c"
> --- gcc/testsuite/g++.dg/cpp/elifdef-3.C.jj	2021-10-05 10:01:36.029293338 +0200
> +++ gcc/testsuite/g++.dg/cpp/elifdef-3.C	2021-10-05 21:27:52.627391684 +0200
> @@ -0,0 +1,62 @@
> +// P2334R1
> +// { dg-do preprocess { target c++23 } }
> +
> +#define A
> +#undef B
> +
> +#elifdef A // { dg-error "#elifdef without #if" }
> +#elifdef B // { dg-error "#elifdef without #if" }
> +#elifndef A // { dg-error "#elifndef without #if" }
> +#elifndef B // { dg-error "#elifndef without #if" }
> +
> +#if 1 // { dg-error "-:began here" }
> +#else
> +#elifdef A // { dg-error "#elifdef after #else" }
> +#endif
> +
> +#if 1 // { dg-error "-:began here" }
> +#else
> +#elifdef B // { dg-error "#elifdef after #else" }
> +#endif
> +
> +#if 1 // { dg-error "-:began here" }
> +#else
> +#elifndef A // { dg-error "#elifndef after #else" }
> +#endif
> +
> +#if 1 // { dg-error "-:began here" }
> +#else
> +#elifndef B // { dg-error "#elifndef after #else" }
> +#endif
> +
> +#if 0
> +#elifdef A = // { dg-error "extra tokens at end of #elifdef directive" }
> +#endif
> +
> +#if 0
> +#elifdef B = // { dg-error "extra tokens at end of #elifdef directive" }
> +#endif
> +
> +#if 0
> +#elifndef A = // { dg-error "extra tokens at end of #elifndef directive" }
> +#endif
> +
> +#if 0
> +#elifndef B = // { dg-error "extra tokens at end of #elifndef directive" }
> +#endif
> +
> +#if 0
> +#elifdef // { dg-error "no macro name given in #elifdef directive" }
> +#endif
> +
> +#if 0
> +#elifndef // { dg-error "no macro name given in #elifndef directive" }
> +#endif
> +
> +#if 0
> +#elifdef , // { dg-error "macro names must be identifiers" }
> +#endif
> +
> +#if 0
> +#elifndef , // { dg-error "macro names must be identifiers" }
> +#endif
> --- gcc/testsuite/g++.dg/cpp/elifdef-4.C.jj	2021-10-05 21:11:19.112005054 +0200
> +++ gcc/testsuite/g++.dg/cpp/elifdef-4.C	2021-10-05 21:11:39.873721037 +0200
> @@ -0,0 +1,5 @@
> +// P2334R1
> +// { dg-do preprocess }
> +// { dg-options "" }
> +
> +#include "../../gcc.dg/cpp/c2x-elifdef-1.c"
> --- gcc/testsuite/g++.dg/cpp/elifdef-5.C.jj	2021-10-05 21:11:54.602519548 +0200
> +++ gcc/testsuite/g++.dg/cpp/elifdef-5.C	2021-10-05 21:28:13.093110471 +0200
> @@ -0,0 +1,63 @@
> +// P2334R1
> +// { dg-do preprocess }
> +// { dg-options "" }
> +
> +#define A
> +#undef B
> +
> +#elifdef A // { dg-error "#elifdef without #if" }
> +#elifdef B // { dg-error "#elifdef without #if" }
> +#elifndef A // { dg-error "#elifndef without #if" }
> +#elifndef B // { dg-error "#elifndef without #if" }
> +
> +#if 1 // { dg-error "-:began here" }
> +#else
> +#elifdef A // { dg-error "#elifdef after #else" }
> +#endif
> +
> +#if 1 // { dg-error "-:began here" }
> +#else
> +#elifdef B // { dg-error "#elifdef after #else" }
> +#endif
> +
> +#if 1 // { dg-error "-:began here" }
> +#else
> +#elifndef A // { dg-error "#elifndef after #else" }
> +#endif
> +
> +#if 1 // { dg-error "-:began here" }
> +#else
> +#elifndef B // { dg-error "#elifndef after #else" }
> +#endif
> +
> +#if 0
> +#elifdef A = // { dg-warning "extra tokens at end of #elifdef directive" }
> +#endif
> +
> +#if 0
> +#elifdef B = // { dg-warning "extra tokens at end of #elifdef directive" }
> +#endif
> +
> +#if 0
> +#elifndef A = // { dg-warning "extra tokens at end of #elifndef directive" }
> +#endif
> +
> +#if 0
> +#elifndef B = // { dg-warning "extra tokens at end of #elifndef directive" }
> +#endif
> +
> +#if 0
> +#elifdef // { dg-error "no macro name given in #elifdef directive" }
> +#endif
> +
> +#if 0
> +#elifndef // { dg-error "no macro name given in #elifndef directive" }
> +#endif
> +
> +#if 0
> +#elifdef , // { dg-error "macro names must be identifiers" }
> +#endif
> +
> +#if 0
> +#elifndef , // { dg-error "macro names must be identifiers" }
> +#endif
> --- gcc/testsuite/g++.dg/cpp/elifdef-6.C.jj	2021-10-05 21:27:05.784035343 +0200
> +++ gcc/testsuite/g++.dg/cpp/elifdef-6.C	2021-10-05 22:17:09.120651040 +0200
> @@ -0,0 +1,65 @@
> +// P2334R1
> +// { dg-do preprocess }
> +// { dg-options "-pedantic" }
> +
> +#define A
> +#undef B
> +
> +#if 0
> +#elifdef A	// { dg-warning "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#define M1 1
> +#endif
> +
> +#if M1 != 1
> +#error "#elifdef A did not apply"
> +#endif
> +
> +#if 0
> +#elifdef B
> +#error "#elifdef B applied"
> +#endif
> +
> +#if 0
> +#elifndef A
> +#error "#elifndef A applied"
> +#endif
> +
> +#if 0
> +#elifndef B	// { dg-warning "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#define M2 2
> +#endif
> +
> +#if M2 != 2
> +#error "#elifndef B did not apply"
> +#endif
> +
> +#if 0
> +#elifdef A	// { dg-warning "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#else
> +#error "#elifdef A did not apply"
> +#endif
> +
> +#if 0
> +#elifndef B	// { dg-warning "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#else
> +#error "#elifndef B did not apply"
> +#endif
> +
> +#if 1
> +#elifdef A	// { dg-warning "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#endif
> +
> +#if 1
> +#elifndef B	// { dg-warning "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#endif
> +
> +// As with #elif, the syntax of the new directives is relaxed after a
> +   non-skipped group.
> +
> +#if 1
> +#elifdef x * y	// { dg-warning "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#endif
> +
> +#if 1
> +#elifndef !	// { dg-warning "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#endif
> --- gcc/testsuite/g++.dg/cpp/elifdef-7.C.jj	2021-10-05 21:29:18.132216791 +0200
> +++ gcc/testsuite/g++.dg/cpp/elifdef-7.C	2021-10-05 22:17:45.439150203 +0200
> @@ -0,0 +1,65 @@
> +// P2334R1
> +// { dg-do preprocess }
> +// { dg-options "-pedantic-errors" }
> +
> +#define A
> +#undef B
> +
> +#if 0
> +#elifdef A	// { dg-error "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#define M1 1
> +#endif
> +
> +#if M1 != 1
> +#error "#elifdef A did not apply"
> +#endif
> +
> +#if 0
> +#elifdef B
> +#error "#elifdef B applied"
> +#endif
> +
> +#if 0
> +#elifndef A
> +#error "#elifndef A applied"
> +#endif
> +
> +#if 0
> +#elifndef B	// { dg-error "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#define M2 2
> +#endif
> +
> +#if M2 != 2
> +#error "#elifndef B did not apply"
> +#endif
> +
> +#if 0
> +#elifdef A	// { dg-error "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#else
> +#error "#elifdef A did not apply"
> +#endif
> +
> +#if 0
> +#elifndef B	// { dg-error "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#else
> +#error "#elifndef B did not apply"
> +#endif
> +
> +#if 1
> +#elifdef A	// { dg-error "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#endif
> +
> +#if 1
> +#elifndef B	// { dg-error "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#endif
> +
> +// As with #elif, the syntax of the new directives is relaxed after a
> +   non-skipped group.
> +
> +#if 1
> +#elifdef x * y	// { dg-error "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#endif
> +
> +#if 1
> +#elifndef !	// { dg-error "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
> +#endif
> 
> 
> 	Jakub
>
diff mbox series

Patch

--- libcpp/init.c.jj	2021-09-02 10:01:15.954715595 +0200
+++ libcpp/init.c	2021-10-05 09:55:15.010620700 +0200
@@ -122,8 +122,8 @@  static const struct lang_flags lang_defa
   /* CXX17    */  { 1,  1,  1,  1,  1,  0,    1,  1,   1,   1,   1,    1,     1,     0,   1,      0,   1,     0,   0,   0 },
   /* GNUCXX20 */  { 1,  1,  1,  1,  1,  0,    0,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   0,   0 },
   /* CXX20    */  { 1,  1,  1,  1,  1,  0,    1,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   0,   0 },
-  /* GNUCXX23 */  { 1,  1,  1,  1,  1,  1,    0,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   1,   0 },
-  /* CXX23    */  { 1,  1,  1,  1,  1,  1,    1,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   1,   0 },
+  /* GNUCXX23 */  { 1,  1,  1,  1,  1,  1,    0,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   1,   1 },
+  /* CXX23    */  { 1,  1,  1,  1,  1,  1,    1,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   1,   1 },
   /* ASM      */  { 0,  0,  1,  0,  0,  0,    0,  0,   0,   0,   0,    0,     0,     0,   0,      0,   0,     0,   0,   0 }
 };
 
--- libcpp/directives.c.jj	2021-05-12 09:44:55.080621650 +0200
+++ libcpp/directives.c	2021-10-05 22:05:52.303984796 +0200
@@ -447,7 +447,11 @@  _cpp_handle_directive (cpp_reader *pfile
       if (dname->val.node.node->is_directive)
 	{
 	  dir = &dtable[dname->val.node.node->directive_index];
-	  if ((dir->flags & ELIFDEF) && !CPP_OPTION (pfile, elifdef))
+	  if ((dir->flags & ELIFDEF)
+	      && !CPP_OPTION (pfile, elifdef)
+	      /* For -std=gnu* modes elifdef is supported with
+		 a pedwarn if pedantic.  */
+	      && CPP_OPTION (pfile, std))
 	    dir = 0;
 	}
     }
@@ -2117,7 +2121,26 @@  do_elif (cpp_reader *pfile)
 	 are skipped and their controlling directives are processed as
 	 if they were in a group that is skipped."  */
       if (ifs->skip_elses)
-	pfile->state.skipping = 1;
+	{
+	  /* In older GNU standards, #elifdef/#elifndef is supported
+	     as an extension, but pedwarn if -pedantic if the presence
+	     of the directive would be rejected.  */
+	  if (pfile->directive != &dtable[T_ELIF]
+	      && ! CPP_OPTION (pfile, elifdef)
+	      && CPP_PEDANTIC (pfile)
+	      && !pfile->state.skipping)
+	    {
+	      if (CPP_OPTION (pfile, cplusplus))
+		cpp_error (pfile, CPP_DL_PEDWARN,
+			   "#%s before C++23 is a GCC extension",
+			   pfile->directive->name);
+	      else
+		cpp_error (pfile, CPP_DL_PEDWARN,
+			   "#%s before C2X is a GCC extension",
+			   pfile->directive->name);
+	    }
+	  pfile->state.skipping = 1;
+	}
       else
 	{
 	  if (pfile->directive == &dtable[T_ELIF])
@@ -2139,6 +2162,22 @@  do_elif (cpp_reader *pfile)
 		  if (pfile->cb.used)
 		    pfile->cb.used (pfile, pfile->directive_line, node);
 		  check_eol (pfile, false);
+		  /* In older GNU standards, #elifdef/#elifndef is supported
+		     as an extension, but pedwarn if -pedantic if the presence
+		     of the directive would change behavior.  */
+		  if (! CPP_OPTION (pfile, elifdef)
+		      && CPP_PEDANTIC (pfile)
+		      && pfile->state.skipping != skip)
+		    {
+		      if (CPP_OPTION (pfile, cplusplus))
+			cpp_error (pfile, CPP_DL_PEDWARN,
+				   "#%s before C++23 is a GCC extension",
+				   pfile->directive->name);
+		      else
+			cpp_error (pfile, CPP_DL_PEDWARN,
+				   "#%s before C2X is a GCC extension",
+				   pfile->directive->name);
+		    }
 		  pfile->state.skipping = skip;
 		}
 	    }
--- gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-1.c.jj	2021-10-05 21:18:52.451803413 +0200
+++ gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-1.c	2021-10-05 21:20:26.883511595 +0200
@@ -0,0 +1,5 @@ 
+/* Test #elifdef and #elifndef in GNU11.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=gnu11" } */
+
+#include "c2x-elifdef-1.c"
--- gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-2.c.jj	2021-10-05 21:19:49.714020075 +0200
+++ gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-2.c	2021-10-05 21:21:14.554859458 +0200
@@ -0,0 +1,63 @@ 
+/* Test #elifdef and #elifndef in GNU11: erroneous usages.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=gnu11" } */
+
+#define A
+#undef B
+
+#elifdef A /* { dg-error "#elifdef without #if" } */
+#elifdef B /* { dg-error "#elifdef without #if" } */
+#elifndef A /* { dg-error "#elifndef without #if" } */
+#elifndef B /* { dg-error "#elifndef without #if" } */
+
+#if 1 /* { dg-error "-:began here" } */
+#else
+#elifdef A /* { dg-error "#elifdef after #else" } */
+#endif
+
+#if 1 /* { dg-error "-:began here" } */
+#else
+#elifdef B /* { dg-error "#elifdef after #else" } */
+#endif
+
+#if 1 /* { dg-error "-:began here" } */
+#else
+#elifndef A /* { dg-error "#elifndef after #else" } */
+#endif
+
+#if 1 /* { dg-error "-:began here" } */
+#else
+#elifndef B /* { dg-error "#elifndef after #else" } */
+#endif
+
+#if 0
+#elifdef A = /* { dg-warning "extra tokens at end of #elifdef directive" } */
+#endif
+
+#if 0
+#elifdef B = /* { dg-warning "extra tokens at end of #elifdef directive" } */
+#endif
+
+#if 0
+#elifndef A = /* { dg-warning "extra tokens at end of #elifndef directive" } */
+#endif
+
+#if 0
+#elifndef B = /* { dg-warning "extra tokens at end of #elifndef directive" } */
+#endif
+
+#if 0
+#elifdef /* { dg-error "no macro name given in #elifdef directive" } */
+#endif
+
+#if 0
+#elifndef /* { dg-error "no macro name given in #elifndef directive" } */
+#endif
+
+#if 0
+#elifdef , /* { dg-error "macro names must be identifiers" } */
+#endif
+
+#if 0
+#elifndef , /* { dg-error "macro names must be identifiers" } */
+#endif
--- gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-3.c.jj	2021-10-05 21:22:42.682650532 +0200
+++ gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-3.c	2021-10-05 22:15:51.698718704 +0200
@@ -0,0 +1,65 @@ 
+/* Test #elifdef and #elifndef in GNU11.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=gnu11 -pedantic" } */
+
+#define A
+#undef B
+
+#if 0
+#elifdef A	/* { dg-warning "#elifdef before C2X is a GCC extension" } */
+#define M1 1
+#endif
+
+#if M1 != 1
+#error "#elifdef A did not apply"
+#endif
+
+#if 0
+#elifdef B
+#error "#elifdef B applied"
+#endif
+
+#if 0
+#elifndef A
+#error "#elifndef A applied"
+#endif
+
+#if 0
+#elifndef B	/* { dg-warning "#elifndef before C2X is a GCC extension" } */
+#define M2 2
+#endif
+
+#if M2 != 2
+#error "#elifndef B did not apply"
+#endif
+
+#if 0
+#elifdef A	/* { dg-warning "#elifdef before C2X is a GCC extension" } */
+#else
+#error "#elifdef A did not apply"
+#endif
+
+#if 0
+#elifndef B	/* { dg-warning "#elifndef before C2X is a GCC extension" } */
+#else
+#error "#elifndef B did not apply"
+#endif
+
+#if 1
+#elifdef A	/* { dg-warning "#elifdef before C2X is a GCC extension" } */
+#endif
+
+#if 1
+#elifndef B	/* { dg-warning "#elifndef before C2X is a GCC extension" } */
+#endif
+
+/* As with #elif, the syntax of the new directives is relaxed after a
+   non-skipped group.  */
+
+#if 1
+#elifdef x * y	/* { dg-warning "#elifdef before C2X is a GCC extension" } */
+#endif
+
+#if 1
+#elifndef !	/* { dg-warning "#elifndef before C2X is a GCC extension" } */
+#endif
--- gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-4.c.jj	2021-10-05 21:26:35.671449109 +0200
+++ gcc/testsuite/gcc.dg/cpp/gnu11-elifdef-4.c	2021-10-05 22:16:23.762276538 +0200
@@ -0,0 +1,65 @@ 
+/* Test #elifdef and #elifndef in GNU11.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=gnu11 -pedantic-errors" } */
+
+#define A
+#undef B
+
+#if 0
+#elifdef A	/* { dg-error "#elifdef before C2X is a GCC extension" } */
+#define M1 1
+#endif
+
+#if M1 != 1
+#error "#elifdef A did not apply"
+#endif
+
+#if 0
+#elifdef B
+#error "#elifdef B applied"
+#endif
+
+#if 0
+#elifndef A
+#error "#elifndef A applied"
+#endif
+
+#if 0
+#elifndef B	/* { dg-error "#elifndef before C2X is a GCC extension" } */
+#define M2 2
+#endif
+
+#if M2 != 2
+#error "#elifndef B did not apply"
+#endif
+
+#if 0
+#elifdef A	/* { dg-error "#elifdef before C2X is a GCC extension" } */
+#else
+#error "#elifdef A did not apply"
+#endif
+
+#if 0
+#elifndef B	/* { dg-error "#elifndef before C2X is a GCC extension" } */
+#else
+#error "#elifndef B did not apply"
+#endif
+
+#if 1
+#elifdef A	/* { dg-error "#elifdef before C2X is a GCC extension" } */
+#endif
+
+#if 1
+#elifndef B	/* { dg-error "#elifndef before C2X is a GCC extension" } */
+#endif
+
+/* As with #elif, the syntax of the new directives is relaxed after a
+   non-skipped group.  */
+
+#if 1
+#elifdef x * y	/* { dg-error "#elifdef before C2X is a GCC extension" } */
+#endif
+
+#if 1
+#elifndef !	/* { dg-error "#elifndef before C2X is a GCC extension" } */
+#endif
--- gcc/testsuite/g++.dg/cpp/elifdef-1.C.jj	2021-10-05 10:00:41.410057024 +0200
+++ gcc/testsuite/g++.dg/cpp/elifdef-1.C	2021-10-05 10:00:33.110173069 +0200
@@ -0,0 +1,3 @@ 
+// { dg-do preprocess { target { ! c++23 } } }
+
+#include "../../gcc.dg/cpp/c11-elifdef-1.c"
--- gcc/testsuite/g++.dg/cpp/elifdef-2.C.jj	2021-10-05 10:01:30.345372808 +0200
+++ gcc/testsuite/g++.dg/cpp/elifdef-2.C	2021-10-05 10:03:36.560608083 +0200
@@ -0,0 +1,4 @@ 
+// P2334R1
+// { dg-do preprocess { target c++23 } }
+
+#include "../../gcc.dg/cpp/c2x-elifdef-1.c"
--- gcc/testsuite/g++.dg/cpp/elifdef-3.C.jj	2021-10-05 10:01:36.029293338 +0200
+++ gcc/testsuite/g++.dg/cpp/elifdef-3.C	2021-10-05 21:27:52.627391684 +0200
@@ -0,0 +1,62 @@ 
+// P2334R1
+// { dg-do preprocess { target c++23 } }
+
+#define A
+#undef B
+
+#elifdef A // { dg-error "#elifdef without #if" }
+#elifdef B // { dg-error "#elifdef without #if" }
+#elifndef A // { dg-error "#elifndef without #if" }
+#elifndef B // { dg-error "#elifndef without #if" }
+
+#if 1 // { dg-error "-:began here" }
+#else
+#elifdef A // { dg-error "#elifdef after #else" }
+#endif
+
+#if 1 // { dg-error "-:began here" }
+#else
+#elifdef B // { dg-error "#elifdef after #else" }
+#endif
+
+#if 1 // { dg-error "-:began here" }
+#else
+#elifndef A // { dg-error "#elifndef after #else" }
+#endif
+
+#if 1 // { dg-error "-:began here" }
+#else
+#elifndef B // { dg-error "#elifndef after #else" }
+#endif
+
+#if 0
+#elifdef A = // { dg-error "extra tokens at end of #elifdef directive" }
+#endif
+
+#if 0
+#elifdef B = // { dg-error "extra tokens at end of #elifdef directive" }
+#endif
+
+#if 0
+#elifndef A = // { dg-error "extra tokens at end of #elifndef directive" }
+#endif
+
+#if 0
+#elifndef B = // { dg-error "extra tokens at end of #elifndef directive" }
+#endif
+
+#if 0
+#elifdef // { dg-error "no macro name given in #elifdef directive" }
+#endif
+
+#if 0
+#elifndef // { dg-error "no macro name given in #elifndef directive" }
+#endif
+
+#if 0
+#elifdef , // { dg-error "macro names must be identifiers" }
+#endif
+
+#if 0
+#elifndef , // { dg-error "macro names must be identifiers" }
+#endif
--- gcc/testsuite/g++.dg/cpp/elifdef-4.C.jj	2021-10-05 21:11:19.112005054 +0200
+++ gcc/testsuite/g++.dg/cpp/elifdef-4.C	2021-10-05 21:11:39.873721037 +0200
@@ -0,0 +1,5 @@ 
+// P2334R1
+// { dg-do preprocess }
+// { dg-options "" }
+
+#include "../../gcc.dg/cpp/c2x-elifdef-1.c"
--- gcc/testsuite/g++.dg/cpp/elifdef-5.C.jj	2021-10-05 21:11:54.602519548 +0200
+++ gcc/testsuite/g++.dg/cpp/elifdef-5.C	2021-10-05 21:28:13.093110471 +0200
@@ -0,0 +1,63 @@ 
+// P2334R1
+// { dg-do preprocess }
+// { dg-options "" }
+
+#define A
+#undef B
+
+#elifdef A // { dg-error "#elifdef without #if" }
+#elifdef B // { dg-error "#elifdef without #if" }
+#elifndef A // { dg-error "#elifndef without #if" }
+#elifndef B // { dg-error "#elifndef without #if" }
+
+#if 1 // { dg-error "-:began here" }
+#else
+#elifdef A // { dg-error "#elifdef after #else" }
+#endif
+
+#if 1 // { dg-error "-:began here" }
+#else
+#elifdef B // { dg-error "#elifdef after #else" }
+#endif
+
+#if 1 // { dg-error "-:began here" }
+#else
+#elifndef A // { dg-error "#elifndef after #else" }
+#endif
+
+#if 1 // { dg-error "-:began here" }
+#else
+#elifndef B // { dg-error "#elifndef after #else" }
+#endif
+
+#if 0
+#elifdef A = // { dg-warning "extra tokens at end of #elifdef directive" }
+#endif
+
+#if 0
+#elifdef B = // { dg-warning "extra tokens at end of #elifdef directive" }
+#endif
+
+#if 0
+#elifndef A = // { dg-warning "extra tokens at end of #elifndef directive" }
+#endif
+
+#if 0
+#elifndef B = // { dg-warning "extra tokens at end of #elifndef directive" }
+#endif
+
+#if 0
+#elifdef // { dg-error "no macro name given in #elifdef directive" }
+#endif
+
+#if 0
+#elifndef // { dg-error "no macro name given in #elifndef directive" }
+#endif
+
+#if 0
+#elifdef , // { dg-error "macro names must be identifiers" }
+#endif
+
+#if 0
+#elifndef , // { dg-error "macro names must be identifiers" }
+#endif
--- gcc/testsuite/g++.dg/cpp/elifdef-6.C.jj	2021-10-05 21:27:05.784035343 +0200
+++ gcc/testsuite/g++.dg/cpp/elifdef-6.C	2021-10-05 22:17:09.120651040 +0200
@@ -0,0 +1,65 @@ 
+// P2334R1
+// { dg-do preprocess }
+// { dg-options "-pedantic" }
+
+#define A
+#undef B
+
+#if 0
+#elifdef A	// { dg-warning "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#define M1 1
+#endif
+
+#if M1 != 1
+#error "#elifdef A did not apply"
+#endif
+
+#if 0
+#elifdef B
+#error "#elifdef B applied"
+#endif
+
+#if 0
+#elifndef A
+#error "#elifndef A applied"
+#endif
+
+#if 0
+#elifndef B	// { dg-warning "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#define M2 2
+#endif
+
+#if M2 != 2
+#error "#elifndef B did not apply"
+#endif
+
+#if 0
+#elifdef A	// { dg-warning "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#else
+#error "#elifdef A did not apply"
+#endif
+
+#if 0
+#elifndef B	// { dg-warning "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#else
+#error "#elifndef B did not apply"
+#endif
+
+#if 1
+#elifdef A	// { dg-warning "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#endif
+
+#if 1
+#elifndef B	// { dg-warning "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#endif
+
+// As with #elif, the syntax of the new directives is relaxed after a
+   non-skipped group. 
+
+#if 1
+#elifdef x * y	// { dg-warning "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#endif
+
+#if 1
+#elifndef !	// { dg-warning "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#endif
--- gcc/testsuite/g++.dg/cpp/elifdef-7.C.jj	2021-10-05 21:29:18.132216791 +0200
+++ gcc/testsuite/g++.dg/cpp/elifdef-7.C	2021-10-05 22:17:45.439150203 +0200
@@ -0,0 +1,65 @@ 
+// P2334R1
+// { dg-do preprocess }
+// { dg-options "-pedantic-errors" }
+
+#define A
+#undef B
+
+#if 0
+#elifdef A	// { dg-error "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#define M1 1
+#endif
+
+#if M1 != 1
+#error "#elifdef A did not apply"
+#endif
+
+#if 0
+#elifdef B
+#error "#elifdef B applied"
+#endif
+
+#if 0
+#elifndef A
+#error "#elifndef A applied"
+#endif
+
+#if 0
+#elifndef B	// { dg-error "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#define M2 2
+#endif
+
+#if M2 != 2
+#error "#elifndef B did not apply"
+#endif
+
+#if 0
+#elifdef A	// { dg-error "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#else
+#error "#elifdef A did not apply"
+#endif
+
+#if 0
+#elifndef B	// { dg-error "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#else
+#error "#elifndef B did not apply"
+#endif
+
+#if 1
+#elifdef A	// { dg-error "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#endif
+
+#if 1
+#elifndef B	// { dg-error "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#endif
+
+// As with #elif, the syntax of the new directives is relaxed after a
+   non-skipped group. 
+
+#if 1
+#elifdef x * y	// { dg-error "#elifdef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#endif
+
+#if 1
+#elifndef !	// { dg-error "#elifndef before C\\\+\\\+23 is a GCC extension" "" { target c++20_down } }
+#endif