--- /dev/null
+/* 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;" } } */
: 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)
{
{
ERROR,
DROP,
- INCLUDE
+ INCLUDE,
+ BEGIN,
+ END
};
/* Given a token, update the state of this tracker and return a
}
++m_state;
m_location = token->src_loc;
- return DROP;
+ return BEGIN;
}
else if (m_state == 1)
{
return ERROR;
}
- return DROP;
+ return END;
}
}
return m_allowed ? INCLUDE : DROP;
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
>= 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;
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
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;
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)
{
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;
}
}
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);
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,
/* 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;
}
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];
}