__VA_OPT__() hypothetical substitutionAuthors: Jay Ghiron
Date: 2026-05-20
Submitted against: C23
Status: Open
GCC and Clang disagree about the validity of the following program (preprocessing only):
#define F()
#define G(...)__VA_OPT__()
G(F(...))
That is, whether or not __VA_OPT__() causes an expansion of the argument:
The preprocessing token sequence for the corresponding argument of a va-opt-replacement is defined as follows. If a (hypothetical) substitution of
__VA_ARGS__as neither an operand of#nor##consists of no preprocessing tokens, the argument consists of a single placemarker preprocessing token (6.10.5.4, 6.10.5.5). Otherwise, the argument consists of the results of the expansion of the contained pp-tokens as the replacement list of the current function-like macro before removal of placemarker tokens, rescanning, and further replacement.
(C23 6.10.5.2 "Argument substitution" paragraph 7.)
If a hypothetical substitution is invalid, is the program invalid?
Since the substitution is hypothetical, does it need to happen when
the result of the substitution does not matter? This difference is
also visible with C2Y __COUNTER__:
#define G(...)__VA_OPT__()
G(__COUNTER__)
int main(){
return __COUNTER__;
}
There is no disagreement about __VA_OPT__(E) if E is defined as:
#define E
In the context of C2Y __COUNTER__, it seems useful to not require
expansion here:
#define H(...)__VA_ARGS__##__VA_OPT__()
/* do nothing macro, not even an extra expansion */
int x=H(__COUNTER__);
int y=H(__COUNTER__);
/* 0 1 with Clang, 1 3 with GCC */