va_arg macro

Hi,

New here, not mean to be spam. If I’m doing something wrong please just tell me.

I’ve found that clang v7 treats va_arg, at least differently from gcc (and c standard says it should accept argument with one void). Clang does not accept va_arg(…,void).
When I was trying to build redis I got this:

hiredis.c:799:31: error: second argument to 'va_arg' is of incomplete type
      'void'
                    va_arg(ap,void);

I then write a work around by replacing `#define va_arg(ap, type) __builtin_va_arg(ap, type)` with those line to stdarg.h (In my environment, the path is /usr/local/Cellar/llvm/7.0.0/lib/clang/7.0.0/include):

#define __kscarlet_transform_cat_(x,y) x##y
#define __kscarlet_transform_cat(x,y) __kscarlet_transform_cat_(x,y)
#define __kscarlet_transform_void() )()__kscarlet_transform_n_(
#define __kscarlet_transform_e(...) __VA_ARGS__
#define __kscarlet_transform_e_(...) __VA_ARGS__
#define __kscarlet_transform_n(...)
#define __kscarlet_transform_n2(...) char
#define __kscarlet_transform_n_(...) __kscarlet_transform_n2
#define __kscarlet_transform_void_(...) __kscarlet_transform_e
#define va_arg(ap,type) __builtin_va_arg(ap,__kscarlet_transform_e_(__kscarlet_transform_void_ __kscarlet_transform_n()(__kscarlet_transform_cat( __kscarlet_transform_,type)()))(type))

This solves the problem, though it polluted global namespace. Do you have any idea to patch it / whether it should be patched?

Best,
BlueFlo0d

I've found that clang v7 treats va_arg, at least differently from gcc
(and c standard says it should accept argument with one void). Clang
does not accept va_arg(...,void).

GCC from version 6 onwards (and perhaps earlier) also gives an error
for this. And I don't see anything in the C standard to say it should
accept void as an argument: looking at section 7.15.1.1 paragraph 2
of C99 it says:

If there is no actual next argument, or if type is not compatible with
the type of the actual next argument (as promoted according to the
default argument promotions), the behavior is undefined, except for the
following cases:
  - one type is a signed integer type, the other type is the corresponding
    unsigned integer type, and the value is representable in both types;
  - one type is pointer to void and the other is a pointer to a character
    type.

void is not compatible with any type, so doing va_arg(something, void) is
undefined behaviour (though I wouldn't be surprised if something elsewhere
makes it invalid to use an incomplete type here for some other reason).
If it were to be permitted I also don't see anything meaningful that the
compiler could do with such a va_arg call, other than to say "void = nothing
therefore va_arg(something, void) means don't consume an argument" in
which case the solution is just to remove the va_arg call.

John

I’ve found that clang v7 treats va_arg, at least differently from gcc
(and c standard says it should accept argument with one void). Clang
does not accept va_arg(…,void).

GCC from version 6 onwards (and perhaps earlier) also gives an error
for this. And I don’t see anything in the C standard to say it should
accept void as an argument: looking at section 7.15.1.1 paragraph 2
of C99 it says:

If there is no actual next argument, or if type is not compatible with
the type of the actual next argument (as promoted according to the
default argument promotions), the behavior is undefined, except for the
following cases:

  • one type is a signed integer type, the other type is the corresponding
    unsigned integer type, and the value is representable in both types;
  • one type is pointer to void and the other is a pointer to a character
    type.

void is not compatible with any type, so doing va_arg(something, void) is
undefined behaviour (though I wouldn’t be surprised if something elsewhere
makes it invalid to use an incomplete type here for some other reason).

The argument needs to be made that it is impossible to pass a void expression as an argument to a function call. This happens to be true: argument passing defers to simple assignment, and the latter is not allowed for void.