C++ Logo

liaison

Advanced search

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

From: Jonathan Wakely <cxx_at_[hidden]>
Date: Wed, 12 Nov 2025 17:39:36 +0000
On Wed, 12 Nov 2025, 15:52 Aaron Ballman via Liaison, <
liaison_at_[hidden]> wrote:

> This is why I'm asking SG22 to step in; this seems like a needless
> incompatibility between C and C++.
>
> void func(int i, ...) {
> va_list list;
>
> va_start(list, blah); // #1
> va_start(list, __COUNTER__); // #2
> }
>
> #1 is required to be diagnosed in C17 and C++23, is required to be
> accepted in C++26.
>

Does it matter though?

Are we really worried that people will start using va_start this way in
C++26 and ... create code that can't be compiled as C++23? Who cares?

Nobody wrote that code yesterday, because it didn't work. Why would anybody
write that code today?

C23 is unclear IMO. The standard says the argument is not evaluated. So if
> the implementation expands to `sizeof(blah)` for some reason, that's
> allowed by the standard wording for va_start because `blah` is not
> evaluated? I think the intent for C23 is clear in that this code is
> expected to be semantically well-formed regardless of what it expands to,
> and there's a recommendation to diagnose if any additional arguments are
> passed.
>
> #2 is required to be diagnosed in C17 and C++23, is required to be
> accepted in C23 and C++26, where __COUNTER__ is expected to increment in C
> and expected to not increment in C++.
>

Again, I find it hard to care.

We want to continue accepting existing code, and want to permit (but not
require) diagnostics for the dumbest mistakes in new code. Beyond that,
does it matter if there's implementation divergence in edge cases that
nobody would ever encounter outside of conformance tests?


> In both cases, I think this boils down to whether the committees expect
> something like:
>
> #define va_start(list, ...) __builtin_va_start(list, __VA_ARGS__)
>
> where the optional arguments passed to the macro are expanded and must be
> valid for a call to a builtin function. I think this is how WG14 expected
> things to work and so #1 would be rejected due to use of an undeclared
> identifier, similar to what would happen with `1 || blah` in an expression
> (`blah` is not evaluated, but it still has to be semantically valid). And
> #2 would increment __COUNTER__ because the macro is expanded for the call
> to the builtin.
>
> Or whether the committees expect something more like:
>
> #define va_start(list, ...) __builtin_va_start(list)
>
> where the optional arguments passed to the macro are not expanded and
> therefore don't have to be valid beyond what the preprocessor requires. I
> think this is how WG21 expected things to work, so #1 would be accepted and
> #2 would not increment __COUNTER__.
>
> ~Aaron
>
>
>
> On Tue, Nov 11, 2025 at 6:46 AM Alex Celeste via Liaison <
> liaison_at_[hidden]> wrote:
>
>> I believe we discussed basically this same question within WG14 before
>> adopting the wording, and the "evaluated" wording was a revision/compromise
>> intended to reduce the structness of the `va_start` macro so that it could
>> still report errors if they were needed, but not to require anything
>> special of it above the behaviour that would be expected for any other
>> macro that doesn't exand its arguments into an evaluated position.
>>
>> So that second argument certainly isn't supposed to be being discarded by
>> any special mechanism that would block incrementing `__COUNTER__` by some
>> means not available to a user-defined macro. The original wording was
>> stricter and might have implied this, but "evaluated" was chosen to
>> indicate that it's the C-level evaluation that definitely doesn't happen,
>> without implying anything about macro expansion. The macro part of this
>> feature should just follow normal macro rules.
>>
>> Thanks,
>>
>> Alex
>>
>> [image: Perforce Software]
>>
>> Alex Celeste | Elder Witch of the C Compiler, Helix QAC
>> <https://www.perforce.com/products/helix-qac>
>>
>> *Perforce Software <https://www.perforce.com>*
>>
>> P +1 612.517.2100
>>
>> Visit Us On: *LinkedIn <https://www.linkedin.com/company/perforce>* | *YouTube
>> <https://www.youtube.com/user/perforcesoftware>*
>>
>> ------------------------------
>> *From:* Liaison <liaison-bounces_at_[hidden]> on behalf of J Decker
>> via Liaison <liaison_at_[hidden]>
>> *Sent:* 11 November 2025 10:53
>> *To:* liaison_at_[hidden] <liaison_at_[hidden]>
>> *Cc:* J Decker <d3ck0r_at_[hidden]>
>> *Subject:* Re: [isocpp-wg14/wg21-liaison] Extra arguments to va_start
>>
>>
>>
>> On Mon, Nov 10, 2025 at 9:10 AM 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.
>>
>>
>> 'Is Evaluated' means 'computing the value of an expression and carrying
>> out its associated side effects'
>> The second argument, not being evaluated, doesn't mean it's ignored, but
>> that it's a literal that gets stuffed into the result without evaluation.
>>
>> It doesn't say 'there's only one argument, and others are ignored' it
>> says 'only the first is evaluated'
>>
>> like doing `&((struct thing *)0)->member ` to get offsetof, the actual
>> pointer there isn't evaluated...
>>
>>
>>
>> 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.
>>
>> Thanks!
>>
>> ~Aaron
>> _______________________________________________
>> Liaison mailing list
>> Liaison_at_[hidden]
>> Subscription: https://lists.isocpp.org/mailman/listinfo.cgi/liaison
>> Searchable archives: http://lists.isocpp.org/liaison/2025/11/index.php
>>
>>
>>
>> *CAUTION:* This email originated from outside of the organization. Do
>> not click on links or open attachments unless you recognize the sender and
>> know the content is safe.
>>
>> This e-mail may contain information that is privileged or confidential.
>> If you are not the intended recipient, please delete the e-mail and any
>> attachments and notify us immediately.
>>
>> _______________________________________________
>> Liaison mailing list
>> Liaison_at_[hidden]
>> Subscription: https://lists.isocpp.org/mailman/listinfo.cgi/liaison
>> Link to this post: http://lists.isocpp.org/liaison/2025/11/1596.php
>>
> _______________________________________________
> Liaison mailing list
> Liaison_at_[hidden]
> Subscription: https://lists.isocpp.org/mailman/listinfo.cgi/liaison
> Link to this post: http://lists.isocpp.org/liaison/2025/11/1597.php
>

Received on 2025-11-12 17:39:56