Home > Software engineering >  Should compilation of previously non-preprocessed source code lead to the same diagnostics as compil
Should compilation of previously non-preprocessed source code lead to the same diagnostics as compil

Time:12-31

Consider this scenario:

$ cat t783.c
#define EXPR ("xxx"   1)
char* s = EXPR;

$ clang t783.c -c
t783.c:2:11: warning: adding 'int' to a string does not append to the string [-Wstring-plus-int]

$ clang t783.c -E | clang -xc - -c
t783.c:2:18: warning: adding 'int' to a string does not append to the string [-Wstring-plus-int]

Here we see that compilation of previously non-preprocessed source code (case 1) leads to the same diagnostics as compilation of previously preprocessed source code (case 2).

Question: is this always true? (Excluding, of course, the diagnostics related to the preprocessor itself.)

In other words: should diagnostics be "preprocessor-insensitive"?

Reason: better understanding of compilers.

CodePudding user response:

No, not always.

For instance, GCC has an option -Wmultistatement-macros, enabled by -Wall, to warn when a macro expands to multiple statements that are not all guarded by the same if, while, etc. See manual. A filled-in version of the manual's example:

void foo(void);
void bar(void);

#define BLAH foo(); bar()

void qux(int cond) {
    if (cond)
        BLAH;
}

The programmer probably intends that foo() and bar() should be called only if cond is true, but actually bar() will always be called.

Running gcc -Wall -c foo.c produces the warning:

foo.c: In function ‘qux’:
foo.c:4:14: warning: macro expands to multiple statements [-Wmultistatement-macros]
    4 | #define BLAH foo(); bar()
      |              ^~~
foo.c:8:2: note: in expansion of macro ‘BLAH’
    8 |  BLAH;
      |  ^~~~
foo.c:7:5: note: some parts of macro expansion are not guarded by this ‘if’ clause
    7 |     if (cond)
      |     ^~

But gcc -E -Wall foo.c > foo.i and gcc -Wall -c foo.i separately do not produce any diagnostics at all.

Evidently there is some information shared between the preprocessing and compilation passes when you run them together, so that the compiler phase knows that foo(); bar() resulted from the expansion of a macro. But the preprocessed source file emitted by gcc -E does not itself contain this information, so when gcc is run on foo.i, it doesn't know that there was originally a macro, so it can't give the warning.


Per the tag, I see no conformance problem with this behavior. The C standard says only, roughly speaking, that an implementation must issue diagnostics in certain cases, and otherwise must translate conforming programs within implementation limits. Beyond this it is free to issue diagnostics whenever it wants, so long as they are non-fatal for a conforming program. There is no rule that the decision of whether to issue a diagnostic, or what it should say, should be solely determined by the contents of the translation unit after preprocessing. The compiler can certainly take into account the contents of the source before preprocessing, or the contents of other unrelated translation units (e.g. to detect when global declarations don't match), or the contents of other files on the system (e.g. compiler configuration files), or the current phase of the moon. No rules against it. And I can't imagine that the standard authors would have wanted to forbid a useful feature like -Wmultistatement-macros.

CodePudding user response:

Yes, it is always the truth. The compiler compiles preprocessed source so it will get the same source in both cases.

  • Related