EDIT: problem explained more in depth here (thank you @Eric Postpischil). It seems to be a bug in GCC.
First, let me start with some context: the code I'm writing is using an API I can't change, on a GCC version I can't change, with compilation flags I'm not allowed to remove, and when I'm done with it must have precisely zero warnings or #pragmas.
EDIT: no unions either.
EDIT2: assume the build system also uses -Wall -ansi -pedantic and every other warnings under the sun. I'll confirm the GCC version tomorrow but I'm fairly certain it's not above GCC 7. In the meantime I'm testing with GCC 6.3.
EDIT3: I'm marking the issue as 'answered'. For completeness' sake, I'm adding some more information below:
I've checked the compiler version being used, and it's not pretty. We're using Mingw and a gcc.exe --version
tells me it's GCC 3.4.5.
Furthermore, compilation flags include wall wextra wcast-qual wpointer-arith wconversion wsign-conversion
along with others that are not relevant to the problem at hand.
Consider the following code:
#include "stdio.h"
#include "stdint.h"
typedef uint32_t MyType[4];
const MyType* foo(const uint8_t* a)
{
return (const MyType*) a;
}
void myapi_foo(const MyType* d) {}
int main()
{
uint8_t a[4*sizeof(uint32_t)];
const MyType* b = foo((const uint8_t*) a);
myapi_foo(b);
return 0;
}
Compiled with GCC and the -Wcast-qual flag, this code will throw the following warning:
warning: cast discards ‘const’ qualifier from pointer target type [-Wcast-qual] return (const MyType*) a;
EDIT: to clarify, the error is on this line:
return (const MyType*) a;
I know the root cause of the problem is the typedef type MyType
which is in fact an array. Sadly, I do not have the luxury of modifying this typedef, nor the API function myapi_foo
and its dubious choice of parameter type.
To be honest, I don't really understand why is the compiler so unhappy about this cast, so clarifications are more than welcome.
What would be the cleanest way of indicating to the compiler everything should be treated as a pointer to const data?
Here are a few 'solutions' that I have found but left me unsatisfied:
return (const MyType*) (uint32_t) a;
. It's very crude, but using uint32_t
as memory addresses has precedent in this project so I might have to use it as a last ditch effort.return (const void*) a;
, which would work regardless of the value of sizeof(MyType*)
. Sadly it doesn't work on the target.Thank you for your time.
This is GCC bug 81631. GCC fails to recognize the cast to const MyType *
retains the const
qualifier. This may be because, in this “pointer to array of four const uint32_t
”, GCC performs a test of whether the array is const
whether than of whether the array elements are const
.
In some GCC versions, including 8.2, a workaround is to change:
return (const MyType*) a;
to:
return (const void *) a;
A more drastic change that is likely to work in more versions is to use:
return (const MyType *) (uintptr_t) a;
It may be a problem that this code passes a
to a function that casts it to const MyType *
:
uint8_t a[4*sizeof(uint32_t)];
const MyType* b = foo((const uint8_t*) a);
In many C implementations, MyType
, being an array of uint32_t
, will require four-byte alignment, but a
will only require one-byte alignment. Per C 2018 6.3.2.3 6, if a
is not correctly aligned for MyType
, the result of the conversion is not defined.
Additionally, this code suggests that the uint_t
array a
may be used as an array of four uint32_t
. That would violate C aliasing rules. The code you show in the question appear to be a sample, not the actual code, so we cannot be sure, but you should consider this.
You can do that :
const MyType* foo(const uint8_t* a)
{
union {
const uint8_t* a;
const MyType* b;
} v;
v.a = a;
return v.b;
}
w.c being your modified file :
pi@raspberrypi:/tmp $ gcc -pedantic -Wall -Wcast-qual w.c
pi@raspberrypi:/tmp $
That works whatever the compiler (no #pragma) or the respective size of int and pointer(no cast between int and pointer), but I am not sure this is very elegant ;-)
It is strange to have that foo function and at the same time compile with Wcast-qual
, it's contradictory
Edit, If you cannot use union you can also do that
const MyType* foo(const uint8_t* a)
{
const MyType* r;
memcpy(&r, &a, sizeof(a));
return r;
}
Compilation :
pi@raspberrypi:/tmp $ gcc -pedantic -Wall -Wcast-qual w.c
pi@raspberrypi:/tmp $
If nothing works, you might like to use the uintptr_t
hammer, if the implmentation provides it. It is optional by the C11 Standard:
const MyType* foo(const uint8_t* a)
{
uintptr_t uip = (uintptr_t) a;
return (const MyType*) uip;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With