CWG2932 [dcl.enum] Value range of empty enumeration without fixed underlying type.
Opened this issue · 15 comments
Full name of submitter (unless configured in github; will be published with the issue): Benjamin Sch.
Reference (section label): [dcl.enum]
Issue description:
enum E { };
constexpr auto x = static_cast<E>(-1);
[dcl.enum]/8 specifies the range of E
to be that of an (hypothetical) integer type with minimal width able to represent all enumerator values. For an empty enumeration [dcl.enum]/8 also specifies that the rule is applied as if a single enumerator with value 0
was present.
The minimal width here is 1
, but both a signed and unsigned integer type with width 1
are able to represent the value 0
. If it is the unsigned one, then static_cast<E>(-1)
is UB and the declaration of x
ill-formed. If it is the signed one, then -1
can be represented and static_cast<E>(-1)
is not UB and the declaration of x
well-formed.
Which of the two determines the value range of E
?
If it is intentionally unspecified, then the static_cast
with unspecified UB should be explicitly disallowed as core constant expression.
The range is intended to be just {0}. This was clear before P1236. After P1236, the intent is that you think of it as an unsigned type with width 0, which of course is narrower than a signed type with width 1. But the wording doesn't make this super clear, because unsigned types with width 0 are impossible (they wouldn't have a signed counterpart). So I think we need some wording fix here.
@jensmaurer The issue mistakenly refers to Section 9.6 [dcl.struct.bind], when it should be 9.7.1 [dcl.enum].
Fixed.
"The width of the smallest bit-field large enough to hold all the values of the enumeration type is M" - but if M is 0, this is incorrect because a bit-field of width 0 can't have any value at all.
@t3nsor As far as I can tell (and correct me please) that sentence seems to be relevant to figure out whether a bit-field is large enough for the purpose of https://eel.is/c++draft/class#bit-4.sentence-3. Since a zero-width bit-field must be unnamed and thus can't be assigned a value, it doesn't matter whether M is 0 or 1 for that purpose.
"The width of the smallest bit-field large enough to hold all the values of the enumeration type is M."
This sentence doesn't just say that a bit-field must have width at least M to be able to hold all values of the enumeration. It says
The width of the smallest bit-field large enough to hold all values of the enumeration
is (i.e., is identical to)
M.
But the former quantity is 1, so if M is 0 then the statement is false.
Looking at the timing, this issue was possibly inspired by my SO question here: https://stackoverflow.com/q/78933393/4358570
While the proposed solution addresses the example with an empty enum, consider the following:
enum foo { bar, baz, qux };
constexpr auto barf = static_cast<foo>(-1);
Size M of the bit-field would be 2 in this case. But, what about the range of values?
Should it be [0..3] (ie, unsigned int :2
) or [-2..1] (ie, int :2
)?
If it were signed, the range would be [-2, 1].
@t3nsor sorry, yes. fixed.
So what's the issue? It has to be the unsigned one, because the signed one wouldn't be able to hold the value 2.
@t3nsor oh, you are right. Unsigned enum will always be implicitly chosen, since it can hold more positive values. Disregard my example, then.
@jensmaurer If that is ok, I'd like to request the issue's wording to be extended to reflect the ambiguity I originally saw:
It is unclear whether the hypothetical integer type for E is a signed integer type of width 1, an unsigned integer type of width 1 or an unsigned integer type of width 0, which does not have a signed counterpart and thus does not exist. The former choice makes the example well-formed, the latter
oneones ill-formed. Before P1236, the specification was clear.
@t3nsor , seems like the desired outcome is M=1 with an unsigned integer type for the empty enum example, right?
@t3nsor , seems like the desired outcome is M=1 with an unsigned integer type for the empty enum example, right?
I think the desired outcome is M = 0 (so the only possible value it can hold is 0), but the minimum bit-field width should be 1, because a bit-field of width 0 can't hold any value at all.