llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.06k stars 11.59k forks source link

Warn-unused-results warnings with expression statements dependent on whether the expr-stmt was typed or generated by the preprocessor #38715

Closed c8cd4046-bf2f-4470-bb42-add0a3666ec0 closed 2 years ago

c8cd4046-bf2f-4470-bb42-add0a3666ec0 commented 5 years ago
Bugzilla Link 39367
Version unspecified
OS Linux
CC @dwblaikie

Extended Description

Hi, my problem appears related to llvm/llvm-project#8590 and llvm/llvm-project#14119

I'm getting some very weird behavior out clang regarding the unused-value warnings.

This compiles without a warning with clang -c file.c:

void fail(void); long f(void); void g(void);
#define CHK(X) (__extension__ ({ long CHK=X; if(0>CHK) fail(); else{;} (void)CHK; CHK; }))
int main()
{
    int n = 0;
    f();
    CHK(f());
    switch(n){case 0: CHK(f()); }
    #define SWITCH(...) switch(n){ __VA_ARGS__ }
    SWITCH(case 0: f(); )

    #if OOPS
    SWITCH(case 0: CHK(f()); )
    #endif
}

but enabling the last swith with the pasted block generates an unused-value warning. clang -DOOPS file.c -c. Perhaps even more shockingly even without the OOPS macro being truthy, the program generates two warnings (or 3 #if !!OOPS) if compiled with clang file.c -E |clang -xc - -c, which ought to give the same result as clang file.c -c.

Complete set of examples (creates file.c in the $PWD):

#!/bin/sh -eu
cat > file.c <<EOF
void fail(void); long f(void); void g(void);
#define CHK(X) (__extension__ ({ long CHK=X; if(0>CHK) fail(); else{;} (void)CHK; CHK; }))
int main()
{
    int n = 0;
    f();
    CHK(f());
    switch(n){case 0: CHK(f()); }
    #define SWITCH(...) switch(n){ __VA_ARGS__ }
    SWITCH(case 0: f(); )

    #if OOPS
    SWITCH(case 0: CHK(f()); )
    #endif
}
EOF
clang -v 2>&1 |head -n1
echo NO WUR
clang -Wall file.c -c
echo WUR ON OOPS=1
clang -Wall -DOOPS=1 file.c -c
echo WUR ON SEPARATE PREPROCESSING "(NO OOPS)"
clang -E -Wall file.c  |clang -xc - -o /dev/null -c

My output:

clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
NO WUR
WUR ON OOPS=1
file.c:13:17: warning: expression result unused [-Wunused-value]
        SWITCH(case 0: CHK(f()); )
                       ^~~~~~~~
file.c:2:83: note: expanded from macro 'CHK'
#define CHK(X) (__extension__ ({ long CHK=X; if(0>CHK) fail(); else{;} (void)CHK; CHK; }))
                                                                                  ^~~
file.c:9:33: note: expanded from macro 'SWITCH'
        #define SWITCH(...) switch(n){ __VA_ARGS__ }
                                       ^~~~~~~~~~~
1 warning generated.
WUR ON SEPARATE PREPROCESSING (NO OOPS)
file.c:8:89: warning: expression result unused [-Wunused-value]
 switch(n){case 0: (__extension__ ({ long CHK=f(); if(0>CHK) fail(); else{;} (void)CHK; CHK; })); }
                                                                                        ^~~
file.c:7:71: warning: expression result unused [-Wunused-value]
 (__extension__ ({ long CHK=f(); if(0>CHK) fail(); else{;} (void)CHK; CHK; }));
                                                                      ^~~
2 warnings generated.

Suggestion:

Either disable unused-value warnings for expression statements like gcc does it or (better) allow __attribute((warn_unused_result)) on variables and if that variable is used for the "return" value of an expression statement, treat that expression statement like a function call to a function with the warn-unused-result attribute.

In my opinion, expr statements are kind of like function calls anyway, so it'd make sense to be able to somehow stick a warn-unused-result attribute to them.

Another idea, if clang wants to warn on unused expr-stmts by default, might be to suppres the warning if the expr-stmt evaluates to a variable mark with the unused attribute.

E.g., perhaps:

#define CHK(X) (__extension__ ({ long __attribute__((unused)) /*<-ADDED*/ CHK=X; if(0>CHK) fail(); else{;} (void)CHK; CHK; /*UNUSED ATTR*/}))

should never generate an unused result warning.

Thanks for consideration.

Best regards, Petr Skocik

dwblaikie commented 3 years ago

I haven't looked into the details of this, but wanted to point out one particular detail:

'if compiled with clang file.c -E |clang -xc - -c, which ought to give the same result as clang file.c -c.'

This is a non-goal of clang. Clang uses macro information to tune diagnostics to improve quality - running clang on original V preprocessed source can/will produce different diagnostic experiences.

Adding trace information to an error/warning about a place in a token sequence if that token sequence was added through the preprocessor is desirable and it naturally generates diagnostic experience that's dependent on whether something's entered manually or through a macro.

Sorry, I was a bit vague there - but I did mean "clang intentionally emits or omits certain diagnostics depending on their macro context". This is a fairly core design decision in Clang that's been made and reinforced (by new applications of that reasoning to improve the signal/noise ratio of various diagnostics over time) for quite some time.

So, going on the title of this bug I'd resolve this as "working as intended" - but the contents of the bug you've described various issues. Perhaps you could repurpose this bug to one of those issues, updating the title as desired?

c8cd4046-bf2f-4470-bb42-add0a3666ec0 commented 3 years ago

I haven't looked into the details of this, but wanted to point out one particular detail:

'if compiled with clang file.c -E |clang -xc - -c, which ought to give the same result as clang file.c -c.'

This is a non-goal of clang. Clang uses macro information to tune diagnostics to improve quality - running clang on original V preprocessed source can/will produce different diagnostic experiences.

Adding trace information to an error/warning about a place in a token sequence if that token sequence was added through the preprocessor is desirable and it naturally generates diagnostic experience that's dependent on whether something's entered manually or through a macro.

But conjuring up an entirely new warning for previously warning-free token sequence if, and only if, the token sequence is entered through the preprocessor or, conversely, silencing a warning in a token sequence if, and only if, it's pasted through the preprocessor, now that's highly counter-intuitive.

dwblaikie commented 5 years ago

I haven't looked into the details of this, but wanted to point out one particular detail:

'if compiled with clang file.c -E |clang -xc - -c, which ought to give the same result as clang file.c -c.'

This is a non-goal of clang. Clang uses macro information to tune diagnostics to improve quality - running clang on original V preprocessed source can/will produce different diagnostic experiences.

Quuxplusone commented 2 years ago

Based on dwblaikie's last comment, I'm going to be bold and close this; but feel free to reopen if there's still something to do here.