C++ Logo

liaison

Advanced search

Re: [isocpp-wg14/wg21-liaison] Extra arguments to va_start

From: Jonathan Wakely <cxx_at_[hidden]>
Date: Mon, 10 Nov 2025 18:59:13 +0000
On Mon, 10 Nov 2025, 17:10 Aaron Ballman via Liaison, <
liaison_at_[hidden]> wrote:

> I think the situation with va_start is a bit confused and there
> appears to be implementation divergence in the wild. Consider this
> code:
>
> void func(int i, ...) {
> va_list list;
> va_start(list, +);
> }
>
> 7.16.2.5p4: Only the first argument passed to va_start is evaluated.
> If any additional arguments expand to include unbalanced parentheses,
> or a preprocessing token that does not convert to a token, the
> behavior is undefined.
>
> So "list" is evaluated and "+" is not. Is the + then discarded as a
> token? So is this valid? GCC and TCC accept, Clang and SDCC reject:
> https://godbolt.org/z/Yf317Kxfx What if `+` was an undeclared
> identifier instead?
>
> (Keep in mind, we use the same "only ... is evaluated" in other
> circumstances where the resulting code is still expected to be
> semantically correct. e.g., `1 || undeclared_identifier` is still a
> constraint violation even though `undeclared_identifier` is not
> evaluated.)
>
> Now think about this in terms of __COUNTER__ from C2y, with this code:
>
> void func(int i, ...) {
> va_list list;
> va_start(list, __COUNTER__);
> static_assert(__COUNTER__ == 0);
> }
>
> Does that static assertion pass or fail? It fails in Clang and GCC,
> doesn't compile correctly in SDCC, and TCC it succeeds:
> https://godbolt.org/z/56qT9sarK At least with Clang and GCC, the
> implementation of the macro is to forward to a builtin function so
> that the builtin can generate appropriate diagnostics, so the
> __COUNTER__ macro is expanded. Avoiding that expansion but keeping the
> same level of QoI is a surprisingly large burden (we'd need a new
> builtin and way to count the number of arguments in __VA_ARGS__ which
> does not expand the tokens, I believe).
>
> I think this is an issue SG22 should take up (hence, I've CCed Nina)
> because the C++ wording is different from the C wording. In C++, the
> tokens (if any) are *discarded* explicitly
> (https://eel.is/c++draft/cstdarg.syn#1.2). I believe in C++, both of
> those examples are expected to compile without diagnostics.
>


I think it would be entirely reasonable to _warn_ for the first example,
because it was nonsense in older standards. The only reason to accept more
than one argument for va_start is so that existing code passing two
arguments will still compile. But all existing code passes an identifier.
There's no reason for new code to start passing nonsense as the second
argument.

Similarly for the second example, which uses a feature that isn't even
present in older standards.

So I think it's reasonable for implementations to issue a diagnostic for
nonsense uses of va_start, but still compile the code. I.e. warn.

But ignoring any possible warning, I agree that C++ is expected to compile
both examples (before and after the
https://cplusplus.github.io/LWG/issue4388 change that we just approved two
days ago).

Received on 2025-11-10 18:59:33