Skip to content

[cpp.replace.general] p14: Clarification of directive-like preprocessing tokens in macro arguments #975

Description

@BlowingWind314

Full name of submitter (unless configured in github; will be published with the issue): Yin Xinyu

Reference (section label): [cpp.replace.general]

Issue description:

[cpp.replace.general]/14 says:

If there are sequences of preprocessing tokens within the list of arguments that would otherwise act as preprocessing directives, the program is ill-formed.

The intention is possibly avoiding multi-line macro invocations that cross text lines and preprocessing directives ([cpp.pre]). Consider:

// ill-formed
#define FOO(...)
FOO(                // text-line
#include <iostream> // pp-directive
)                   // text-line

Although it's impossible to perform a multi-line macro invocation starting at a preprocessing directive, the current wording would accept the following

// well-formed
#define FOO(...)
FOO(                // text-line
#bluh ) bluh        // pp-directive

since the conditionally-supported directive #bluh ) bluh is not within the list of arguments consisting of merely #bluh.

Implementations have no consensus on how to deal with this case (godbolt). By the way they have not even diagnosed the first example as ill-formed.

This can result in an even weirder situation, although results are all ill-formed. Consider:

#define FOO(...)
#define LPAREN (
#define NIL
FOO(                // text-line
#if LPAREN X ) NIL  // pp-directive
#endif              // pp-directive

and

#define FOO(...)
#ifdef X
FOO(                // text-line
#else ))            // pp-directive
#endif              // pp-directive

It's hard to tell what would happen, where X may be in different branches.

To avoid these complex and anti-intuitive circumstances, we'd better edit the wording of [cpp.replace.general]/14 to forbid these cases.

If an application such as stringization is needed, the following is well-formed.

#define STR(x) STR2(x)
#define STR2(x) #x
#define HASH #
#define NIL
STR(
    HASH include <iostream>
)
STR(
    NIL #include <iostream>
)

The arguments in STR2 is safe, considering [cpp.rescan]/4

The resulting completely macro-replaced preprocessing token sequence is not processed as a preprocessing directive even if it resembles one, but all pragma unary operator expressions within it are then processed as specified in [cpp.pragma.op] below.

Suggested resolution:

Change [cpp.replace.general]/14:

If there are sequences of preprocessing tokens within the list of arguments that , along with all preprocessing tokens immediately following them, would otherwise act as preprocessing directives, the program is ill-formed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions