Which enum values are undefined behavior in C++14, and why?

  • A+
Category:Languages

A footnote in the standard implies that any enum expression value is defined behavior; why does Clang's undefined behavior sanitizer flag out-of-range values?

Consider the following program:

enum A {B = 3, C = 7};  int main() {   A d = static_cast<A>(8);   return d + B; } 

The output under the undefined behavior sanitizer is:

$ clang++-5.0 -fsanitize=undefined -ggdb3 enum.cc && ./a.out  enum.cc:5:10: runtime error: load of value 8, which is not a valid value for type 'A' 

Note that the error is not on the static_cast, but on the addition. This is also true is an A is created (but not initialized) and then an int with value 8 is memcpyied into the A - the ubsan error is on the addition, not the initial load.

IIUC, ubsan in newer clangs does flag an error on the static_cast in C++17 mode. I don't know if that mode also finds an error in the memcpy. In any case, this question is focused on C++14.

The reported error comports with the following parts of the standard:

dcl.enum:

For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type. Otherwise, the values of the enumeration are the values representable by a hypothetical integer types with minimal range exponent M such that all enumerators can be represented. The width of the smallest bit-field large enough to hold all the values of the enumeration type is M. It is possible to define an enumeration that has values not defined by any of its enumerators. If the enumerator-list is empty, the values of the enumeration are as if the enumeration had a single enumerator with value 0.100

So the values of the enumeration A are 0 through 7, inclusive, and the "range exponent" M is 3. Evaluating an expression of type A with value 8 is undefined behavior according to expr.pre:

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.

But there is one hiccup: that footnote from dcl.enum reads:

This set of values is used to define promotion and conversion semantics for the enumeration type. It does not preclude an expression of enumeration type from having a value that falls outside this range. [emphasis mine]

Question: Why is an expression with value 8 and type A undefined behavior if "[dcl.enum] does not preclude an expression of enumeration type from having a value that falls outside this range"?

 


Clang flags the use of static_cast on a value that is out of range. The behavior is undefined, if the integral value is not within the range of the enumeration.

C++ standard 5.2.9 Static cast [expr.static.cast] paragraph 7

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 enumeration value is unspecified / undefined (since C++17).

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: