From patchwork Sat Sep 16 21:49:18 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Tromey X-Patchwork-Id: 814564 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-462318-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="jZh3yXqO"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3xvmC51LvCz9sPs for ; Sun, 17 Sep 2017 07:49:51 +1000 (AEST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id; q=dns; s=default; b=U+mjcpwbik9R wIsQeSPdi2xIMHGZJfvySEjDkDht9pHIOP8NDuqwV6GG+PX/8yEFPy8qyRfYG5IO 2+TGT/s+mTTSZob2gjyxm7OQ8uFqGetby90zmyRpPd5/W7O02tDANxfX+omRWFbb JBAroCrvOiBVvcTBbVTKVYjp1r+W/os= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id; s=default; bh=OgA9Odxdkui4QcBa7u W/fzVN4Po=; b=jZh3yXqO1cWVZrk1GTVpswx0tdR9gVZ7elv7GhDABQ3Ksn1pdG 6gtVbABKgZ3bDnS2SEBbY0/yDz/emySU44XQa+tzexUn049zRd8umJRTCYySHPKZ 0oPiszjSa4fMI1XfxwV3MIAv1ZzD+pPNLaAA4P5pSIS1O1IxsjNpVk0FQ= Received: (qmail 94839 invoked by alias); 16 Sep 2017 21:49:41 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 94823 invoked by uid 89); 16 Sep 2017 21:49:41 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.3 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy=CPP, een, lifted X-HELO: gproxy4-pub.mail.unifiedlayer.com Received: from gproxy4-pub.mail.unifiedlayer.com (HELO gproxy4-pub.mail.unifiedlayer.com) (69.89.23.142) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sat, 16 Sep 2017 21:49:37 +0000 Received: from CMOut01 (unknown [10.0.90.82]) by gproxy4.mail.unifiedlayer.com (Postfix) with ESMTP id 0FEE5175C23 for ; Sat, 16 Sep 2017 15:49:34 -0600 (MDT) Received: from box522.bluehost.com ([74.220.219.122]) by CMOut01 with id AMpW1w0032f2jeq01MpZeG; Sat, 16 Sep 2017 15:49:34 -0600 X-Authority-Analysis: v=2.2 cv=K4VSJ2eI c=1 sm=1 tr=0 a=GsOEXm/OWkKvwdLVJsfwcA==:117 a=GsOEXm/OWkKvwdLVJsfwcA==:17 a=2JCJgTwv5E4A:10 a=b4LDLZbEAAAA:8 a=zstS-IiYAAAA:8 a=b74LG7I0rPilUTJ1FakA:9 a=C0Wf_tdlRHR_hAus:21 a=lIV4lehHCgUs_0Qe:21 a=LvL4KW8RksIA:10 a=20T61YgZp4ItGotXEy2O:22 a=4G6NA9xxw8l3yy4pmD5M:22 Received: from 75-166-76-94.hlrn.qwest.net ([75.166.76.94]:57670 helo=bapiya.Home) by box522.bluehost.com with esmtpsa (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.87) (envelope-from ) id 1dtKxt-004BTw-U0; Sat, 16 Sep 2017 15:49:30 -0600 From: Tom Tromey To: gcc-patches@gcc.gnu.org Cc: Tom Tromey Subject: [RFA] Implement __VA_OPT__ Date: Sat, 16 Sep 2017 15:49:18 -0600 Message-Id: <20170916214918.14930-1-tom@tromey.com> X-BWhitelist: no X-Exim-ID: 1dtKxt-004BTw-U0 X-Source-Sender: 75-166-76-94.hlrn.qwest.net (bapiya.Home) [75.166.76.94]:57670 X-Source-Auth: tom+tromey.com X-Email-Count: 1 X-Source-Cap: ZWx5bnJvYmk7ZWx5bnJvYmk7Ym94NTIyLmJsdWVob3N0LmNvbQ== X-Local-Domain: yes 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 * doc/cpp.texi (Variadic Macros): Document __VA_OPT__. gcc/testsuite/ChangeLog 2017-09-16 Tom Tromey * 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 * 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) : Update comment. (struct spec_nodes) : New field. * init.c (struct lang_flags) : 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) : New constants. (struct cpp_options) : 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__")); }