Boost logo

Boost :

Subject: Re: [boost] gcc-4.4.0 and boost::optional is giving (spurious?) strict-aliasing warnings
From: Kim Barrett (kab.conundrums_at_[hidden])
Date: 2009-05-06 21:37:17


  At 7:17 PM -0300 5/5/09, Brad Spencer wrote:
> Before I get to the details, let me start by saying that I suspect
> that these warnings are probably spurious. But, before I go and start
> asking the compiler folks if they are bugs, I'd like to check here
> first.
>
> If you take unmodified boost-1.38.0 and try to use boost::optional
> with gcc-4.4.0 -Wall -O3, you seem to get warnings about
> strict-aliasing rules being broken. If boost::optional (and
> boost::function) are really broken, then we're probably in trouble :)
>
> test.cc:
>
> #include <boost/optional.hpp>
> boost::optional<int> x;
> int func()
> {
> return *x;
> }
>
> On x86_64-unknown-linux-gnu:
>
> $ /opt/gcc-4.4.0/bin/g++ -Wall -O3 -I. -c -o test.o test.cc
> test.cc: In function 'int func()':
> test.cc:5: warning: dereferencing pointer '<anonymous>' does break
>strict-aliasing rules
> ./boost/optional/optional.hpp:594: note: initialized from here
>
> I have seen some similar issues with boost::bind and boost::function,
> but I haven't isolated them down to something this simple yet. I
> figured I'd start by asking about this.
>
> So, is it safe to presume that boost::optional is not really breaking
> the strict aliasing rules? (It's not immediately obvious.) If so,
> then I can ask the gcc folks about this.

If I've traced through the code properly and still remember the strict
aliasing rules at all, then I think boost::optional is in fact in
violation of those rules.

[Note that the relevant parts of the C99 standard for this discussion
are 6.5#6-7 and 6.5.2.3#5.]

In boost/aligned_storage.hpp, the class template
boost::aligned_storage defines its address member function thusly:

     void* address() { return this; }
     const void* address() const { return this; }

There is code in boost/optional/optional.hpp (and probably the other
libs you mention) which looks like:

     internal_type* get_object() {
         return static_cast<internal_type*>(m_storage.address());
     }

What this leads, after some expansion, to something roughly equivalent
to

     static_cast<internal_type*>(
         static_cast<void*>(
             <a pointer to an instance of aligned_storage> ))

which I believe is not strict aliasing safe. It is effectively a cast
from aligned_storage* to internal_type*, with a trip through void*
along the way. That trip through void* does not make the conversion
satisfy the strict aliasing rules.

[Note that it is not the cast itself that violates the strict aliasing
rules, it is the later dereference of the pointer resulting from the
cast.]

[Note that I'm pretty sure that switching from static_cast to
reinterpret_cast doesn't help.]

One of the escape hatches provided by the strict aliasing rules
involves ensuring that there is a union type in scope at the point of
the conversion which contains both of the involved types. [The
"visible union" rule of 6.5.2.3#5.] However, that isn't generally
applicable to something like optional, because unions are restricted
to POD types.

Another escape hatch offered by the strict aliasing rules involves
memcpy and memmove. [6.5#6] I think that the following implementation of
aligned_storage::address will satisfy the strict aliasing rules:

     void* address() {
         void* data = data_.data_.buf;
         return memmove(data, data, sizeof(*data));
     }

And I think gcc will optimize away the memmove, at least at sufficient
optimization levels. But note that I have not tested this at all.

[Hm, using a size of 0 or 1 or some similar small value might be
sufficient to quiet the compiler; I'm not certain whether it is
sufficient to satisfy the letter of the C standard.]

[Or perhaps the "correct" definition from a C perspective is

     void* address() {
         return memmove(this, data_.data_.buf, sizeof(*this));
     }

except that applying memmove to a non-POD is undefined. I keep getting
confused every time I try to get my head wrapped around the strict
aliasing rules and their implications.]

The idea is that memmove (and memcpy, but that requres non-overlapping
source and destination, which we don't have here) is defined by the
aliasing rules to strip away the type of the destination, in a way
that a simple cast does not.

[And yes, I agree that this is rather grotesque.]

[Assuming this change is correct, it might also address the warnings
from other libs like function, which I think are also making use of
aligned_storage.]

Unfortunately, while the aliasing rules permit one to cast a pointer
to any type to a char* and then go poking around looking at the raw
binary data, the reverse is generally not permitted. Thus, the
following implementation of aligned_storage::address is not correct:

     void* address() { return data_.data_.buf; }

Of course, one could just turn off strict aliasing for gcc, using the
-fno-strict-aliasing option. Doing so would put you in good company;
many large projects (including the Linux kernel, BSD, rtems, Python,
bjam(!), others that I don't recall off the top of my head) turn off
strict aliasing, with such reasons as the following cited

- the strict aliasing rules and their implications are arcane,

- the strict aliasing rules forbid idioms common in low-level system
programming type code,

- the methods for dealing with strict aliasing in such low-level
system code make the code harder to understand,

- the benefits derived from enabling strict aliasing are not worth the
headaches

- tools (including compilers that support strict aliasing) don't (and
perhaps can't) provide good diagnostics about violations. gcc, for
example, specifically documents that all levels for enabled strict
aliasing warnings can produce both false positives and false
negatives.

I'm not in a position to try anything with gcc4.4 right now. If anyone
tries any of my suggestions above, I'd be interested in the
results. Note that to be really thorough, one should carefully inspect
the generated code if the warnings go away.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk