After hours of experiments, it seems to be a limitation bug of GCC. The newer GCCs do not have this issue.
1. Forward declaration, no may_alias
(wrong behavior)
Here's a minimal demonstration of the strict aliasing problem:
#include <stdio.h>
struct MyType; // Forward declaration here, without may_alias.
void foo(struct MyType *, int *b);
struct MyType {
short a;
}; // Full definition here, without may_alias.
void f(struct MyType *my_type, int *b)
{
*b = 1;
my_type->a = 0;
if (*b == 1) {
printf("Strict aliasing problem\n");
}
}
int main(void) {
int b;
f((struct MyType *)&b, &b);
return 0;
}
Compile it with GCC 5.4.0:
$ gcc -O2 -o main main.c
$ ./main
Strict aliasing problem
2. No forward declaration, no may_alias
(wrong behavior)
Same as above.
3. Forward declaration, may_alias
(won't compile)
struct __attribute__((may_alias)) MyType; // Forward declaration here, with may_alias.
struct __attribute__((may_alias)) MyType {
short a;
}; // Full definition here, with may_alias.
Compile it with GCC 5.4.0:
$ gcc -O2 -o main main.c
main.c:11:10: error: conflicting types for ‘foo’
void foo(struct MyType *my_type, int *b)
^
main.c:5:10: note: previous declaration of ‘foo’ was here
void foo(struct MyType *, int *b);
Seems like GCC thinks struct MyType;
is a different type. However, I didn't find anyway to add may_alias
attribute to a forward declaration. According to the order doc:
It is ignored if the content of the structure, union or enumerated type is not defined in the specifier in which the attribute specifier list is used—that is, in usages such as struct attribute((foo)) bar with no following opening brace.
A potential workaround would be declaring foo
like this:
void foo(struct MyType __attribute__((may_alias)) *, int *b);
However, this is not a good solution, seems like this syntax might be not yet supported:
Note again that this does not work with most attributes; for example, the usage of ‘aligned’ and ‘noreturn’ attributes given above is not yet supported.
And although it compiles, the may_alias
attribute doesn't work:
$ gcc -O2 -o main main.c
$ ./main
Strict aliasing problem
4. No forward declaration, may_alias
(good)
This is the only way that works.
$ gcc -O2 -o main main.c
$ ./main
$
However, in my case, forward declaration is required. My workaround is to use void *
instead of struct MyType *
:
// In the .h file, no definition of MyType yet.
void foo(void *my_type, int *b);
// In the .c file, has the definition of MyType.
void foo(void *t, int *b)
{
struct MyType *my_type = t;
// ...
}
This is not elegant at all, but is the only way that works at the moment.