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 gcc/ChangeLog * doc/cpp.texi (Variadic Macros): Document __VA_OPT__. gcc/testsuite/ChangeLog * c-c++-common/cpp/va-opt-pedantic.c: New file. * c-c++-common/cpp/va-opt.c: New file. * c-c++-common/cpp/va-opt-error.c: New file. libcpp/ChangeLog * pch.c (cpp_read_state): Set n__VA_OPT__. * macro.c (vaopt_state): New class. (_cpp_arguments_ok): Check va_opt flag. (replace_args, create_iso_definition): Use vaopt_state. * lex.c (lex_identifier_intern): Possibly issue errors for __VA_OPT__. (lex_identifier): Likewise. (maybe_va_opt_error): New function. * internal.h (struct lexer_state) <va_args_ok>: Update comment. (struct spec_nodes) <n__VA_OPT__>: New field. * init.c (struct lang_flags) <va_opt>: New field. (lang_defaults): Add entries for C++2A. Update all entries for va_opt. (cpp_set_lang): Initialize va_opt. * include/cpplib.h (struct cpp_options) <va_opt>: New field. * identifiers.c (_cpp_init_hashtable): Initialize n__VA_OPT__. From-SVN: r254707
This commit is contained in:
parent
4d85d48027
commit
fb771b9dad
14 changed files with 369 additions and 34 deletions
|
@ -1,3 +1,7 @@
|
|||
2017-11-13 Tom Tromey <tom@tromey.com>
|
||||
|
||||
* doc/cpp.texi (Variadic Macros): Document __VA_OPT__.
|
||||
|
||||
2017-11-13 Carl Love <cel@us.ibm.com>
|
||||
|
||||
* config/rs6000/rs6000-c.c (altivec_overloaded_builtins):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
2017-11-13 Tom Tromey <tom@tromey.com>
|
||||
|
||||
* c-c++-common/cpp/va-opt-pedantic.c: New file.
|
||||
* c-c++-common/cpp/va-opt.c: New file.
|
||||
* c-c++-common/cpp/va-opt-error.c: New file.
|
||||
|
||||
2017-11-13 Carl Love <cel@us.ibm.com>
|
||||
|
||||
* gcc.target/powerpc/builtins-6-p9-runnable.c: Add new runnable test.
|
||||
|
|
28
gcc/testsuite/c-c++-common/cpp/va-opt-error.c
Normal file
28
gcc/testsuite/c-c++-common/cpp/va-opt-error.c
Normal file
|
@ -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" } */
|
5
gcc/testsuite/c-c++-common/cpp/va-opt-pedantic.c
Normal file
5
gcc/testsuite/c-c++-common/cpp/va-opt-pedantic.c
Normal file
|
@ -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" } */
|
42
gcc/testsuite/c-c++-common/cpp/va-opt.c
Normal file
42
gcc/testsuite/c-c++-common/cpp/va-opt.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -1,3 +1,22 @@
|
|||
2017-11-13 Tom Tromey <tom@tromey.com>
|
||||
|
||||
* pch.c (cpp_read_state): Set n__VA_OPT__.
|
||||
* macro.c (vaopt_state): New class.
|
||||
(_cpp_arguments_ok): Check va_opt flag.
|
||||
(replace_args, create_iso_definition): Use vaopt_state.
|
||||
* lex.c (lex_identifier_intern): Possibly issue errors for
|
||||
__VA_OPT__.
|
||||
(lex_identifier): Likewise.
|
||||
(maybe_va_opt_error): New function.
|
||||
* internal.h (struct lexer_state) <va_args_ok>: Update comment.
|
||||
(struct spec_nodes) <n__VA_OPT__>: New field.
|
||||
* init.c (struct lang_flags) <va_opt>: New field.
|
||||
(lang_defaults): Add entries for C++2A. Update all entries for
|
||||
va_opt.
|
||||
(cpp_set_lang): Initialize va_opt.
|
||||
* include/cpplib.h (struct cpp_options) <va_opt>: New field.
|
||||
* identifiers.c (_cpp_init_hashtable): Initialize n__VA_OPT__.
|
||||
|
||||
2017-11-13 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* include/line-map.h (linenum_type): Move this typedef and the
|
||||
|
|
|
@ -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__"));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -91,30 +91,31 @@ struct lang_flags
|
|||
char digit_separators;
|
||||
char trigraphs;
|
||||
char utf8_char_literals;
|
||||
char va_opt;
|
||||
};
|
||||
|
||||
static const struct lang_flags lang_defaults[] =
|
||||
{ /* c99 c++ xnum xid c11 std digr ulit rlit udlit bincst digsep trig u8chlit */
|
||||
/* GNUC89 */ { 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* GNUC99 */ { 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
|
||||
/* GNUC11 */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
|
||||
/* GNUC17 */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
|
||||
/* STDC89 */ { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0 },
|
||||
/* STDC94 */ { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0 },
|
||||
/* STDC99 */ { 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0 },
|
||||
/* STDC11 */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0 },
|
||||
/* STDC17 */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0 },
|
||||
/* GNUCXX */ { 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },
|
||||
/* CXX98 */ { 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0 },
|
||||
/* GNUCXX11 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0 },
|
||||
/* CXX11 */ { 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0 },
|
||||
/* GNUCXX14 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0 },
|
||||
/* CXX14 */ { 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
|
||||
/* GNUCXX17 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1 },
|
||||
/* CXX17 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1 },
|
||||
/* GNUCXX2A */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1 },
|
||||
/* CXX2A */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1 },
|
||||
/* ASM */ { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
{ /* c99 c++ xnum xid c11 std digr ulit rlit udlit bincst digsep trig u8chlit vaopt */
|
||||
/* GNUC89 */ { 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1 },
|
||||
/* GNUC99 */ { 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1 },
|
||||
/* GNUC11 */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1 },
|
||||
/* GNUC17 */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1 },
|
||||
/* STDC89 */ { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
|
||||
/* STDC94 */ { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0 },
|
||||
/* STDC99 */ { 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0 },
|
||||
/* STDC11 */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0 },
|
||||
/* STDC17 */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0 },
|
||||
/* GNUCXX */ { 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1 },
|
||||
/* CXX98 */ { 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0 },
|
||||
/* GNUCXX11 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1 },
|
||||
/* CXX11 */ { 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0 },
|
||||
/* GNUCXX14 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1 },
|
||||
/* CXX14 */ { 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
|
||||
/* GNUCXX17 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1 },
|
||||
/* CXX17 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0 },
|
||||
/* GNUCXX2A */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1 },
|
||||
/* CXX2A */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1 },
|
||||
/* ASM */ { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
/* Sets internal flags correctly for a given language. */
|
||||
|
@ -139,6 +140,7 @@ cpp_set_lang (cpp_reader *pfile, enum c_lang lang)
|
|||
CPP_OPTION (pfile, digit_separators) = l->digit_separators;
|
||||
CPP_OPTION (pfile, trigraphs) = l->trigraphs;
|
||||
CPP_OPTION (pfile, utf8_char_literals) = l->utf8_char_literals;
|
||||
CPP_OPTION (pfile, va_opt) = l->va_opt;
|
||||
}
|
||||
|
||||
/* Initialize library global state. */
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
|
30
libcpp/lex.c
30
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,
|
||||
|
|
170
libcpp/macro.c
170
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 *,
|
||||
|
@ -776,7 +925,8 @@ _cpp_arguments_ok (cpp_reader *pfile, cpp_macro *macro, const cpp_hashnode *node
|
|||
|
||||
if (argc < macro->paramc)
|
||||
{
|
||||
/* As an extension, variadic arguments are allowed to not appear in
|
||||
/* In C++2a (here the va_opt flag is used), and also as a GNU
|
||||
extension, variadic arguments are allowed to not appear in
|
||||
the invocation at all.
|
||||
e.g. #define debug(format, args...) something
|
||||
debug("string");
|
||||
|
@ -786,7 +936,8 @@ _cpp_arguments_ok (cpp_reader *pfile, cpp_macro *macro, const cpp_hashnode *node
|
|||
|
||||
if (argc + 1 == macro->paramc && macro->variadic)
|
||||
{
|
||||
if (CPP_PEDANTIC (pfile) && ! macro->syshdr)
|
||||
if (CPP_PEDANTIC (pfile) && ! macro->syshdr
|
||||
&& ! CPP_OPTION (pfile, va_opt))
|
||||
{
|
||||
if (CPP_OPTION (pfile, cplusplus))
|
||||
cpp_error (pfile, CPP_DL_PEDWARN,
|
||||
|
@ -1678,6 +1829,8 @@ replace_args (cpp_reader *pfile, cpp_hashnode *node, cpp_macro *macro,
|
|||
num_macro_tokens);
|
||||
}
|
||||
i = 0;
|
||||
vaopt_state vaopt_tracker (pfile, macro->variadic,
|
||||
args[macro->paramc - 1].count > 0);
|
||||
for (src = macro->exp.tokens; src < limit; src++)
|
||||
{
|
||||
unsigned int arg_tokens_count;
|
||||
|
@ -1685,6 +1838,10 @@ replace_args (cpp_reader *pfile, cpp_hashnode *node, cpp_macro *macro,
|
|||
const cpp_token **paste_flag = NULL;
|
||||
const cpp_token **tmp_token_ptr;
|
||||
|
||||
/* __VA_OPT__ handling. */
|
||||
if (vaopt_tracker.update (src) != vaopt_state::INCLUDE)
|
||||
continue;
|
||||
|
||||
if (src->type != CPP_MACRO_ARG)
|
||||
{
|
||||
/* Allocate a virtual location for token SRC, and add that
|
||||
|
@ -3076,6 +3233,9 @@ create_iso_definition (cpp_reader *pfile, cpp_macro *macro)
|
|||
*token = *ctoken;
|
||||
}
|
||||
|
||||
/* The argument doesn't matter here. */
|
||||
vaopt_state vaopt_tracker (pfile, macro->variadic, true);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/* Check the stringifying # constraint 6.10.3.2.1 of
|
||||
|
@ -3144,10 +3304,16 @@ create_iso_definition (cpp_reader *pfile, cpp_macro *macro)
|
|||
}
|
||||
}
|
||||
|
||||
if (vaopt_tracker.update (token) == vaopt_state::ERROR)
|
||||
return false;
|
||||
|
||||
following_paste_op = (token->type == CPP_PASTE);
|
||||
token = lex_expansion_token (pfile, macro);
|
||||
}
|
||||
|
||||
if (!vaopt_tracker.completed ())
|
||||
return false;
|
||||
|
||||
macro->exp.tokens = (cpp_token *) BUFF_FRONT (pfile->a_buff);
|
||||
macro->traditional = 0;
|
||||
|
||||
|
|
|
@ -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__"));
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue