25

I'm doing some experiments with the new _Generic keyword and stumbled upon a special case regarding multiple evaluations. See the following:

#include <stdio.h>

#define write_char(c) _Generic(c, char: putchar, const char: putchar)(c)

int main(void)
{
    const char *s = "foo";

    write_char(*s++);
    write_char(*s++);
    write_char(*s++);

    putchar('\n');
}

This compiles fine and produces the expected result with GCC:

$ gcc -std=c11 -Wall plusplus.c -o plusplus
$ ./plusplus 
foo

On the other hand, Clang outputs a big honking warning:

$ clang -std=c11 plusplus.c -o plusplus
plusplus.c:9:18: warning: multiple unsequenced modifications to 's'
      [-Wunsequenced]
    write_char(*s++);
                 ^~
plusplus.c:3:32: note: expanded from macro 'write_char'
#define write_char(c) _Generic(c, char: putchar, const char: putchar)(c)

...

Yet the result is as expected:

$ ./plusplus
foo

I checked the draft of the standard, which says (at p. 97 of the PDF):

The controlling expression of a generic selection is not evaluated.

This seems to precisely address the problem of side-effects in macros (e.g., MIN and MAX).

Now, can I safely ignore Clang's warning, or am I wrong?

michaelmeyer
  • 7,985
  • 7
  • 30
  • 36
  • 5
    Sounds like you should be reporting a problem to the Clang development team, rather than ignoring it. You may be the first to have encountered the issue. What happens when you write `putchar(*s++)`? Does that generate the warning too? Presumably not. – Jonathan Leffler Dec 20 '14 at 20:31
  • 3
    I think you discovered a bug in clang! – haccks Dec 20 '14 at 20:32
  • Fans of `_Generic` may be vaguely reminded of this question, so I'll link to it here and they can decide whether it's related (I don't think it is): http://stackoverflow.com/questions/24743520/incompatible-pointer-types-passing-in-generic-macro/ – Pascal Cuoq Dec 20 '14 at 20:39
  • @JonathanLeffler: I don't get a warning wen calling `putchar()` directly. Of course I'm OK for filing a bug report, I just wasn't sure whether I understood the thing right. – michaelmeyer Dec 20 '14 at 20:43
  • 3
    @PascalCuoq, no, it is not related. The warning that was observed there was justified. Here it isn't, there are no unsequenced evaluations of `++` in the given code. – Jens Gustedt Dec 20 '14 at 20:46
  • @PascalCuoq: I agree with Jens that this is different from the other question/bug, but it is similar in that both point out that generics are new (added in C11) and still not always handled reliably, so using generics can lead to unexpected problems with compilers. It is crucial to test on as many compilers as you can (or, at least, all the compilers you're going to be using with the genericized code) to make sure there aren't hidden gotchas. – Jonathan Leffler Dec 20 '14 at 20:50
  • 3
    @doukremt, I don't know if you filed the bug already (don't see it in the bug tracker), but after trying you example and seeing no errors I looked over commit history in Clang and found [this commit](http://reviews.llvm.org/rL223266) from Dec 3 2014. So it was actually a bug, and it's already fixed. – xaizek Feb 07 '15 at 14:41
  • 1
    @xaizek For reference of future readers, you could post this as the correct answer to the question. – Lundin Mar 05 '15 at 16:11

2 Answers2

3

As I mentioned in comments, you posted the question about two weeks after the bug was fixed in Clangs trunk. See revision rL223266 (December 3 2014). The fix is included in Clang 3.6.

Now, can I safely ignore Clang's warning, or am I wrong?

We already know that you're right, so here is a way to ignore pragmas in Clang for the future:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunsequenced"

write_char(*s++);

#pragma clang diagnostic pop

To do not repeat it on every use of the macro, you could put _Pragma in its body:

#define write_char(c) \
    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Wunsequenced\"") \
    _Generic(c, char: putchar, const char: putchar)(c) \
    _Pragma("clang diagnostic pop")
xaizek
  • 5,098
  • 1
  • 34
  • 60
0

It seems that was a bug. It has now been solved since clang 3.6 onward as shown here.

edmz
  • 8,220
  • 2
  • 26
  • 45