PR preprocessor/83063 - __VA_OPT__ and ##
authorJason Merrill <jason@redhat.com>
Thu, 15 Feb 2018 17:43:01 +0000 (12:43 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Thu, 15 Feb 2018 17:43:01 +0000 (12:43 -0500)
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 <jakub@redhat.com>
From-SVN: r257696

gcc/testsuite/c-c++-common/cpp/va-opt-2.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/cpp/va-opt-3.c [new file with mode: 0644]
libcpp/ChangeLog
libcpp/macro.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 (file)
index 0000000..cff2d6c
--- /dev/null
@@ -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 (file)
index 0000000..1a5a7b2
--- /dev/null
@@ -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;" } } */
index 9c675a52d0c760e891cc9086a7e443c98caabcbe..2f6e1f21a9205ba8de733046a05873b1da32bfdd 100644 (file)
@@ -1,3 +1,17 @@
+2018-02-15  Jason Merrill  <jason@redhat.com>
+           Jakub Jelinek  <jakub@redhat.com>
+
+       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  <jakub@redhat.com>
 
        PR preprocessor/69869
index f994ac584ccad6caff3bf21603fcda5947b315a9..776af7bd00eae4f0571f61c36ceaa1a276c256d3 100644 (file)
@@ -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];
 }