[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#715271: Preprocessor handles _Pragma badly



Package: gcc
Version: 4:4.7.2-1

The `_Pragma' keyword is handled badly by GCC's preprocessor.  Consider
the following program, which is a simplified version of a warning
suppression framework and a test case.

#include <stdio.h>

#define PRAGMA(x) _Pragma(#x)

#define WARNING(warn) PRAGMA(GCC diagnostic ignored warn)

#define SANDWICH(warns, filling)					\
  _Pragma("GCC diagnostic push")                                        \
  warns									\
  filling								\
  _Pragma("GCC diagnostic pop")

#define CHECK(cond) __extension__ ({					\
  SANDWICH(WARNING("-Waddress"), !!(cond); )				\
})

#define WRAP(x) x

int main(void)
{
  int y = 0;
  if (CHECK(&y)) puts("Hello, world!");
  return (0);
}

This doesn't emit warnings.  Unfortunately, doesn't have the intended
effect, as the preprocessor output reveals:

int main(void)
{
  int y = 0;
  if (__extension__ ({
# 22 "<stdin>"
#pragma GCC diagnostic ignored "-Waddress"
# 22 "<stdin>"

# 22 "<stdin>"
#pragma GCC diagnostic push
# 22 "<stdin>"
  !!(&y);
# 22 "<stdin>"
#pragma GCC diagnostic pop
# 22 "<stdin>"
  })) puts("Hello, world!");
  return (0);
}

Somehow the `GCC diagnostic ignored "-Waddress"' has escaped from its
sandwich.

What's going on?  A plausible explanation is that the pragmata are being
acted upon immediately they're encountered during macro expansion, and
macro arguments are expanded before being substituted into the body.
Let's try to fix this by making SANDWICH more complicated: in
particular, we'll offset the WARNING calls to delay their expansion
until the rescanning phase.

#include <stdio.h>

#define PRAGMA(x) _Pragma(#x)

#define MUFFLE_t(warn) PRAGMA(GCC diagnostic ignored warn)
#define MUFFLE_nil(warn)
#define MUFFLE(emitp, warn) MUFFLE_##emitp(warn)

#define WARNING(warn) (t, warn) MUFFLE

#define SANDWICH(warns, filling)					\
  _Pragma("GCC diagnostic push")                                        \
  MUFFLE warns (nil, nil)						\
  filling								\
  _Pragma("GCC diagnostic pop")

#define CHECK(cond) __extension__ ({					\
  SANDWICH(WARNING("-Waddress"), !!(cond); )				\
})

#define WRAP(x) x

int main(void)
{
  int y = 0;
  if (CHECK(&y)) puts("Hello, world!");
  return (0);
}

This now appears to have the correct effect.  Unfortunately, it actually
doesn't.

[gibson /tmp/mdw]gcc -c -O2 -g -Wall -pedantic pragbug2.c
pragbug2.c: In function ‘main’:
pragbug2.c:26:1: warning: the address of ‘y’ will always evaluate as
‘true’ [-Waddress]
[gibson /tmp/mdw]

Interestingly, though, the preprocessor output looks fine, and, just for
extra amusement,

[gibson /tmp/mdw]gcc -c -O2 -g -Wall -pedantic -no-integrated-cpp pragbug2.c
[gibson /tmp/mdw]

My guess is that there's some kind of special channel directly from the
integrated preprocessor into the main compiler's pragma-handling, so the
whole pragma sandwich goes by during macro expansion, before the
compiler proper sees any of the actual code.  What's clear is that
there's something rather wrong with the way _Pragma is handled by the
preprocessor, which is causing out-of-order behaviour and inconsistency
between the integrated and separate preprocessors.

-- [mdw]


Reply to: