cgccpragma

issues interleaving `#pragma` and `__attribute__` on GCC


Background

Note: This is not important for the question

I have some macros that act as slightly more general version of attributes, that I attach to functions. Depending on the compiler and compiler version, they will either expand to an attribute (e.g. __attribute__((nonnull))), or a warning (e.g. _Pragma("message \"nonnull attribute unavailable\"")). Since the attribute macros can be in any order, this can lead to them being intermixed (e.g. warning attribute attribute warning warning).

The result is that I can have code like the following:

Sample

#pragma message "one"
__attribute__((cold))
#pragma message "two"
__attribute__((noreturn))
#pragma message "three"
void test(void);

Issue

Running clang, this sample code compiles perfectly fine, printing the three messages as expected.

input.c:1:9: warning: one [-W#pragma-messages]
#pragma message "one"
        ^
input.c:3:9: warning: two [-W#pragma-messages]
#pragma message "two"
        ^
input.c:5:9: warning: three [-W#pragma-messages]
#pragma message "three"
        ^

Running gcc however, gives me an error on the second #pragma. If I comment that out, I get an error on the third #pragma.

input.c:1:9: note: ‘#pragma message: one’
    1 | #pragma message "one"
      |         ^~~~~~~
input.c:3:9: error: expected identifier or ‘(’ before ‘#pragma’
    3 | #pragma message "two"
      |         ^~~~~~~
input.c:1:9: note: ‘#pragma message: one’
    1 | #pragma message "one"
      |         ^~~~~~~
input.c:5:9: error: expected identifier or ‘(’ before ‘#pragma’
    5 | #pragma message "three"
      |         ^~~~~~~

With experimentation, it seems that gcc does not accept any #pragma directives after an __attribute__.

Reproducible Example

Uncommenting the #pragma causes a reproducible error on GCC v11.4.0

__attribute__((nonnull))
// #pragma message "this is an error"
void test(void);

Question

Is there any fix or solution to allow me to intermingle the #pragma and __attribute__ directives on gcc, just like clang allows?


Solution

  • I've tried a number of different combinations and they all produce the results you're seeing.

    This could be due to the different ways that gcc and clang do parsing. clang has the preprocessor fully integrated. For gcc, it's a separate pass/program.

    So, AFAICT, no direct solution.

    I have some macros that act as slightly more general version of attributes, that I attach to functions. Depending on the compiler and compiler version, they will either expand to an attribute (e.g. __attribute__((nonnull))), or a warning (e.g. _Pragma("message \"nonnull attribute unavailable\"")).

    You didn't specify how the macros are conditionally generated, but I'll take a guess. You have something similar to autoconf that probes the system and generates (e.g. config.h).

    There may be a workaround, if you [just] want to warn/error on an unsupported attribute.

    This is a bit of a hack, but ...

    #if HAVE_INLINE
    #define always_inline   always_inline
    #else
    #define always_inline   always_inline_not_supported
    #endif
    
    __attribute__ ((cold))
    __attribute__ ((noreturn))
    __attribute__ ((always_inline))
    void
    test(void *);
    

    Compiling with -DHAVE_INLINE compiles cleanly.

    Compiling without -DHAVE_INLINE produces:

    x.c:11:1: warning: ‘always_inline_not_supported’ attribute directive ignored [-Wattributes]
     test(void *);
     ^~~~
    

    It can be even simpler:

    #if !HAVE_INLINE
    #define always_inline   always_inline_not_supported
    #endif
    
    __attribute__ ((cold))
    __attribute__ ((noreturn))
    __attribute__ ((always_inline))
    void
    test(void *);