Varargs macros subtly broken

Jamie Lokier jamie.lokier@cern.ch
Mon Sep 25 09:43:00 GMT 2000


Zack Weinberg wrote:
> > Ah yes, that is one form of a varargs bug I've been meaning to report.
> > I've got macros which work fine with GCC 2.95, but don't work with _or_
> > without -traditional now.  (Sorry, I'll try to provide an example
> > tomorrow).
> 
> Please do provide an example.  I wouldn't be surprised to find genuine
> bugs in the varargs macro code.
> 
> The new semantics are not that different; in most cases it will be possible
> to convert macros.

I see that now.  Attached are examples of macros that used to work but
are broken now.  Preprocessing fails with errors.

I've included examples using the old syntax, and also converted ones for
__VA_ARGS__ syntax.  Both exhibit the same problems with the current
preprocessor

Btw, I found this while trying to build our application using RedHat 7.0
Beta -- they are shipping GCC 2.96.2 which has the problem CPP.  Ew.

I tested today with the CVS snapshot -- same problems, but the warnings
about token pasting have been fixed.

-- Jamie

/* This file illustrates a few changes in preprocessor behaviour using
   varargs from EGCS 1.1.2 to GCC 2.96.2.

   Observe: gnu_count expansions differ.
            gnu_repeat is the same.
            gnu_index makes preprocessing fail.

   I've included some attempts at using the C99 syntax.
   I haven't bothered with c99_index.

   Observe: c99_count functionality can't quite do the same as gnu_count.
            c99_repeat is fine though.

   Bonus observations:

        - ## swallows the token in rest arg macros, even with
          __VA_ARGS__.  According to the CPP manual, this is an
          unreliable feature that is deprecated, and it's use is warned
          about.  Actually there is no warning.  As you can see from
          this code, that feature is essential, though only when the
          discarded token is ",", to get the correct results here.  Can
          we have a decision to not deprecate it at least in the comma
          case, provided that is compatible with ISO C99?  (Presumably
          ISO C99 doesn't define the behaviour of pasting "," before
          another token anyway -- leaving a loophole which GCC can
          exploit to discard it).

        - The output has spurious newlines, followed by a corrective "#
          LINE" directive.

   -- Jamie Lokier <jamie.lokier@cern.ch>, 25/Sep/2000

   Permission is granted to use, copy, modify and distribute this file
   freely for any purpose whatsoever.  This file is free software, and
   there's no warranty. */

/********** Definitions. ***********/

#define dup3(x)            x,x,x

/* Count elements in a list (0 to 10 max). */
#define gnu_count(y...)   _gnu_count1 ( , ##y)
#define _gnu_count1(y...) _gnu_count2 (y,10,9,8,7,6,5,4,3,2,1,0)
#define _gnu_count2(_,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,n,ys...) n

/* Tail of a list. */
#define gnu_tail(y...)    _gnu_tail (y)
#define _gnu_tail(x,y...) y

/* Repeat N times. */
#define gnu_repeat(n, x) gnu_tail (_gnu_repeat (n, x))
#define _gnu_repeat(n, x) _gnu_repeat_##n (x)
#define _gnu_repeat_0(x)
#define _gnu_repeat_1(x) ,x
#define _gnu_repeat_2(x) ,x,x
#define _gnu_repeat_3(x) ,x,x,x
#define _gnu_repeat_4(x) ,x,x,x,x
#define _gnu_repeat_5(x) ,x,x,x,x,x

#define _gnu_keep(xs...) xs
#define _gnu_discard(xs...)
#define _gnu_split_r(n,xs...) _gnu_split_rd (n,_gnu_keep,_gnu_discard xs)
#define _gnu_split_d(n,xs...) _gnu_split_rd (n,_gnu_discard,_gnu_keep xs)
#define _gnu_split_rd(n,xs...) _gnu_split_##n (xs)
#define _gnu_split_0(a,b,xs...) a() b(xs)
#define _gnu_split_1(a,b,x0,xs...) a(x0) b(xs)
#define _gnu_split_2(a,b,x0,x1,xs...) a(x0,x1) b(xs)
#define _gnu_split_3(a,b,x0,x1,x2,xs...) a(x0,x1,x2) b(xs)
#define _gnu_split_4(a,b,x0,x1,x2,x3,xs...) a(x0,x1,x2,x3) b(xs)
#define _gnu_split_5(a,b,x0,x1,x2,x3,x4,xs...) a(x0,x1,x2,x3,x4) b(xs)

/* List manipulations.  Surprise: index zero is the rightmost element. */
#define gnu_take(n, xs...) \
  _gnu_split_d (_gnu_count1 ( , ## xs), _gnu_repeat (n, _gnu_error) , ## xs)
#define gnu_drop(n, xs...) \
  _gnu_split_d (n,,_gnu_split_r  (_gnu_count1 ( , ## xs), _gnu_repeat (n, _gnu_error) , ## xs))
#define gnu_index(pos, xs...) gnu_take (1, gnu_drop (pos , ## xs))

/* C99 __VA_ARGS__ versions */
#define c99_count(...)    _c99_count1 ( , ##__VA_ARGS__)/* If only ## worked.*/
#define _c99_count1(...)  _c99_count2 (__VA_ARGS__,10,9,8,7,6,5,4,3,2,1,0)
#define _c99_count2(_,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,n,...) n

#define c99_tail(...)     _c99_tail (__VA_ARGS__)
#define _c99_tail(x,...)  __VA_ARGS__

#define _c99_keep(...)    __VA_ARGS__
#define _c99_discard(...)
#define _c99_split_r(n,...) _c99_split_rd(n,_c99_keep,_c99_discard __VA_ARGS__)
#define _c99_split_d(n,...) _c99_split_rd(n,_c99_discard,_c99_keep __VA_ARGS__)
#define _c99_split_rd(n,...) _c99_split_##n (__VA_ARGS__)
#define _c99_split_0(a,b,...) a() b(__VA_ARGS__)
#define _c99_split_1(a,b,x0,...) a(x0) b(__VA_ARGS__)
#define _c99_split_2(a,b,x0,x1,...) a(x0,x1) b(__VA_ARGS__)
#define _c99_split_3(a,b,x0,x1,x2,...) a(x0,x1,x2) b(__VA_ARGS__)
#define _c99_split_4(a,b,x0,x1,x2,x3,...) a(x0,x1,x2,x3) b(__VA_ARGS__)
#define _c99_split_5(a,b,x0,x1,x2,x3,x4,...) a(x0,x1,x2,x3,x4) b(__VA_ARGS__)

/* List manipulations.  Surprise: index zero is the rightmost element. */
#define c99_take(n, ...) \
  _c99_split_d (_c99_count1 ( , ## __VA_ARGS__), _c99_repeat (n, _c99_error) , ## __VA_ARGS__)
#define c99_drop(n, ...) \
  _c99_split_d (n,,_c99_split_r  (_c99_count1 ( , ## __VA_ARGS__), _c99_repeat (n, _c99_error) , ## __VA_ARGS__))
#define c99_index(pos, ...) c99_take (1, c99_drop (pos , ## __VA_ARGS__))

/************** Expansions **************/

/* Correct answers are 0, 0, 1, 2, 10 (works with older GNU CPPs).
   Current CVS gives 0, 1, 1, 2, 10. */
_gnu_count1 ();
gnu_count ();
gnu_count (A);
gnu_count (,);
gnu_count (A, B, C, D, E, F, G, H, I, J);

/* These are fine. */
_gnu_tail (dup3 ("1"), dup3 ("2")); /* Should be "2", "2", "2". */
gnu_tail  (dup3 ("1"), dup3 ("2")); /* Should be "1", "1", "2", "2", "2". */

/* Correct answers are empty, "x" and "x", "x", "x". */
gnu_repeat (0, "x");
gnu_repeat (1, "x");
gnu_repeat (3, "x");

/* Correct answers are "e", "b", "a", empty.
   Current CVS barfs with `not enough arguments for macro "_gnu_split_N"'
   where N varies for each line. */
gnu_index (0, "a", "b", "c", "d", "e");
gnu_index (3, "a", "b", "c", "d", "e");
gnu_index (4, "a", "b", "c", "d", "e");
gnu_index (5, "a", "b", "c", "d", "e");

/************* C99 tests *************/

/* The answers I would like are 0, 0, 1, 2, 10 as for the non-C99 version.
   Current CVS gives 0, 1, 1, 2, 10. */
_c99_count1 ();
c99_count ();
c99_count (A);
c99_count (,);
c99_count (A, B, C, D, E, F, G, H, I, J);

/* These are fine. */
_c99_tail (dup3 ("1"), dup3 ("2")); /* Should be "2", "2", "2". */
c99_tail  (dup3 ("1"), dup3 ("2")); /* Should be "1", "1", "2", "2", "2". */

/* Correct answers are "e", "b", "a", empty.
   Current CVS barfs with `not enough arguments for macro "_c99_split_N"'
   where N varies for each line. */
c99_index (0, "a", "b", "c", "d", "e");
c99_index (3, "a", "b", "c", "d", "e");
c99_index (4, "a", "b", "c", "d", "e");
c99_index (5, "a", "b", "c", "d", "e");


More information about the Gcc mailing list