This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
[Bug c/50581] stdarg doesn't support array types
- From: "joseph at codesourcery dot com" <gcc-bugzilla at gcc dot gnu dot org>
- To: gcc-bugs at gcc dot gnu dot org
- Date: Sat, 01 Oct 2011 14:07:42 +0000
- Subject: [Bug c/50581] stdarg doesn't support array types
- Auto-submitted: auto-generated
- References: <bug-50581-4@http.gcc.gnu.org/bugzilla/>
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50581
--- Comment #3 from joseph at codesourcery dot com <joseph at codesourcery dot com> 2011-10-01 14:07:42 UTC ---
On Sat, 1 Oct 2011, Wolfgang at Solfrank dot net wrote:
> > There is no possible valid use of passing arrays to va_arg.
>
> What makes you think so? While the 1003.1 definition of va_arg explicitly
That the C semantics mean that:
* for C90 it is impossible to access values in the non-lvalue array passed
(the existence of the possibility of passing an array by value to a
variadic function in C90 is one of the most obscure and useless C
standards corner cases there is);
* for C99, calling va_arg with such a type always results in undefined
behavior at runtime because a pointer will have been passed rather than an
array.
So no valid C program can ever actually access a value in this way. Of
course, if you tried to pass an *lvalue* array (the normal case for arrays
in C) then it was converted to a pointer rather than passed by value, for
both C90 and C99, and so you had undefined behavior at runtime, for both
C90 and C99.
If you want to access a pointer, you need to pass a pointer type to
va_arg. This is just like accessing a promoted float or char where you
need to specify "double" or "int". Which is why generating an abort with
a warning at compile time seems appropriate - it's the well-established
practice for "char" and "float" here. (Given the obscurity, there should
probably be a warning for C90 as well, but without the abort.)
> > In C90, it is technically possible to use va_arg in this case without
> > undefined behavior. The argument passed to the function would have to be
> > a non-lvalue array - for example, an array in a structure returned from
> > another function. The result of va_arg would itself be a non-lvalue
> > array, which it is not possible to convert to a pointer, so it is not
> > possible to access the values in the array in any way; all that can be
> > done is to discard the value (call va_arg for its side effects) or to pass
> > it to another variadic function.
>
> Well, I'm not sure that I buy that. But even then, the current implementation
> in gcc doesn't generate the correct code even only for the side effects. The
> generated code in fact assumes that the array is passed by value, i.e. the
> pointer into the argument list (or something equivalent) is incremented by the
> size of the array instead of the size of a pointer.
Here is an example program that is valid as C90 but not as C99. It passes
for me with all the C90 options I tried. That is, the caller and callee
are consistent about the space expected to be taken by the array on the
stack, which is all that's required since there is no way of accessing the
array's value. (C ABIs won't generally specify this for interoperation
between implementations, given that passing by value an array whose value
you can't access isn't useful and the possibility of doing so has
disappeared in C99.)
#include <stdarg.h>
extern void abort (void);
extern void exit (int);
typedef char array[10000];
struct s { array a; } x;
void g (int a, ...);
int
main (void)
{
g (0, (0, x).a, 1234);
exit (0);
}
void
g (int a, ...)
{
va_list ap;
va_start (ap, a);
va_arg (ap, array);
if (va_arg (ap, int) != 1234)
abort ();
va_end (ap);
}
> My main use case for this feature isn't with random arrays, but with va_list
> itself. On some architectures (AFAIK all architectures that pass some
> arguments in registers) gcc implements va_list as a one element array of some
> structure. Without my proposed change, it isn't possible to have a va_list as
> an argument to a variadic function. This is what my second example in the bug
Passing va_list as a function argument is generally hard, whether or not
variadic, since you don't know whether it will be passed by reference or
by value or what the type of the address of a va_list parameter will be.
Portable code needs to pass a pointer to va_list or a structure containing
va_list or use some other such means of avoiding dependence on whether
va_list is an array.