Search code examples
cgccclangpreprocessor

What happened to my semicolon? (gcc preprocessor)


I thought I'd be clever and write a little preprocessor wrapper around Disable/Enable interrupts, like this:

#define WITH_INTERRUPTS_DISABLED(_body)                                        \
  do {                                                                         \
    uint32_t primask = DisableGlobalIRQ();                                     \
    do {                                                                       \
      _body                                                                    \
    } while (0);                                                               \
    EnableGlobalIRQ(primask);                                                  \
  } while (0)

The GCC preprocessor, when presented with this source:

WITH_INTERRUPTS_DISABLED(
  i += 2; 
  quux();
);

... generates this output (reformatted for readability):

do {
  uint32_t primask = DisableGlobalIRQ();
  do {
    i += 2;
    quux();
  } while (0);
  EnableGlobalIRQ(primask);
} while (0)

According to Mr. Godbolt, all of the GCC variants behave as above, omitting the closing semicolon. At the same time, the output of all of clang variants INCLUDE the semicolon.

Is there a way to convince GCC to include the semicolon?


Solution

  • Nothing happened to the semicolon, GCC produced it just fine. It would be extremely startling if GCC and clang differed on something this basic, which surely must be well defined by the C standard and is widely used in real-world code.

    However the default settings of the Godbolt Compiler Explorer will filter it out, probably because it looks like a comment in assembly language. Example from interjay: https://godbolt.org/z/94e4q3ehK

    If you turn off "Filter:Comments" you will see the semicolon is there, preceded by a couple of blank lines: https://godbolt.org/z/7n896or8Y

      do { uint32_t primask = DisableGlobalIRQ(); do { i += 2; quux(); } while (0); EnableGlobalIRQ(primask); } while (0)
    
    
       ;
    

    As interjay points out, the Explorer is designed to display assembly output, not preprocessed C, and so it's not surprising that -E is not supported very well.

    It appears clang omits the blank lines, which I guess happens to prevent Godbolt from thinking it's a comment:

      do { uint32_t primask = DisableGlobalIRQ(); do { i += 2; quux(); } while (0); EnableGlobalIRQ(primask); } while (0);
    

    I don't think there's any compliance problem with this difference, since the sequence of tokens is the same in both cases.