[clang] 3d20806 - [clang][DR2621] using enum NAME lookup fix

Nathan Sidwell via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 28 08:50:40 PDT 2022


Author: Nathan Sidwell
Date: 2022-09-28T08:50:27-07:00
New Revision: 3d2080683f1dc37010fb56cf7d0e1632cda00f15

URL: https://github.com/llvm/llvm-project/commit/3d2080683f1dc37010fb56cf7d0e1632cda00f15
DIFF: https://github.com/llvm/llvm-project/commit/3d2080683f1dc37010fb56cf7d0e1632cda00f15.diff

LOG: [clang][DR2621] using enum NAME lookup fix

Although using-enum's grammar is 'using elaborated-enum-specifier',
the lookup for the enum is ordinary lookup (and not the tagged-type
lookup that normally occurs wth an tagged-type specifier).  Thus (a)
we can find typedefs and (b) do not find enum tags hidden by a non-tag
name (the struct stat thing).

This reimplements that part of using-enum handling, to address DR2621,
where clang's behaviour does not match std intent (and other
compilers).

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D134283

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Sema/Sema.h
    clang/lib/Parse/ParseDeclCXX.cpp
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/test/CXX/drs/dr26xx.cpp
    clang/test/CodeCompletion/using-enum.cpp
    clang/test/Parser/cxx20-using-enum.cpp
    clang/test/SemaCXX/cxx20-using-enum.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0dd3b54aa4753..0bae33cdf4234 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -356,6 +356,8 @@ C++20 Feature Support
   the time of checking, which should now allow the libstdc++ ranges implementation
   to work for at least trivial examples.  This fixes
   `Issue 44178 <https://github.com/llvm/llvm-project/issues/44178>`_.
+- Clang implements DR2621, correcting a defect in ``using enum`` handling.  The
+  name is found via ordinary lookup so typedefs are found.
 
 C++2b Feature Support
 ^^^^^^^^^^^^^^^^^^^^^

diff  --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 2abb5f09292ec..ee401200ddfa5 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -607,6 +607,9 @@ def warn_cxx17_compat_using_enum_declaration : Warning<
 def ext_using_enum_declaration : ExtWarn<
   "using enum declaration is a C++20 extension">,
   InGroup<CXX20>;
+def err_using_enum_expect_identifier : Error<
+  "using enum %select{requires an enum or typedef name|"
+  "does not permit an elaborated enum specifier}0">;
 def err_constructor_bad_name : Error<
   "missing return type for function %0; did you mean the constructor name %1?">;
 def err_destructor_tilde_identifier : Error<

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 93c598b2eba48..2b544928ab763 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -562,6 +562,8 @@ def warn_cxx17_compat_using_decl_class_member_enumerator : Warning<
   "with C++ standards before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore;
 def err_using_enum_is_dependent : Error<
   "using-enum cannot name a dependent type">;
+def err_using_enum_not_enum : Error<
+  "%0 is not an enumerated type">;
 def err_ambiguous_inherited_constructor : Error<
   "constructor of %0 inherited from multiple base class subobjects">;
 def note_ambiguous_inherited_constructor_using : Note<

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index b6fd8174e1d87..72995befeadf2 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6125,7 +6125,9 @@ class Sema final {
                               const ParsedAttributesView &AttrList);
   Decl *ActOnUsingEnumDeclaration(Scope *CurScope, AccessSpecifier AS,
                                   SourceLocation UsingLoc,
-                                  SourceLocation EnumLoc, const DeclSpec &);
+                                  SourceLocation EnumLoc,
+                                  SourceLocation IdentLoc, IdentifierInfo &II,
+                                  CXXScopeSpec *SS = nullptr);
   Decl *ActOnAliasDeclaration(Scope *CurScope, AccessSpecifier AS,
                               MultiTemplateParamsArg TemplateParams,
                               SourceLocation UsingLoc, UnqualifiedId &Name,

diff  --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index d0bf89799c258..52e627a375442 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -678,6 +678,8 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context,
 ///
 ///     using-enum-declaration: [C++20, dcl.enum]
 ///       'using' elaborated-enum-specifier ;
+///       The terminal name of the elaborated-enum-specifier undergoes
+///       ordinary lookup
 ///
 ///     elaborated-enum-specifier:
 ///       'enum' nested-name-specifier[opt] identifier
@@ -697,21 +699,47 @@ Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
 
     DiagnoseCXX11AttributeExtension(PrefixAttrs);
 
-    DeclSpec DS(AttrFactory);
-    ParseEnumSpecifier(UELoc, DS, TemplateInfo, AS,
-                       // DSC_trailing has the semantics we desire
-                       DeclSpecContext::DSC_trailing);
-
     if (TemplateInfo.Kind) {
       SourceRange R = TemplateInfo.getSourceRange();
       Diag(UsingLoc, diag::err_templated_using_directive_declaration)
           << 1 /* declaration */ << R << FixItHint::CreateRemoval(R);
+      SkipUntil(tok::semi);
+      return nullptr;
+    }
+    CXXScopeSpec SS;
+    if (ParseOptionalCXXScopeSpecifier(SS, /*ParsedType=*/nullptr,
+                                       /*ObectHasErrors=*/false,
+                                       /*EnteringConttext=*/false,
+                                       /*MayBePseudoDestructor=*/nullptr,
+                                       /*IsTypename=*/false,
+                                       /*IdentifierInfo=*/nullptr,
+                                       /*OnlyNamespace=*/false,
+                                       /*InUsingDeclaration=*/true)) {
+      SkipUntil(tok::semi);
+      return nullptr;
+    }
 
+    if (Tok.is(tok::code_completion)) {
+      cutOffParsing();
+      Actions.CodeCompleteUsing(getCurScope());
+      return nullptr;
+    }
+
+    if (!Tok.is(tok::identifier)) {
+      Diag(Tok.getLocation(), diag::err_using_enum_expect_identifier)
+          << Tok.is(tok::kw_enum);
+      SkipUntil(tok::semi);
+      return nullptr;
+    }
+    IdentifierInfo *IdentInfo = Tok.getIdentifierInfo();
+    SourceLocation IdentLoc = ConsumeToken();
+    Decl *UED = Actions.ActOnUsingEnumDeclaration(
+        getCurScope(), AS, UsingLoc, UELoc, IdentLoc, *IdentInfo, &SS);
+    if (!UED) {
+      SkipUntil(tok::semi);
       return nullptr;
     }
 
-    Decl *UED = Actions.ActOnUsingEnumDeclaration(getCurScope(), AS, UsingLoc,
-                                                  UELoc, DS);
     DeclEnd = Tok.getLocation();
     if (ExpectAndConsume(tok::semi, diag::err_expected_after,
                          "using-enum declaration"))

diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 2516d18b31c25..b72bded8397ad 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -11851,30 +11851,30 @@ Decl *Sema::ActOnUsingDeclaration(Scope *S, AccessSpecifier AS,
 Decl *Sema::ActOnUsingEnumDeclaration(Scope *S, AccessSpecifier AS,
                                       SourceLocation UsingLoc,
                                       SourceLocation EnumLoc,
-                                      const DeclSpec &DS) {
-  switch (DS.getTypeSpecType()) {
-  case DeclSpec::TST_error:
-    // This will already have been diagnosed
+                                      SourceLocation IdentLoc,
+                                      IdentifierInfo &II, CXXScopeSpec *SS) {
+  assert(!SS->isInvalid() && "ScopeSpec is invalid");
+  ParsedType TypeRep = getTypeName(II, IdentLoc, S, SS);
+  if (!TypeRep) {
+    Diag(IdentLoc, SS && isDependentScopeSpecifier(*SS)
+                       ? diag::err_using_enum_is_dependent
+                       : diag::err_unknown_typename)
+        << II.getName()
+        << SourceRange(SS ? SS->getBeginLoc() : IdentLoc, IdentLoc);
     return nullptr;
+  }
 
-  case DeclSpec::TST_enum:
-    break;
-
-  case DeclSpec::TST_typename:
-    Diag(DS.getTypeSpecTypeLoc(), diag::err_using_enum_is_dependent);
+  auto *Enum = dyn_cast_if_present<EnumDecl>(TypeRep.get()->getAsTagDecl());
+  if (!Enum) {
+    Diag(IdentLoc, diag::err_using_enum_not_enum) << TypeRep.get();
     return nullptr;
-
-  default:
-    llvm_unreachable("unexpected DeclSpec type");
   }
 
-  // As with enum-decls, we ignore attributes for now.
-  auto *Enum = cast<EnumDecl>(DS.getRepAsDecl());
   if (auto *Def = Enum->getDefinition())
     Enum = Def;
 
-  auto *UD = BuildUsingEnumDeclaration(S, AS, UsingLoc, EnumLoc,
-                                       DS.getTypeSpecTypeNameLoc(), Enum);
+  auto *UD =
+      BuildUsingEnumDeclaration(S, AS, UsingLoc, EnumLoc, IdentLoc, Enum);
   if (UD)
     PushOnScopeChains(UD, S, /*AddToContext*/ false);
 

diff  --git a/clang/test/CXX/drs/dr26xx.cpp b/clang/test/CXX/drs/dr26xx.cpp
index 1178cefa5bcdf..37d971a3ae1f2 100644
--- a/clang/test/CXX/drs/dr26xx.cpp
+++ b/clang/test/CXX/drs/dr26xx.cpp
@@ -1,5 +1,19 @@
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify
 
+namespace dr2621 { // dr2621: yes
+enum class E { a };
+namespace One {
+using E_t = E;
+using enum E_t; // typedef ok
+auto v = a;
+}
+namespace Two {
+using dr2621::E;
+int E; // we see this
+using enum E; // expected-error {{unknown type name E}}
+}
+}
+
 namespace dr2628 { // dr2628: yes
 
 template <bool A = false, bool B = false>

diff  --git a/clang/test/CodeCompletion/using-enum.cpp b/clang/test/CodeCompletion/using-enum.cpp
index 1619e36c18f61..dfe297a2c0681 100644
--- a/clang/test/CodeCompletion/using-enum.cpp
+++ b/clang/test/CodeCompletion/using-enum.cpp
@@ -2,6 +2,6 @@ enum class AAA { X, Y, Z };
 
 namespace N2 {
   using enum AAA;
-  // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:4:14 %s | FileCheck -check-prefix=CHECK-CC1 %s
+  // RUN: %clang_cc1 -std=c++20 -fsyntax-only -code-completion-at=%s:4:14 %s | FileCheck -check-prefix=CHECK-CC1 %s
   // CHECK-CC1: COMPLETION: AAA
 };

diff  --git a/clang/test/Parser/cxx20-using-enum.cpp b/clang/test/Parser/cxx20-using-enum.cpp
index d6744d3a91290..78b9d00848c78 100644
--- a/clang/test/Parser/cxx20-using-enum.cpp
+++ b/clang/test/Parser/cxx20-using-enum.cpp
@@ -4,9 +4,9 @@ namespace GH57347 {
 namespace A {}
 
 void f() {
-  using enum A::+; // expected-error {{expected identifier}}
-  using enum; // expected-error {{expected identifier or '{'}}
-  using enum class; // expected-error {{expected identifier or '{'}}
-  using enum : blah; // expected-error {{unknown type name 'blah'}} expected-error {{unnamed enumeration must be a definition}}
+  using enum A::+; // expected-error {{using enum requires an enum or typedef name}}
+  using enum; // expected-error {{using enum requires an enum or typedef name}}
+  using enum class; // expected-error {{using enum requires an enum or typedef name}}
+  using enum enum q; // expected-error {{using enum does not permit an elaborated enum specifier}}
 }
 }

diff  --git a/clang/test/SemaCXX/cxx20-using-enum.cpp b/clang/test/SemaCXX/cxx20-using-enum.cpp
index daf35ebfefbbc..91dd0bac992f8 100644
--- a/clang/test/SemaCXX/cxx20-using-enum.cpp
+++ b/clang/test/SemaCXX/cxx20-using-enum.cpp
@@ -8,7 +8,7 @@ namespace Bob {
 enum A { a, // expected-note{{declared here}}
          b,
          c };
-class C; // expected-note{{previous use}}
+class C;
 enum class D : int;
 enum class D { d,
                e,
@@ -20,11 +20,11 @@ using enum Bob::A;
 #if __cplusplus < 202002
 // expected-warning at -2{{is a C++20 extension}}
 #endif
-using enum Bob::B; // expected-error{{no enum named 'B'}}
+using enum Bob::B; // expected-error{{unknown type name B}}
 #if __cplusplus < 202002
 // expected-warning at -2{{is a C++20 extension}}
 #endif
-using enum Bob::C; // expected-error{{tag type that does not match}}
+using enum Bob::C; // expected-error{{'Bob::C' is not an enumerated type}}
 #if __cplusplus < 202002
 // expected-warning at -2{{is a C++20 extension}}
 #endif
@@ -38,6 +38,16 @@ using enum Bob::D;
 #if __cplusplus < 202002
 // expected-warning at -2{{is a C++20 extension}}
 #endif
+
+void DR2621() {
+  using A_t = Bob::A;
+  using enum A_t;
+#if __cplusplus < 202002
+// expected-warning at -2{{is a C++20 extension}}
+#endif
+  A_t x = a;
+}
+
 } // namespace One
 
 namespace Two {


        


More information about the cfe-commits mailing list