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