5

The C11 standard admits vagueness with regard to at least one situation that can arise in macro expansion, when a function like macro expands to its unenvoked name, and is invoked by the next preprocessing token. The example given in the standard is this.

#define f(a) a*g
#define g(a) f(a)

// may produce either 2*f(9) or 2*9*g
f(2)(9)

That example does not clarify what happens when a macro, M, is expanded, and all or part of the result contributes via token concatenation to a second preprocessing token, M, which is invoked.

Question: Is such an invocation blocked?

Here is an example of such an invocation. This issue tends to only come up when using a fairly complicated set of macros, so this example is contrived for the sake of simplicity.

// arity gives the arity of its args as a decimal integer (good up to 4 args)
#define arity(...) arity_help(__VA_ARGS__,4,3,2,1,)
#define arity_help(_1,_2,_3,_4,_5,...) _5

// define 'test' to mimic 'arity' by calling it twice
#define test(...) test_help_A( arity(__VA_ARGS__) )
#define test_help_A(k) test_help_B(k)
#define test_help_B(k) test_help_##k
#define test_help_1 arity(1)
#define test_help_2 arity(1,2)
#define test_help_3 arity(1,2,3)
#define test_help_4 arity(1,2,3,4)

// does this expand to '1' or 'arity(1)'?
test(X)

test(X) expands to test_help_A( arity(X) ), which invokes test_help_A on rescanning, which expands its arg before substitution, and so is identical to test_help_A(1), which produces test_help_B(1), which produces test_help_1. This much is clear.

So, the question comes in here. test_help_1 is produced using a character, 1, that came from an expansion of arity. So can the expansion of test_help_1 invoke arity again? My versions of gcc and clang each think so.

Can anyone argue that the interpretations made by gcc and clang are required by something in the standard?

Is anyone aware of an implementation that interprets this situation differently?

Kyle
  • 878
  • 6
  • 14
  • What do you mean by "blocked"? – user2357112 Jul 25 '16 at 20:11
  • In the first example, the one from the standard, it is possible for the implementation to 'block' the second expansion of `f` to end with `2*f(9)`, rather than `2*9*g`. The invocation of f is possible to consider invalid because it can be argued to be recursive. In other words, the invocation of `f` is blocked. I am asking if the same reasoning could justify an implementation in blocking the last expansion of `arity`. – Kyle Jul 25 '16 at 20:14

1 Answers1

2

I think that gcc's and clang's interpretation are correct. The two expansions of arity are not in the same call path. The first descends from the expansion of test_help_A's argument, the second from the expansion of test_help_A itself.

The idea of these rules is to guarantee that there can't be infinite recursion, which is guaranteed, here. There is progress in the evaluation of the macro between the two calls.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • 1
    BTW, the next time you ask such quite technical question, could you please reduce your example to the minimum necessary? Here the multiple arguments to `arity` and the different versions of `test_help_X` are just distracting. – Jens Gustedt Jul 25 '16 at 21:23
  • I guess I should have used a smaller maximum arity, but eliminating `test_help_B` would produce `test_help_arity(__VA_ARGS__)`, wouldn't it? – Kyle Jul 25 '16 at 21:48
  • I'm not sure that argument applies, as the standard states that it is unspecified whether an invocation is nested if it gets its name from a macro replacement and its parenthetically enclosed arg list from outside that replacement. Such split nesting, so to speak, would not create a potential for recursion either, yet it is unspecified. My example simply extends that idea of a split nesting to the macro name being separated by the expansion border, rather than the macro name and its arg list being separated. The two situations seems too related to make any assumptions not supported elsewhere. – Kyle Jul 26 '16 at 00:30
  • That being said, thank you for your answer, it helps me to feel more comfortable in assuming that such situations do not block macro expansion. It would seem pretty perverse if they did. I think I'll just proceed on that assumption. – Kyle Jul 26 '16 at 00:34