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

[C++] Accessing enumerators from a scoped member enum through operator . triggers an error #113844

Open
TheCalligrapher opened this issue Oct 27, 2024 · 6 comments
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema"

Comments

@TheCalligrapher
Copy link

TheCalligrapher commented Oct 27, 2024

Here's the code sample that demonsrates the problem

struct S
{
  enum T { A };
  enum class U { B };
};

int main()
{
  S s;

  S::A;    // OK
  S::T::A; // OK
  S::U::B; // OK

  s.A;     // OK
  s.T::A;  // OK
  s.U::B;  // Error???
}

All six lines that refer to enumerators from enums declared inside class S are perfectly valid. However, Clang issues an error

error: 'S::U::B' is not a member of class 'S'
   17 |   s.U::B;  // Error???
      |     ~~~^

for last one (i.e. s.U::B). Why?

What makes it especially weird is that Clang has no problems with the qualified name in the s.T::A line, i.e. it does allow one to optionally use a qualified name to access enumerators from a "classic" unscoped enum (using qualified name with such enums is a possibility since C++11). However, for some unknown reason Clang rejects a similar attempt to access an enumerator from a scoped enum.

GCC and MSVC++ have no issues with such code.

@keinflue
Copy link

According to [class.mem.general]/3 only enumerators of unscoped enumerations are members of the class.

[expr.ref]/6.5 establishes the meaning of the member access expression only for member enumerators.

However, [expr.ref]/6 also claims "Otherwise, one of the following rules applies." which doesn't seem to be true here. That looks like a defect to me. The suggested resolution for CWG 2902 would however clarify that this is ill-formed (because U::B is not a member of the class).

@EugeneZelenko EugeneZelenko added clang:frontend Language frontend issues, e.g. anything involving "Sema" and removed new issue labels Oct 28, 2024
@llvmbot
Copy link
Member

llvmbot commented Oct 28, 2024

@llvm/issue-subscribers-clang-frontend

Author: AndreyT (TheCalligrapher)

Here's the code sample that demonsrates the problem
struct S
{
  enum T { A };
  enum class U { B };
};

int main()
{
  S s;

  S::A;    // OK
  S::T::A; // OK
  S::U::B; // OK

  s.A;     // OK
  s.T::A;  // OK
  s.U::B;  // Error???
}

All six lines that refer to enumerators from enums declared inside class S are perfectly valid. However, Clang issues an error

error: 'S::U::B' is not a member of class 'S'
   17 |   s.U::B;  // Error???
      |     ~~~^

for last one (i.e. s.U::B). Why?

What makes it especially weird is that Clang has no problems with the qualified name in the s.T::A line, i.e. it does allow one to optionally use a qualified name to access enumerators from a "classic" unscoped enum (using qualified name with such enums is a possibility since C++11). However, for some unknown reason Clang rejects a similar attempt to access an enumerator from a scoped enum.

GCC and MSVC++ have no issues with such code.

@zygoloid
Copy link
Collaborator

Note that the interpretation of the rules that allows the final example would also allow:

enum class E { a };
class B {} b;
E e = b.E::a;

(indeed that's basically the same thing as far as the language is concerned). [expr.ref]/7.5 doesn't say what happens here, for an E2 that is a non-member enumerator, so at best this is UB. But it seems appropriate to reject, as CWG2902's suggested resolution says.

@TheCalligrapher
Copy link
Author

TheCalligrapher commented Oct 28, 2024

Oh, I see. Thank you for the clarification. [class.mem.general]/3 does indeed make formal sense, even if the behavior might look "illogical" on the surface.

Sorry for the false alarm.

@TheCalligrapher TheCalligrapher changed the title C++: Accessing enumerators from a scoped member enum through operator . triggers an error [C++] Accessing enumerators from a scoped member enum through operator . triggers an error Oct 28, 2024
@TheCalligrapher
Copy link
Author

... However, it is possible to "dump" the enumerators from a scoped enum into the class scope by means of a using enum declaration

struct S
{
  enum class U { B };
  using enum U;
};

With this combination of declarations Clang happily accepts s.B syntax

S s;
s.B; // OK

which is not surprising, since this is basically the example from [enum.udecl].

But if I try referring to that B through a qualified name after the dot operator (as U::B or S::U::B) Clang issues an error

S s;
s.U::B;    // error: 'S::U::B' is not a member of class 'S'
s.S::U::B; // error: 'S::U::B' is not a member of class 'S'

Of course, this is a weird code, but still... are both of these really supposed to be erroneous? (GCC appears to accept all these variants.)

@keinflue
Copy link

keinflue commented Nov 4, 2024

I do not see why the earlier reasoning shouldn't apply to s.B; as well. B is still not a member of the class and so this should also be ill-formed (or maybe technically UB as @zygoloid mentioned).

This seems to be an editorial issue in the standard. I have reported it here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema"
Projects
None yet
Development

No branches or pull requests

5 participants