Implement __VA_OPT__
authorTom Tromey <tom@tromey.com>
Mon, 13 Nov 2017 20:17:42 +0000 (20:17 +0000)
committerTom Tromey <tromey@gcc.gnu.org>
Mon, 13 Nov 2017 20:17:42 +0000 (20:17 +0000)
This implements __VA_OPT__, a new preprocessor feature added in C++2A.
The paper can be found here:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0306r4.html

gcc/ChangeLog

        * doc/cpp.texi (Variadic Macros): Document __VA_OPT__.

gcc/testsuite/ChangeLog

        * c-c++-common/cpp/va-opt-pedantic.c: New file.
        * c-c++-common/cpp/va-opt.c: New file.
        * c-c++-common/cpp/va-opt-error.c: New file.

libcpp/ChangeLog

        * pch.c (cpp_read_state): Set n__VA_OPT__.
        * macro.c (vaopt_state): New class.
        (_cpp_arguments_ok): Check va_opt flag.
        (replace_args, create_iso_definition): Use vaopt_state.
        * lex.c (lex_identifier_intern): Possibly issue errors for
        __VA_OPT__.
        (lex_identifier): Likewise.
        (maybe_va_opt_error): New function.
        * internal.h (struct lexer_state) <va_args_ok>: Update comment.
        (struct spec_nodes) <n__VA_OPT__>: New field.
        * init.c (struct lang_flags) <va_opt>: New field.
        (lang_defaults): Add entries for C++2A.  Update all entries for
        va_opt.
        (cpp_set_lang): Initialize va_opt.
        * include/cpplib.h (struct cpp_options) <va_opt>: New field.
        * identifiers.c (_cpp_init_hashtable): Initialize n__VA_OPT__.

From-SVN: r254707

14 files changed:
gcc/ChangeLog
gcc/doc/cpp.texi
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/cpp/va-opt-error.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/cpp/va-opt-pedantic.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/cpp/va-opt.c [new file with mode: 0644]
libcpp/ChangeLog
libcpp/identifiers.c
libcpp/include/cpplib.h
libcpp/init.c
libcpp/internal.h
libcpp/lex.c
libcpp/macro.c
libcpp/pch.c

index b393e0d0c016582e584546d2098861147e0e6cfe..b7ac84c44d700879373a50c716435c6be349bc15 100644 (file)
@@ -1,3 +1,7 @@
+2017-11-13  Tom Tromey  <tom@tromey.com>
+
+       * doc/cpp.texi (Variadic Macros): Document __VA_OPT__.
+
 2017-11-13  Carl Love  <cel@us.ibm.com>
 
        * config/rs6000/rs6000-c.c (altivec_overloaded_builtins):
index 8cafb6554f8fcacdbbc482772a3948530b9159ec..94437d5403e70187ad331bdc5dfaa97bc004b15e 100644 (file)
@@ -1675,20 +1675,27 @@ macro.  We could define @code{eprintf} like this, instead:
 @end smallexample
 
 @noindent
-This formulation looks more descriptive, but unfortunately it is less
-flexible: you must now supply at least one argument after the format
-string.  In standard C, you cannot omit the comma separating the named
-argument from the variable arguments.  Furthermore, if you leave the
-variable argument empty, you will get a syntax error, because
-there will be an extra comma after the format string.
+This formulation looks more descriptive, but historically it was less
+flexible: you had to supply at least one argument after the format
+string.  In standard C, you could not omit the comma separating the
+named argument from the variable arguments.  (Note that this
+restriction has been lifted in C++2a, and never existed in GNU C; see
+below.)
+
+Furthermore, if you left the variable argument empty, you would have
+gotten a syntax error, because there would have been an extra comma
+after the format string.
 
 @smallexample
 eprintf("success!\n", );
      @expansion{} fprintf(stderr, "success!\n", );
 @end smallexample
 
-GNU CPP has a pair of extensions which deal with this problem.  First,
-you are allowed to leave the variable argument out entirely:
+This has been fixed in C++2a, and GNU CPP also has a pair of
+extensions which deal with this problem.
+
+First, in GNU CPP, and in C++ beginning in C++2a, you are allowed to
+leave the variable argument out entirely:
 
 @smallexample
 eprintf ("success!\n")
@@ -1696,8 +1703,24 @@ eprintf ("success!\n")
 @end smallexample
 
 @noindent
-Second, the @samp{##} token paste operator has a special meaning when
-placed between a comma and a variable argument.  If you write
+Second, C++2a introduces the @code{@w{__VA_OPT__}} function macro.
+This macro may only appear in the definition of a variadic macro.  If
+the variable argument has any tokens, then a @code{@w{__VA_OPT__}}
+invocation expands to its argument; but if the variable argument does
+not have any tokens, the @code{@w{__VA_OPT__}} expands to nothing:
+
+@smallexample
+#define eprintf(format, @dots{}) \\
+  fprintf (stderr, format __VA_OPT__(,) __VA_ARGS__)
+@end smallexample
+
+@code{@w{__VA_OPT__}} is also available in GNU C and GNU C++.
+
+Historically, GNU CPP has also had another extension to handle the
+trailing comma: the @samp{##} token paste operator has a special
+meaning when placed between a comma and a variable argument.  Despite
+the introduction of @code{@w{__VA_OPT__}}, this extension remains
+supported in GNU CPP, for backward compatibility.  If you write
 
 @smallexample
 #define eprintf(format, @dots{}) fprintf (stderr, format, ##__VA_ARGS__)
@@ -1730,6 +1753,9 @@ of macro.  It may also be forbidden in open text; the standard is
 ambiguous.  We recommend you avoid using it except for its defined
 purpose.
 
+Likewise, C++ forbids @code{@w{__VA_OPT__}} anywhere outside the
+replacement list of a variadic macro.
+
 Variadic macros became a standard part of the C language with C99.  
 GNU CPP previously supported them
 with a named variable argument
index 69633de1bea1cba9b78b44df4fc1beb218ee1104..b1c2f3e05301a3b7aad233042e55995fe656fc5d 100644 (file)
@@ -1,3 +1,9 @@
+2017-11-13  Tom Tromey  <tom@tromey.com>
+
+       * c-c++-common/cpp/va-opt-pedantic.c: New file.
+       * c-c++-common/cpp/va-opt.c: New file.
+       * c-c++-common/cpp/va-opt-error.c: New file.
+
 2017-11-13  Carl Love  <cel@us.ibm.com>
 
        * gcc.target/powerpc/builtins-6-p9-runnable.c: Add new runnable test.
diff --git a/gcc/testsuite/c-c++-common/cpp/va-opt-error.c b/gcc/testsuite/c-c++-common/cpp/va-opt-error.c
new file mode 100644 (file)
index 0000000..f32f055
--- /dev/null
@@ -0,0 +1,28 @@
+/* { dg-do preprocess }*/
+/* { dg-options "-std=gnu99" { target c } } */
+/* { dg-options "-std=c++2a" { target c++ } } */
+
+#define ERR1(x) __VA_OPT__ /* { dg-warning "__VA_OPT__ can only appear" } */
+#define ERR2(x) __VA_OPT__( /* { dg-warning "can only appear" } */
+#define ERR3(x) __VA_OPT__() /* { dg-warning "can only appear" } */
+
+#define ERR4(x,...) __VA_OPT__ /* { dg-error "unterminated __VA_OPT__" } */
+#define ERR5(x,...) __VA_OPT__( /* { dg-error "unterminated" } */
+#define ERR6(x,...) __VA_OPT__(() /* { dg-error "unterminated" } */
+
+#define ERR7(x,...) __VA_OPT__(__VA_OPT__) /* { dg-error "may not appear" } */
+#define ERR7(x,...) __VA_OPT__(__VA_OPT__()) /* { dg-error "may not appear" } */
+
+#define ERR8(x, y,...) x __VA_OPT__(##) y /* { dg-error "either end" } */
+#define ERR9(x, y,...) x __VA_OPT__(x ##) y /* { dg-error "either end" } */
+#define ERRA(x, y,...) x x __VA_OPT__(## y) /* { dg-error "either end" } */
+
+#define ERRB __VA_OPT__ /* { dg-warning "can only appear" } */
+#define ERRC(__VA_OPT__) x /* { dg-warning "can only appear" } */
+
+__VA_OPT__ /* { dg-warning "can only appear" } */
+
+#define ERRD(x)
+ERRD(__VA_OPT__) /* { dg-warning "can only appear" } */
+
+#define __VA_OPT__ /* { dg-warning "can only appear" } */
diff --git a/gcc/testsuite/c-c++-common/cpp/va-opt-pedantic.c b/gcc/testsuite/c-c++-common/cpp/va-opt-pedantic.c
new file mode 100644 (file)
index 0000000..5887bf5
--- /dev/null
@@ -0,0 +1,5 @@
+/* { dg-do preprocess }*/
+/* { dg-options "-std=c11 -pedantic-errors" { target c } } */
+/* { dg-options "-std=c++17 -pedantic-errors" { target c++ } } */
+
+#define CALL(F, ...) F (7 __VA_OPT__(,) __VA_ARGS__) /* { dg-error "__VA_OPT__ is not available" } */
diff --git a/gcc/testsuite/c-c++-common/cpp/va-opt.c b/gcc/testsuite/c-c++-common/cpp/va-opt.c
new file mode 100644 (file)
index 0000000..243d33b
--- /dev/null
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99" { target c } } */
+/* { dg-options "-std=c++2a" { target c++ } } */
+
+extern void f0 (void);
+extern void f1 (int);
+extern void f2 (int, int);
+extern void f3 (int, int, int);
+extern void f4 (int, int, int, int);
+extern int s (const char *);
+
+#define CALL(F, ...) F (7 __VA_OPT__(,) __VA_ARGS__)
+#define CP(F, X, Y, ...) F (__VA_OPT__(X ## Y,) __VA_ARGS__)
+#define CS(F, ...) F(__VA_OPT__(s(# __VA_ARGS__)))
+#define D(F, ...) F(__VA_OPT__(__VA_ARGS__) __VA_OPT__(,) __VA_ARGS__)
+#define CALL0(...) __VA_OPT__(f2)(0 __VA_OPT__(,)__VA_ARGS__)
+
+void t (void)
+{
+  CALL (f1);
+  CALL (f1, );
+  CALL (f2, 1);
+  CALL (f3, 1, 2);
+
+  int one = 1;
+  int two = 2;
+  int onetwo = 23;
+
+  CP (f0, one, two);
+  CP (f0, one, two, );
+  CP (f2, one, two, 3);
+
+  CS (f0);
+  CS (f1, 1, 2, 3, 4);
+
+  D (f0);
+  D (f2, 1);
+  D (f4, 1, 2);
+
+  CALL0 ();
+  CALL0 (23);
+}
index 35bcef907451c014efbf09f52d2c3e84d520739f..70c834c61d065e4ae08f637607fd682734e36715 100644 (file)
@@ -1,3 +1,22 @@
+2017-11-13  Tom Tromey  <tom@tromey.com>
+
+       * pch.c (cpp_read_state): Set n__VA_OPT__.
+       * macro.c (vaopt_state): New class.
+       (_cpp_arguments_ok): Check va_opt flag.
+       (replace_args, create_iso_definition): Use vaopt_state.
+       * lex.c (lex_identifier_intern): Possibly issue errors for
+       __VA_OPT__.
+       (lex_identifier): Likewise.
+       (maybe_va_opt_error): New function.
+       * internal.h (struct lexer_state) <va_args_ok>: Update comment.
+       (struct spec_nodes) <n__VA_OPT__>: New field.
+       * init.c (struct lang_flags) <va_opt>: New field.
+       (lang_defaults): Add entries for C++2A.  Update all entries for
+       va_opt.
+       (cpp_set_lang): Initialize va_opt.
+       * include/cpplib.h (struct cpp_options) <va_opt>: New field.
+       * identifiers.c (_cpp_init_hashtable): Initialize n__VA_OPT__.
+
 2017-11-13  David Malcolm  <dmalcolm@redhat.com>
 
        * include/line-map.h (linenum_type): Move this typedef and the
index 220f9b97f0d6f9b9b48e3f9f4513c10f09c1f406..e456fd3a4fcac1e6ba4ba4eae2506141c488686c 100644 (file)
@@ -70,6 +70,8 @@ _cpp_init_hashtable (cpp_reader *pfile, cpp_hash_table *table)
   s->n_false           = cpp_lookup (pfile, DSC("false"));
   s->n__VA_ARGS__       = cpp_lookup (pfile, DSC("__VA_ARGS__"));
   s->n__VA_ARGS__->flags |= NODE_DIAGNOSTIC;
+  s->n__VA_OPT__        = cpp_lookup (pfile, DSC("__VA_OPT__"));
+  s->n__VA_OPT__->flags |= NODE_DIAGNOSTIC;
   s->n__has_include__   = cpp_lookup (pfile, DSC("__has_include__"));
   s->n__has_include_next__ = cpp_lookup (pfile, DSC("__has_include_next__"));
 }
index 5a14858c44fcee55c8abe8e6303afce6daaefe01..101b33aef480062de821fb40074667572b05b7cc 100644 (file)
@@ -478,6 +478,9 @@ struct cpp_options
   /* Nonzero for C++ 2014 Standard digit separators.  */
   unsigned char digit_separators;
 
+  /* Nonzero for C++2a __VA_OPT__ feature.  */
+  unsigned char va_opt;
+
   /* Holds the name of the target (execution) character set.  */
   const char *narrow_charset;
 
index ecc81e3138a54ac5c3d77fcbc54ba66065ee4c0f..8423656ad10ab680942245fd8c2eb2de55ae4b39 100644 (file)
@@ -91,30 +91,31 @@ struct lang_flags
   char digit_separators;
   char trigraphs;
   char utf8_char_literals;
+  char va_opt;
 };
 
 static const struct lang_flags lang_defaults[] =
-{ /*              c99 c++ xnum xid c11 std digr ulit rlit udlit bincst digsep trig u8chlit */
-  /* GNUC89   */  { 0,  0,  1,  0,  0,  0,  1,   0,   0,   0,    0,     0,     0,   0 },
-  /* GNUC99   */  { 1,  0,  1,  1,  0,  0,  1,   1,   1,   0,    0,     0,     0,   0 },
-  /* GNUC11   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0 },
-  /* GNUC17   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0 },
-  /* STDC89   */  { 0,  0,  0,  0,  0,  1,  0,   0,   0,   0,    0,     0,     1,   0 },
-  /* STDC94   */  { 0,  0,  0,  0,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0 },
-  /* STDC99   */  { 1,  0,  1,  1,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0 },
-  /* STDC11   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    0,     0,     1,   0 },
-  /* STDC17   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    0,     0,     1,   0 },
-  /* GNUCXX   */  { 0,  1,  1,  1,  0,  0,  1,   0,   0,   0,    0,     0,     0,   0 },
-  /* CXX98    */  { 0,  1,  0,  1,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0 },
-  /* GNUCXX11 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    0,     0,     0,   0 },
-  /* CXX11    */  { 1,  1,  0,  1,  1,  1,  1,   1,   1,   1,    0,     0,     1,   0 },
-  /* GNUCXX14 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   0 },
-  /* CXX14    */  { 1,  1,  0,  1,  1,  1,  1,   1,   1,   1,    1,     1,     1,   0 },
-  /* GNUCXX17 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   1 },
-  /* CXX17    */  { 1,  1,  1,  1,  1,  1,  1,   1,   1,   1,    1,     1,     0,   1 },
-  /* GNUCXX2A */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   1 },
-  /* CXX2A    */  { 1,  1,  1,  1,  1,  1,  1,   1,   1,   1,    1,     1,     0,   1 },
-  /* ASM      */  { 0,  0,  1,  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 */
+  /* GNUC89   */  { 0,  0,  1,  0,  0,  0,  1,   0,   0,   0,    0,     0,     0,   0,      1 },
+  /* GNUC99   */  { 1,  0,  1,  1,  0,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1 },
+  /* GNUC11   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1 },
+  /* GNUC17   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1 },
+  /* STDC89   */  { 0,  0,  0,  0,  0,  1,  0,   0,   0,   0,    0,     0,     1,   0,      0 },
+  /* STDC94   */  { 0,  0,  0,  0,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0,      0 },
+  /* STDC99   */  { 1,  0,  1,  1,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0,      0 },
+  /* STDC11   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    0,     0,     1,   0,      0 },
+  /* STDC17   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    0,     0,     1,   0,      0 },
+  /* GNUCXX   */  { 0,  1,  1,  1,  0,  0,  1,   0,   0,   0,    0,     0,     0,   0,      1 },
+  /* CXX98    */  { 0,  1,  0,  1,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0,      0 },
+  /* GNUCXX11 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    0,     0,     0,   0,      1 },
+  /* CXX11    */  { 1,  1,  0,  1,  1,  1,  1,   1,   1,   1,    0,     0,     1,   0,      0 },
+  /* GNUCXX14 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   0,      1 },
+  /* CXX14    */  { 1,  1,  0,  1,  1,  1,  1,   1,   1,   1,    1,     1,     1,   0,      0 },
+  /* GNUCXX17 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   1,      1 },
+  /* CXX17    */  { 1,  1,  1,  1,  1,  1,  1,   1,   1,   1,    1,     1,     0,   1,      0 },
+  /* GNUCXX2A */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   1,      1 },
+  /* CXX2A    */  { 1,  1,  1,  1,  1,  1,  1,   1,   1,   1,    1,     1,     0,   1,      1 },
+  /* ASM      */  { 0,  0,  1,  0,  0,  0,  0,   0,   0,   0,    0,     0,     0,   0,      0 }
 };
 
 /* Sets internal flags correctly for a given language.  */
@@ -139,6 +140,7 @@ cpp_set_lang (cpp_reader *pfile, enum c_lang lang)
   CPP_OPTION (pfile, digit_separators)          = l->digit_separators;
   CPP_OPTION (pfile, trigraphs)                         = l->trigraphs;
   CPP_OPTION (pfile, utf8_char_literals)        = l->utf8_char_literals;
+  CPP_OPTION (pfile, va_opt)                    = l->va_opt;
 }
 
 /* Initialize library global state.  */
index f24e85cfb119ab7e6131c07c51e7dd7e55433a0d..0a33abafd43f34daadd4339387a112c199536672 100644 (file)
@@ -246,7 +246,7 @@ struct lexer_state
      all directives apart from #define.  */
   unsigned char save_comments;
 
-  /* Nonzero if lexing __VA_ARGS__ is valid.  */
+  /* Nonzero if lexing __VA_ARGS__ and __VA_OPT__ are valid.  */
   unsigned char va_args_ok;
 
   /* Nonzero if lexing poisoned identifiers is valid.  */
@@ -282,6 +282,7 @@ struct spec_nodes
   cpp_hashnode *n_true;                        /* C++ keyword true */
   cpp_hashnode *n_false;               /* C++ keyword false */
   cpp_hashnode *n__VA_ARGS__;          /* C99 vararg macros */
+  cpp_hashnode *n__VA_OPT__;           /* C++ vararg macros */
   cpp_hashnode *n__has_include__;      /* __has_include__ operator */
   cpp_hashnode *n__has_include_next__; /* __has_include_next__ operator */
 };
index 8af09e50295535048b2f9f7105bd24ea2ab968e1..a8dc3bae5e4e7b575462d493c9c11c58b07a58fa 100644 (file)
@@ -1352,6 +1352,28 @@ forms_identifier_p (cpp_reader *pfile, int first,
   return false;
 }
 
+/* Helper function to issue error about improper __VA_OPT__ use.  */
+static void
+maybe_va_opt_error (cpp_reader *pfile)
+{
+  if (CPP_PEDANTIC (pfile) && !CPP_OPTION (pfile, va_opt))
+    {
+      /* __VA_OPT__ should not be accepted at all, but allow it in
+        system headers.  */
+      if (!cpp_in_system_header (pfile))
+       cpp_error (pfile, CPP_DL_PEDWARN,
+                  "__VA_OPT__ is not available until C++2a");
+    }
+  else if (!pfile->state.va_args_ok)
+    {
+      /* __VA_OPT__ should only appear in the replacement list of a
+        variadic macro.  */
+      cpp_error (pfile, CPP_DL_PEDWARN,
+                "__VA_OPT__ can only appear in the expansion"
+                " of a C++2a variadic macro");
+    }
+}
+
 /* Helper function to get the cpp_hashnode of the identifier BASE.  */
 static cpp_hashnode *
 lex_identifier_intern (cpp_reader *pfile, const uchar *base)
@@ -1396,6 +1418,9 @@ lex_identifier_intern (cpp_reader *pfile, const uchar *base)
                       " of a C99 variadic macro");
        }
 
+      if (result == pfile->spec_nodes.n__VA_OPT__)
+       maybe_va_opt_error (pfile);
+
       /* For -Wc++-compat, warn about use of C++ named operators.  */
       if (result->flags & NODE_WARN_OPERATOR)
        cpp_warning (pfile, CPP_W_CXX_OPERATOR_NAMES,
@@ -1485,6 +1510,11 @@ lex_identifier (cpp_reader *pfile, const uchar *base, bool starts_ucn,
                       " of a C99 variadic macro");
        }
 
+      /* __VA_OPT__ should only appear in the replacement list of a
+        variadic macro.  */
+      if (result == pfile->spec_nodes.n__VA_OPT__)
+       maybe_va_opt_error (pfile);
+
       /* For -Wc++-compat, warn about use of C++ named operators.  */
       if (result->flags & NODE_WARN_OPERATOR)
        cpp_warning (pfile, CPP_W_CXX_OPERATOR_NAMES,
index fab1cb051dc78569c3adf2661b22c043d8ed09c3..bf473eae358699db4d4845e96cd19da1d5ed629f 100644 (file)
@@ -89,6 +89,155 @@ struct macro_arg_saved_data {
   union _cpp_hashnode_value value;
 };
 
+static const char *vaopt_paste_error =
+  N_("'##' cannot appear at either end of __VA_OPT__");
+
+/* A class for tracking __VA_OPT__ state while iterating over a
+   sequence of tokens.  This is used during both macro definition and
+   expansion.  */
+class vaopt_state {
+
+ public:
+
+  /* Initialize the state tracker.  ANY_ARGS is true if variable
+     arguments were provided to the macro invocation.  */
+  vaopt_state (cpp_reader *pfile, bool is_variadic, bool any_args)
+    : m_pfile (pfile),
+    m_allowed (any_args),
+    m_variadic (is_variadic),
+    m_state (0),
+    m_last_was_paste (false),
+    m_paste_location (0),
+    m_location (0)
+  {
+  }
+
+  enum update_type
+  {
+    ERROR,
+    DROP,
+    INCLUDE
+  };
+
+  /* Given a token, update the state of this tracker and return a
+     boolean indicating whether the token should be be included in the
+     expansion.  */
+  update_type update (const cpp_token *token)
+  {
+    /* If the macro isn't variadic, just don't bother.  */
+    if (!m_variadic)
+      return INCLUDE;
+
+    if (token->type == CPP_NAME
+       && token->val.node.node == m_pfile->spec_nodes.n__VA_OPT__)
+      {
+       if (m_state > 0)
+         {
+           cpp_error_at (m_pfile, CPP_DL_ERROR, token->src_loc,
+                         "__VA_OPT__ may not appear in a __VA_OPT__");
+           return ERROR;
+         }
+       ++m_state;
+       m_location = token->src_loc;
+       return DROP;
+      }
+    else if (m_state == 1)
+      {
+       if (token->type != CPP_OPEN_PAREN)
+         {
+           cpp_error_at (m_pfile, CPP_DL_ERROR, m_location,
+                         "__VA_OPT__ must be followed by an "
+                         "open parenthesis");
+           return ERROR;
+         }
+       ++m_state;
+       return DROP;
+      }
+    else if (m_state >= 2)
+      {
+       if (m_state == 2 && token->type == CPP_PASTE)
+         {
+           cpp_error_at (m_pfile, CPP_DL_ERROR, token->src_loc,
+                         vaopt_paste_error);
+           return ERROR;
+         }
+       /* Advance states before further considering this token, in
+          case we see a close paren immediately after the open
+          paren.  */
+       if (m_state == 2)
+         ++m_state;
+
+       bool was_paste = m_last_was_paste;
+       m_last_was_paste = false;
+       if (token->type == CPP_PASTE)
+         {
+           m_last_was_paste = true;
+           m_paste_location = token->src_loc;
+         }
+       else if (token->type == CPP_OPEN_PAREN)
+         ++m_state;
+       else if (token->type == CPP_CLOSE_PAREN)
+         {
+           --m_state;
+           if (m_state == 2)
+             {
+               /* Saw the final paren.  */
+               m_state = 0;
+
+               if (was_paste)
+                 {
+                   cpp_error_at (m_pfile, CPP_DL_ERROR, token->src_loc,
+                                 vaopt_paste_error);
+                   return ERROR;
+                 }
+
+               return DROP;
+             }
+         }
+       return m_allowed ? INCLUDE : DROP;
+      }
+
+    /* Nothing to do with __VA_OPT__.  */
+    return INCLUDE;
+  }
+
+  /* Ensure that any __VA_OPT__ was completed.  If ok, return true.
+     Otherwise, issue an error and return false.  */
+  bool completed ()
+  {
+    if (m_variadic && m_state != 0)
+      cpp_error_at (m_pfile, CPP_DL_ERROR, m_location,
+                   "unterminated __VA_OPT__");
+    return m_state == 0;
+  }
+
+ private:
+
+  /* The cpp_reader.  */
+  cpp_reader *m_pfile;
+
+  /* True if there were varargs.  */
+  bool m_allowed;
+  /* True if the macro is variadic.  */
+  bool m_variadic;
+
+  /* The state variable:
+     0 means not parsing
+     1 means __VA_OPT__ seen, looking for "("
+     2 means "(" seen (so the next token can't be "##")
+     >= 3 means looking for ")", the number encodes the paren depth.  */
+  int m_state;
+
+  /* If true, the previous token was ##.  This is used to detect when
+     a paste occurs at the end of the sequence.  */
+  bool m_last_was_paste;
+  /* The location of the paste token.  */
+  source_location m_paste_location;
+
+  /* Location of the __VA_OPT__ token.  */
+  source_location m_location;
+};
+
 /* Macro expansion.  */
 
 static int enter_macro_context (cpp_reader *, cpp_hashnode *,
@@ -776,7 +925,8 @@ _cpp_arguments_ok (cpp_reader *pfile, cpp_macro *macro, const cpp_hashnode *node
 
   if (argc < macro->paramc)
     {
-      /* As an extension, variadic arguments are allowed to not appear in
+      /* In C++2a (here the va_opt flag is used), and also as a GNU
+        extension, variadic arguments are allowed to not appear in
         the invocation at all.
         e.g. #define debug(format, args...) something
         debug("string");
@@ -786,7 +936,8 @@ _cpp_arguments_ok (cpp_reader *pfile, cpp_macro *macro, const cpp_hashnode *node
 
       if (argc + 1 == macro->paramc && macro->variadic)
        {
-         if (CPP_PEDANTIC (pfile) && ! macro->syshdr)
+         if (CPP_PEDANTIC (pfile) && ! macro->syshdr
+             && ! CPP_OPTION (pfile, va_opt))
            {
              if (CPP_OPTION (pfile, cplusplus))
                cpp_error (pfile, CPP_DL_PEDWARN,
@@ -1678,6 +1829,8 @@ replace_args (cpp_reader *pfile, cpp_hashnode *node, cpp_macro *macro,
                                 num_macro_tokens);
     }
   i = 0;
+  vaopt_state vaopt_tracker (pfile, macro->variadic,
+                            args[macro->paramc - 1].count > 0);
   for (src = macro->exp.tokens; src < limit; src++)
     {
       unsigned int arg_tokens_count;
@@ -1685,6 +1838,10 @@ replace_args (cpp_reader *pfile, cpp_hashnode *node, cpp_macro *macro,
       const cpp_token **paste_flag = NULL;
       const cpp_token **tmp_token_ptr;
 
+      /* __VA_OPT__ handling.  */
+      if (vaopt_tracker.update (src) != vaopt_state::INCLUDE)
+       continue;
+
       if (src->type != CPP_MACRO_ARG)
        {
          /* Allocate a virtual location for token SRC, and add that
@@ -3076,6 +3233,9 @@ create_iso_definition (cpp_reader *pfile, cpp_macro *macro)
       *token = *ctoken;
     }
 
+  /* The argument doesn't matter here.  */
+  vaopt_state vaopt_tracker (pfile, macro->variadic, true);
+
   for (;;)
     {
       /* Check the stringifying # constraint 6.10.3.2.1 of
@@ -3144,10 +3304,16 @@ create_iso_definition (cpp_reader *pfile, cpp_macro *macro)
            }
        }
 
+      if (vaopt_tracker.update (token) == vaopt_state::ERROR)
+       return false;
+
       following_paste_op = (token->type == CPP_PASTE);
       token = lex_expansion_token (pfile, macro);
     }
 
+  if (!vaopt_tracker.completed ())
+    return false;
+
   macro->exp.tokens = (cpp_token *) BUFF_FRONT (pfile->a_buff);
   macro->traditional = 0;
 
index cad4b872cda6e38d4b20bcdeba2db3cc945eaced..b685a38a854545bd73ef6e90c181986b8d8af621 100644 (file)
@@ -835,6 +835,7 @@ cpp_read_state (cpp_reader *r, const char *name, FILE *f,
     s->n_true          = cpp_lookup (r, DSC("true"));
     s->n_false         = cpp_lookup (r, DSC("false"));
     s->n__VA_ARGS__     = cpp_lookup (r, DSC("__VA_ARGS__"));
+    s->n__VA_OPT__      = cpp_lookup (r, DSC("__VA_OPT__"));
     s->n__has_include__ = cpp_lookup (r, DSC("__has_include__"));
     s->n__has_include_next__ = cpp_lookup (r, DSC("__has_include_next__"));
   }