GCC : Static array index in function argument doesn't trigger any warning - c

I'm trying to understand the use of the "static" keyword as an array index in a function declaration in C.
After reading this article, I tried to declare such a function, and purposefully passing it an array that is too short:
#include <stdio.h>
#include <stdlib.h>
void print_string10(char string10[static 10]) {
// Should trigger a warning if the argument is NULL or an array of less than 10 elements
printf("%s\n",string10);
}
int main(void) {
char short_string[] = "test";
print_string10(short_string); // should trigger a warning as the string is 5 long
return EXIT_SUCCESS;
}
Compiling with clang as in the article triggers the warning, but gcc -Wall -Werror does not, it compiles and run fine.
I couldn't find an explanation, is that a normal behaviour for GCC to omit this warning?

Why it does not need to trigger a warning is because of the section of the standard where it occurs - 6.7.6.3p7:
Semantics
[...]
A declaration of a parameter as ''array of type'' shall be adjusted to ''qualified pointer to type'', where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.
It appears in the semantics section. A conforming implementation is only required to diagnose those that appear in the constraints. Even when it does not diagnose the violation here, it can use the knowledge of the static keyword to infer that the argument is not null, and that loop unrolling and other optimizations could expect an array that would have at least that many elements.
Do also note, that the example 5 there says that
void f(double (* restrict a)[5]);
void f(double a[restrict][5]);
void f(double a[restrict 3][5]);
void f(double a[restrict static 3][5]);
are all compatible, i.e. you can mix and match them in function pointer assignments without casts even though one has the static dimension!
It seems that clang (perhaps rightly so) loses its ability to diagnose anything if the call is realized through a function pointer:
void (*f)(double a[restrict static 3]);
int main(void) {
double a[1] = {0};
f(a);
}
(no diagnostics in Clang 7.0 - remove the * and you will get them).

It looks like this is a bug in GCC. It seems there were some disagreements regarding whether or not this should be reported at compile time. Nonetheless, it was accepted as a bug but there doesn't seem to be a priority on fixing it.
This is detailed in bug report 50584. Comment 9 in particular states:
(In reply to Malcolm Inglis from comment #4)
Could someone change the status of this bug?
Notwithstanding whether the feature is pretty or ugly, GCC does accept
this code and warnings for undefined behavior, when possible, are
desired. Thus, confirmed.
This does not mean anyone is going to work on fixing this. Please, if
you are using this feature and would like to see this warning in GCC,
please consider contributing it:
https://gcc.gnu.org/wiki/GettingStarted#Basics:_Contributing_to_GCC_in_10_easy_steps
If you start working on this, it would be good to say so here.

Related

Why type cast a variable on its own line in C [duplicate]

This question already has answers here:
Why cast unused return values to void?
(10 answers)
Closed 1 year ago.
An often used statement like (void)x; allows to suppress warnings about unused variable x. But if I try compiling the following, I get some results I don't quite understand:
int main()
{
int x;
(short)x;
(void)x;
(int)x;
}
Compiling this with g++, I get the following warnings:
$ g++ test.cpp -Wall -Wextra -o test
test.cpp: In function ‘int main()’:
test.cpp:4:13: warning: statement has no effect [-Wunused-value]
(short)x;
^
test.cpp:6:11: warning: statement has no effect [-Wunused-value]
(int)x;
^
So I conclude that casting to void is very different from casting to any other types, be the target type the same as decltype(x) or something different. My guess at possible explanations is:
It is just a convention that (void)x; but not the other casts will suppress warnings. All the statements equally don't have any effect.
This difference is somehow related to the fact that void x; isn't a valid statement while short x; is.
Which of these if any is more correct? If none, then how can the difference in compiler warnings be explained?
Casting to void is used to suppress compiler warnings. The Standard says in §5.2.9/4 says,
Any expression can be explicitly converted to type “cv void.” The
expression value is discarded.
This statement:
(void)x;
Says "Ignore the value of x." There is no such type as void - it is the absence of a type. So it's very different from this:
(int)x;
Which says "Treat x as if it were an integer." When the resulting integer is ignored, you get a warning (if it's enabled).
When you ignore something which is nothing, it is not considered a problem by GCC--and with good reason, since casting to void is an idiomatic way to ignore a variable explicitly in C and C++.
The standard does not mandate generating a warning ("diagnostic" in standardese) for unused local variables or function parameters. Likewise, it does not mandate how such a warning might be suppressed. Casting a variable expression to void to suppress this warning has become an idiom in the C and later C++ community instead because the result cannot be used in any way (other than e.g. (int)x), so it's unlikely that the corresponding code is just missing. E.g.:
(int)x; // maybe you meant f((int)x);
(void)x; // cannot have intended f((void)x);
(void)x; // but remote possibility: f((void*)x);
Personally, I find this convention too obscure still, which is why I prefer to use a function template:
template<typename T>
inline void ignore(const T&) {} // e.g. ignore(x);
The idiomatic way to ignore function parameters is, however, to omit their name (as seen above). A frequent use I have for this function is when I need to be able to name a function parameter in conditionally compiled code such as an assert. I find e.g. the following more legible than the use of #ifdef NDEBUG:
void rate(bool fantastic)
{
assert(fantastic);
ignore(fantastic);
}
Possible use:
auto it = list_.before_begin();
for (auto& entry : list_)
{
(void)entry; //suppress warning
++it;
}
Now the iterator 'it' points to the last element

Static size array parameter [duplicate]

I'm trying to understand the use of the "static" keyword as an array index in a function declaration in C.
After reading this article, I tried to declare such a function, and purposefully passing it an array that is too short:
#include <stdio.h>
#include <stdlib.h>
void print_string10(char string10[static 10]) {
// Should trigger a warning if the argument is NULL or an array of less than 10 elements
printf("%s\n",string10);
}
int main(void) {
char short_string[] = "test";
print_string10(short_string); // should trigger a warning as the string is 5 long
return EXIT_SUCCESS;
}
Compiling with clang as in the article triggers the warning, but gcc -Wall -Werror does not, it compiles and run fine.
I couldn't find an explanation, is that a normal behaviour for GCC to omit this warning?
Why it does not need to trigger a warning is because of the section of the standard where it occurs - 6.7.6.3p7:
Semantics
[...]
A declaration of a parameter as ''array of type'' shall be adjusted to ''qualified pointer to type'', where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.
It appears in the semantics section. A conforming implementation is only required to diagnose those that appear in the constraints. Even when it does not diagnose the violation here, it can use the knowledge of the static keyword to infer that the argument is not null, and that loop unrolling and other optimizations could expect an array that would have at least that many elements.
Do also note, that the example 5 there says that
void f(double (* restrict a)[5]);
void f(double a[restrict][5]);
void f(double a[restrict 3][5]);
void f(double a[restrict static 3][5]);
are all compatible, i.e. you can mix and match them in function pointer assignments without casts even though one has the static dimension!
It seems that clang (perhaps rightly so) loses its ability to diagnose anything if the call is realized through a function pointer:
void (*f)(double a[restrict static 3]);
int main(void) {
double a[1] = {0};
f(a);
}
(no diagnostics in Clang 7.0 - remove the * and you will get them).
It looks like this is a bug in GCC. It seems there were some disagreements regarding whether or not this should be reported at compile time. Nonetheless, it was accepted as a bug but there doesn't seem to be a priority on fixing it.
This is detailed in bug report 50584. Comment 9 in particular states:
(In reply to Malcolm Inglis from comment #4)
Could someone change the status of this bug?
Notwithstanding whether the feature is pretty or ugly, GCC does accept
this code and warnings for undefined behavior, when possible, are
desired. Thus, confirmed.
This does not mean anyone is going to work on fixing this. Please, if
you are using this feature and would like to see this warning in GCC,
please consider contributing it:
https://gcc.gnu.org/wiki/GettingStarted#Basics:_Contributing_to_GCC_in_10_easy_steps
If you start working on this, it would be good to say so here.

Parameters declared restrict and compiler warnings

Neither gcc 5 nor clang 3.6 give warnings where the constraints of the restrict qualifier are violated, even when called with -Wall. Consider the following code fragment:
extern void f(char *restrict p, char *restrict q);
void g(char *p)
{
f(p, p);
}
Naively, I'd expect that the violation can be determined statically, and I was expecting that -Wall would give a warning. Have I missed a flag somewhere, or is there some problem with giving warnings that I'm not seeing?
Starting with version 8, gcc gives a helpful warning for the above code:
a.c: In function ‘g’:
a.c:5:5: warning: passing argument 1 to restrict-qualified parameter aliases with argument 2 [-Wrestrict]
f(p, p);
^
Given the code:
void test(int *restrict a, int *restrict b, int c)
{
a[0] += b[c];
}
the only scenario affected by the restrict qualifier would be a==b+c;
in that case, the pointer in a would be used to modify an object which is
also accessed through unrelated pointer in b. In all other cases that
would be defined in the absence of restrict, no object that is accessed
via a would also be accessed via b, nor vice versa.
A compiler that could see that a function that was passed restrict-
qualified pointer arguments used them in violation of the rules associated
therewith might helpfully warn of such violation, but a compiler that can't
see into a function would have no way of knowing what combinations of
arguments would be valid or invalid.
The restrict keyword is an explicit assurance from the programmer that the pointers in question don't alias. In essence, it allows the compiler to omit alias analysis for those pointers, because the programmer has already provided the presumed answer. In addition to enabling better optimization, this can also save compilation time. In large programs, analysis can be quite expensive, so that's potentially a big deal in its own right.
So, the answer to your question is, I believe, "the compilers aren't looking, because the code tells them not to bother"

What does the void statement in C do?

What does putting void; on a line do in C? The compiler is warning about it but i dont understand. What is the point in being able to put void on a line like this?
#include <stdio.h>
int main() {
void;
printf("word dude");
return 1;
}
eh
$ gcc -pedantic -ansi -Wall -Wextra eh.c -o eh
eh.c: In function 'main':
eh.c:4:2: warning: useless type name in empty declaration
$ ./eh
word dude
People seem to be getting confused what I'm asking: What does this line mean, does it do anything? why is it valid?
void;
Removed void cast as it's causing unnecessary discussion.
This question got me interested because my first thought was K&R. I went back to my old K&R book (Appendix A p.192) on found blurb about declarations (transcripted):
8. Declarations
Declarations are used to specify the interpretation which C gives to each identifier; they do not necessarily reserve storage associated with the identifier. Declarations have
the form
declaration:
decl-specifier declarator-listopt;
The declarators in the declarator-list contain the identifiers being declared. The decl-specifiers consist of a sequence of type and storage class specifiers.
decl-specifiers:
type-specifier decl-specifiersopt
sc-specifier decl-specifiersopt
This leads me to believe that delarator lists are optional (meaning it can be empty).
To add to this confusion on following page, it lists the set of legal type-specifier values and void is not one of them.
In my narrow interpretation that this may be still legal (but obsolete) C.
void; // 1
(void)printf("word dude"); // 2
The first statement is an invalid statement: this is not C.
In the second statement the (void) cast makes your intent clear to discard the return value. Another common use is to make silent some static analysis tools like Lint.
void;
There's no point in doing that, it doesn't do anything.
Did you get this out of a book? Must be a mistake.
Somewhat unrelated, a return value if 0 is usually used to indicate the program terminated normally, any other value indicates a problem (by convention)
Update:
Looks like you added another line:
(void)printf("word dude");
this - unlike the previous void; - tells the compiler explictly to ignore the return value of the printf() function (the number of characters printed) and prevent warning messages a picky/pedantic setting on the compiler or other tools, like lint, to stop from complaining.
You don't see this for frequently used functions generally, though you may for others.
It prints the same error if you change void with int or any other type.
void is a type and it expects something after that type. It has no sense at all as writing only int without a variable (or function) name after it.
void is used as a data type in C, and come to think of it, I've never heard of an assembly language equivalent of NOP in C, so having void by itself on a line is probably an error.
It looks like the old style of declaring arguments to a function. That's equivalent to int main(void). Some compilers still accept it, some don't. For example, this code compiles in two compilers I tried:
void main(i, j) {
int;
long;
i = 12;
j = 123456;
printf("%d %d\n", i, j);
}
The answer to Untyped arguments in a C function declaration has a more precise definition for this case.
In any case, this is not recommended, so can we just stick with the "normal" way of declaring functions, please? :)

Splint warning "Statement has no effect" due to function pointer

I'm trying to check a C program with Splint (in strict mode). I annotated the source code with semantic comments to help Splint understand my program. Everything was fine, but I just can't get rid of a warning:
Statement has no effect (possible undected modification through call to unconstrained function my_function_pointer).
Statement has no visible effect --- no values are modified. It may modify something through a call to an unconstrained function. (Use -noeffectuncon to inhibit warning)
This is caused by a function call through a function pointer. I prefer not to use the no-effect-uncon flag, but rather write some more annotations to fix it up. So I decorated my typedef with the appropriate #modifies clause, but Splint seems to be completely ignoring it. The problem can be reduced to:
#include <stdio.h>
static void foo(int foobar)
/*#globals fileSystem#*/
/*#modifies fileSystem#*/
{
printf("foo: %d\n", foobar);
}
typedef void (*my_function_pointer_type)(int)
/*#globals fileSystem#*/
/*#modifies fileSystem#*/;
int main(/*#unused#*/ int argc, /*#unused#*/ char * argv[])
/*#globals fileSystem#*/
/*#modifies fileSystem#*/
{
my_function_pointer_type my_function_pointer = foo;
int foobar = 123;
printf("main: %d\n", foobar);
/* No warning */
/* foo(foobar); */
/* Warning: Statement has no effect */
my_function_pointer(foobar);
return(EXIT_SUCCESS);
}
I've read the manual, but there's not much information regarding function pointers and their semantic annotations, so I don't know whether I'm doing something wrong or this is some kind of bug (by the way, it's not already listed here: http://www.splint.org/bugs.html).
Has anyone managed to successfully check a program like this with Splint in strict mode? Please help me find the way to make Splint happy :)
Thanks in advance.
Update #1: splint-3.1.2 (windows version) yields the warning, while splint-3.1.1 (Linux x86 version) does not complain about it.
Update #2: Splint doesn't care whether the assignment and the call are short or long way:
/* assignment (short way) */
my_function_pointer_type my_function_pointer = foo;
/* assignment (long way) */
my_function_pointer_type my_function_pointer = &foo;
...
/* call (short way) */
my_function_pointer(foobar);
/* call (long way) */
(*my_function_pointer)(foobar);
Update #3: I'm not interested in inhibiting the warning. That's easy:
/*#-noeffectuncon#*/
my_function_pointer(foobar);
/*#=noeffectuncon#*/
What I'm looking for is the right way to express:
"this function pointer points to a function which #modifies stuff, so it does have side-effects"
Maybe you are confusing splint by relying on the implicit conversion from "function name" to "pointer to function" in your assignment of my_function_pointer. Instead, try the following:
// notice the &-character in front of foo
my_function_pointer_type my_function_pointer = &foo;
Now you have an explicit conversion and splint doesn't need to guess.
This is just speculation, though. I haven't tested it.
I'm not familiar with splint, but it looks to me that it will check function calls to see if they produce an effect, but it doesn't do analysis to see what a function pointer points to. Therefore, as far as it's concerned, a function pointer could be anything, and "anything" includes functions with no effect, and so you'll continue to get that warning on any use of a function pointer to call a function, unless you so something with the return value. The fact that there's not much on function pointers in the manual may mean they don't handle them properly.
Is there some sort of "trust me" annotation for an entire statement that you can use with function calls through pointers? It wouldn't be ideal, but it would allow you to get a clean run.
I believe the warning is correct. You're casting a value as a pointer but doing nothing with it.
A cast merely makes the value visible in a different manner; it doesn't change the value in any way. In this case you've told the compiler to view "foobar" as a pointer but since you're not doing anything with that view, the statement isn't doing anything (has no effect).

Resources