Skip to content

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

Description

@keinflue

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.

Activity

  1. t3nsor commented on Aug 31, 2024

    @t3nsor

    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.

  2. jensmaurer commented on Aug 31, 2024

    @jensmaurer
    Member
  3. 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.[/+] on Aug 31, 2024
  4. keinflue commented on Aug 31, 2024

    @keinflue
    Author

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

  5. jensmaurer commented on Aug 31, 2024

    @jensmaurer
    Member

    Fixed.

  6. t3nsor commented on Aug 31, 2024

    @t3nsor

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

  7. keinflue commented on Aug 31, 2024

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

  8. t3nsor commented on Aug 31, 2024

    @t3nsor

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

  9. dimitry-ishenko commented on Sep 1, 2024

    @dimitry-ishenko

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

  10. t3nsor commented on Sep 1, 2024

    @t3nsor

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

  11. dimitry-ishenko commented on Sep 1, 2024

    @dimitry-ishenko

    @t3nsor sorry, yes. fixed.

  12. t3nsor commented on Sep 1, 2024

    @t3nsor

    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.

  13. dimitry-ishenko commented on Sep 1, 2024

    @dimitry-ishenko

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

  14. keinflue commented on Sep 2, 2024

    @keinflue
    Author

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

  15. jensmaurer commented on Sep 2, 2024

    @jensmaurer
    Member

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

  16. t3nsor commented on Sep 2, 2024

    @t3nsor

    @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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions