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"
Related
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.
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.
Consider the following code:
void doesnt_modify(const int *);
int foo(int *n) {
*n = 42;
doesnt_modify(n);
return *n;
}
where the definition of doesnt_modify isn’t visible for the compiler. Thus, it must assume, that doesnt_modify changes the object n points to and must read *n before the return (the last line cannot be replaced by return 42;).
Assume, doesnt_modify doesn’t modify *n. I thought about the following to allow the optimization:
int foo_r(int *n) {
*n = 42;
{ /* New scope is important, I think. */
const int *restrict n_restr = n;
doesnt_modify(n_restr);
return *n_restr;
}
}
This has the drawback that the caller of doesnt_modify has to tell the compiler *n isn’t modified, rather than that the function itself could tell the compiler via its prototype. Simply restrict-qualifying the parameter to doesnt_modify in the declaration doesn’t suffice, cf. “Is top-level volatile or restrict significant [...]?”.
When compiling with gcc -std=c99 -O3 -S (or Clang with the same options), all functions are compiled to equivalent assembly, all re-reading the 42 from *n.
Would a compiler be allowed to do this optimization (replace the last line by return 42;) for foo_r? If not, is there a (portable, if possible) way to tell the compiler doesnt_modify doesn’t modify what its argument points to? Is there a way compilers do understand and make use of?
Does any function have UB (provided doesnt_modify doesn’t modify its argument’s pointee)?
Why I think, restrict could help here (From C11 (n1570) 6.7.3.1 “Formal definition of restrict”, p4 [emph. mine]):
[In this case, B is the inner block of foo_r, P is n_restr, T is const int, and X is the object denoted by *n, I think.]
During each execution of B, let L be any lvalue that has &L based on P. If L is used to access the value of the object X that it designates, and X is also modified (by any means), then the following requirements apply: T shall not be const-qualified. […]
$ clang --version
Ubuntu clang version 3.5.0-4ubuntu2 (tags/RELEASE_350/final) (based on LLVM 3.5.0)
Target: x86_64-pc-linux-gnu
Gcc version is 4.9.2, on an x86 32bit target.
Version 1 seems clearly specified by the formal definition of restrict (C11 6.7.3.1). For the following code:
const int *restrict P = n;
doesnt_modify(P);
return *P;
the symbols used in 6.7.3.1 are:
B - that block of code
P - the variable P
T - the type of *P which is const int
X - the (non-const) int being pointed to by P
L - the lvalue *P is what we're interested in
6.7.3.1/4 (partial):
During each execution of B, let L be any lvalue that has &L based on P. If L is used to access the value of the object X that it designates, and X is also modified (by any means), then the following requirements apply: T shall not be const-qualified
[...]
If these requirements are not met, then the behavior is undefined.
Note that T is const-qualified. Therefore, if X is modified in any way during this block (which includes during the call to a function in that block), the behaviour is undefined.
Therefore the compiler can optimize as if doesnt_modify did not modify X.
Version 2 is a bit more difficult for the compiler. 6.7.6.3/15 says that top-level qualifiers are not considered in prototype compatibility -- although they aren't ignored completely.
So although the prototype says:
void doesnt_modify2(const int *restrict p);
it could still be that the body of the function is declared as void doesnt_modify2(const int *p) and therefore might modify *p.
My conclusion is that if and only if the compiler can see the definition for doesnt_modify2 and confirm that p is declared restrict in the definition's parameter list then it would be able to perform the optimization.
Generally, restrict means that the pointer is not aliased (i.e. only it or a pointer derived from it can be used to access the pointed-to object).
With const, this means that the pointed-to object cannot be modified by well-formed code.
There is, however, nothing to stop the programmer breaking the rules using an explicit type conversion to remove the constness. Then the compiler (having been beaten into submission by the programmer) will permit an attempt to modify the pointed-to object without any complaint. This, strictly speaking, results in undefined behaviour so any result imaginable is then permitted including - possibly - modifying the pointed-to object.
If not, is there a (portable, if possible) way to tell the compiler doesnt_modify doesn’t modify what its argument points to?
No such way.
Compiler optimizers have difficulty optimizing when pointer and reference function parameters are involved. Because the implementation of that function can cast away constness compilers assume that T const* is as bad as T*.
Hence, in your example, after the call doesnt_modify(n) it must reload *n from memory.
See 2013 Keynote: Chandler Carruth: Optimizing the Emergent Structures of C++. It applies to C as well.
Adding restrict keyword here does not change the above.
Simultaneous use of a restrict qualifier on a pointer-type parameter and a const qualifier on its target type would invite a compiler to assume that no region of storage which is accessed during the lifetime of the pointer object via the pointer contained therein or any pointer derived from it, will be modified via any means during that pointer's lifetime. It generally says nothing whatsoever about regions of storage which are not accessed using the pointer in question.
The only situations where const restrict would have implications for an entire object would be those where pointer is declared using array syntax with a static bound. In that situation, behavior would only be defined in cases where the entire array object could be read (without invoking UB). Since reading any part of the array object which changes during function execution would invoke UB, code would be allowed to assume that no portion of the array can be changed in any fashion whatsoever.
Unfortunately, while a compiler that knew that a function's actual definition starts with:
void foo(int const thing[restrict static 1]);
would be entitled to assume that no part of *thing would be changed during the function's execution, even if the object might be one the function could otherwise access via pointer not derived from thing, the fact that a function's prototype includes such qualifiers would not compel its definition to do likewise.
Using GCC and C99 mode, I have a function declared as:
void func(float *X);
When I call the function, I use a volatile array Y:
volatile float Y[2];
int main()
{
func(Y);
return 0;
}
When compiling (with -Wall), I get the following warning:
warning: passing argument 1 of ‘func’ discards qualifiers from pointer target type
blah.c:4: note: expected ‘float *’ but argument is of type ‘volatile float *’
I can eliminate it with an explicit (float *) type cast, but this repeats in many places in the code.
Is there a way to eliminate this specific warning, with an option or a pragma (or something equivalent)?
No, you can't turn that warning off. It's telling you you're violating the type system. If you want to call func you either need to pass it pointers to non-volatile data or change the function signature to accept pointers to volatile data.
The Standard allows compilers to do anything they like if a non-qualified pointer is used to access a volatile-qualified object. This allows for platforms where some volatile-qualified objects may require special instructions to access them, where e.g. a write via volatile uint16_t* might do generate code equivalent to:
if ((uintptr_t)ptr >= 0xFFFF0000)
__outport16(0xFFFF & (uintptr_t)ptr, value);
else
(uint16_t*)ptr = value;
If a compiler writer takes the attitude that compilers should only exploit such freedoms on obscure platforms were doing otherwise would be expensive, and should provide sensible behaviors on platforms where doing so would cost almost nothing, and if the calling code in the original example knows that no outside entities will access Y during the execution of func, then code targeting that compiler will be able to achieve the required behavior without a diagnostic merely by casting Y's address to a float*. Unfortunately, the people maintaining gcc and clang seem to believe that when the Standard refers to "non-portable or erroneous constructs", it really means "non-portable, i.e. erroneous, constructs" rather than "constructs which are not portable to every single conforming machine in the Universe, and would be erroneous if such portability were intended. Casting the pointer to float* will silence the warning on gcc or clang, but I wouldn't count on it causing them to yield sensible code.
For the following C code (for swapping two numbers) I am getting the "conflicting types" error for swap function:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a,b;
printf("enter the numbers to be swapped");
scanf("%d%d",&a,&b);
printf("before swap");
printf("a=%d,b=%d",a,b);
swap(&a,&b,sizeof(int));
printf("after swap");
printf("a=%d,b=%d",a,b);
getch();
}
void swap(void *p1,void *p2,int size)
{
char buffer[size];
memcpy(buffer,p1,size);
memcpy(p1,p2,size);
memcpy(p2,buffer,size);
return(0);
}
Compiler diagnostics:
<source>:10:6: warning: implicit declaration of function 'swap' [-Wimplicit-function-declaration]
swap(&a,&b,sizeof(int));
^~~~
program:15:6: warning: conflicting types for 'swap'
void swap(void *p1,void *p2,int size)
^~~~
Can anybody tell why that error is coming?
What is the solution for that?
The problem is that swap was not declared before it is used. Thus it is assigned a "default signature", one which will in this case not match its actual signature. Quote Andrey T:
The arguments are passed through a set
of strictly defined conversions. int *
pointers will be passed as int *
pointers, for example. In other words,
the parameter types are temporarily
"deduced" from argument types. Only
the return type is assumed to be int.
Aside from that, your code produces a bunch of other warnings. If using gcc, compile with -Wall -pedantic (or even with -Wextra), and be sure to fix each warning before continuing to program additional functionality. Also, you may want to tell the compiler whether you are writing ANSI C (-ansi) or C99 (-std=c99).
Some remarks:
Put spaces after commas.
Make main return an int.
And make it return 0 or return EXIT_SUCCESS.
Import the definition of getch: #include <curses.h>.
Or just use getchar.
Import the definition of memcpy: #include <string.h>.
Don't return something in a void function.
You may want to use malloc to allocate a buffer of variable size. That will also work with older compilers:
void swap(void *p1, void *p2, int size) {
void *buffer = malloc(size);
memcpy(buffer, p1, size);
memcpy(p1, p2, size);
memcpy(p2, buffer, size);
free(buffer);
}
You need to declare swap before using it. For example, put swap above main, or add a prototype for swap like this:
void swap(void *,void *,int);
int main ()
Incidentally main should be int not void and usually it returns the value zero, unless there is an error.
First off, the actual error message wouldn't hurt.
Secondly, making buffer of [size] only works on some compilers (that's a new feature, not all compilers have it yet). Are you sure yours does?
Thirdly, you need to declare swap before calling it. Add a prototype at the top of the file:
void swap(void *p1,void *p2,int size);
You failed to declare your swap explicitly, forcing the compiler to make assumptions about the function at the point of the call. The compiler, in accordance with C rules, will assume that swap is
int swap(int *, int *, size_t)
Later you declare your swap as
void swap(void *, void *, int)
which is obviously different from what the compiler assumed. This is the conflict the compiler is telling you about.
Also, your void swap attempts to return 0. What were you trying to achieve by that?
P.S. It's int main, not void main.
P.P.S. The program is not guaranteed to produce any output if its output does not end in a new-line character.
You may wonder why the program compiles at all without a prototype for swap(), and that's because the compiler is more than a C99 tool. It also compiles C89 and K&R C programs.
C89 added the prototypes. Prior to C89, the compiler didn't need to see the declaration (the prototype) of a function unless it returned something other than int and the types of the formal parameters were not known to the compiler at all. The compiler just called every function with the types of the actual arguments, which received a set of default argument promotions to simplify things. The programmer would run the lint utility to cross-check actual and formal parameters. This program is still shipped with the BSD distributions.
K&R programs and their corresponding code styles are still accepted by your compiler, so when it sees a function for which no prototype is available it just goes ahead and calls it anyway.
In this case, you switch paradigms in between the call and the definition of the function. The K&R C assumptions the compiler made about the undeclared function when it had to generate a call turned out not to be valid. Even if you had written the whole program in the K&R style the compiler would have made the same complaints when it found out the real types of the function arguments.
With GCC, this is a warning,
when you dont declare a function before using it, the compiler tries to guess the declaration using the type of call made to that function.Hence the behavior.
Well, it compiles on http://codepad.org (after removing the getch(), which is irrelevant). Maybe your compiler complains about using memcpy on non-restricted pointers?
In swap() p1 and p2 are not guaranteed not to be aliases. This is an actual bug waiting to happen - calling swap on &a[i] and &a[j] might blow up memcpy when i==j. Either use memmove (which is guaranteed not to blow up on overlapped areas) or declare the pointers restricted.