public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [RFA] Implement __VA_OPT__
@ 2017-09-16 21:49 Tom Tromey
  2017-09-17  9:43 ` Alexander Monakov
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2017-09-16 21:49 UTC (permalink / raw)
  To: gcc-patches; +Cc: Tom Tromey

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

I am not completely sure that I have handled the error reporting
correctly.  I chose to allow __VA_OPT__ generally, on the theory that,
because it is a reserved identifier, programs should not be referring
to it anyway.  Also, this approach makes it possible for __VA_OPT__ to
be used in system headers even when the -std/-pedantic settings may
otherwise disallow it.

Bootstrapped and regression tested on x86-64 Fedora 25.

gcc/ChangeLog
2017-09-16  Tom Tromey  <tom@tromey.com>

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

gcc/testsuite/ChangeLog
2017-09-16  Tom Tromey  <tom@tromey.com>

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

libcpp/ChangeLog
2017-09-16  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.
	* 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 (enum c_lang) <CLK_GNUCXX2A, CLK_CXX2A>: New
	constants.
	(struct cpp_options) <va_opt>: New field.
	* identifiers.c (_cpp_init_hashtable): Initialize n__VA_OPT__.
---
 gcc/ChangeLog                                 |   4 +
 gcc/doc/cpp.texi                              |  46 +++++--
 gcc/testsuite/ChangeLog                       |   5 +
 gcc/testsuite/c-c++-common/cpp/va-opt-error.c |  28 +++++
 gcc/testsuite/c-c++-common/cpp/va-opt.c       |  42 +++++++
 libcpp/ChangeLog                              |  20 +++
 libcpp/identifiers.c                          |   2 +
 libcpp/include/cpplib.h                       |   3 +
 libcpp/init.c                                 |  40 +++---
 libcpp/internal.h                             |   3 +-
 libcpp/lex.c                                  |  30 +++++
 libcpp/macro.c                                | 170 +++++++++++++++++++++++++-
 libcpp/pch.c                                  |   1 +
 13 files changed, 362 insertions(+), 32 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/cpp/va-opt-error.c
 create mode 100644 gcc/testsuite/c-c++-common/cpp/va-opt.c

diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi
index 52f2606..3990165 100644
--- a/gcc/doc/cpp.texi
+++ b/gcc/doc/cpp.texi
@@ -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++20, 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 een 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++20, and GNU CPP also has a pair of
+extensions which deal with this problem.
+
+First, in GNU CPP, and in C++ beginning in C++20, 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++20 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 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
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
index 0000000..7718916
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/va-opt-error.c
@@ -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-error "__VA_OPT__ can only appear" } */
+#define ERR2(x) __VA_OPT__( /* { dg-error "can only appear" } */
+#define ERR3(x) __VA_OPT__() /* { dg-error "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-error "can only appear" } */
+#define ERRC(__VA_OPT__) x /* { dg-error "can only appear" } */
+
+__VA_OPT__ /* { dg-error "can only appear" } */
+
+#define ERRD(x)
+ERRD(__VA_OPT__) /* { dg-error "can only appear" } */
+
+#define __VA_OPT__ /* { dg-error "can only appear" } */
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
index 0000000..243d33b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/va-opt.c
@@ -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);
+}
diff --git a/libcpp/identifiers.c b/libcpp/identifiers.c
index 220f9b9..e456fd3 100644
--- a/libcpp/identifiers.c
+++ b/libcpp/identifiers.c
@@ -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__"));
 }
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 804132a..fc98175 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -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;
 
diff --git a/libcpp/init.c b/libcpp/init.c
index 16ff202..2bf76a7 100644
--- a/libcpp/init.c
+++ b/libcpp/init.c
@@ -91,28 +91,29 @@ 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 },
-  /* 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 },
-  /* 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,      0 },
+  /* GNUC99   */  { 1,  0,  1,  1,  0,  0,  1,   1,   1,   0,    0,     0,     0,   0,      0 },
+  /* GNUC11   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      0 },
+  /* 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 },
+  /* GNUCXX   */  { 0,  1,  1,  1,  0,  0,  1,   0,   0,   0,    0,     0,     0,   0,      0 },
+  /* 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,      0 },
+  /* 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,      0 },
+  /* CXX14    */  { 1,  1,  0,  1,  1,  1,  1,   1,   1,   1,    1,     1,     1,   0,      0 },
+  /* GNUCXX1Z */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   1,      0 },
+  /* CXX1Z    */  { 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.  */
@@ -137,6 +138,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.  */
diff --git a/libcpp/internal.h b/libcpp/internal.h
index f24e85c..0a33aba 100644
--- a/libcpp/internal.h
+++ b/libcpp/internal.h
@@ -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 */
 };
diff --git a/libcpp/lex.c b/libcpp/lex.c
index 40ff801..eca4a23 100644
--- a/libcpp/lex.c
+++ b/libcpp/lex.c
@@ -1396,6 +1396,21 @@ lex_identifier_intern (cpp_reader *pfile, const uchar *base)
 		       " 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__
+	  && !pfile->state.va_args_ok)
+	{
+	  if (CPP_OPTION (pfile, cplusplus))
+	    cpp_error (pfile, CPP_DL_ERROR,
+		       "__VA_OPT__ can only appear in the expansion"
+		       " of a C++11 variadic macro");
+	  else
+	    cpp_error (pfile, CPP_DL_ERROR,
+		       "__VA_OPT__ can only appear in the expansion"
+		       " of a C99 variadic macro");
+	}
+
       /* 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 +1500,21 @@ 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__
+	  && !pfile->state.va_args_ok)
+	{
+	  if (CPP_OPTION (pfile, cplusplus))
+	    cpp_error (pfile, CPP_DL_ERROR,
+		       "__VA_OPT__ can only appear in the expansion"
+		       " of a C++11 variadic macro");
+	  else
+	    cpp_error (pfile, CPP_DL_ERROR,
+		       "__VA_OPT__ can only appear in the expansion"
+		       " of a C99 variadic macro");
+	}
+
       /* For -Wc++-compat, warn about use of C++ named operators.  */
       if (result->flags & NODE_WARN_OPERATOR)
 	cpp_warning (pfile, CPP_W_CXX_OPERATOR_NAMES,
diff --git a/libcpp/macro.c b/libcpp/macro.c
index de18c22..b16a24e 100644
--- a/libcpp/macro.c
+++ b/libcpp/macro.c
@@ -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 *,
@@ -768,7 +917,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");
@@ -778,7 +928,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,
@@ -1670,6 +1821,8 @@ replace_args (cpp_reader *pfile, cpp_hashnode *node, cpp_macro *macro,
 				 num_macro_tokens);
     }
   i = 0;
+  vaopt_state state_tracker (pfile, macro->variadic,
+			     args[macro->paramc - 1].count > 0);
   for (src = macro->exp.tokens; src < limit; src++)
     {
       unsigned int arg_tokens_count;
@@ -1677,6 +1830,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 (state_tracker.update (src) != vaopt_state::INCLUDE)
+	continue;
+
       if (src->type != CPP_MACRO_ARG)
 	{
 	  /* Allocate a virtual location for token SRC, and add that
@@ -3068,6 +3225,9 @@ create_iso_definition (cpp_reader *pfile, cpp_macro *macro)
       *token = *ctoken;
     }
 
+  /* The argument doesn't matter here.  */
+  vaopt_state state_tracker (pfile, macro->variadic, true);
+
   for (;;)
     {
       /* Check the stringifying # constraint 6.10.3.2.1 of
@@ -3136,10 +3296,16 @@ create_iso_definition (cpp_reader *pfile, cpp_macro *macro)
 	    }
 	}
 
+      if (state_tracker.update (token) == vaopt_state::ERROR)
+	return false;
+
       following_paste_op = (token->type == CPP_PASTE);
       token = lex_expansion_token (pfile, macro);
     }
 
+  if (!state_tracker.completed ())
+    return false;
+
   macro->exp.tokens = (cpp_token *) BUFF_FRONT (pfile->a_buff);
   macro->traditional = 0;
 
diff --git a/libcpp/pch.c b/libcpp/pch.c
index cad4b87..b685a38 100644
--- a/libcpp/pch.c
+++ b/libcpp/pch.c
@@ -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__"));
   }
-- 
2.9.4

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFA] Implement __VA_OPT__
  2017-09-16 21:49 [RFA] Implement __VA_OPT__ Tom Tromey
@ 2017-09-17  9:43 ` Alexander Monakov
  2017-09-17 15:13   ` Tom Tromey
  0 siblings, 1 reply; 13+ messages in thread
From: Alexander Monakov @ 2017-09-17  9:43 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gcc-patches

On Sat, 16 Sep 2017, Tom Tromey wrote:
> --- a/gcc/doc/cpp.texi
> +++ b/gcc/doc/cpp.texi
> @@ -1675,20 +1675,27 @@ macro.  We could define @code{eprintf} like this, instead:
[snip]
> +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++20, and never existed in GNU C; see
> +below.)

Shouldn't this say 'C++2a' (more instances follow)?


> +Historically, GNU has also had another extension to handle the

'GNU CPP'?

> --- a/libcpp/init.c
> +++ b/libcpp/init.c
> @@ -91,28 +91,29 @@ struct lang_flags
>  static const struct lang_flags lang_defaults[] =
> -{ /*              c99 c++ xnum xid c11 std digr ulit rlit udlit bincst digsep trig u8chlit */
[snip]
> -  /* 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 */
[snip]
> +  /* GNUCXX1Z */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   1,      0 },
> +  /* CXX1Z    */  { 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 }
>  };

This hunk reverts CXX17 back to CXX1Z.

> --- a/libcpp/lex.c
> +++ b/libcpp/lex.c
> @@ -1396,6 +1396,21 @@ lex_identifier_intern (cpp_reader *pfile, const uchar *base)
>  		       " 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__
> +	  && !pfile->state.va_args_ok)
> +	{
> +	  if (CPP_OPTION (pfile, cplusplus))
> +	    cpp_error (pfile, CPP_DL_ERROR,
> +		       "__VA_OPT__ can only appear in the expansion"
> +		       " of a C++11 variadic macro");
> +	  else
> +	    cpp_error (pfile, CPP_DL_ERROR,
> +		       "__VA_OPT__ can only appear in the expansion"
> +		       " of a C99 variadic macro");
> +	}
> +
>        /* 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 +1500,21 @@ 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__
> +	  && !pfile->state.va_args_ok)
> +	{
> +	  if (CPP_OPTION (pfile, cplusplus))
> +	    cpp_error (pfile, CPP_DL_ERROR,
> +		       "__VA_OPT__ can only appear in the expansion"
> +		       " of a C++11 variadic macro");
> +	  else
> +	    cpp_error (pfile, CPP_DL_ERROR,
> +		       "__VA_OPT__ can only appear in the expansion"
> +		       " of a C99 variadic macro");
> +	}
> +
>        /* For -Wc++-compat, warn about use of C++ named operators.  */
>        if (result->flags & NODE_WARN_OPERATOR)
>  	cpp_warning (pfile, CPP_W_CXX_OPERATOR_NAMES,

These two hunks add more duplication in already-duplicated 'if' statement
bodies.  Is it possible to move the whole body to a separate function?

I guess the references to C++11 and C99 are duplicated from the preceding 'if',
but is that correct here?  And why is it useful to distinguish between C and C++
for this error in the first place, why not simply say 'cpp_error (...,
"__VA_OPT__ can only appear in the expansion of a variadic macro");'?

> --- a/libcpp/macro.c
> +++ b/libcpp/macro.c
> @@ -1670,6 +1821,8 @@ replace_args (cpp_reader *pfile, cpp_hashnode *node, cpp_macro *macro,
>  				 num_macro_tokens);
>      }
>    i = 0;
> +  vaopt_state state_tracker (pfile, macro->variadic,
> +			     args[macro->paramc - 1].count > 0);

The name 'state_tracker' seems too general for what it does, I think
'vaopt_st' or such would have been a better fit (sorry for the bikeshed).

Thanks.
Alexander

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFA] Implement __VA_OPT__
  2017-09-17  9:43 ` Alexander Monakov
@ 2017-09-17 15:13   ` Tom Tromey
  2017-09-17 15:42     ` Tom Tromey
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2017-09-17 15:13 UTC (permalink / raw)
  To: Alexander Monakov; +Cc: Tom Tromey, gcc-patches

>>>>> "Alexander" == Alexander Monakov <amonakov@ispras.ru> writes:

Alexander> This hunk reverts CXX17 back to CXX1Z.

Thanks for noticing, I'd written this before Jakub's patch and so the
error came in during the rebase.

Alexander> These two hunks add more duplication in already-duplicated 'if' statement
Alexander> bodies.  Is it possible to move the whole body to a separate function?

I suppose, but this is just copying what pre-existing code does.

Alexander> I guess the references to C++11 and C99 are duplicated from the preceding 'if',
Alexander> but is that correct here?  And why is it useful to distinguish between C and C++
Alexander> for this error in the first place, why not simply say 'cpp_error (...,
Alexander> "__VA_OPT__ can only appear in the expansion of a variadic macro");'?

I don't really understand the rationale for why the errors are phrased
the way they are, but I notice the C errors generally mention C99 and
the C++ errors generally mention C++11.  So, since I didn't have a
rationale, I copied what is already there.  I thought maybe GCC is just
emitting the default standard choice for each language.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFA] Implement __VA_OPT__
  2017-09-17 15:13   ` Tom Tromey
@ 2017-09-17 15:42     ` Tom Tromey
  2017-09-17 15:52       ` Tom Tromey
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2017-09-17 15:42 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Alexander Monakov, gcc-patches

Tom> I don't really understand the rationale for why the errors are phrased
Tom> the way they are, but I notice the C errors generally mention C99 and
Tom> the C++ errors generally mention C++11.  So, since I didn't have a
Tom> rationale, I copied what is already there.  I thought maybe GCC is just
Tom> emitting the default standard choice for each language.

Here's an updated version of the patch.  I've tried to address your
comments (except the one about the text of the error message, see above)
and also some comments I got off-list.

thanks,
Tom

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index e213db6..362f50e 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,7 @@
+2017-09-16  Tom Tromey  <tom@tromey.com>
+
+	* doc/cpp.texi (Variadic Macros): Document __VA_OPT__.
+
 2017-09-16  Richard Sandiford  <richard.sandiford@linaro.org>
 
 	PR tree-optimization/82228
diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi
index 52f2606..5647d26f 100644
--- a/gcc/doc/cpp.texi
+++ b/gcc/doc/cpp.texi
@@ -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 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
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 1a94535..2538c4b 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2017-09-16  Tom Tromey  <tom@tromey.com>
+
+	* c-c++-common/cpp/va-opt.c: New file.
+	* c-c++-common/cpp/va-opt-error.c: New file.
+
 2017-09-15  Andrew Sutton  <andrew.n.sutton@gmail.com>
 	    Jakub Jelinek  <jakub@redhat.com>
 
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
index 0000000..7718916
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/va-opt-error.c
@@ -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-error "__VA_OPT__ can only appear" } */
+#define ERR2(x) __VA_OPT__( /* { dg-error "can only appear" } */
+#define ERR3(x) __VA_OPT__() /* { dg-error "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-error "can only appear" } */
+#define ERRC(__VA_OPT__) x /* { dg-error "can only appear" } */
+
+__VA_OPT__ /* { dg-error "can only appear" } */
+
+#define ERRD(x)
+ERRD(__VA_OPT__) /* { dg-error "can only appear" } */
+
+#define __VA_OPT__ /* { dg-error "can only appear" } */
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
index 0000000..243d33b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/va-opt.c
@@ -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);
+}
diff --git a/libcpp/ChangeLog b/libcpp/ChangeLog
index 0621074..65ce80c 100644
--- a/libcpp/ChangeLog
+++ b/libcpp/ChangeLog
@@ -1,3 +1,23 @@
+2017-09-16  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.
+	* 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 (enum c_lang) <CLK_GNUCXX2A, CLK_CXX2A>: New
+	constants.
+	(struct cpp_options) <va_opt>: New field.
+	* identifiers.c (_cpp_init_hashtable): Initialize n__VA_OPT__.
+
 2017-09-15  Andrew Sutton  <andrew.n.sutton@gmail.com>
 	    Jakub Jelinek  <jakub@redhat.com>
 
diff --git a/libcpp/identifiers.c b/libcpp/identifiers.c
index 220f9b9..e456fd3 100644
--- a/libcpp/identifiers.c
+++ b/libcpp/identifiers.c
@@ -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__"));
 }
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 804132a..fc98175 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -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;
 
diff --git a/libcpp/init.c b/libcpp/init.c
index 16ff202..49500b7 100644
--- a/libcpp/init.c
+++ b/libcpp/init.c
@@ -91,28 +91,29 @@ 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 },
-  /* 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 },
-  /* 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,      0 },
+  /* GNUC99   */  { 1,  0,  1,  1,  0,  0,  1,   1,   1,   0,    0,     0,     0,   0,      0 },
+  /* GNUC11   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      0 },
+  /* 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 },
+  /* GNUCXX   */  { 0,  1,  1,  1,  0,  0,  1,   0,   0,   0,    0,     0,     0,   0,      0 },
+  /* 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,      0 },
+  /* 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,      0 },
+  /* 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,      0 },
+  /* 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.  */
@@ -137,6 +138,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.  */
diff --git a/libcpp/internal.h b/libcpp/internal.h
index f24e85c..0a33aba 100644
--- a/libcpp/internal.h
+++ b/libcpp/internal.h
@@ -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 */
 };
diff --git a/libcpp/lex.c b/libcpp/lex.c
index 40ff801..9cfc693 100644
--- a/libcpp/lex.c
+++ b/libcpp/lex.c
@@ -1352,6 +1352,25 @@ 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, cpp_hashnode *node)
+{
+  /* __VA_OPT__ should only appear in the replacement list of a
+     variadic macro.  */
+  if (node == pfile->spec_nodes.n__VA_OPT__ && !pfile->state.va_args_ok)
+    {
+      if (CPP_OPTION (pfile, cplusplus))
+	cpp_error (pfile, CPP_DL_ERROR,
+		   "__VA_OPT__ can only appear in the expansion"
+		   " of a C++11 variadic macro");
+      else
+	cpp_error (pfile, CPP_DL_ERROR,
+		   "__VA_OPT__ can only appear in the expansion"
+		   " of a C99 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 +1415,8 @@ lex_identifier_intern (cpp_reader *pfile, const uchar *base)
 		       " of a C99 variadic macro");
 	}
 
+      maybe_va_opt_error (pfile, result);
+
       /* 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 +1506,10 @@ 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.  */
+      maybe_va_opt_error (pfile, result);
+
       /* For -Wc++-compat, warn about use of C++ named operators.  */
       if (result->flags & NODE_WARN_OPERATOR)
 	cpp_warning (pfile, CPP_W_CXX_OPERATOR_NAMES,
diff --git a/libcpp/macro.c b/libcpp/macro.c
index de18c22..35ec59a 100644
--- a/libcpp/macro.c
+++ b/libcpp/macro.c
@@ -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 *,
@@ -768,7 +917,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");
@@ -778,7 +928,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,
@@ -1670,6 +1821,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;
@@ -1677,6 +1830,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
@@ -3068,6 +3225,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
@@ -3136,10 +3296,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;
 
diff --git a/libcpp/pch.c b/libcpp/pch.c
index cad4b87..b685a38 100644
--- a/libcpp/pch.c
+++ b/libcpp/pch.c
@@ -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__"));
   }

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFA] Implement __VA_OPT__
  2017-09-17 15:42     ` Tom Tromey
@ 2017-09-17 15:52       ` Tom Tromey
  2017-10-09 15:37         ` Tom Tromey
  2017-11-02 18:02         ` Jason Merrill
  0 siblings, 2 replies; 13+ messages in thread
From: Tom Tromey @ 2017-09-17 15:52 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Alexander Monakov, gcc-patches

And, darn it, I forgot to save cpp.texi, leaving out a couple of tweaks
there.

Here's v3.  Sorry about the noise.

Tom

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index e213db6..362f50e 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,7 @@
+2017-09-16  Tom Tromey  <tom@tromey.com>
+
+	* doc/cpp.texi (Variadic Macros): Document __VA_OPT__.
+
 2017-09-16  Richard Sandiford  <richard.sandiford@linaro.org>
 
 	PR tree-optimization/82228
diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi
index 52f2606..33e72c6 100644
--- a/gcc/doc/cpp.texi
+++ b/gcc/doc/cpp.texi
@@ -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
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 1a94535..2538c4b 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2017-09-16  Tom Tromey  <tom@tromey.com>
+
+	* c-c++-common/cpp/va-opt.c: New file.
+	* c-c++-common/cpp/va-opt-error.c: New file.
+
 2017-09-15  Andrew Sutton  <andrew.n.sutton@gmail.com>
 	    Jakub Jelinek  <jakub@redhat.com>
 
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
index 0000000..7718916
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/va-opt-error.c
@@ -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-error "__VA_OPT__ can only appear" } */
+#define ERR2(x) __VA_OPT__( /* { dg-error "can only appear" } */
+#define ERR3(x) __VA_OPT__() /* { dg-error "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-error "can only appear" } */
+#define ERRC(__VA_OPT__) x /* { dg-error "can only appear" } */
+
+__VA_OPT__ /* { dg-error "can only appear" } */
+
+#define ERRD(x)
+ERRD(__VA_OPT__) /* { dg-error "can only appear" } */
+
+#define __VA_OPT__ /* { dg-error "can only appear" } */
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
index 0000000..243d33b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/va-opt.c
@@ -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);
+}
diff --git a/libcpp/ChangeLog b/libcpp/ChangeLog
index 0621074..65ce80c 100644
--- a/libcpp/ChangeLog
+++ b/libcpp/ChangeLog
@@ -1,3 +1,23 @@
+2017-09-16  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.
+	* 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 (enum c_lang) <CLK_GNUCXX2A, CLK_CXX2A>: New
+	constants.
+	(struct cpp_options) <va_opt>: New field.
+	* identifiers.c (_cpp_init_hashtable): Initialize n__VA_OPT__.
+
 2017-09-15  Andrew Sutton  <andrew.n.sutton@gmail.com>
 	    Jakub Jelinek  <jakub@redhat.com>
 
diff --git a/libcpp/identifiers.c b/libcpp/identifiers.c
index 220f9b9..e456fd3 100644
--- a/libcpp/identifiers.c
+++ b/libcpp/identifiers.c
@@ -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__"));
 }
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 804132a..fc98175 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -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;
 
diff --git a/libcpp/init.c b/libcpp/init.c
index 16ff202..49500b7 100644
--- a/libcpp/init.c
+++ b/libcpp/init.c
@@ -91,28 +91,29 @@ 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 },
-  /* 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 },
-  /* 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,      0 },
+  /* GNUC99   */  { 1,  0,  1,  1,  0,  0,  1,   1,   1,   0,    0,     0,     0,   0,      0 },
+  /* GNUC11   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      0 },
+  /* 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 },
+  /* GNUCXX   */  { 0,  1,  1,  1,  0,  0,  1,   0,   0,   0,    0,     0,     0,   0,      0 },
+  /* 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,      0 },
+  /* 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,      0 },
+  /* 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,      0 },
+  /* 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.  */
@@ -137,6 +138,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.  */
diff --git a/libcpp/internal.h b/libcpp/internal.h
index f24e85c..0a33aba 100644
--- a/libcpp/internal.h
+++ b/libcpp/internal.h
@@ -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 */
 };
diff --git a/libcpp/lex.c b/libcpp/lex.c
index 40ff801..9cfc693 100644
--- a/libcpp/lex.c
+++ b/libcpp/lex.c
@@ -1352,6 +1352,25 @@ 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, cpp_hashnode *node)
+{
+  /* __VA_OPT__ should only appear in the replacement list of a
+     variadic macro.  */
+  if (node == pfile->spec_nodes.n__VA_OPT__ && !pfile->state.va_args_ok)
+    {
+      if (CPP_OPTION (pfile, cplusplus))
+	cpp_error (pfile, CPP_DL_ERROR,
+		   "__VA_OPT__ can only appear in the expansion"
+		   " of a C++11 variadic macro");
+      else
+	cpp_error (pfile, CPP_DL_ERROR,
+		   "__VA_OPT__ can only appear in the expansion"
+		   " of a C99 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 +1415,8 @@ lex_identifier_intern (cpp_reader *pfile, const uchar *base)
 		       " of a C99 variadic macro");
 	}
 
+      maybe_va_opt_error (pfile, result);
+
       /* 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 +1506,10 @@ 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.  */
+      maybe_va_opt_error (pfile, result);
+
       /* For -Wc++-compat, warn about use of C++ named operators.  */
       if (result->flags & NODE_WARN_OPERATOR)
 	cpp_warning (pfile, CPP_W_CXX_OPERATOR_NAMES,
diff --git a/libcpp/macro.c b/libcpp/macro.c
index de18c22..35ec59a 100644
--- a/libcpp/macro.c
+++ b/libcpp/macro.c
@@ -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 *,
@@ -768,7 +917,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");
@@ -778,7 +928,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,
@@ -1670,6 +1821,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;
@@ -1677,6 +1830,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
@@ -3068,6 +3225,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
@@ -3136,10 +3296,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;
 
diff --git a/libcpp/pch.c b/libcpp/pch.c
index cad4b87..b685a38 100644
--- a/libcpp/pch.c
+++ b/libcpp/pch.c
@@ -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__"));
   }

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFA] Implement __VA_OPT__
  2017-09-17 15:52       ` Tom Tromey
@ 2017-10-09 15:37         ` Tom Tromey
  2017-10-19 22:08           ` Tom Tromey
  2017-11-02 18:02         ` Jason Merrill
  1 sibling, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2017-10-09 15:37 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Alexander Monakov, gcc-patches

>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:

[ __VA_OPT__ ]
Tom> Here's v3.

Ping.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFA] Implement __VA_OPT__
  2017-10-09 15:37         ` Tom Tromey
@ 2017-10-19 22:08           ` Tom Tromey
  2017-11-02 16:03             ` Tom Tromey
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2017-10-19 22:08 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gcc-patches

>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:

Tom> [ __VA_OPT__ ]
Tom> Here's v3.

Tom> Ping.

Ping #2.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFA] Implement __VA_OPT__
  2017-10-19 22:08           ` Tom Tromey
@ 2017-11-02 16:03             ` Tom Tromey
  0 siblings, 0 replies; 13+ messages in thread
From: Tom Tromey @ 2017-11-02 16:03 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gcc-patches

Tom> [ __VA_OPT__ ]
Tom> Here's v3.

Tom> Ping.

Tom> Ping #2.

Ping #3.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFA] Implement __VA_OPT__
  2017-09-17 15:52       ` Tom Tromey
  2017-10-09 15:37         ` Tom Tromey
@ 2017-11-02 18:02         ` Jason Merrill
  2017-11-12 12:51           ` Tom Tromey
  1 sibling, 1 reply; 13+ messages in thread
From: Jason Merrill @ 2017-11-02 18:02 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Alexander Monakov, gcc-patches

On 09/17/2017 11:44 AM, Tom Tromey wrote:
> +@code{@w{__VA_OPT__}} is also available in GNU C and GNU C++.

> +{ /*              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,      0 },
> +  /* GNUC99   */  { 1,  0,  1,  1,  0,  0,  1,   1,   1,   0,    0,     0,     0,   0,      0 },
> +  /* GNUC11   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      0 },
[...]

Do we want 1s for vaopt in the GNU rows, then?  It seems to only be used 
for controlling the pedwarn about needing at least one argument for the 
variadic parameter.

> +maybe_va_opt_error (cpp_reader *pfile, cpp_hashnode *node)

Do we also want to look at the va_opt option in this function, to 
complain if pedantic and it isn't set?

Jason

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFA] Implement __VA_OPT__
  2017-11-02 18:02         ` Jason Merrill
@ 2017-11-12 12:51           ` Tom Tromey
  2017-11-13 17:26             ` Jason Merrill
  2017-11-14  8:48             ` [PATCH] Fix __VA_OPT__ testsuite fallout Jakub Jelinek
  0 siblings, 2 replies; 13+ messages in thread
From: Tom Tromey @ 2017-11-12 12:51 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Tom Tromey, Alexander Monakov, gcc-patches

>>>>> "Jason" == Jason Merrill <jason@redhat.com> writes:

Jason> Do we want 1s for vaopt in the GNU rows, then?  It seems to only be
Jason> used for controlling the pedwarn about needing at least one argument
Jason> for the variadic parameter.

It seems reasonable to me, but I wasn't sure.  I've made the change.

>> +maybe_va_opt_error (cpp_reader *pfile, cpp_hashnode *node)

Jason> Do we also want to look at the va_opt option in this function, to
Jason> complain if pedantic and it isn't set?

Thanks for noticing that.  I also made it suppress the error for system
headers.

I'm still too happy with the error message, so if you have any
suggestions there, please let me know.  I removed the "C99" branch from
the earlier error message as well, since this isn't a C feature at all.
Again, please check the wording.

thanks,
Tom

commit f163032782a1b5ed05ff3a3bab5a2e76c203d124
Author: Tom Tromey <tom@tromey.com>
Date:   Thu Sep 14 12:41:51 2017 -0600

    Implement __VA_OPT__
    
    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
    
    I am not completely sure that I have handled the error reporting
    correctly.  I chose to allow __VA_OPT__ generally, on the theory that,
    because it is a reserved identifier, programs should not be referring
    to it anyway.  Also, this approach makes it possible for __VA_OPT__ to
    be used in system headers even when the -std/-pedantic settings may
    otherwise disallow it.
    
    Bootstrapped and regression tested on x86-64 Fedora 25.
    
    gcc/ChangeLog
    2017-09-16  Tom Tromey  <tom@tromey.com>
    
            * doc/cpp.texi (Variadic Macros): Document __VA_OPT__.
    
    gcc/testsuite/ChangeLog
    2017-11-11  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.
    
    libcpp/ChangeLog
    2017-09-16  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__.

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index e213db6e2a0..362f50ecb98 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,7 @@
+2017-09-16  Tom Tromey  <tom@tromey.com>
+
+	* doc/cpp.texi (Variadic Macros): Document __VA_OPT__.
+
 2017-09-16  Richard Sandiford  <richard.sandiford@linaro.org>
 
 	PR tree-optimization/82228
diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi
index 52f2606eadc..33e72c63f59 100644
--- a/gcc/doc/cpp.texi
+++ b/gcc/doc/cpp.texi
@@ -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
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 1a94535b0ee..f40350a3e4e 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2017-11-11  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-09-15  Andrew Sutton  <andrew.n.sutton@gmail.com>
 	    Jakub Jelinek  <jakub@redhat.com>
 
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
index 00000000000..f32f0551723
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/va-opt-error.c
@@ -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
index 00000000000..5887bf5a484
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/va-opt-pedantic.c
@@ -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
index 00000000000..243d33b2cf1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/va-opt.c
@@ -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);
+}
diff --git a/libcpp/ChangeLog b/libcpp/ChangeLog
index 0621074b53b..4dfa5aa77b8 100644
--- a/libcpp/ChangeLog
+++ b/libcpp/ChangeLog
@@ -1,3 +1,22 @@
+2017-09-16  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-09-15  Andrew Sutton  <andrew.n.sutton@gmail.com>
 	    Jakub Jelinek  <jakub@redhat.com>
 
diff --git a/libcpp/identifiers.c b/libcpp/identifiers.c
index 220f9b97f0d..e456fd3a4fc 100644
--- a/libcpp/identifiers.c
+++ b/libcpp/identifiers.c
@@ -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__"));
 }
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 804132a44da..fc98175fb08 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -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;
 
diff --git a/libcpp/init.c b/libcpp/init.c
index 16ff202c8cf..c3b4641d49d 100644
--- a/libcpp/init.c
+++ b/libcpp/init.c
@@ -91,28 +91,29 @@ 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 },
-  /* 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 },
-  /* 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 },
+  /* 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 },
+  /* 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.  */
@@ -137,6 +138,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.  */
diff --git a/libcpp/internal.h b/libcpp/internal.h
index f24e85cfb11..0a33abafd43 100644
--- a/libcpp/internal.h
+++ b/libcpp/internal.h
@@ -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 */
 };
diff --git a/libcpp/lex.c b/libcpp/lex.c
index 40ff801e8e3..38a9f3c6614 100644
--- a/libcpp/lex.c
+++ b/libcpp/lex.c
@@ -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,
diff --git a/libcpp/macro.c b/libcpp/macro.c
index de18c2210cf..35ec59a7c87 100644
--- a/libcpp/macro.c
+++ b/libcpp/macro.c
@@ -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 *,
@@ -768,7 +917,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");
@@ -778,7 +928,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,
@@ -1670,6 +1821,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;
@@ -1677,6 +1830,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
@@ -3068,6 +3225,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
@@ -3136,10 +3296,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;
 
diff --git a/libcpp/pch.c b/libcpp/pch.c
index cad4b872cda..b685a38a854 100644
--- a/libcpp/pch.c
+++ b/libcpp/pch.c
@@ -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__"));
   }

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFA] Implement __VA_OPT__
  2017-11-12 12:51           ` Tom Tromey
@ 2017-11-13 17:26             ` Jason Merrill
  2017-11-14  8:48             ` [PATCH] Fix __VA_OPT__ testsuite fallout Jakub Jelinek
  1 sibling, 0 replies; 13+ messages in thread
From: Jason Merrill @ 2017-11-13 17:26 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Alexander Monakov, gcc-patches List

On Sun, Nov 12, 2017 at 2:33 AM, Tom Tromey <tom@tromey.com> wrote:
> I'm still [not] too happy with the error message, so if you have any
> suggestions there, please let me know.  I removed the "C99" branch from
> the earlier error message as well, since this isn't a C feature at all.
> Again, please check the wording.

I'm ambivalent about mentioning the standard level.  And I find the
use of "expansion" in this and the existing __VA_ARGS__ error odd, as
expansion is what you *do* with a macro; I'd prefer "definition".

But I suppose we might as well be consistent with the existing error.

The patch is OK, thanks.

Jason

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH] Fix __VA_OPT__ testsuite fallout
  2017-11-12 12:51           ` Tom Tromey
  2017-11-13 17:26             ` Jason Merrill
@ 2017-11-14  8:48             ` Jakub Jelinek
  2017-11-17  0:37               ` Jeff Law
  1 sibling, 1 reply; 13+ messages in thread
From: Jakub Jelinek @ 2017-11-14  8:48 UTC (permalink / raw)
  To: Jason Merrill, Tom Tromey; +Cc: gcc-patches

Hi!

On Sun, Nov 12, 2017 at 12:33:03AM -0700, Tom Tromey wrote:
>    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");
> @@ -778,7 +928,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,

This change broke the macsyntx.c and sysmac1.c tests, which were expecting
the "requires at least one" pedwarn, but were using -std=gnu99 which now
enables the __VA_OPT__ support and thus allows as many arguments in the
invocation as there are parameters in the macro definition (excluding the
...).

The following patch turns those into dg-bogus and adds copies of the test
that use -std=c99 that doesn't include __VA_OPT__ support and check for the
pedwarns there.

Tested on x86_64-linux, ok for trunk?

2017-11-14  Jakub Jelinek  <jakub@redhat.com>

	* gcc.dg/cpp/macsyntx.c (var1, rest): Don't expect
	"requires at least one" warning.
	* gcc.dg/cpp/sysmac1.c (foo): Likewise.
	* gcc.dg/cpp/macsyntx2.c: New test.
	* gcc.dg/cpp/sysmac3.c: New test.
	* gcc.dg/cpp/sysmac3.h: New file.

--- gcc/testsuite/gcc.dg/cpp/macsyntx.c.jj	2014-07-14 09:30:29.000000000 +0200
+++ gcc/testsuite/gcc.dg/cpp/macsyntx.c	2017-11-14 09:21:16.794091580 +0100
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000 Free Software Foundation, Inc.  */
+/* Copyright (C) 2000-2017 Free Software Foundation, Inc.  */
 
 /* { dg-do preprocess } */
 /* { dg-options "-pedantic -std=gnu99" } */
@@ -51,15 +51,15 @@ one(ichi\
 two(ichi)			/* { dg-error "requires 2" } */
 var0()				/* OK.  */
 var0(ichi)			/* OK.  */
-var1()				/* { dg-warning "requires at least one" } */
-var1(ichi)			/* { dg-warning "requires at least one" } */
+var1()				/* { dg-bogus "requires at least one" } */
+var1(ichi)			/* { dg-bogus "requires at least one" } */
 var1(ichi, ni)			/* OK.  */
 
 /* This tests two oddities of GNU rest args - omitting a comma is OK,
    and backtracking a token on pasting an empty rest args.  */
 #define rest(x, y...) x ## y	/* { dg-warning "ISO C" } */
 rest(ichi,)			/* OK.  */
-rest(ichi)			/* { dg-warning "requires at least one" } */
+rest(ichi)			/* { dg-bogus "requires at least one" } */
 #if 23 != rest(2, 3)		/* OK, no warning.  */
 #error 23 != 23 !!
 #endif
--- gcc/testsuite/gcc.dg/cpp/sysmac1.c.jj	2014-07-14 09:30:29.000000000 +0200
+++ gcc/testsuite/gcc.dg/cpp/sysmac1.c	2017-11-14 09:19:07.581694406 +0100
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001 Free Software Foundation, Inc.  */
+/* Copyright (C) 2001-2017 Free Software Foundation, Inc.  */
 
 /* { dg-do preprocess } */
 /* { dg-options "-std=gnu99 -pedantic -Wtraditional -ftrack-macro-expansion=0" } */
@@ -22,5 +22,5 @@
 (str);				/* { dg-warning "used with arguments" } */
 (sys_str);			/* { dg-bogus "used with arguments" } */
 
-foo (one_arg);			/* { dg-warning "requires at least one" } */
+foo (one_arg);			/* { dg-bogus "requires at least one" } */
 sys_foo (one_arg);		/* { dg-bogus "requires at least one" } */
--- gcc/testsuite/gcc.dg/cpp/macsyntx2.c.jj	2017-11-14 09:18:31.248145232 +0100
+++ gcc/testsuite/gcc.dg/cpp/macsyntx2.c	2017-11-14 09:21:07.675204306 +0100
@@ -0,0 +1,72 @@
+/* Copyright (C) 2000-2017 Free Software Foundation, Inc.  */
+
+/* { dg-do preprocess } */
+/* { dg-options "-pedantic -std=c99" } */
+
+/* Tests macro syntax, for both definition and invocation, including:-
+
+   o Full range of macro definition semantics.
+   o No. of arguments supplied to function-like macros.
+   o Odd GNU rest args behavior.
+   o Macro arguments do not flow into the rest of the file.  */
+
+
+/* Test basic macro definition syntax.  The macros are all called
+   "foo" deliberately to provoke an (excess) redefinition warning in
+   case the macros succeed in being entered in the macro hash table
+   despite being an error.
+
+   Split a couple of the lines to check that the errors appear on the
+   right line (i.e. are associated with the correct token).  */
+
+#define ;			/* { dg-error "identifier" } */
+#define SEMI;			/* { dg-warning "space" } */
+#define foo(X			/* { dg-error "missing" } */
+#define foo\
+(X,)				/* { dg-error "parameter name" } */
+#define foo(, X)		/* { dg-error "parameter name" } */
+#define foo(X, X)		/* { dg-error "duplicate" } */
+#define foo(X Y)		/* { dg-error "comma" } */
+#define foo(()			/* { dg-error "may not appear" } */
+#define foo(..., X)		/* { dg-error "missing" } */
+#define foo \
+__VA_ARGS__			/* { dg-warning "__VA_ARGS__" } */
+#define goo(__VA_ARGS__)	/* { dg-warning "__VA_ARGS__" } */
+#define hoo(...) __VA_ARGS__	/* OK.  */
+#define __VA_ARGS__		/* { dg-warning "__VA_ARGS__" } */
+__VA_ARGS__			/* { dg-warning "__VA_ARGS__" } */
+
+/* test # of supplied arguments.  */
+#define none()
+#define one(x)
+#define two(x, y)
+#define var0(...)
+#define var1(x, ...)
+none()				/* OK.  */
+none(ichi)			/* { dg-error "passed 1" } */
+one()				/* OK.  */
+one(ichi)			/* OK.  */
+one(ichi\
+, ni)				/* { dg-error "passed 2" } */
+two(ichi)			/* { dg-error "requires 2" } */
+var0()				/* OK.  */
+var0(ichi)			/* OK.  */
+var1()				/* { dg-warning "requires at least one" } */
+var1(ichi)			/* { dg-warning "requires at least one" } */
+var1(ichi, ni)			/* OK.  */
+
+/* This tests two oddities of GNU rest args - omitting a comma is OK,
+   and backtracking a token on pasting an empty rest args.  */
+#define rest(x, y...) x ## y	/* { dg-warning "ISO C" } */
+rest(ichi,)			/* OK.  */
+rest(ichi)			/* { dg-warning "requires at least one" } */
+#if 23 != rest(2, 3)		/* OK, no warning.  */
+#error 23 != 23 !!
+#endif
+
+/* Test that we don't allow arguments to flow into the rest of the
+   file.  */
+#define half_invocation do_nowt(2
+#define do_nowt(x) x
+half_invocation )		/* OK.  */
+do_nowt (half_invocation))	/* { dg-error "unterminated argument" } */
--- gcc/testsuite/gcc.dg/cpp/sysmac3.c.jj	2017-11-14 09:14:20.945250987 +0100
+++ gcc/testsuite/gcc.dg/cpp/sysmac3.c	2017-11-14 09:17:34.787845791 +0100
@@ -0,0 +1,26 @@
+/* Copyright (C) 2001-2017 Free Software Foundation, Inc.  */
+
+/* { dg-do preprocess } */
+/* { dg-options "-std=c99 -pedantic -Wtraditional -ftrack-macro-expansion=0" } */
+
+/* Tests diagnostics are suppressed for some macros defined in system
+   headers.  */
+
+/* Source: Neil Booth, 15 Jan 2001.  */
+
+#include "sysmac3.h"
+
+#define uint 1U
+#define str(x) x
+#define foo(x, y...) bar(x, y)	/* { dg-warning "named variadic macros" } */
+
+#if uint			/* { dg-warning "traditional C rejects" } */
+#endif
+#if sys_uint			/* { dg-bogus "traditional C rejects" } */
+#endif
+
+(str);				/* { dg-warning "used with arguments" } */
+(sys_str);			/* { dg-bogus "used with arguments" } */
+
+foo (one_arg);			/* { dg-warning "requires at least one" } */
+sys_foo (one_arg);		/* { dg-bogus "requires at least one" } */
--- gcc/testsuite/gcc.dg/cpp/sysmac3.h.jj	2017-11-14 09:16:52.313372813 +0100
+++ gcc/testsuite/gcc.dg/cpp/sysmac3.h	2017-11-14 09:17:01.947253276 +0100
@@ -0,0 +1,10 @@
+/* Indented to avoid "suggest hiding ..." warnings.   */
+ #pragma GCC system_header
+
+#define sys_uint 1U
+#define sys_str(x) x
+#define sys_foo(x, y...) bar (x, y)
+
+#define sys_uint 1U
+#define sys_fl 1.0f
+#define sys_ld 1.0L


	Jakub

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Fix __VA_OPT__ testsuite fallout
  2017-11-14  8:48             ` [PATCH] Fix __VA_OPT__ testsuite fallout Jakub Jelinek
@ 2017-11-17  0:37               ` Jeff Law
  0 siblings, 0 replies; 13+ messages in thread
From: Jeff Law @ 2017-11-17  0:37 UTC (permalink / raw)
  To: Jakub Jelinek, Jason Merrill, Tom Tromey; +Cc: gcc-patches

On 11/14/2017 01:30 AM, Jakub Jelinek wrote:
> Hi!
> 
> On Sun, Nov 12, 2017 at 12:33:03AM -0700, Tom Tromey wrote:
>>    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");
>> @@ -778,7 +928,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,
> 
> This change broke the macsyntx.c and sysmac1.c tests, which were expecting
> the "requires at least one" pedwarn, but were using -std=gnu99 which now
> enables the __VA_OPT__ support and thus allows as many arguments in the
> invocation as there are parameters in the macro definition (excluding the
> ...).
> 
> The following patch turns those into dg-bogus and adds copies of the test
> that use -std=c99 that doesn't include __VA_OPT__ support and check for the
> pedwarns there.
> 
> Tested on x86_64-linux, ok for trunk?
> 
> 2017-11-14  Jakub Jelinek  <jakub@redhat.com>
> 
> 	* gcc.dg/cpp/macsyntx.c (var1, rest): Don't expect
> 	"requires at least one" warning.
> 	* gcc.dg/cpp/sysmac1.c (foo): Likewise.
> 	* gcc.dg/cpp/macsyntx2.c: New test.
> 	* gcc.dg/cpp/sysmac3.c: New test.
> 	* gcc.dg/cpp/sysmac3.h: New file.
OK.
jeff

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2017-11-17  0:18 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-09-16 21:49 [RFA] Implement __VA_OPT__ Tom Tromey
2017-09-17  9:43 ` Alexander Monakov
2017-09-17 15:13   ` Tom Tromey
2017-09-17 15:42     ` Tom Tromey
2017-09-17 15:52       ` Tom Tromey
2017-10-09 15:37         ` Tom Tromey
2017-10-19 22:08           ` Tom Tromey
2017-11-02 16:03             ` Tom Tromey
2017-11-02 18:02         ` Jason Merrill
2017-11-12 12:51           ` Tom Tromey
2017-11-13 17:26             ` Jason Merrill
2017-11-14  8:48             ` [PATCH] Fix __VA_OPT__ testsuite fallout Jakub Jelinek
2017-11-17  0:37               ` Jeff Law

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).