From 60887f8c2df851fd14988578dfe23126e2e8b9b5 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Thu, 15 Feb 2018 12:43:01 -0500 Subject: [PATCH] PR preprocessor/83063 - __VA_OPT__ and ## PR preprocessor/83708 * macro.c (vaopt_state): Reorder m_last_was_paste before m_state. (vaopt_state::vaopt_state): Adjust. (vaopt_state::update_flags): Add BEGIN and END. (vaopt_state::update): Return them. (copy_paste_flag): Factor out of replace_args. (last_token_is): New. (replace_args): Handle BEGIN and END. Avoid padding there. (tokens_buff_last_token_ptr): Return NULL if no tokens. Co-Authored-By: Jakub Jelinek From-SVN: r257696 --- gcc/testsuite/c-c++-common/cpp/va-opt-2.c | 41 +++++++ gcc/testsuite/c-c++-common/cpp/va-opt-3.c | 94 +++++++++++++++ libcpp/ChangeLog | 14 +++ libcpp/macro.c | 135 ++++++++++++++++++---- 4 files changed, 261 insertions(+), 23 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/cpp/va-opt-2.c create mode 100644 gcc/testsuite/c-c++-common/cpp/va-opt-3.c diff --git a/gcc/testsuite/c-c++-common/cpp/va-opt-2.c b/gcc/testsuite/c-c++-common/cpp/va-opt-2.c new file mode 100644 index 00000000000..cff2d6cbe5d --- /dev/null +++ b/gcc/testsuite/c-c++-common/cpp/va-opt-2.c @@ -0,0 +1,41 @@ +/* PR preprocessor/83063 */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu99" { target c } } */ +/* { dg-options "-std=c++2a" { target c++ } } */ + +#define f1(...) int b##__VA_OPT__(c) +#define f2(...) int __VA_OPT__(c)##d +#define f3(...) int e##__VA_OPT__() +#define f4(...) int __VA_OPT__()##f +#define f5(...) int g##__VA_OPT__(h)##i +#define f6(...) int j##__VA_OPT__()##k +#define f7(...) int l##__VA_OPT__() +#define f8(...) int __VA_OPT__()##m +#define f9(...) int n##__VA_OPT__()##o +#define f10(x, ...) int x##__VA_OPT__(x) +#define f11(x, ...) int __VA_OPT__(x)##x +#define f12(x, ...) int x##__VA_OPT__(x)##x +f1 (1, 2, 3); +f1 (); +f2 (1, 2); +f2 (); +f3 (1); +f4 (2); +f5 (6, 7); +f5 (); +f6 (8); +f7 (); +f8 (); +f9 (); +f10 (p, 5, 6); +f10 (p); +f11 (q, 7); +f11 (q); +f12 (r, 1, 2, 3, 4, 5); +f12 (r); + +int +main () +{ + return bc + b + cd + d + e + f + ghi + gi + jk + l + m + no + pp + p + qq + q + rrr + rr; +} diff --git a/gcc/testsuite/c-c++-common/cpp/va-opt-3.c b/gcc/testsuite/c-c++-common/cpp/va-opt-3.c new file mode 100644 index 00000000000..1a5a7b2383e --- /dev/null +++ b/gcc/testsuite/c-c++-common/cpp/va-opt-3.c @@ -0,0 +1,94 @@ +/* PR preprocessor/83063 */ +/* PR preprocessor/83708 */ +/* { dg-do preprocess } */ +/* { dg-options "-std=gnu99" { target c } } */ +/* { dg-options "-std=c++2a" { target c++ } } */ + +#define f1(...) b##__VA_OPT__(c) +#define f2(...) __VA_OPT__(c)##d +#define f3(...) e##__VA_OPT__() +#define f4(...) __VA_OPT__()##f +#define f5(...) g##__VA_OPT__(h)##i +#define f6(...) j##__VA_OPT__()##k +#define f7(...) l##__VA_OPT__() +#define f8(...) __VA_OPT__()##m +#define f9(...) n##__VA_OPT__()##o +#define f10(x, ...) x##__VA_OPT__(x) +#define f11(x, ...) __VA_OPT__(x)##x +#define f12(x, ...) x##__VA_OPT__(x)##x +#define f13(...) __VA_OPT__(a)__VA_OPT__(b)c +#define f14(a, b, c, ...) __VA_OPT__(a)__VA_OPT__(b)c +#define f15(a, b, c, ...) __VA_OPT__(a b)__VA_OPT__(b c)a/**/__VA_OPT__(c a)a +#define m1 ( +#define f16() f17 m1 ) +#define f17() f18 m1 ) +#define f18() m2 m1 ) +#define m3f17() g +#define f19(x, ...) m3 ## __VA_OPT__(x x f16() #x) +#define f20(x, ...) __VA_OPT__(x x)##m4() +#define f21() f17 +#define f17m4() h +#define f22(x,...) 1 ## __VA_OPT__(x ## x 2) ## 3 +#define f23(x,...) 1 ## __VA_OPT__(x 2) ## 3 +#define f24(x,...) 1 ## __VA_OPT__(2 x) ## 3 +#define f25(x,...) 1 ## __VA_OPT__(2 x ## x) ## 3 +t1 f1 (1, 2, 3); +/* { dg-final { scan-file va-opt-3.i "t1 bc;" } } */ +t2 f1 (); +/* { dg-final { scan-file va-opt-3.i "t2 b;" } } */ +t3 f2 (1, 2); +/* { dg-final { scan-file va-opt-3.i "t3 cd;" } } */ +t4 f2 (); +/* { dg-final { scan-file va-opt-3.i "t4 d;" } } */ +t5 f3 (1); +/* { dg-final { scan-file va-opt-3.i "t5 e;" } } */ +t6 f4 (2); +/* { dg-final { scan-file va-opt-3.i "t6 f;" } } */ +t7 f5 (6, 7); +/* { dg-final { scan-file va-opt-3.i "t7 ghi;" } } */ +t8 f5 (); +/* { dg-final { scan-file va-opt-3.i "t8 gi;" } } */ +t9 f6 (8); +/* { dg-final { scan-file va-opt-3.i "t9 jk;" } } */ +t10 f7 (); +/* { dg-final { scan-file va-opt-3.i "t10 l;" } } */ +t11 f8 (); +/* { dg-final { scan-file va-opt-3.i "t11 m;" } } */ +t12 f9 (); +/* { dg-final { scan-file va-opt-3.i "t12 no;" } } */ +t13 f10 (p, 5, 6); +/* { dg-final { scan-file va-opt-3.i "t13 pp;" } } */ +t14 f10 (p); +/* { dg-final { scan-file va-opt-3.i "t14 p;" } } */ +t15 f11 (q, 7); +/* { dg-final { scan-file va-opt-3.i "t15 qq;" } } */ +t16 f11 (q); +/* { dg-final { scan-file va-opt-3.i "t16 q;" } } */ +t17 f12 (r, 1, 2, 3, 4, 5); +/* { dg-final { scan-file va-opt-3.i "t17 rrr;" } } */ +t18 f12 (r); +/* { dg-final { scan-file va-opt-3.i "t18 rr;" } } */ +t19 f13 (1, 2); +/* { dg-final { scan-file va-opt-3.i "t19 a b c;" } } */ +t20 f13 (); +/* { dg-final { scan-file va-opt-3.i "t20 c;" } } */ +t21 f14 (3, 4, 5, 2); +/* { dg-final { scan-file va-opt-3.i "t21 3 4 5;" } } */ +t22 f14 (3, 4, 5); +/* { dg-final { scan-file va-opt-3.i "t22 5;" } } */ +t23 f15 (6, 7, 8, 9); +/* { dg-final { scan-file va-opt-3.i "t23 6 7 7 8 6 8 6 6;" } } */ +t24 f15 (6, 7, 8); +/* { dg-final { scan-file va-opt-3.i "t24 6 6;" } } */ +t25 f19 (f16 (), 1); +/* { dg-final { scan-file va-opt-3.i {t25 g f18 \( \) f17 \( \) "f16 \(\)";} } } */ +t26 f20 (f21 (), 2); +/* { dg-final { scan-file va-opt-3.i "t26 f17 h;" } } */ +t27 f22 (, x); +/* { dg-final { scan-file va-opt-3.i "t27 123;" } } */ +t28 f23 (, x); +/* { dg-final { scan-file va-opt-3.i "t28 123;" } } */ +t29 f24 (, x); +/* { dg-final { scan-file va-opt-3.i "t29 123;" } } */ +t30 f25 (, x); +/* { dg-final { scan-file va-opt-3.i "t30 123;" } } */ diff --git a/libcpp/ChangeLog b/libcpp/ChangeLog index 9c675a52d0c..2f6e1f21a92 100644 --- a/libcpp/ChangeLog +++ b/libcpp/ChangeLog @@ -1,3 +1,17 @@ +2018-02-15 Jason Merrill + Jakub Jelinek + + PR preprocessor/83063 - __VA_OPT__ and ## + PR preprocessor/83708 + * macro.c (vaopt_state): Reorder m_last_was_paste before m_state. + (vaopt_state::vaopt_state): Adjust. + (vaopt_state::update_flags): Add BEGIN and END. + (vaopt_state::update): Return them. + (copy_paste_flag): Factor out of replace_args. + (last_token_is): New. + (replace_args): Handle BEGIN and END. Avoid padding there. + (tokens_buff_last_token_ptr): Return NULL if no tokens. + 2018-01-31 Jakub Jelinek PR preprocessor/69869 diff --git a/libcpp/macro.c b/libcpp/macro.c index f994ac584cc..776af7bd00e 100644 --- a/libcpp/macro.c +++ b/libcpp/macro.c @@ -105,8 +105,8 @@ class vaopt_state { : m_pfile (pfile), m_allowed (any_args), m_variadic (is_variadic), - m_state (0), m_last_was_paste (false), + m_state (0), m_paste_location (0), m_location (0) { @@ -116,7 +116,9 @@ class vaopt_state { { ERROR, DROP, - INCLUDE + INCLUDE, + BEGIN, + END }; /* Given a token, update the state of this tracker and return a @@ -139,7 +141,7 @@ class vaopt_state { } ++m_state; m_location = token->src_loc; - return DROP; + return BEGIN; } else if (m_state == 1) { @@ -191,7 +193,7 @@ class vaopt_state { return ERROR; } - return DROP; + return END; } } return m_allowed ? INCLUDE : DROP; @@ -220,6 +222,9 @@ class vaopt_state { bool m_allowed; /* True if the macro is variadic. */ bool m_variadic; + /* 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 state variable: 0 means not parsing @@ -228,9 +233,6 @@ class vaopt_state { >= 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; @@ -1701,6 +1703,30 @@ expanded_token_index (cpp_reader *pfile, cpp_macro *macro, return cur_replacement_token - macro->exp.tokens; } +/* Copy whether PASTE_LEFT is set from SRC to *PASTE_FLAG. */ + +static void +copy_paste_flag (cpp_reader *pfile, const cpp_token **paste_flag, + const cpp_token *src) +{ + cpp_token *token = _cpp_temp_token (pfile); + token->type = (*paste_flag)->type; + token->val = (*paste_flag)->val; + if (src->flags & PASTE_LEFT) + token->flags = (*paste_flag)->flags | PASTE_LEFT; + else + token->flags = (*paste_flag)->flags & ~PASTE_LEFT; + *paste_flag = token; +} + +/* True IFF the last token emitted into BUFF (if any) is PTR. */ + +static bool +last_token_is (_cpp_buff *buff, const cpp_token **ptr) +{ + return (ptr && tokens_buff_last_token_ptr (buff) == ptr); +} + /* Replace the parameters in a function-like macro of NODE with the actual ARGS, and place the result in a newly pushed token context. Expand each argument before replacing, unless it is operated upon @@ -1833,6 +1859,7 @@ replace_args (cpp_reader *pfile, cpp_hashnode *node, cpp_macro *macro, i = 0; vaopt_state vaopt_tracker (pfile, macro->variadic, args[macro->paramc - 1].count > 0); + const cpp_token **vaopt_start = NULL; for (src = macro->exp.tokens; src < limit; src++) { unsigned int arg_tokens_count; @@ -1841,8 +1868,58 @@ replace_args (cpp_reader *pfile, cpp_hashnode *node, cpp_macro *macro, const cpp_token **tmp_token_ptr; /* __VA_OPT__ handling. */ - if (vaopt_tracker.update (src) != vaopt_state::INCLUDE) - continue; + vaopt_state::update_type vostate = vaopt_tracker.update (src); + if (vostate != vaopt_state::INCLUDE) + { + if (vostate == vaopt_state::BEGIN) + { + /* Padding on the left of __VA_OPT__ (unless RHS of ##). */ + if (src != macro->exp.tokens && !(src[-1].flags & PASTE_LEFT)) + { + const cpp_token *t = padding_token (pfile, src); + unsigned index = expanded_token_index (pfile, macro, src, i); + /* Allocate a virtual location for the padding token and + append the token and its location to BUFF and + VIRT_LOCS. */ + tokens_buff_add_token (buff, virt_locs, t, + t->src_loc, t->src_loc, + map, index); + } + vaopt_start = tokens_buff_last_token_ptr (buff); + } + else if (vostate == vaopt_state::END) + { + const cpp_token **start = vaopt_start; + vaopt_start = NULL; + + /* Remove any tail padding from inside the __VA_OPT__. */ + paste_flag = tokens_buff_last_token_ptr (buff); + while (paste_flag && paste_flag != start + && (*paste_flag)->type == CPP_PADDING) + { + tokens_buff_remove_last_token (buff); + paste_flag = tokens_buff_last_token_ptr (buff); + } + + if (src->flags & PASTE_LEFT) + { + /* With a non-empty __VA_OPT__ on the LHS of ##, the last + token should be flagged PASTE_LEFT. */ + if (paste_flag && (*paste_flag)->type != CPP_PADDING) + copy_paste_flag (pfile, paste_flag, src); + } + else + { + /* Otherwise, avoid paste on RHS, __VA_OPT__(c)d or + __VA_OPT__(c)__VA_OPT__(d). */ + const cpp_token *t = &pfile->avoid_paste; + tokens_buff_add_token (buff, virt_locs, + t, t->src_loc, t->src_loc, + NULL, 0); + } + } + continue; + } if (src->type != CPP_MACRO_ARG) { @@ -1921,8 +1998,11 @@ replace_args (cpp_reader *pfile, cpp_hashnode *node, cpp_macro *macro, else paste_flag = tmp_token_ptr; } - /* Remove the paste flag if the RHS is a placemarker. */ - else if (arg_tokens_count == 0) + /* Remove the paste flag if the RHS is a placemarker, unless the + previous emitted token is at the beginning of __VA_OPT__; + placemarkers within __VA_OPT__ are ignored in that case. */ + else if (arg_tokens_count == 0 + && tmp_token_ptr != vaopt_start) paste_flag = tmp_token_ptr; } } @@ -1934,11 +2014,26 @@ replace_args (cpp_reader *pfile, cpp_hashnode *node, cpp_macro *macro, track_macro_expansion), MACRO_ARG_TOKEN_EXPANDED, arg, arg->expanded); + + if (last_token_is (buff, vaopt_start)) + { + /* We're expanding an arg at the beginning of __VA_OPT__. + Skip padding. */ + while (arg_tokens_count) + { + const cpp_token *t = macro_arg_token_iter_get_token (&from); + if (t->type != CPP_PADDING) + break; + macro_arg_token_iter_forward (&from); + --arg_tokens_count; + } + } } /* Padding on the left of an argument (unless RHS of ##). */ if ((!pfile->state.in_directive || pfile->state.directive_wants_padding) - && src != macro->exp.tokens && !(src[-1].flags & PASTE_LEFT)) + && src != macro->exp.tokens && !(src[-1].flags & PASTE_LEFT) + && !last_token_is (buff, vaopt_start)) { const cpp_token *t = padding_token (pfile, src); unsigned index = expanded_token_index (pfile, macro, src, i); @@ -2023,7 +2118,8 @@ replace_args (cpp_reader *pfile, cpp_hashnode *node, cpp_macro *macro, NODE_NAME (node), src->val.macro_arg.arg_no); /* Avoid paste on RHS (even case count == 0). */ - if (!pfile->state.in_directive && !(src->flags & PASTE_LEFT)) + if (!pfile->state.in_directive && !(src->flags & PASTE_LEFT) + && !last_token_is (buff, vaopt_start)) { const cpp_token *t = &pfile->avoid_paste; tokens_buff_add_token (buff, virt_locs, @@ -2033,16 +2129,7 @@ replace_args (cpp_reader *pfile, cpp_hashnode *node, cpp_macro *macro, /* Add a new paste flag, or remove an unwanted one. */ if (paste_flag) - { - cpp_token *token = _cpp_temp_token (pfile); - token->type = (*paste_flag)->type; - token->val = (*paste_flag)->val; - if (src->flags & PASTE_LEFT) - token->flags = (*paste_flag)->flags | PASTE_LEFT; - else - token->flags = (*paste_flag)->flags & ~PASTE_LEFT; - *paste_flag = token; - } + copy_paste_flag (pfile, paste_flag, src); i += arg_tokens_count; } @@ -2213,6 +2300,8 @@ tokens_buff_count (_cpp_buff *buff) static const cpp_token ** tokens_buff_last_token_ptr (_cpp_buff *buff) { + if (BUFF_FRONT (buff) == buff->base) + return NULL; return &((const cpp_token **) BUFF_FRONT (buff))[-1]; } -- 2.30.2