This is a bug in libstdc++, from the cfe-dev
mailing list thread with title -fsanitize=undefined and shared libraries says:
This is a bug in libstdc++. You will be able to work around it with a
sanitizer blacklist file, once Will's patch for that lands, but for
now, filtering them out manually is likely to be your best option.
Here's a patch to fix it; I'll be looking into pushing this to
libstdc++ upstream in the next few days. [...]
As I noted to dyp in the comments it is not uncommon to see systems where clang
uses libstdc++
as opposed to libc++
and if we test this on Coliru explicitly using libstdc++ via -stdlib=libstdc++
we indeed can reproduce the issue.
The following libstdc++
bug report: bad enum values computed by operator~ in ios_base.h covers this issue and says:
The overloaded operator~s defined for the enumerations in ios_base.h
have the following form:
Enum operator~(Enum e) { return Enum(~static_cast<int>(e)); }
The ~ creates values outside the range of values of the enumeration
type, so the cast back to the Enum type has an unspecified value (see
[expr.static.cast]p10), and in practice it produces an Enum value
outside the range of representable values for the Enum type, so
behavior is undefined.
For reference [expr.static.cast]p10 says:
A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is
unchanged if the original value is within the range of the enumeration values (7.2). Otherwise, the resulting
value is unspecified (and might not be in that range). A value of floating-point type can also be converted
to an enumeration type. The resulting value is the same as converting the original value to the underlying
type of the enumeration (4.9), and subsequently to the enumeration type.
and as hvd says this is formally unspecified behavior but Richard points out that in practice is ends up being undefined behavior.
T.C. points out this was changed from unspecified to undefined behavior by DR 1766: Values outside the range of the values of an enumeration:
Although issue 1094 clarified that the value of an expression of enumeration type might not be within the range of the values of the enumeration after a conversion to the enumeration type (see 5.2.9 [expr.static.cast] paragraph 10), the result is simply an unspecified value. This should probably be strengthened to produce undefined behavior, in light of the fact that undefined behavior makes an expression non-constant. See also 9.6 [class.bit] paragraph 4.
The new wording appears in the draft standard in N4431.
os << std::hex;
seems also to reproduce the issue. – Bein