In most situations where compilers would be capable of generating "strict-aliasing" warnings, they could just as easily recognize the relationship between the actions on storage using different lvalues. The reason that the Standard doesn't require that compilers recognize access to storage using lvalues of different types is to avoid requiring them to pessimistically assume aliasing in cases where they would otherwise have no reason to expect it [and would thus have no reason to issue any warnings about it].
As written, the Standard does not recognize any circumstances in which an lvalue of non-character type can be derived from another lvalue and used to access storage as its own type. Even something like:
struct foo {int x;} s = {0};
s.x = 1;
invokes UB because it uses an lvalue of type int
to access the storage of an object of type struct foo
. The authors of the Standard rely upon compiler writers to recognize situations where common sense would imply that they should behave predictably without regard for whether the Standard actually requires it. I don't think any compiler is so stupid as to not recognize that an operation on s.x
is actually an operation on s
, and there are many other situations where the authors of the Standard thought compilers would have the sense to recognize such things without being ordered to do so.
Unfortunately, some compiler writers have come to view the rules as justification for assuming that code which looks like it would access storage of one type using lvalues of another, doesn't do so, rather than merely making such assumptions about code that doesn't look like it does so. Given something like:
void doSomehing(structs s2 *p);
void test(struct s1 *p)
{
doSomething((struct s2*)p};
}
there are at a few ways that something like test
might be invoked:
1. It might receive a pointer to a `struct s1`, which `doSomething` will need to operate upon [perhaps using the Common Initial Sequence guarantee] as though it is a `struct s2`. Further, either:
1a. During the execution of `doSomething` storage accessed exclusively via pointers derived from a struct s2 like the one that was passed in will be accessed exclusively via such means, or...
1b. During the execution of `doSomething` storage accessed via things of that type will also be accessed via other unrelated means.
2. It might receive a pointer to a `struct s2`, which has been cast to a `struct s1*` for some reason in the calling code.
3. It might receive a pointer to a `struct s1`, which `doSomething` will process as though it's a `struct s1`, despite the fact that it accepts a parameter of type `struct s2`.
A compiler might observe that none of the situations whose behavior is defined by the Standard are very likely, and thus decide to issue a warning on that basis. On the other hand, the most common situation by far would be #1a, which a compiler really should be able to handle in predictable fashion, without difficulty, whether the Standard requires it or not, by ensuring that any operations on things of type struct s2
which are performed within the function get sequenced between operations on type struct s1
which precede the call, and those on type struct s1
which follow the function call. Unfortunately, gcc and clang don't do that.