]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
preprocessor: Support C2X #elifdef, #elifndef
authorJoseph Myers <joseph@codesourcery.com>
Tue, 11 May 2021 23:54:01 +0000 (23:54 +0000)
committerJoseph Myers <joseph@codesourcery.com>
Tue, 11 May 2021 23:54:01 +0000 (23:54 +0000)
C2X adds #elifdef and #elifndef preprocessor directives; these have
also been proposed for C++.  Implement these directives in libcpp
accordingly.

In this implementation, #elifdef and #elifndef are treated as
non-directives for any language version other than c2x and gnu2x (if
the feature is accepted for C++, it can trivially be enabled for
relevant C++ versions).  In strict conformance modes for prior
language versions, this is required, as illustrated by the
c11-elifdef-1.c test added.

Bootstrapped with no regressions for x86_64-pc-linux-gnu.

libcpp/
* include/cpplib.h (struct cpp_options): Add elifdef.
* init.c (struct lang_flags): Add elifdef.
(lang_defaults): Update to include elifdef initializers.
(cpp_set_lang): Set elifdef for pfile based on language.
* directives.c (STDC2X, ELIFDEF): New macros.
(EXTENSION): Increase value to 3.
(DIRECTIVE_TABLE): Add #elifdef and #elifndef.
(_cpp_handle_directive): Do not treat ELIFDEF directives as
directives for language versions without the #elifdef feature.
(do_elif): Handle #elifdef and #elifndef.
(do_elifdef, do_elifndef): New functions.

gcc/testsuite/
* gcc.dg/cpp/c11-elifdef-1.c, gcc.dg/cpp/c2x-elifdef-1.c,
gcc.dg/cpp/c2x-elifdef-2.c: New tests.

gcc/testsuite/gcc.dg/cpp/c11-elifdef-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/cpp/c2x-elifdef-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/cpp/c2x-elifdef-2.c [new file with mode: 0644]
libcpp/directives.c
libcpp/include/cpplib.h
libcpp/init.c

diff --git a/gcc/testsuite/gcc.dg/cpp/c11-elifdef-1.c b/gcc/testsuite/gcc.dg/cpp/c11-elifdef-1.c
new file mode 100644 (file)
index 0000000..2d5809a
--- /dev/null
@@ -0,0 +1,16 @@
+/* Test #elifdef and #elifndef not in C11.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=c11 -pedantic-errors" } */
+
+#define A
+#undef B
+
+#if 0
+#elifdef A
+#error "#elifdef A applied"
+#endif
+
+#if 0
+#elifndef B
+#error "#elifndef B applied"
+#endif
diff --git a/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-1.c b/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-1.c
new file mode 100644 (file)
index 0000000..b23e311
--- /dev/null
@@ -0,0 +1,57 @@
+/* Test #elifdef and #elifndef in C2x.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+#define A
+#undef B
+
+#if 0
+#elifdef A
+#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
+#define M2 2
+#endif
+
+#if M2 != 2
+#error "#elifndef B did not apply"
+#endif
+
+#if 0
+#elifdef A
+#else
+#error "#elifdef A did not apply"
+#endif
+
+#if 0
+#elifndef B
+#else
+#error "#elifndef B did not apply"
+#endif
+
+/* As with #elif, the syntax of the new directives is relaxed after a
+   non-skipped group.  */
+
+#if 1
+#elifdef x * y
+#endif
+
+#if 1
+#elifndef !
+#endif
diff --git a/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-2.c b/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-2.c
new file mode 100644 (file)
index 0000000..9132832
--- /dev/null
@@ -0,0 +1,63 @@
+/* Test #elifdef and #elifndef in C2x: erroneous usages.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+#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
index 795f93e664bc18b1ab6e311bb3f4aa72e68e8e9e..261a584c550936a9f725893cd51887008819995e 100644 (file)
@@ -56,10 +56,12 @@ struct pragma_entry
 
 /* Values for the origin field of struct directive.  KANDR directives
    come from traditional (K&R) C.  STDC89 directives come from the
-   1989 C standard.  EXTENSION directives are extensions.  */
+   1989 C standard.  STDC2X directives come from the C2X standard.  EXTENSION
+   directives are extensions.  */
 #define KANDR          0
 #define STDC89         1
-#define EXTENSION      2
+#define STDC2X         2
+#define EXTENSION      3
 
 /* Values for the flags field of struct directive.  COND indicates a
    conditional; IF_COND an opening conditional.  INCL means to treat
@@ -67,13 +69,17 @@ struct pragma_entry
    means this directive should be handled even if -fpreprocessed is in
    effect (these are the directives with callback hooks).
 
-   EXPAND is set on directives that are always macro-expanded.  */
+   EXPAND is set on directives that are always macro-expanded.
+
+   ELIFDEF is set on directives that are only handled for standards with the
+   #elifdef / #elifndef feature.  */
 #define COND           (1 << 0)
 #define IF_COND                (1 << 1)
 #define INCL           (1 << 2)
 #define IN_I           (1 << 3)
 #define EXPAND         (1 << 4)
 #define DEPRECATED     (1 << 5)
+#define ELIFDEF                (1 << 6)
 
 /* Defines one #-directive, including how to handle it.  */
 typedef void (*directive_handler) (cpp_reader *);
@@ -148,6 +154,8 @@ static void cpp_pop_definition (cpp_reader *, struct def_pragma_macro *);
   D(undef,     T_UNDEF,        KANDR,     IN_I)                        \
   D(line,      T_LINE,         KANDR,     EXPAND)                      \
   D(elif,      T_ELIF,         STDC89,    COND | EXPAND)               \
+  D(elifdef,   T_ELIFDEF,      STDC2X,    COND | ELIFDEF)              \
+  D(elifndef,  T_ELIFNDEF,     STDC2X,    COND | ELIFDEF)              \
   D(error,     T_ERROR,        STDC89,    0)                           \
   D(pragma,    T_PRAGMA,       STDC89,    IN_I)                        \
   D(warning,   T_WARNING,      EXTENSION, 0)                           \
@@ -437,7 +445,11 @@ _cpp_handle_directive (cpp_reader *pfile, bool indented)
   if (dname->type == CPP_NAME)
     {
       if (dname->val.node.node->is_directive)
-       dir = &dtable[dname->val.node.node->directive_index];
+       {
+         dir = &dtable[dname->val.node.node->directive_index];
+         if ((dir->flags & ELIFDEF) && !CPP_OPTION (pfile, elifdef))
+           dir = 0;
+       }
     }
   /* We do not recognize the # followed by a number extension in
      assembler code.  */
@@ -2079,8 +2091,8 @@ do_else (cpp_reader *pfile)
     }
 }
 
-/* Handle a #elif directive by not changing if_stack either.  See the
-   comment above do_else.  */
+/* Handle a #elif, #elifdef or #elifndef directive by not changing if_stack
+   either.  See the comment above do_else.  */
 static void
 do_elif (cpp_reader *pfile)
 {
@@ -2088,12 +2100,13 @@ do_elif (cpp_reader *pfile)
   struct if_stack *ifs = buffer->if_stack;
 
   if (ifs == NULL)
-    cpp_error (pfile, CPP_DL_ERROR, "#elif without #if");
+    cpp_error (pfile, CPP_DL_ERROR, "#%s without #if", pfile->directive->name);
   else
     {
       if (ifs->type == T_ELSE)
        {
-         cpp_error (pfile, CPP_DL_ERROR, "#elif after #else");
+         cpp_error (pfile, CPP_DL_ERROR, "#%s after #else",
+                    pfile->directive->name);
          cpp_error_with_line (pfile, CPP_DL_ERROR, ifs->line, 0,
                               "the conditional began here");
        }
@@ -2107,8 +2120,29 @@ do_elif (cpp_reader *pfile)
        pfile->state.skipping = 1;
       else
        {
-         pfile->state.skipping = ! _cpp_parse_expr (pfile, false);
-         ifs->skip_elses = ! pfile->state.skipping;
+         if (pfile->directive == &dtable[T_ELIF])
+           pfile->state.skipping = !_cpp_parse_expr (pfile, false);
+         else
+           {
+             cpp_hashnode *node = lex_macro_node (pfile, false);
+
+             if (node)
+               {
+                 bool macro_defined = _cpp_defined_macro_p (node);
+                 if (!_cpp_maybe_notify_macro_use (pfile, node,
+                                                   pfile->directive_line))
+                   /* It wasn't a macro after all.  */
+                   macro_defined = false;
+                 bool skip = (pfile->directive == &dtable[T_ELIFDEF]
+                              ? !macro_defined
+                              : macro_defined);
+                 if (pfile->cb.used)
+                   pfile->cb.used (pfile, pfile->directive_line, node);
+                 check_eol (pfile, false);
+                 pfile->state.skipping = skip;
+               }
+           }
+         ifs->skip_elses = !pfile->state.skipping;
        }
 
       /* Invalidate any controlling macro.  */
@@ -2116,6 +2150,20 @@ do_elif (cpp_reader *pfile)
     }
 }
 
+/* Handle a #elifdef directive.  */
+static void
+do_elifdef (cpp_reader *pfile)
+{
+  do_elif (pfile);
+}
+
+/* Handle a #elifndef directive.  */
+static void
+do_elifndef (cpp_reader *pfile)
+{
+  do_elif (pfile);
+}
+
 /* #endif pops the if stack and resets pfile->state.skipping.  */
 static void
 do_endif (cpp_reader *pfile)
index 41d75d9e731b6225a5c1bdc651a95e82914bea5f..2cdaf19362ac8cab8699877eda92398dbc294a9a 100644 (file)
@@ -497,6 +497,9 @@ struct cpp_options
   /* Nonzero for the '::' token.  */
   unsigned char scope;
 
+  /* Nonzero for the '#elifdef' and '#elifndef' directives.  */
+  unsigned char elifdef;
+
   /* Nonzero means tokenize C++20 module directives.  */
   unsigned char module_directives;
 
index 18a2341c2d02b9511b6961f7444ea22fbb245e56..d5d4e994d2ad6c0c0229f351154c7e833c1da5bf 100644 (file)
@@ -95,34 +95,35 @@ struct lang_flags
   char scope;
   char dfp_constants;
   char size_t_literals;
+  char elifdef;
 };
 
 static const struct lang_flags lang_defaults[] =
-{ /*              c99 c++ xnum xid c11 std digr ulit rlit udlit bincst digsep trig u8chlit vaopt scope dfp szlit */
-  /* GNUC89   */  { 0,  0,  1,  0,  0,  0,  1,   0,   0,   0,    0,     0,     0,   0,      1,   1,     0,   0 },
-  /* GNUC99   */  { 1,  0,  1,  1,  0,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1,   1,     0,   0 },
-  /* GNUC11   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1,   1,     0,   0 },
-  /* GNUC17   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1,   1,     0,   0 },
-  /* GNUC2X   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    1,     1,     0,   1,      1,   1,     1,   0 },
-  /* STDC89   */  { 0,  0,  0,  0,  0,  1,  0,   0,   0,   0,    0,     0,     1,   0,      0,   0,     0,   0 },
-  /* STDC94   */  { 0,  0,  0,  0,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0,      0,   0,     0,   0 },
-  /* STDC99   */  { 1,  0,  1,  1,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0,      0,   0,     0,   0 },
-  /* STDC11   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    0,     0,     1,   0,      0,   0,     0,   0 },
-  /* STDC17   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    0,     0,     1,   0,      0,   0,     0,   0 },
-  /* STDC2X   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    1,     1,     1,   1,      0,   1,     1,   0 },
-  /* GNUCXX   */  { 0,  1,  1,  1,  0,  0,  1,   0,   0,   0,    0,     0,     0,   0,      1,   1,     0,   0 },
-  /* CXX98    */  { 0,  1,  0,  1,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0,      0,   1,     0,   0 },
-  /* GNUCXX11 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    0,     0,     0,   0,      1,   1,     0,   0 },
-  /* CXX11    */  { 1,  1,  0,  1,  1,  1,  1,   1,   1,   1,    0,     0,     1,   0,      0,   1,     0,   0 },
-  /* GNUCXX14 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   0,      1,   1,     0,   0 },
-  /* CXX14    */  { 1,  1,  0,  1,  1,  1,  1,   1,   1,   1,    1,     1,     1,   0,      0,   1,     0,   0 },
-  /* GNUCXX17 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   0 },
-  /* CXX17    */  { 1,  1,  1,  1,  1,  1,  1,   1,   1,   1,    1,     1,     0,   1,      0,   1,     0,   0 },
-  /* GNUCXX20 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   0 },
-  /* CXX20    */  { 1,  1,  1,  1,  1,  1,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   0 },
-  /* GNUCXX23 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   1 },
-  /* CXX23    */  { 1,  1,  1,  1,  1,  1,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   1 },
-  /* ASM      */  { 0,  0,  1,  0,  0,  0,  0,   0,   0,   0,    0,     0,     0,   0,      0,   0,     0,   0 }
+{ /*              c99 c++ xnum xid c11 std digr ulit rlit udlit bincst digsep trig u8chlit vaopt scope dfp szlit elifdef */
+  /* GNUC89   */  { 0,  0,  1,  0,  0,  0,  1,   0,   0,   0,    0,     0,     0,   0,      1,   1,     0,   0,   0 },
+  /* GNUC99   */  { 1,  0,  1,  1,  0,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1,   1,     0,   0,   0 },
+  /* GNUC11   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1,   1,     0,   0,   0 },
+  /* GNUC17   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1,   1,     0,   0,   0 },
+  /* GNUC2X   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    1,     1,     0,   1,      1,   1,     1,   0,   1 },
+  /* STDC89   */  { 0,  0,  0,  0,  0,  1,  0,   0,   0,   0,    0,     0,     1,   0,      0,   0,     0,   0,   0 },
+  /* STDC94   */  { 0,  0,  0,  0,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0,      0,   0,     0,   0,   0 },
+  /* STDC99   */  { 1,  0,  1,  1,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0,      0,   0,     0,   0,   0 },
+  /* STDC11   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    0,     0,     1,   0,      0,   0,     0,   0,   0 },
+  /* STDC17   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    0,     0,     1,   0,      0,   0,     0,   0,   0 },
+  /* STDC2X   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    1,     1,     1,   1,      0,   1,     1,   0,   1 },
+  /* GNUCXX   */  { 0,  1,  1,  1,  0,  0,  1,   0,   0,   0,    0,     0,     0,   0,      1,   1,     0,   0,   0 },
+  /* CXX98    */  { 0,  1,  0,  1,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0,      0,   1,     0,   0,   0 },
+  /* GNUCXX11 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    0,     0,     0,   0,      1,   1,     0,   0,   0 },
+  /* CXX11    */  { 1,  1,  0,  1,  1,  1,  1,   1,   1,   1,    0,     0,     1,   0,      0,   1,     0,   0,   0 },
+  /* GNUCXX14 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   0,      1,   1,     0,   0,   0 },
+  /* CXX14    */  { 1,  1,  0,  1,  1,  1,  1,   1,   1,   1,    1,     1,     1,   0,      0,   1,     0,   0,   0 },
+  /* GNUCXX17 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   0,   0 },
+  /* CXX17    */  { 1,  1,  1,  1,  1,  1,  1,   1,   1,   1,    1,     1,     0,   1,      0,   1,     0,   0,   0 },
+  /* GNUCXX20 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   0,   0 },
+  /* CXX20    */  { 1,  1,  1,  1,  1,  1,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   0,   0 },
+  /* GNUCXX23 */  { 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,     0,   1,      1,   1,     0,   1,   0 },
+  /* ASM      */  { 0,  0,  1,  0,  0,  0,  0,   0,   0,   0,    0,     0,     0,   0,      0,   0,     0,   0,   0 }
 };
 
 /* Sets internal flags correctly for a given language.  */
@@ -151,6 +152,7 @@ cpp_set_lang (cpp_reader *pfile, enum c_lang lang)
   CPP_OPTION (pfile, scope)                     = l->scope;
   CPP_OPTION (pfile, dfp_constants)             = l->dfp_constants;
   CPP_OPTION (pfile, size_t_literals)           = l->size_t_literals;
+  CPP_OPTION (pfile, elifdef)                   = l->elifdef;
 }
 
 /* Initialize library global state.  */