Authors: Jay Ghiron
Date: 2026-05-28
Submitted against: C23
Status: Open
Implementations disagree about the meaning of the following text, specifically when concatenating a placemarker preprocessing token with an identifier that was disabled from being expanded:
For both object-like and function-like macro invocations, before the replacement list is reexamined for more macro names to replace, each instance of a
##preprocessing token in the replacement list (not from an argument) is deleted and the preceding preprocessing token is concatenated with the following preprocessing token. Placemarker preprocessing tokens are handled specially: concatenation of two placemarkers results in a single placemarker preprocessing token, and concatenation of a placemarker with a non-placemarker preprocessing token results in the non-placemarker preprocessing token. If the result is not a valid preprocessing token, the behavior is undefined. The resulting token is available for further macro replacement. The order of evaluation of##operators is unspecified.
(C23 6.10.5.4 "The ## operator" paragraph 3.)
Consider the following macros:
#define FOO()FOO
#define CAT(X,Y,...)__VA_OPT__(X)##__VA_OPT__(Y)
FOO() will expand to FOO, but the resulting identifier cannot be
expanded again. Therefore FOO()() will expand to FOO() rather
than FOO. CAT(FOO(),,,)() does the same as FOO()(), but it
concatenates the resulting identifier with a placemarker preprocessing
token before attempting to expand it again. If "The resulting token
is available for further macro replacement." is interpreted as
applying to this concatenation, then it should expand to FOO.
Otherwise, it should expand to FOO(). GCC and EDG expand it to
FOO, but Clang and MSVC expand it to FOO(). The interpretation of
this wording by GCC is not consistent however, see also GCC issue
125145.
I believe the intent here is that concatenating with a placemarker
preprocessing should be effectless, meaning that the result of
CAT(FOO(),,,)() should be FOO(). It would be useful to add
wording to clarify what the result here is.
Modify C23 6.10.5.4 paragraph 3:
The resulting token is available for further macro replacement if neither of the two concatenated tokens are placemarker preprocessing tokens.