Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CWG2932 [dcl.enum] Value range of empty enumeration without fixed underlying type. #604

Open
keinflue opened this issue Aug 31, 2024 · 15 comments

Comments

@keinflue
Copy link

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.

@t3nsor
Copy link

t3nsor commented Aug 31, 2024

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
Copy link
Member

CWG2932

@jensmaurer jensmaurer changed the title [dcl.enum] Value range of empty enumeration without fixed underlying type. CWG2932 [dcl.enum] Value range of empty enumeration without fixed underlying type. Aug 31, 2024
@keinflue
Copy link
Author

@jensmaurer The issue mistakenly refers to Section 9.6 [dcl.struct.bind], when it should be 9.7.1 [dcl.enum].

@jensmaurer
Copy link
Member

Fixed.

@t3nsor
Copy link

t3nsor commented Aug 31, 2024

"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.

@keinflue
Copy link
Author

@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.

@t3nsor
Copy link

t3nsor commented Aug 31, 2024

"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.

@dimitry-ishenko
Copy link

dimitry-ishenko commented Sep 1, 2024

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)?

@t3nsor
Copy link

t3nsor commented Sep 1, 2024

If it were signed, the range would be [-2, 1].

@dimitry-ishenko
Copy link

@t3nsor sorry, yes. fixed.

@t3nsor
Copy link

t3nsor commented Sep 1, 2024

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.

@dimitry-ishenko
Copy link

dimitry-ishenko commented Sep 1, 2024

@t3nsor oh, you are right. Unsigned enum will always be implicitly chosen, since it can hold more positive values. Disregard my example, then.

@keinflue
Copy link
Author

keinflue commented Sep 2, 2024

@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.

@jensmaurer
Copy link
Member

@t3nsor , seems like the desired outcome is M=1 with an unsigned integer type for the empty enum example, right?

@t3nsor
Copy link

t3nsor commented Sep 2, 2024

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants