---
gcc/testsuite/c-c++-common/cpp/va_count.c | 35 ++
libcpp/identifiers.cc | 2 +
libcpp/include/cpplib.h | 3 +
libcpp/init.cc | 58 ++--
libcpp/internal.h | 3 +-
libcpp/lex.cc | 24 ++
libcpp/macro.cc | 368 +++++++++++++++++++++-
libcpp/pch.cc | 1 +
8 files changed, 461 insertions(+), 33 deletions(-)
create mode 100644 gcc/testsuite/c-c++-common/cpp/va_count.c
diff --git a/gcc/testsuite/c-c++-common/cpp/va_count.c
b/gcc/testsuite/c-c++-common/cpp/va_count.c
new file mode 100644
index 00000000000..95579eb2310
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/va_count.c
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c2y" { target c } } */
+
+#define SA(expr, val) _Static_assert((expr) == (val), #expr " != " #val)
+
+static const int x = __VA_COUNT__(1, 2, 3); // int x = 3;
+static const int y = __VA_COUNT__(); // int y = 0;
+#define MACRO(...) __VA_COUNT__(__VA_ARGS__)
+static const int z = MACRO(a, b, c, d); // int z = 4;
+static const int w = MACRO(); // int w = 0;
+static const int a = MACRO(a, , c, d); // int a = 4;
+static const int b = MACRO(a, (, c, )d); // int b = 2;
+#define MACRO2(...) __VA_COUNT__(__VA_COUNT__( __VA_ARGS__ ))
+static const int c = MACRO2(a, b); // int c = 1;
+static const int d = MACRO2(); // int d = 1;
+
+SA(x, 3);
+SA(y, 0);
+SA(z, 4);
+SA(w, 0);
+SA(a, 4);
+SA(b, 2);
+SA(c, 1);
+SA(d, 1);
+
+#if __VA_COUNT__(1,2,3) != 3
+# error bad __VA_COUNT__(1,2,3)
+#endif
+
+
+int
+main ()
+{
+ return x + y + z + w + a + b + c + d; // 3 + 0 + 4 + 0 + 4 + 2 + 1 + 1 = 15
+}
diff --git a/libcpp/identifiers.cc b/libcpp/identifiers.cc
index 0b56c276483..f79c8f34bec 100644
--- a/libcpp/identifiers.cc
+++ b/libcpp/identifiers.cc
@@ -80,6 +80,8 @@ _cpp_init_hashtable (cpp_reader *pfile, cpp_hash_table *table,
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__VA_COUNT__ = cpp_lookup (pfile, DSC("__VA_COUNT__"));
+ s->n__VA_COUNT__->flags |= NODE_DIAGNOSTIC;
/* __has_include{,_next} are inited in cpp_init_builtins. */
}
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index bc0d7714e96..e3d004c75ca 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -545,6 +545,9 @@ struct cpp_options
/* Nonzero for C++20 __VA_OPT__ feature. */
unsigned char va_opt;
+ /* Nonzero for C2y N3792 feature __VA_COUNT__. */
+ unsigned char va_count;
+
/* Nonzero for the '::' token. */
unsigned char scope;
diff --git a/libcpp/init.cc b/libcpp/init.cc
index a480d1d4a26..bcaced7462e 100644
--- a/libcpp/init.cc
+++ b/libcpp/init.cc
@@ -114,6 +114,7 @@ struct lang_flags
unsigned int embed : 1;
unsigned int imaginary_constants : 1;
unsigned int low_ucns : 1;
+ unsigned int va_count : 1;
};
static const struct lang_flags lang_defaults[] = {
@@ -124,34 +125,34 @@ static const struct lang_flags lang_defaults[] = {
c c n x c d s i l l l c s r l o o d l d d l d t f b m u
9 + u i 1 i t g i i i s e i i p p f i e i i u a a e a c
9 + m d 1 d d r t t t t p g t t e p t f r m c l l d g n */
- /* GNUC89 */ { 0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 },
- /* GNUC99 */ { 1,0,1,1,0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 },
- /* GNUC11 */ { 1,0,1,1,1,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 },
- /* GNUC17 */ { 1,0,1,1,1,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 },
- /* GNUC23 */ { 1,0,1,1,1,1,0,1,1,1,0,1,1,0,1,1,1,1,0,1,1,0,0,0,1,1,0,1 },
- /* GNUC2Y */ { 1,0,1,1,1,1,0,1,1,1,0,1,1,0,1,1,1,1,0,1,1,1,0,1,1,1,1,1 },
- /* STDC89 */ { 0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- /* STDC94 */ { 0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- /* STDC99 */ { 1,0,1,1,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- /* STDC11 */ { 1,0,1,1,1,0,1,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- /* STDC17 */ { 1,0,1,1,1,0,1,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
- /* STDC23 */ { 1,0,1,1,1,1,1,1,1,0,0,1,1,0,1,1,1,1,0,1,1,0,0,0,1,1,0,1 },
- /* STDC2Y */ { 1,0,1,1,1,1,1,1,1,0,0,1,1,0,1,1,1,1,0,1,1,1,0,1,1,1,1,1 },
- /* GNUCXX */ { 0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1 },
- /* CXX98 */ { 0,1,0,1,0,1,1,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,1 },
- /* GNUCXX11 */ { 1,1,1,1,1,1,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1 },
- /* CXX11 */ { 1,1,0,1,1,1,1,1,1,1,1,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,1 },
- /* GNUCXX14 */ { 1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1 },
- /* CXX14 */ { 1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,0,0,1,0,0,1 },
- /* GNUCXX17 */ { 1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,1,0,0,1 },
- /* CXX17 */ { 1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,0,0,0,0,0,0,1,0,0,1 },
- /* GNUCXX20 */ { 1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,1,0,0,1 },
- /* CXX20 */ { 1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,1,0,0,1 },
- /* GNUCXX23 */ { 1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,0,0,1 },
- /* CXX23 */ { 1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,0,0,1 },
- /* GNUCXX26 */ { 1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,1,0,1 },
- /* CXX26 */ { 1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,1,0,1 },
- /* ASM */ { 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
+ /* GNUC89 */ { 0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 },
+ /* GNUC99 */ { 1,0,1,1,0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 },
+ /* GNUC11 */ { 1,0,1,1,1,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 },
+ /* GNUC17 */ { 1,0,1,1,1,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 },
+ /* GNUC23 */ { 1,0,1,1,1,1,0,1,1,1,0,1,1,0,1,1,1,1,0,1,1,0,0,0,1,1,0,1,0 },
+ /* GNUC2Y */ { 1,0,1,1,1,1,0,1,1,1,0,1,1,0,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1 },
+ /* STDC89 */ { 0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ /* STDC94 */ { 0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ /* STDC99 */ { 1,0,1,1,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ /* STDC11 */ { 1,0,1,1,1,0,1,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ /* STDC17 */ { 1,0,1,1,1,0,1,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ /* STDC23 */ { 1,0,1,1,1,1,1,1,1,0,0,1,1,0,1,1,1,1,0,1,1,0,0,0,1,1,0,1,0 },
+ /* STDC2Y */ { 1,0,1,1,1,1,1,1,1,0,0,1,1,0,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1 },
+ /* GNUCXX */ { 0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,0 },
+ /* CXX98 */ { 0,1,0,1,0,1,1,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0 },
+ /* GNUCXX11 */ { 1,1,1,1,1,1,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,0 },
+ /* CXX11 */ { 1,1,0,1,1,1,1,1,1,1,1,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0 },
+ /* GNUCXX14 */ { 1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,0 },
+ /* CXX14 */ { 1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0 },
+ /* GNUCXX17 */ { 1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,1,0,0,1,0 },
+ /* CXX17 */ { 1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,0,0,0,0,0,0,1,0,0,1,0 },
+ /* GNUCXX20 */ { 1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,1,0,0,1,0 },
+ /* CXX20 */ { 1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,1,0,0,1,0 },
+ /* GNUCXX23 */ { 1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,0,0,1,0 },
+ /* CXX23 */ { 1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,0,0,1,0 },
+ /* GNUCXX26 */ { 1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,1,0,1,0 },
+ /* CXX26 */ { 1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,1,0,1,0 },
+ /* ASM */ { 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
};
/* Sets internal flags correctly for a given language. */
@@ -190,6 +191,7 @@ cpp_set_lang (cpp_reader *pfile, enum c_lang lang)
CPP_OPTION (pfile, embed) = l->embed;
CPP_OPTION (pfile, imaginary_constants) = l->imaginary_constants;
CPP_OPTION (pfile, low_ucns) = l->low_ucns;
+ CPP_OPTION (pfile, va_count) = l->va_count;
}
/* Initialize library global state. */
diff --git a/libcpp/internal.h b/libcpp/internal.h
index 017e980a964..7f5d08fc379 100644
--- a/libcpp/internal.h
+++ b/libcpp/internal.h
@@ -263,7 +263,7 @@ struct lexer_state
all directives apart from #define. */
unsigned char save_comments;
- /* Nonzero if lexing __VA_ARGS__ and __VA_OPT__ are valid. */
+ /* Nonzero if lexing __VA_ARGS__, __VA_COUNT__ and __VA_OPT__ are valid. */
unsigned char va_args_ok;
/* Nonzero if lexing poisoned identifiers is valid. */
@@ -306,6 +306,7 @@ struct spec_nodes
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__VA_COUNT__; /* C2y N3792 vararg macros */
enum {M_EXPORT, M_MODULE, M_IMPORT, M__IMPORT, M_HWM};
diff --git a/libcpp/lex.cc b/libcpp/lex.cc
index df278534eee..e0a7152de42 100644
--- a/libcpp/lex.cc
+++ b/libcpp/lex.cc
@@ -2146,6 +2146,26 @@ forms_identifier_p (cpp_reader *pfile, int first,
return false;
}
+/* Helper function to issue error about improper __VA_COUNT__ use. */
+static void
+maybe_va_count_error (cpp_reader *pfile)
+{
+ if (CPP_PEDANTIC (pfile) && !CPP_OPTION (pfile, va_count))
+ {
+ /* __VA_COUNT__ should not be accepted at all, but allow it in
+ system headers. */
+ if (!_cpp_in_system_header (pfile))
+ {
+ if (CPP_OPTION (pfile, cplusplus))
+ cpp_pedwarning (pfile, CPP_W_PEDANTIC,
+ "%<__VA_COUNT__%> is not standardised or proposed
for C++");
+ else
+ cpp_pedwarning (pfile, CPP_W_PEDANTIC,
+ "%<__VA_COUNT__%> is not available until C2y");
+ }
+ }
+}
+
/* Helper function to issue error about improper __VA_OPT__ use. */
static void
maybe_va_opt_error (cpp_reader *pfile)
@@ -2214,6 +2234,10 @@ identifier_diagnostics_on_lex (cpp_reader *pfile,
cpp_hashnode *node)
if (node == pfile->spec_nodes.n__VA_OPT__)
maybe_va_opt_error (pfile);
+ /* __VA_COUNT__ checks */
+ if (node == pfile->spec_nodes.n__VA_COUNT__)
+ maybe_va_count_error (pfile);
+
/* For -Wc++-compat, warn about use of C++ named operators. */
if (node->flags & NODE_WARN_OPERATOR)
cpp_warning (pfile, CPP_W_CXX_OPERATOR_NAMES,
diff --git a/libcpp/macro.cc b/libcpp/macro.cc
index c29a34302b6..c7bafb60de0 100644
--- a/libcpp/macro.cc
+++ b/libcpp/macro.cc
@@ -93,6 +93,9 @@ struct macro_arg_saved_data {
static const char *vaopt_paste_error =
N_("'##' cannot appear at either end of __VA_OPT__");
+static const char *vacount_paren_error =
+ N_("%<__VA_COUNT__%> must be followed by an open parenthesis");
+
static void expand_arg (cpp_reader *, macro_arg *);
/* A class for tracking __VA_OPT__ state while iterating over a
@@ -276,6 +279,159 @@ class vaopt_state {
update_type m_update;
};
+/* A class for tracking __VA_COUNT__ state while iterating over a
+ sequence of tokens. This is used during macro definition (to
+ validate structure) and during expansion (to count arguments and
+ replace the invocation). */
+class vacount_state {
+ public:
+ enum update_type
+ {
+ ERROR,
+ DROP,
+ INCLUDE,
+ BEGIN,
+ END
+ };
+
+ vacount_state (cpp_reader *pfile)
+ : m_pfile (pfile),
+ m_stringify (false),
+ m_state (0),
+ m_paren_depth (0),
+ m_count (0),
+ m_started (false),
+ m_paste_left (false),
+ m_location (0),
+ m_flags (0)
+ {
+ }
+
+ /* Given a token, update the state of this tracker and return an
+ update_type indicating whether the token should be included in
+ the output stream. For __VA_COUNT__, tokens of the invocation
+ are always dropped, and END indicates that a replacement token
+ should be emitted. */
+ update_type update (const cpp_token *token)
+ {
+ /* Start recognizing a __VA_COUNT__ invocation only when we're not
+ already inside one. Nested __VA_COUNT__ tokens inside the operand
+ list are just ordinary tokens and must not reset state or error. */
+ if (m_state == 0
+ && token->type == CPP_NAME
+ && token->val.node.node == m_pfile->spec_nodes.n__VA_COUNT__) {
+ m_state = 1;
+ m_location = token->src_loc;
+ m_stringify = (token->flags & STRINGIFY_ARG) != 0;
+ m_flags = token->flags & (PREV_WHITE | PREV_FALLTHROUGH);
+ m_paren_depth = 0;
+ m_count = 0;
+ m_started = false;
+ m_paste_left = false;
+ return BEGIN;
+ }
+ else if (m_state == 1)
+ {
+ /* Require a following '(' (definition-time constraint). */
+ if (token->type != CPP_OPEN_PAREN)
+ {
+ cpp_error_at (m_pfile, CPP_DL_ERROR, m_location,
+ vacount_paren_error);
+ m_state = 0;
+ return ERROR;
+ }
+ m_state = 2;
+ /* Drop the '(' from output: the whole invocation is replaced. */
+ return DROP;
+ }
+ else if (m_state >= 2)
+ {
+ /* Drop padding/comments inside the invocation, they should not
+ affect argument splitting. */
+ if (token->type == CPP_PADDING
+ || token->type == CPP_COMMENT)
+ return DROP;
+
+ /* First significant token after '(' determines empty invocation. */
+ if (!m_started)
+ {
+ if (token->type == CPP_CLOSE_PAREN)
+ {
+ /* __VA_COUNT__() => 0. */
+ m_paste_left = (token->flags & PASTE_LEFT) != 0;
+ m_state = 0;
+ m_count = 0;
+ return END;
+ }
+ /* Non-empty invocation => at least one argument (possibly empty).
*/
+ m_started = true;
+ m_count = 1;
+ }
+
+ if (token->type == CPP_OPEN_PAREN)
+ {
+ ++m_paren_depth;
+ return DROP;
+ }
+ else if (token->type == CPP_CLOSE_PAREN)
+ {
+ if (m_paren_depth-- == 0)
+ {
+ /* Final ')'. */
+ m_paste_left = (token->flags & PASTE_LEFT) != 0;
+ m_state = 0;
+ return END;
+ }
+ return DROP;
+ }
+ else if (token->type == CPP_COMMA)
+ {
+ if (m_paren_depth == 0)
+ ++m_count;
+ return DROP;
+ }
+ else if (token->type == CPP_EOF
+ || (token->type == CPP_HASH && (token->flags & BOL)))
+ {
+ cpp_error_at (m_pfile, CPP_DL_ERROR, m_location,
+ "unterminated %<__VA_COUNT__%>");
+ m_state = 0;
+ m_count = 0;
+ return ERROR;
+ }
+
+ return DROP;
+ }
+
+ return INCLUDE;
+ }
+
+ bool completed ()
+ {
+ if (m_state != 0)
+ cpp_error_at (m_pfile, CPP_DL_ERROR, m_location,
+ "unterminated %<__VA_COUNT__%>");
+ return m_state == 0;
+ }
+
+ bool stringify () const { return m_stringify; }
+ unsigned long count () const { return m_count; }
+ bool paste_left () const { return m_paste_left; }
+ location_t location () const { return m_location; }
+ unsigned flags () const { return m_flags; }
+
+ private:
+ cpp_reader *m_pfile;
+ bool m_stringify;
+ int m_state;
+ unsigned m_paren_depth;
+ unsigned long m_count;
+ bool m_started;
+ bool m_paste_left;
+ location_t m_location;
+ unsigned m_flags;
+};
+
/* Macro expansion. */
static cpp_macro *get_deferred_or_lazy_macro (cpp_reader *, cpp_hashnode *,
@@ -312,7 +468,8 @@ static const cpp_token **arg_token_ptr_at (const macro_arg
*,
size_t,
enum macro_arg_token_kind,
location_t **virt_location);
-
+static int maybe_expand_va_count (cpp_reader *, const cpp_token *,
+ location_t);
static void macro_arg_token_iter_init (macro_arg_token_iter *, bool,
enum macro_arg_token_kind,
const macro_arg *,
@@ -895,6 +1052,180 @@ builtin_macro (cpp_reader *pfile, cpp_hashnode *node,
return 1;
}
+/* Try to expand __VA_COUNT__(...) at the current point in the token stream.
+ NAME is the __VA_COUNT__ token we just read. NAME_VIRT_LOC is the
+ virtual location associated to NAME (as returned by cpp_get_token_1).
+
+ On success, pushes a context containing a single token (either a
+ preprocessing-number token or, for #__VA_COUNT__, a string token)
+ and returns 1. If there is no following '(', returns 0 and leaves
+ the token stream unchanged (as per function-like macro rules). */
+static int
+maybe_expand_va_count (cpp_reader *pfile, const cpp_token *name,
+ location_t name_virt_loc)
+{
+ const cpp_token *token, *padding = NULL;
+ location_t virt_loc = 0;
+ unsigned long m = 0;
+ bool stringify = (name->flags & STRINGIFY_ARG) != 0;
+ bool paste_left = false;
+ unsigned out_flags = name->flags & (PREV_WHITE | PREV_FALLTHROUGH);
+
+ /* Parse like funlike_invocation_p/collect_args: inhibit expansion while
+ recognizing and consuming the parenthesized argument list. */
+ int saved_prevent_expansion = pfile->state.prevent_expansion;
+ int saved_keep_tokens = pfile->keep_tokens;
+ int saved_parsing_args = pfile->state.parsing_args;
+
+ pfile->state.prevent_expansion++;
+ pfile->keep_tokens++;
+ pfile->state.parsing_args = 1;
+
+ /* Search for the opening '(' while preserving intervening padding. */
+ for (;;)
+ {
+ token = cpp_get_token_1 (pfile, &virt_loc);
+ if (token->type != CPP_PADDING)
+ break;
+ gcc_assert ((token->flags & PREV_WHITE) == 0);
+ if (padding == NULL
+ || padding->val.source == NULL
+ || (!(padding->val.source->flags & PREV_WHITE)
+ && token->val.source == NULL))
+ padding = token;
+ }
+
+ if (token->type != CPP_OPEN_PAREN)
+ {
+ /* Not an invocation: back up and re-insert padding if needed. */
+ if (token->type != CPP_EOF || token == &pfile->endarg)
+ {
+ _cpp_backup_tokens (pfile, 1);
+ if (padding)
+ _cpp_push_token_context (pfile, NULL, padding, 1);
+ }
+ pfile->state.parsing_args = saved_parsing_args;
+ pfile->keep_tokens = saved_keep_tokens;
+ pfile->state.prevent_expansion = saved_prevent_expansion;
+ return 0;
+ }
+
+ /* We are in an invocation. */
+ pfile->state.parsing_args = 2;
+
+ /* Use the same counting rule as function-like macro arg parsing:
+ m = 0 for empty list, else 1 + #top-level commas. */
+ {
+ unsigned paren_depth = 0;
+ bool started = false;
+
+ for (;;)
+ {
+ token = cpp_get_token_1 (pfile, &virt_loc);
+
+ if (token->type == CPP_PADDING || token->type == CPP_COMMENT)
+ continue;
+
+ if (!started)
+ {
+ if (token->type == CPP_CLOSE_PAREN)
+ {
+ m = 0;
+ paste_left = (token->flags & PASTE_LEFT) != 0;
+ break;
+ }
+ started = true;
+ m = 1;
+ }
+
+ if (token->type == CPP_OPEN_PAREN)
+ ++paren_depth;
+ else if (token->type == CPP_CLOSE_PAREN)
+ {
+ if (paren_depth-- == 0)
+ {
+ paste_left = (token->flags & PASTE_LEFT) != 0;
+ break;
+ }
+ }
+ else if (token->type == CPP_COMMA)
+ {
+ if (paren_depth == 0)
+ ++m;
+ }
+ else if (token->type == CPP_EOF
+ || (token->type == CPP_HASH && (token->flags & BOL)))
+ {
+ cpp_error (pfile, CPP_DL_ERROR,
+ "unterminated argument list invoking macro %qs",
+ "__VA_COUNT__");
+ m = 0;
+ break;
+ }
+ }
+ }
+
+ pfile->state.parsing_args = saved_parsing_args;
+ pfile->keep_tokens = saved_keep_tokens;
+ pfile->state.prevent_expansion = saved_prevent_expansion;
+
+ if (m > (unsigned long) LONG_MAX)
+ {
+ cpp_error_with_line (pfile, CPP_DL_ERROR, name->src_loc, 0,
+ "%<__VA_COUNT__%> result %lu is not representable "
+ "as %<signed long%>", m);
+ m = 0;
+ }
+
+ /* Materialize the replacement token. */
+ cpp_token *numtok = _cpp_temp_token (pfile);
+ {
+ size_t buflen = 3 * sizeof (long) + 2;
+ uchar *buf = _cpp_unaligned_alloc (pfile, buflen);
+ sprintf ((char *) buf, "%ld", (long) m);
+ numtok->type = CPP_NUMBER;
+ numtok->val.str.text = buf;
+ numtok->val.str.len = strlen ((const char *) buf);
+ numtok->src_loc = name->src_loc;
+ numtok->flags = out_flags | (paste_left ? PASTE_LEFT : 0);
+ }
+
+ const cpp_token *out = numtok;
+ if (stringify)
+ {
+ const cpp_token *arr[1] = { out };
+ out = stringify_arg (pfile, arr, 1);
+ /* Preserve spacing/paste semantics on the string token too. */
+ cpp_token *t = _cpp_temp_token (pfile);
+ *t = *out;
+ t->flags |= out_flags;
+ if (paste_left)
+ t->flags |= PASTE_LEFT;
+ t->src_loc = name->src_loc;
+ out = t;
+ }
+
+ /* Push the token in its own context, like builtin_macro does. */
+ if (pfile->context->tokens_kind == TOKENS_KIND_EXTENDED)
+ {
+ location_t *virt_locs = NULL;
+ _cpp_buff *token_buf = tokens_buff_new (pfile, 1, &virt_locs);
+ const line_map_macro *map =
+ linemap_enter_macro (pfile->line_table,
+ pfile->spec_nodes.n__VA_COUNT__,
+ name_virt_loc, 1);
+ tokens_buff_add_token (token_buf, virt_locs, out,
+ name_virt_loc, name->src_loc, map, 0);
+ push_extended_tokens_context (pfile, pfile->spec_nodes.n__VA_COUNT__,
+ token_buf, virt_locs,
+ (const cpp_token **) token_buf->base, 1);
+ }
+ else
+ _cpp_push_token_context (pfile, NULL, out, 1);
+
+ return 1;
+}
+
/* Copies SRC, of length LEN, to DEST, adding backslashes before all
backslashes and double quotes. DEST must be of sufficient size.
Returns a pointer to the end of the string. */
@@ -3057,6 +3388,24 @@ cpp_get_token_1 (cpp_reader *pfile, location_t *location)
node = result->val.node.node;
+ /* __VA_COUNT__(...) handling, modeled after __VA_OPT__ being a
+ special reserved identifier: expand only when it is actually
+ invoked (i.e. followed by '('), using function-like macro
+ argument parsing rules. */
+ if (node == pfile->spec_nodes.n__VA_COUNT__)
+ {
+ if (!pfile->state.prevent_expansion
+ && maybe_expand_va_count (pfile, result, virt_loc))
+ {
+ if (pfile->state.in_directive)
+ continue;
+ result = padding_token (pfile, result);
+ goto out;
+ }
+ /* Not an invocation => leave as-is. */
+ break;
+ }
+
if (node->type == NT_VOID || (result->flags & NO_EXPAND))
break;
@@ -3791,9 +4140,10 @@ create_iso_definition (cpp_reader *pfile)
if (macro->count > 1 && token[-1].type == CPP_HASH && macro->fun_like)
{
if (token->type == CPP_MACRO_ARG
- || (macro->variadic
- && token->type == CPP_NAME
- && token->val.node.node == pfile->spec_nodes.n__VA_OPT__))
+ || (token->type == CPP_NAME
+ && (token->val.node.node == pfile->spec_nodes.n__VA_COUNT__
+ || (macro->variadic
+ && token->val.node.node ==
pfile->spec_nodes.n__VA_OPT__))))
{
if (token->flags & PREV_WHITE)
token->flags |= SP_PREV_WHITE;
@@ -3826,6 +4176,16 @@ create_iso_definition (cpp_reader *pfile)
}
if (!vaopt_tracker.completed ())
goto out;
+ /* Validate balanced __VA_COUNT__(...) in macro definitions. */
+ {
+ vacount_state vacount_tracker (pfile);
+ /* Walk the replacement list tokens already collected. */
+ for (unsigned j = 0; j < macro->count; ++j)
+ if (vacount_tracker.update (¯o->exp.tokens[j]) ==
vacount_state::ERROR)
+ goto out;
+ if (!vacount_tracker.completed ())
+ goto out;
+ }
break;
}
diff --git a/libcpp/pch.cc b/libcpp/pch.cc
index b27c299eb3f..04fc53a2942 100644
--- a/libcpp/pch.cc
+++ b/libcpp/pch.cc
@@ -852,6 +852,7 @@ cpp_read_state (cpp_reader *r, const char *name, FILE *f,
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__VA_COUNT__ = cpp_lookup (r, DSC("__VA_COUNT__"));
}
old_state = r->state;
--
2.51.0