Compound literal and designated initializer warning from GCC but not Clang - c

Compiling with gcc -std=c99 -Wextra this piece of code:
#include <stdio.h>
struct T {
int a;
int *b;
int c;
};
int main(void)
{
struct T t = {.b = ((int []){1, 1})};
printf("%d\n", t.b[1]);
return 0;
}
Is giving me a warning:
demo.c:11:12: warning: missing initializer for field ‘c’ of ‘struct T’ [-Wmissing-field-initializers]
struct T t = {.b = ((int []){1, 1})};
^
demo.c:6:9: note: ‘c’ declared here
int c;
^
But designated initializers are supposed to initialize to zero the rest of the members even if they are ommited.
Why the warning? (clang compiles the same piece of code without warnings)
gcc version 6.3.0 20170516 (Debian 6.3.0-18)
clang version 3.8.1-24 (tags/RELEASE_381/final)

It looks like a gcc "consistency bug", here is the relevant code snippet in gcc/c/c-typeck.c
7436 /* Warn when some struct elements are implicitly initialized to zero. */
7437 if (warn_missing_field_initializers
7438 && constructor_type
7439 && TREE_CODE (constructor_type) == RECORD_TYPE
7440 && constructor_unfilled_fields)
7441 {
7442 bool constructor_zeroinit =
7443 (vec_safe_length (constructor_elements) == 1
7444 && integer_zerop ((*constructor_elements)[0].value));
7445
7446 /* Do not warn for flexible array members or zero-length arrays. */
7447 while (constructor_unfilled_fields
7448 && (!DECL_SIZE (constructor_unfilled_fields)
7449 || integer_zerop (DECL_SIZE (constructor_unfilled_fields))))
7450 constructor_unfilled_fields = DECL_CHAIN (constructor_unfilled_fields);
7451
7452 if (constructor_unfilled_fields
7453 /* Do not warn if this level of the initializer uses member
7454 designators; it is likely to be deliberate. */
7455 && !constructor_designated
7456 /* Do not warn about initializing with ` = {0}'. */
7457 && !constructor_zeroinit)
7458 {
7459 if (warning_at (input_location, OPT_Wmissing_field_initializers,
7460 "missing initializer for field %qD of %qT",
7461 constructor_unfilled_fields,
7462 constructor_type))
7463 inform (DECL_SOURCE_LOCATION (constructor_unfilled_fields),
7464 "%qD declared here", constructor_unfilled_fields);
7465 }
7466 }
The intent of the code appears to be to warn if any attribute constructor has an unfilled-field. The fact that you are not getting a warning on element 'a' is likely the "consistency bug" here.
If -Wextra is intended to turn on the missing initializers warning, then it has. The question is, should the "missing initializers warning" exclude omitted attributes? It seems that gcc and clang disagree about this - and it might be fine for them to?
This may not be the answer you are looking for .. but hope it helps with your understanding of the situation. :). GCC team has a consistency bug, but their code's intent seems to be warn in these cases, whereas clang, empirically, will not.

Why the warning?
Because -Wmissing-field-initializers is set by -Wextra and you set the latter in the gcc call.
-Wextra is picky, and -Wmissing-field-initializers is not even element of -Wall.
Omitting just some field initializers but not all is a source of error. In an array / struct with has some hundreds elements and you are just initializing some of them, then it's almost impossible for a human to grasp that by just looking at the code.

Related

How to force Werror=declaration-after-statement with -std=c99 in clang

I would like to have compiler throw an error every time there is a declaration after statement because that is the coding style I want to enforce, but I also want to compile with -std=c99 since I use some of the specific c99 features.
The problem is that in c99 declarations are allowed anywhere in the code, not just at the beginning of a block.
Take a look at the following program:
// prog.c
#include <stdio.h>
int main(void)
{
printf("hello world\n");
int i = 0;
return 0;
}
If I compile this code with gcc like this:
gcc -std=c99 -Werror=declaration-after-statement prog.c
it throws the following error:
prog.c: In function ‘main’:
prog.c:6:9: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
6 | int i = 0;
| ^~~
cc1: some warnings being treated as errors
This is the behavior I would like to have when compiling with clang, but clang behaves differently.
If I compile the same code with clang like this:
clang -std=c99 -Werror=declaration-after-statement prog.c
it throws no errors.
Only if I compile the code with clang like this it throws the error I want:
clang -std=c90 -Werror=declaration-after-statement prog.c
prog.c:6:6: error: ISO C90 forbids mixing declarations and code [-Werror,-Wdeclaration-after-statement]
int i = 0;
^
1 error generated.
But this is not good for me because I need to use -std=c99.
Is it possible to force -Werror=declaration-after-statement along with -std=c99 when compiling with clang?
Looking at the source code of clang it seems like not supported.
The diagnostic is defined in clang/include/clang/Basic/DiagnosticSemaKind.td
def ext_mixed_decls_code : Extension<
"ISO C90 forbids mixing declarations and code">,
InGroup<DiagGroup<"declaration-after-statement">>;
And its only usage is in clang/lib/Sema/SemaStmt.cpp
StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R,
ArrayRef<Stmt *> Elts, bool isStmtExpr) {
const unsigned NumElts = Elts.size();
// If we're in C89 mode, check that we don't have any decls after stmts. If
// so, emit an extension diagnostic.
if (!getLangOpts().C99 && !getLangOpts().CPlusPlus) {
// Note that __extension__ can be around a decl.
unsigned i = 0;
// Skip over all declarations.
for (; i != NumElts && isa<DeclStmt>(Elts[i]); ++i)
/*empty*/;
// We found the end of the list or a statement. Scan for another declstmt.
for (; i != NumElts && !isa<DeclStmt>(Elts[i]); ++i)
/*empty*/;
if (i != NumElts) {
Decl *D = *cast<DeclStmt>(Elts[i])->decl_begin();
Diag(D->getLocation(), diag::ext_mixed_decls_code); // <-- here
}
}
...
Note the !getLangOpts().C99 in the if. The diagnose code will never execute with a standard above c90.
Well one thing you can surely try is build clang by yourself and delete that part of the if so end up with if (!getLangOpts().CPlusPlus).
I tried and it worked for me.
You can configure the clang build with cmake -G "Ninja" -DCMAKE_BUILD_TYPE="Release" -DLLVM_ENABLE_PROJECTS="clang" -DLLVM_TARGETS_TO_BUILD="X86" -DCMAKE_C_COMPILER="/usr/bin/gcc" -DCMAKE_CXX_COMPILER="/usr/bin/g++" -DLLVM_PARALLEL_LINK_JOBS=2 -DLLVM_OPTIMIZED_TABLEGEN=ON path/to/llvm-project/llvm

Clang parameter -Wno-conditional-type-mismatch and GCC

The following simple C example program correctly emits a compile warning:
#include <stdio.h>
int main(int argc, char *argv[])
{
int *p = NULL;
int q = 1;
if(1 == 2 ? p : q) { printf("Info\n"); }
return(0);
}
The warning emitted is "warning: pointer/integer type mismatch in conditional expression".
In clang, the parameter "-Wno-conditional-type-mismatch" works in such a way that no warnings appear for the example code.
In GCC, I was looking for a similar option. The best I could find was the parameter "-fcond-mismatch", which would allow the example code. From the man page:
Allow conditional expressions with mismatched types in the second and third arguments. The
value of such an expression is void. This option is not supported for C++.
But when using this option, GCC keeps complaining:
# gcc -fcond-mismatch -c example.c
example.c: In function 'main':
example.c:8:17: warning: pointer/integer type mismatch in conditional expression
8 | if(1 == 2 ? p : q) { printf("Info\n"); }
Using GCC 10.3.0, would there be a GCC option or compiler flag to suppress the warning, in a similar way as clang does?
I don't think there is any such flag. GCC's manual says, under -Werror=, "The warning message for each controllable warning includes the option that controls the warning."
Since this message does not mention such an option, this suggests it is not controllable.

Why does GCC 9.1.0 sometimes complain about this use of strncpy()?

This is a 40-line MCVE (Minimal, Complete, Verifiable Example) — or something close to minimal — cut down from a 1675 line source file that originally included 32 headers (and most of those included multiple other headers — compiling it with gcc -H lists 464 headers from the project and the system, many of them several times). That file is working code that previously compiled without warnings (GCC 8.3.0), but not with GCC 9.1.0. All structure, function, type, variable names have been changed.
pf31.c
#include <string.h>
enum { SERVERNAME_LEN = 128 };
typedef struct ServerQueue
{
char server_name[SERVERNAME_LEN + 1];
struct ServerQueue *next;
} ServerQueue;
extern int function_under_test(char *servername);
#ifdef SUPPRESS_BUG
extern int function_using_name(char *name);
#endif /* SUPPRESS_BUG */
extern int GetServerQueue(const char *servername, ServerQueue *queue);
int
function_under_test(char *servername)
{
ServerQueue queue;
char name[SERVERNAME_LEN + 1];
if (GetServerQueue(servername, &queue) != 0)
return -1;
char *name_in_queue = queue.server_name;
if (name_in_queue)
strncpy(name, name_in_queue, SERVERNAME_LEN);
else
strncpy(name, servername, SERVERNAME_LEN);
name[SERVERNAME_LEN] = '\0';
#ifdef SUPPRESS_BUG
return function_using_name(name);
#else
return 0;
#endif /* SUPPRESS_BUG */
}
Compilation
When compiled using GCC 9.1.0 (on a Mac running macOS 10.14.5 Mojave, or on a Linux VM running RedHat 5.x — don't ask!), with the option -DSUPPRESS_BUG I get no error, but with the option -USUPPRESS_BUG, I get an error:
$ gcc -std=c11 -O3 -g -Wall -Wextra -Werror -DSUPPRESS_BUG -c pf31.c
$ gcc -std=c11 -O3 -g -Wall -Wextra -Werror -USUPPRESS_BUG -c pf31.c
In file included from /usr/include/string.h:417,
from pf31.c:1:
pf31.c: In function ‘function_under_test’:
pf31.c:30:9: error: ‘__builtin_strncpy’ output may be truncated copying 128 bytes from a string of length 128 [-Werror=stringop-truncation]
30 | strncpy(name, name_in_queue, SERVERNAME_LEN);
| ^~~~~~~
cc1: all warnings being treated as errors
$
When I compile using GCC 8.3.0, I get no errors reported.
Question
Two sides of one question:
Why does GCC 9.1.0 complain about the use of strncpy() when the code is compiled with -USUPPRESS_BUG?
Why doesn't it complain when the code is compiled with -DSUPPRESS_BUG?
Corollary: is there a way to work around this unwanted warning that works with older GCC versions as well as 9.1.0. I've not yet found one. There's also a strong element of "I don't think it should be necessary, because this is using strncpy() to limit the amount of data copied, which is what it is designed for".
Another variant
I have another non-erroring variant, changing the signature of the function_under_test() — here's a set of diffs:
11c11
< extern int function_under_test(char *servername);
---
> extern int function_under_test(char *servername, ServerQueue *queue);
20c20
< function_under_test(char *servername)
---
> function_under_test(char *servername, ServerQueue *queue)
22d21
< ServerQueue queue;
25c24
< if (GetServerQueue(servername, &queue) != 0)
---
> if (GetServerQueue(servername, queue) != 0)
27c26
< char *name_in_queue = queue.server_name;
---
> char *name_in_queue = queue->server_name;
This compiles cleanly regardless of whether SUPPRESS_BUG is defined or not.
As you can guess from the SUPPRESS_BUG terminology, I'm tending towards the view that this is bug in GCC, but I'm kinda cautious about claiming it is one just yet.
More about the the original code: the function itself was 540 lines long; the strncpy() block occurs about 170 lines into the function; the variable corresponding to name was used further down the function in a number of function call, some of which take name as an argument and supply a return value for the function. This corresponds more to the -DSUPPRESS_BUG code, except that in the 'real code', the bug is not suppressed.
This is a GCC bug tracked as PR88780. According to Martin's comment, this warning did not exist prior to GCC 8.
GCC is shipped with this known bug, as it is not deemed release-critical.
To be honest, I am not 100% sure it is the bug. The point is, there are known false-positives. If you feel like helping the GCC project, you can find the most appropriate bug among strncpy / Wstringop-truncation bugs and post your example there. It would be more helpful if you minimized it further (say, with creduce); minimizing the compile string is also appreciated (that would be rather trivial, I guess).
Several compilation warnings related to strncpy were found in GCC 9.0 and reported here and here.
One of them is the error mentioned in the question which seems to occur in the file string_fortified.h:
/usr/include/bits/string_fortified.h:106:10: warning: ‘__builtin_strncpy’ output may be truncated copying 16 bytes from a string of length 16 [-Wstringop-truncation]
106 | return __builtin___strncpy_chk (__dest, __src, __len, __bos (__dest));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The response to this was given on April 15, 2019 was:
Thank you for the report, however as GCC 9 still under development. We do not see the above errors in the current stable GCC 7.4 or GCC 8.3. We appreciate the advanced notice, and will accept PRs to fix issues against GCC 9, but for now our target compiler is gcc stable.
So I believe the errors are probably a result of versions 9 and 9.1 being not stable versions. Hopefully they will be eliminated when these versions become stable.

gcc -Wall -Wuninitialized is not throwing warning for uninitialized variable

gcc version - gcc (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4
Compiling below code with -Wall -Wuninitialized is throwing warning as expected
warning: ‘test’ is used uninitialized in this function [-Wuninitialized]
#include<stdio.h>
#include<stdlib.h>
void check (int test )
{
printf("%d",test);
}
int
main()
{
int test;
check(test);
return 0;
}
But compiling below code with -Wall -Wuninitialized is not throwing warning
#include<stdio.h>
#include<stdlib.h>
void check (int test )
{
printf("%d",test);
}
int
main()
{
int test;
int condition = 0;
if(condition == 27)
test = 10;
check(test);
return 0;
}
Shouldn't it throw warning?. is it anyway related to compiler optimization?
What an user understands as a false positive may be different for the particular
user. Some users are interested in cases that are hidden because of
actions of the optimizers combined with the current environment.
However, many users aren't, since that case is hidden because it
cannot arise in the compiled code. The canonical example is (MM05):
int x;
if (f ())
x = 3;
return x;
where 'f' always return non-zero for the current environment, and
thus, it may be optimised away. Here, a group of users would like to
get an uninitialized warning since 'f' may return zero when compiled
elsewhere. Yet, other group of users would consider spurious a warning
about a situation that cannot arise in the executable being compiled.
https://gcc.gnu.org/wiki/Better_Uninitialized_Warnings#Proposal
EDIT: MMO5 is already fixed
https://gcc.gnu.org/wiki/Better_Uninitialized_Warnings#MM05
Yes, gcc is not warning you because the code is optimized away. Some people think this is the right thing, because it might be that way by a compile time configuration value. Others think it's the wrong thing, because it hides possible mistakes.
You are not the first to notice this problem with GCC. There's a whole proposal about it.
clang with -Wall detects the problem and even suggests a fix.
$ make
cc -Wall -g test.c -o test
test.c:15:7: warning: variable 'test' is used uninitialized whenever 'if' condition is false
[-Wsometimes-uninitialized]
if(condition == 27)
^~~~~~~~~~~~~~~
test.c:18:10: note: uninitialized use occurs here
check(test);
^~~~
test.c:15:4: note: remove the 'if' if its condition is always true
if(condition == 27)
^~~~~~~~~~~~~~~~~~~
test.c:12:12: note: initialize the variable 'test' to silence this warning
int test;
^
= 0
1 warning generated.

How to enforce the usage of return values in C

I'm searching for a compiler flag for gcc and if possible for clang and the Microsoft compilers as well, that triggers a warning (error with -Werror) if a non-void function is called without using the return value like this:
int test() {
return 4;
}
int main(void) {
test(); //should trigger a warning
int number = test(); //shouldn't trigger the warning
return 0;
}
If there is no such compiler flag, maybe some way to tell the clang static analyzer to complain about it.
EDIT: To clarify my original question: I actually meant using the return value, not only assigning it.
I never used it myself (do you really need it?), you can try
defining the function with warn_unused_result attribute
enabling -Wunused-result flag with gcc.
This will tell you about any unused value from the function return.
In case, any doubts, SEE IT LIVE or SEE IT LIVE AGAIN Thanks to M.M for the link in the comment
Or:
#include <stdio.h>
extern int func1(void) __attribute__((warn_unused_result));
extern int func2(void);
int main(void)
{
func1();
int rc1 = func1();
int rc2 = func1();
func2();
printf("%d\n", rc1);
return 0;
}
Compilation (GCC 5.1.0 on Mac OS X 10.10.5):
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -c warn.c
warn.c: In function ‘main’:
warn.c:10:9: error: unused variable ‘rc2’ [-Werror=unused-variable]
int rc2 = func1();
^
warn.c:8:5: error: ignoring return value of ‘func1’, declared with attribute warn_unused_result [-Werror=unused-result]
func1();
^
cc1: all warnings being treated as errors
$
Some static code analyzers like splint can check for these kind of things:
$ splint check.c
Splint 3.1.2 --- 03 May 2009
check.c: (in function main)
check.c:6:5: Return value (type int) ignored: test()
Result returned by function call is not used. If this is intended, can cast
result to (void) to eliminate message. (Use -retvalint to inhibit warning)
Unlike #Sourav's answer, this does not require a specific __attribute__ annotation on the target function, but on the other hand possibly emits many warnings. Its usually possible to suppress the warnings for specific functions or function calls by using annotations (like /*#alt void#*/).

Resources